Home Reference Source

src/utils/attr-list.ts

  1. const DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/; // eslint-disable-line no-useless-escape
  2. const ATTR_LIST_REGEX = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g; // eslint-disable-line no-useless-escape
  3.  
  4. // adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js
  5. export class AttrList {
  6. [key: string]: any;
  7.  
  8. constructor(attrs: string | Record<string, any>) {
  9. if (typeof attrs === 'string') {
  10. attrs = AttrList.parseAttrList(attrs);
  11. }
  12.  
  13. for (const attr in attrs) {
  14. if (attrs.hasOwnProperty(attr)) {
  15. this[attr] = attrs[attr];
  16. }
  17. }
  18. }
  19.  
  20. decimalInteger(attrName: string): number {
  21. const intValue = parseInt(this[attrName], 10);
  22. if (intValue > Number.MAX_SAFE_INTEGER) {
  23. return Infinity;
  24. }
  25.  
  26. return intValue;
  27. }
  28.  
  29. hexadecimalInteger(attrName: string) {
  30. if (this[attrName]) {
  31. let stringValue = (this[attrName] || '0x').slice(2);
  32. stringValue = (stringValue.length & 1 ? '0' : '') + stringValue;
  33.  
  34. const value = new Uint8Array(stringValue.length / 2);
  35. for (let i = 0; i < stringValue.length / 2; i++) {
  36. value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16);
  37. }
  38.  
  39. return value;
  40. } else {
  41. return null;
  42. }
  43. }
  44.  
  45. hexadecimalIntegerAsNumber(attrName: string): number {
  46. const intValue = parseInt(this[attrName], 16);
  47. if (intValue > Number.MAX_SAFE_INTEGER) {
  48. return Infinity;
  49. }
  50.  
  51. return intValue;
  52. }
  53.  
  54. decimalFloatingPoint(attrName: string): number {
  55. return parseFloat(this[attrName]);
  56. }
  57.  
  58. optionalFloat(attrName: string, defaultValue: number): number {
  59. const value = this[attrName];
  60. return value ? parseFloat(value) : defaultValue;
  61. }
  62.  
  63. enumeratedString(attrName: string): string | undefined {
  64. return this[attrName];
  65. }
  66.  
  67. bool(attrName: string): boolean {
  68. return this[attrName] === 'YES';
  69. }
  70.  
  71. decimalResolution(
  72. attrName: string
  73. ):
  74. | {
  75. width: number;
  76. height: number;
  77. }
  78. | undefined {
  79. const res = DECIMAL_RESOLUTION_REGEX.exec(this[attrName]);
  80. if (res === null) {
  81. return undefined;
  82. }
  83.  
  84. return {
  85. width: parseInt(res[1], 10),
  86. height: parseInt(res[2], 10),
  87. };
  88. }
  89.  
  90. static parseAttrList(input: string): Record<string, any> {
  91. let match;
  92. const attrs = {};
  93. const quote = '"';
  94. ATTR_LIST_REGEX.lastIndex = 0;
  95. while ((match = ATTR_LIST_REGEX.exec(input)) !== null) {
  96. let value = match[2];
  97.  
  98. if (
  99. value.indexOf(quote) === 0 &&
  100. value.lastIndexOf(quote) === value.length - 1
  101. ) {
  102. value = value.slice(1, -1);
  103. }
  104.  
  105. attrs[match[1]] = value;
  106. }
  107. return attrs;
  108. }
  109. }