hls-streamer 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +189 -190
  2. package/dist/Interfaces/HlsStreamer.d.ts +20 -0
  3. package/dist/Interfaces/HlsStreamer.d.ts.map +1 -1
  4. package/dist/Libs/FileLib.d.ts +22 -1
  5. package/dist/Libs/FileLib.d.ts.map +1 -1
  6. package/dist/Libs/FileLib.js +273 -108
  7. package/dist/Libs/FileLib.js.map +1 -1
  8. package/dist/Libs/FormatDetector.d.ts +8 -0
  9. package/dist/Libs/FormatDetector.d.ts.map +1 -0
  10. package/dist/Libs/FormatDetector.js +63 -0
  11. package/dist/Libs/FormatDetector.js.map +1 -0
  12. package/dist/Parsers/AacParser.d.ts +15 -0
  13. package/dist/Parsers/AacParser.d.ts.map +1 -0
  14. package/dist/Parsers/AacParser.js +246 -0
  15. package/dist/Parsers/AacParser.js.map +1 -0
  16. package/dist/Parsers/FlacParser.d.ts +13 -0
  17. package/dist/Parsers/FlacParser.d.ts.map +1 -0
  18. package/dist/Parsers/FlacParser.js +200 -0
  19. package/dist/Parsers/FlacParser.js.map +1 -0
  20. package/dist/Parsers/IAudioParser.d.ts +31 -0
  21. package/dist/Parsers/IAudioParser.d.ts.map +1 -0
  22. package/dist/Parsers/IAudioParser.js +2 -0
  23. package/dist/Parsers/IAudioParser.js.map +1 -0
  24. package/dist/Parsers/Mp3Parser.d.ts +19 -0
  25. package/dist/Parsers/Mp3Parser.d.ts.map +1 -0
  26. package/dist/Parsers/Mp3Parser.js +270 -0
  27. package/dist/Parsers/Mp3Parser.js.map +1 -0
  28. package/dist/Parsers/OggParser.d.ts +11 -0
  29. package/dist/Parsers/OggParser.d.ts.map +1 -0
  30. package/dist/Parsers/OggParser.js +160 -0
  31. package/dist/Parsers/OggParser.js.map +1 -0
  32. package/dist/Parsers/ParserFactory.d.ts +10 -0
  33. package/dist/Parsers/ParserFactory.d.ts.map +1 -0
  34. package/dist/Parsers/ParserFactory.js +43 -0
  35. package/dist/Parsers/ParserFactory.js.map +1 -0
  36. package/dist/Parsers/WavParser.d.ts +9 -0
  37. package/dist/Parsers/WavParser.d.ts.map +1 -0
  38. package/dist/Parsers/WavParser.js +135 -0
  39. package/dist/Parsers/WavParser.js.map +1 -0
  40. package/dist/cjs/Interfaces/HlsStreamer.d.ts +20 -0
  41. package/dist/cjs/Interfaces/HlsStreamer.d.ts.map +1 -1
  42. package/dist/cjs/Libs/FileLib.d.ts +22 -1
  43. package/dist/cjs/Libs/FileLib.d.ts.map +1 -1
  44. package/dist/cjs/Libs/FileLib.js +276 -108
  45. package/dist/cjs/Libs/FileLib.js.map +1 -1
  46. package/dist/cjs/Libs/FormatDetector.d.ts +8 -0
  47. package/dist/cjs/Libs/FormatDetector.d.ts.map +1 -0
  48. package/dist/cjs/Libs/FormatDetector.js +67 -0
  49. package/dist/cjs/Libs/FormatDetector.js.map +1 -0
  50. package/dist/cjs/Parsers/AacParser.d.ts +15 -0
  51. package/dist/cjs/Parsers/AacParser.d.ts.map +1 -0
  52. package/dist/cjs/Parsers/AacParser.js +250 -0
  53. package/dist/cjs/Parsers/AacParser.js.map +1 -0
  54. package/dist/cjs/Parsers/FlacParser.d.ts +13 -0
  55. package/dist/cjs/Parsers/FlacParser.d.ts.map +1 -0
  56. package/dist/cjs/Parsers/FlacParser.js +204 -0
  57. package/dist/cjs/Parsers/FlacParser.js.map +1 -0
  58. package/dist/cjs/Parsers/IAudioParser.d.ts +31 -0
  59. package/dist/cjs/Parsers/IAudioParser.d.ts.map +1 -0
  60. package/dist/cjs/Parsers/IAudioParser.js +3 -0
  61. package/dist/cjs/Parsers/IAudioParser.js.map +1 -0
  62. package/dist/cjs/Parsers/Mp3Parser.d.ts +19 -0
  63. package/dist/cjs/Parsers/Mp3Parser.d.ts.map +1 -0
  64. package/dist/cjs/Parsers/Mp3Parser.js +274 -0
  65. package/dist/cjs/Parsers/Mp3Parser.js.map +1 -0
  66. package/dist/cjs/Parsers/OggParser.d.ts +11 -0
  67. package/dist/cjs/Parsers/OggParser.d.ts.map +1 -0
  68. package/dist/cjs/Parsers/OggParser.js +164 -0
  69. package/dist/cjs/Parsers/OggParser.js.map +1 -0
  70. package/dist/cjs/Parsers/ParserFactory.d.ts +10 -0
  71. package/dist/cjs/Parsers/ParserFactory.d.ts.map +1 -0
  72. package/dist/cjs/Parsers/ParserFactory.js +47 -0
  73. package/dist/cjs/Parsers/ParserFactory.js.map +1 -0
  74. package/dist/cjs/Parsers/WavParser.d.ts +9 -0
  75. package/dist/cjs/Parsers/WavParser.d.ts.map +1 -0
  76. package/dist/cjs/Parsers/WavParser.js +139 -0
  77. package/dist/cjs/Parsers/WavParser.js.map +1 -0
  78. package/dist/cjs/errors/HlsStreamerErrors.d.ts +3 -0
  79. package/dist/cjs/errors/HlsStreamerErrors.d.ts.map +1 -1
  80. package/dist/cjs/errors/HlsStreamerErrors.js +8 -1
  81. package/dist/cjs/errors/HlsStreamerErrors.js.map +1 -1
  82. package/dist/cjs/index.d.ts +6 -7
  83. package/dist/cjs/index.d.ts.map +1 -1
  84. package/dist/cjs/index.js +161 -138
  85. package/dist/cjs/index.js.map +1 -1
  86. package/dist/errors/HlsStreamerErrors.d.ts +3 -0
  87. package/dist/errors/HlsStreamerErrors.d.ts.map +1 -1
  88. package/dist/errors/HlsStreamerErrors.js +6 -0
  89. package/dist/errors/HlsStreamerErrors.js.map +1 -1
  90. package/dist/index.d.ts +6 -7
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +162 -139
  93. package/dist/index.js.map +1 -1
  94. package/package.json +10 -3
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WavParser = void 0;
4
+ class WavParser {
5
+ getFormat() {
6
+ return 'wav';
7
+ }
8
+ canParse(buffer) {
9
+ return buffer.length >= 12 &&
10
+ buffer.toString('ascii', 0, 4) === 'RIFF' &&
11
+ buffer.toString('ascii', 8, 12) === 'WAVE';
12
+ }
13
+ analyze(buffer, opts = {}) {
14
+ const warnings = [];
15
+ const size = opts.fileSize ?? buffer.length;
16
+ if (buffer.length < 44) {
17
+ return {
18
+ format: 'wav',
19
+ size,
20
+ duration: 0,
21
+ audioDataSize: 0,
22
+ frames: [],
23
+ warnings: ['File too small to be valid WAV']
24
+ };
25
+ }
26
+ if (!this.canParse(buffer)) {
27
+ return {
28
+ format: 'wav',
29
+ size,
30
+ duration: 0,
31
+ audioDataSize: 0,
32
+ frames: [],
33
+ warnings: ['Not a valid WAV file']
34
+ };
35
+ }
36
+ const riffSize = buffer.readUInt32LE(4);
37
+ let offset = 12;
38
+ let fmtData = null;
39
+ let dataOffset = -1;
40
+ let dataSize = 0;
41
+ while (offset + 8 <= buffer.length) {
42
+ const chunkId = buffer.toString('ascii', offset, offset + 4);
43
+ const chunkSize = buffer.readUInt32LE(offset + 4);
44
+ if (chunkId === 'fmt ') {
45
+ if (chunkSize < 16) {
46
+ warnings.push('Invalid fmt chunk size');
47
+ break;
48
+ }
49
+ fmtData = {
50
+ audioFormat: buffer.readUInt16LE(offset + 8),
51
+ channels: buffer.readUInt16LE(offset + 10),
52
+ sampleRate: buffer.readUInt32LE(offset + 12),
53
+ byteRate: buffer.readUInt32LE(offset + 16),
54
+ blockAlign: buffer.readUInt16LE(offset + 20),
55
+ bitsPerSample: buffer.readUInt16LE(offset + 22)
56
+ };
57
+ }
58
+ else if (chunkId === 'data') {
59
+ dataOffset = offset + 8;
60
+ dataSize = chunkSize;
61
+ }
62
+ offset += 8 + chunkSize;
63
+ if (chunkSize % 2 !== 0) {
64
+ offset++;
65
+ }
66
+ }
67
+ if (!fmtData) {
68
+ return {
69
+ format: 'wav',
70
+ size,
71
+ duration: 0,
72
+ audioDataSize: 0,
73
+ frames: [],
74
+ warnings: ['Missing fmt chunk']
75
+ };
76
+ }
77
+ if (dataOffset === -1) {
78
+ return {
79
+ format: 'wav',
80
+ size,
81
+ duration: 0,
82
+ audioDataSize: 0,
83
+ frames: [],
84
+ warnings: ['Missing data chunk']
85
+ };
86
+ }
87
+ if (fmtData.audioFormat !== 1) {
88
+ warnings.push(`Unsupported audio format: ${fmtData.audioFormat} (only PCM supported)`);
89
+ }
90
+ const bytesPerSample = fmtData.bitsPerSample / 8;
91
+ const totalSamples = dataSize / (bytesPerSample * fmtData.channels);
92
+ const duration = fmtData.sampleRate > 0 ? totalSamples / fmtData.sampleRate : 0;
93
+ const frames = [];
94
+ const samplesPerFrame = fmtData.sampleRate;
95
+ const bytesPerFrame = samplesPerFrame * bytesPerSample * fmtData.channels;
96
+ let frameOffset = dataOffset;
97
+ let frameIndex = 0;
98
+ let remainingBytes = dataSize;
99
+ while (remainingBytes > 0) {
100
+ const frameBytes = Math.min(bytesPerFrame, remainingBytes);
101
+ const frameSamples = frameBytes / (bytesPerSample * fmtData.channels);
102
+ const frameDuration = frameSamples / fmtData.sampleRate;
103
+ frames.push({
104
+ index: frameIndex,
105
+ offset: frameOffset,
106
+ length: frameBytes,
107
+ duration: frameDuration,
108
+ samples: Math.floor(frameSamples),
109
+ sampleRate: fmtData.sampleRate,
110
+ bitrate: Math.round((fmtData.byteRate * 8) / 1000)
111
+ });
112
+ frameOffset += frameBytes;
113
+ remainingBytes -= frameBytes;
114
+ frameIndex++;
115
+ }
116
+ const metadata = {
117
+ format: 'wav',
118
+ size,
119
+ duration,
120
+ audioDataSize: dataSize,
121
+ sampleRate: fmtData.sampleRate,
122
+ channels: fmtData.channels,
123
+ bitDepth: fmtData.bitsPerSample,
124
+ averageBitrate: Math.round((fmtData.byteRate * 8) / 1000),
125
+ frames
126
+ };
127
+ if (warnings.length > 0) {
128
+ metadata.warnings = warnings;
129
+ }
130
+ metadata.metadata = {
131
+ audioFormat: fmtData.audioFormat,
132
+ blockAlign: fmtData.blockAlign,
133
+ riffSize
134
+ };
135
+ return metadata;
136
+ }
137
+ }
138
+ exports.WavParser = WavParser;
139
+ //# sourceMappingURL=WavParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WavParser.js","sourceRoot":"","sources":["../../../src/Parsers/WavParser.ts"],"names":[],"mappings":"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"}
@@ -13,4 +13,7 @@ export declare class InvalidRangeError extends HlsStreamerError {
13
13
  export declare class InvalidParameterError extends HlsStreamerError {
14
14
  constructor(parameter: string, value: any);
15
15
  }
16
+ export declare class UnsupportedFormatError extends HlsStreamerError {
17
+ constructor(format: string);
18
+ }
16
19
  //# sourceMappingURL=HlsStreamerErrors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HlsStreamerErrors.d.ts","sourceRoot":"","sources":["../../../src/errors/HlsStreamerErrors.ts"],"names":[],"mappings":"AAGA,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAKD,qBAAa,iBAAkB,SAAQ,gBAAgB;gBACzC,QAAQ,EAAE,MAAM;CAI7B;AAKD,qBAAa,gBAAiB,SAAQ,gBAAgB;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAKD,qBAAa,iBAAkB,SAAQ,gBAAgB;gBACzC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAI/C;AAKD,qBAAa,qBAAsB,SAAQ,gBAAgB;gBAC7C,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;CAI1C"}
1
+ {"version":3,"file":"HlsStreamerErrors.d.ts","sourceRoot":"","sources":["../../../src/errors/HlsStreamerErrors.ts"],"names":[],"mappings":"AAGA,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAKD,qBAAa,iBAAkB,SAAQ,gBAAgB;gBACzC,QAAQ,EAAE,MAAM;CAI7B;AAKD,qBAAa,gBAAiB,SAAQ,gBAAgB;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAKD,qBAAa,iBAAkB,SAAQ,gBAAgB;gBACzC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAI/C;AAKD,qBAAa,qBAAsB,SAAQ,gBAAgB;gBAC7C,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;CAI1C;AAKD,qBAAa,sBAAuB,SAAQ,gBAAgB;gBAC9C,MAAM,EAAE,MAAM;CAI3B"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InvalidParameterError = exports.InvalidRangeError = exports.InvalidFileError = exports.FileNotFoundError = exports.HlsStreamerError = void 0;
3
+ exports.UnsupportedFormatError = exports.InvalidParameterError = exports.InvalidRangeError = exports.InvalidFileError = exports.FileNotFoundError = exports.HlsStreamerError = void 0;
4
4
  class HlsStreamerError extends Error {
5
5
  constructor(message) {
6
6
  super(message);
@@ -36,4 +36,11 @@ class InvalidParameterError extends HlsStreamerError {
36
36
  }
37
37
  }
38
38
  exports.InvalidParameterError = InvalidParameterError;
39
+ class UnsupportedFormatError extends HlsStreamerError {
40
+ constructor(format) {
41
+ super(`Unsupported audio format: ${format}`);
42
+ this.name = 'UnsupportedFormatError';
43
+ }
44
+ }
45
+ exports.UnsupportedFormatError = UnsupportedFormatError;
39
46
  //# sourceMappingURL=HlsStreamerErrors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HlsStreamerErrors.js","sourceRoot":"","sources":["../../../src/errors/HlsStreamerErrors.ts"],"names":[],"mappings":";;;AAGA,MAAa,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AALD,4CAKC;AAKD,MAAa,iBAAkB,SAAQ,gBAAgB;IACrD,YAAY,QAAgB;QAC1B,KAAK,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AALD,8CAKC;AAKD,MAAa,gBAAiB,SAAQ,gBAAgB;IACpD,YAAY,OAAe;QACzB,KAAK,CAAC,iBAAiB,OAAO,EAAE,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AALD,4CAKC;AAKD,MAAa,iBAAkB,SAAQ,gBAAgB;IACrD,YAAY,SAAiB,EAAE,OAAe;QAC5C,KAAK,CAAC,wBAAwB,SAAS,SAAS,OAAO,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AALD,8CAKC;AAKD,MAAa,qBAAsB,SAAQ,gBAAgB;IACzD,YAAY,SAAiB,EAAE,KAAU;QACvC,KAAK,CAAC,sBAAsB,SAAS,MAAM,KAAK,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AALD,sDAKC"}
1
+ {"version":3,"file":"HlsStreamerErrors.js","sourceRoot":"","sources":["../../../src/errors/HlsStreamerErrors.ts"],"names":[],"mappings":";;;AAGA,MAAa,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AALD,4CAKC;AAKD,MAAa,iBAAkB,SAAQ,gBAAgB;IACrD,YAAY,QAAgB;QAC1B,KAAK,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AALD,8CAKC;AAKD,MAAa,gBAAiB,SAAQ,gBAAgB;IACpD,YAAY,OAAe;QACzB,KAAK,CAAC,iBAAiB,OAAO,EAAE,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AALD,4CAKC;AAKD,MAAa,iBAAkB,SAAQ,gBAAgB;IACrD,YAAY,SAAiB,EAAE,OAAe;QAC5C,KAAK,CAAC,wBAAwB,SAAS,SAAS,OAAO,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AALD,8CAKC;AAKD,MAAa,qBAAsB,SAAQ,gBAAgB;IACzD,YAAY,SAAiB,EAAE,KAAU;QACvC,KAAK,CAAC,sBAAsB,SAAS,MAAM,KAAK,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AALD,sDAKC;AAKD,MAAa,sBAAuB,SAAQ,gBAAgB;IAC1D,YAAY,MAAc;QACxB,KAAK,CAAC,6BAA6B,MAAM,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AALD,wDAKC"}
@@ -5,23 +5,22 @@ export declare class HlsStreamer {
5
5
  private readonly fileName;
6
6
  private readonly baseUrl;
7
7
  private readonly enableFastStart;
8
+ private readonly formatOverride;
8
9
  private fileInfo?;
9
- private segmentCache;
10
- private frameAlignedSegments;
10
+ private segments;
11
11
  constructor(options: HlsStreamerOptions);
12
12
  private validateOptions;
13
13
  private validateFile;
14
14
  private getFileInfo;
15
15
  getFileBuffer(startByte: number, endByte: number): Promise<Buffer>;
16
16
  createM3U8(): Promise<string>;
17
- private calculateSegmentCount;
17
+ private getSegments;
18
+ private computeTargetSizes;
19
+ private computeTargetSizesFromBytes;
20
+ private buildSegmentsWithoutFrameTable;
18
21
  private estimateSegmentDuration;
19
22
  private buildSegmentUrl;
20
23
  private calculateSegmentSize;
21
- private calculateSegment;
22
- private getFrameAlignedSegment;
23
- private findNextMp3Frame;
24
- private calculatefirst2SegmentSize;
25
24
  private padNumber;
26
25
  getSegmentDuration(segmentIndex: number): Promise<number>;
27
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAA4B,MAAM,0BAA0B,CAAC;AAYxF,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,CAAc;IAC/B,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,oBAAoB,CAAqD;gBAKrE,OAAO,EAAE,kBAAkB;IAWvC,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,YAAY;YAgBN,WAAW;IA2BnB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmDlE,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IA6BnC,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,uBAAuB;IAO/B,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,gBAAgB;YAuBV,sBAAsB;IA+BpC,OAAO,CAAC,gBAAgB;IAwCxB,OAAO,CAAC,0BAA0B;IAIlC,OAAO,CAAC,SAAS;IAOX,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAoBhE;AAGD,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAA8B,MAAM,0BAA0B,CAAC;AAe1F,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,CAAgB;IACjC,OAAO,CAAC,QAAQ,CAA4B;gBAKhC,OAAO,EAAE,kBAAkB;IAYvC,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,YAAY;YAyBN,WAAW;IAkBnB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA4BlE,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;YA+BrB,WAAW;IAiFzB,OAAO,CAAC,kBAAkB;IAK1B,OAAO,CAAC,2BAA2B;IAmBnC,OAAO,CAAC,8BAA8B;IA0BtC,OAAO,CAAC,uBAAuB;IAe/B,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,SAAS;IAOX,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAchE;AAGD,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC"}
package/dist/cjs/index.js CHANGED
@@ -21,6 +21,7 @@ exports.HlsStreamer = void 0;
21
21
  const node_fs_1 = __importDefault(require("node:fs"));
22
22
  const node_path_1 = __importDefault(require("node:path"));
23
23
  const FileLib_1 = require("./Libs/FileLib");
24
+ const FormatDetector_1 = require("./Libs/FormatDetector");
24
25
  const HlsStreamerErrors_1 = require("./errors/HlsStreamerErrors");
25
26
  class HlsStreamer {
26
27
  constructor(options) {
@@ -54,31 +55,32 @@ class HlsStreamer {
54
55
  writable: true,
55
56
  value: void 0
56
57
  });
57
- Object.defineProperty(this, "fileInfo", {
58
+ Object.defineProperty(this, "formatOverride", {
58
59
  enumerable: true,
59
60
  configurable: true,
60
61
  writable: true,
61
62
  value: void 0
62
63
  });
63
- Object.defineProperty(this, "segmentCache", {
64
+ Object.defineProperty(this, "fileInfo", {
64
65
  enumerable: true,
65
66
  configurable: true,
66
67
  writable: true,
67
- value: new Map()
68
+ value: void 0
68
69
  });
69
- Object.defineProperty(this, "frameAlignedSegments", {
70
+ Object.defineProperty(this, "segments", {
70
71
  enumerable: true,
71
72
  configurable: true,
72
73
  writable: true,
73
- value: new Map()
74
+ value: void 0
74
75
  });
75
76
  this.validateOptions(options);
76
- this.validateFile(options.filePath);
77
+ this.validateFile(options.filePath, options.format);
77
78
  this.filePath = options.filePath;
78
79
  this.segmentSize = (options.segmentSizeKB ?? 512) * 1024;
79
80
  this.fileName = options.fileName ?? "file";
80
81
  this.baseUrl = options.baseUrl ?? "";
81
82
  this.enableFastStart = options.enableFastStart ?? false;
83
+ this.formatOverride = options.format || undefined;
82
84
  }
83
85
  validateOptions(options) {
84
86
  if (!options.filePath || typeof options.filePath !== 'string') {
@@ -92,7 +94,7 @@ class HlsStreamer {
92
94
  throw new HlsStreamerErrors_1.InvalidParameterError('fileName', options.fileName);
93
95
  }
94
96
  }
95
- validateFile(filePath) {
97
+ validateFile(filePath, format) {
96
98
  if (!node_fs_1.default.existsSync(filePath)) {
97
99
  throw new HlsStreamerErrors_1.FileNotFoundError(filePath);
98
100
  }
@@ -100,25 +102,27 @@ class HlsStreamer {
100
102
  if (!stat.isFile()) {
101
103
  throw new HlsStreamerErrors_1.InvalidFileError('Path is not a file');
102
104
  }
103
- const ext = node_path_1.default.extname(filePath).toLowerCase();
104
- if (ext !== '.mp3') {
105
- throw new HlsStreamerErrors_1.InvalidFileError('Only MP3 files are supported');
105
+ if (format) {
106
+ const supportedFormats = ['mp3', 'aac', 'm4a', 'ogg', 'flac', 'wav'];
107
+ if (!supportedFormats.includes(format.toLowerCase())) {
108
+ throw new HlsStreamerErrors_1.UnsupportedFormatError(format);
109
+ }
110
+ }
111
+ else {
112
+ if (!FormatDetector_1.FormatDetector.isSupportedExtension(filePath)) {
113
+ const ext = node_path_1.default.extname(filePath);
114
+ throw new HlsStreamerErrors_1.UnsupportedFormatError(ext || 'unknown');
115
+ }
106
116
  }
107
117
  }
108
118
  async getFileInfo() {
109
119
  if (!this.fileInfo) {
110
- const stat = await node_fs_1.default.promises.stat(this.filePath);
111
- const size = stat.size;
112
- if (size <= 0) {
120
+ const analysis = await FileLib_1.FileLib.analyzeAudioFile(this.filePath, this.formatOverride);
121
+ if (analysis.size <= 0) {
113
122
  throw new HlsStreamerErrors_1.InvalidFileError('File is empty');
114
123
  }
115
- if (this.segmentSize > size) {
116
- throw new HlsStreamerErrors_1.InvalidFileError('Segment size is larger than file size');
117
- }
118
- this.fileInfo = {
119
- size,
120
- duration: 0
121
- };
124
+ this.fileInfo = analysis;
125
+ this.segments = undefined;
122
126
  }
123
127
  return this.fileInfo;
124
128
  }
@@ -130,26 +134,14 @@ class HlsStreamer {
130
134
  if (endByte > fileInfo.size) {
131
135
  throw new HlsStreamerErrors_1.InvalidRangeError(startByte, endByte);
132
136
  }
133
- const searchBufferSize = Math.min(8192, fileInfo.size);
134
- const searchStartByte = Math.max(0, startByte - 1024);
135
- const searchEndByte = Math.min(fileInfo.size, startByte + searchBufferSize);
137
+ const length = endByte - startByte;
136
138
  const fd = node_fs_1.default.openSync(this.filePath, "r");
137
139
  try {
138
- const searchBuffer = Buffer.alloc(searchEndByte - searchStartByte);
139
- node_fs_1.default.readSync(fd, searchBuffer, 0, searchBuffer.length, searchStartByte);
140
- const targetOffsetInSearch = startByte - searchStartByte;
141
- const frameAlignedOffsetInSearch = this.findNextMp3Frame(searchBuffer, Math.max(0, targetOffsetInSearch));
142
- const actualStartByte = searchStartByte + frameAlignedOffsetInSearch;
143
- const actualEndByte = Math.min(endByte, fileInfo.size);
144
- const bufferLength = actualEndByte - actualStartByte;
145
- if (bufferLength <= 0) {
146
- const fallbackLength = endByte - startByte;
147
- const fallbackBuffer = Buffer.alloc(fallbackLength);
148
- const bytesRead = node_fs_1.default.readSync(fd, fallbackBuffer, 0, fallbackLength, startByte);
149
- return fallbackBuffer.subarray(0, bytesRead);
140
+ if (length === 0) {
141
+ return Buffer.alloc(0);
150
142
  }
151
- const buffer = Buffer.alloc(bufferLength);
152
- const bytesRead = node_fs_1.default.readSync(fd, buffer, 0, bufferLength, actualStartByte);
143
+ const buffer = Buffer.alloc(length);
144
+ const bytesRead = node_fs_1.default.readSync(fd, buffer, 0, length, startByte);
153
145
  return buffer.subarray(0, bytesRead);
154
146
  }
155
147
  finally {
@@ -157,40 +149,146 @@ class HlsStreamer {
157
149
  }
158
150
  }
159
151
  async createM3U8() {
160
- const fileInfo = await this.getFileInfo();
161
- const segmentCount = this.calculateSegmentCount(fileInfo.size);
152
+ const [fileInfo, segments] = await Promise.all([
153
+ this.getFileInfo(),
154
+ this.getSegments()
155
+ ]);
156
+ if (!segments.length) {
157
+ throw new HlsStreamerErrors_1.InvalidFileError('Unable to generate segments for MP3 file');
158
+ }
159
+ const maxSegmentDuration = segments.reduce((max, segment) => Math.max(max, segment.duration), 0);
160
+ const targetDurationSeconds = Math.max(1, Math.ceil(maxSegmentDuration || fileInfo.duration || 1));
162
161
  const m3u8 = [
163
162
  '#EXTM3U',
164
163
  '#EXT-X-VERSION:6',
165
164
  '#EXT-X-PLAYLIST-TYPE:VOD',
166
- '#EXT-X-TARGETDURATION:14',
165
+ `#EXT-X-TARGETDURATION:${targetDurationSeconds}`,
167
166
  '#EXT-X-MEDIA-SEQUENCE:0',
168
167
  ];
169
- for (let i = 0; i < segmentCount; i++) {
170
- const { start, end } = await this.getFrameAlignedSegment(i, fileInfo.size);
171
- const estimatedDuration = this.estimateSegmentDuration(end - start);
172
- const segmentUrl = this.buildSegmentUrl(start, end, i);
173
- m3u8.push(`#EXTINF:${estimatedDuration.toFixed(3)}`);
168
+ segments.forEach((segment, index) => {
169
+ const segmentUrl = this.buildSegmentUrl(segment.start, segment.end, index);
170
+ m3u8.push(`#EXTINF:${segment.duration.toFixed(3)}`);
174
171
  m3u8.push(segmentUrl);
175
- }
172
+ });
176
173
  m3u8.push('#EXT-X-ENDLIST');
177
174
  return m3u8.join('\n');
178
175
  }
179
- calculateSegmentCount(fileSize) {
180
- if (!this.enableFastStart) {
181
- return Math.ceil(fileSize / this.segmentSize);
176
+ async getSegments() {
177
+ if (this.segments) {
178
+ return this.segments;
179
+ }
180
+ const fileInfo = await this.getFileInfo();
181
+ if (!fileInfo.frames.length) {
182
+ this.segments = this.buildSegmentsWithoutFrameTable(fileInfo);
183
+ return this.segments;
184
+ }
185
+ const targetSizes = this.computeTargetSizes(fileInfo);
186
+ const segments = [];
187
+ const frames = fileInfo.frames;
188
+ let frameCursor = 0;
189
+ const consumeFrames = (targetBytes) => {
190
+ if (frameCursor >= frames.length) {
191
+ return;
192
+ }
193
+ const startFrameIndex = frameCursor;
194
+ let consumedBytes = 0;
195
+ let duration = 0;
196
+ while (frameCursor < frames.length) {
197
+ const frame = frames[frameCursor];
198
+ const nextBytes = consumedBytes + frame.length;
199
+ if (consumedBytes > 0 && nextBytes > targetBytes) {
200
+ break;
201
+ }
202
+ consumedBytes = nextBytes;
203
+ duration += frame.duration;
204
+ frameCursor++;
205
+ if (consumedBytes >= targetBytes) {
206
+ break;
207
+ }
208
+ }
209
+ if (frameCursor === startFrameIndex && frameCursor < frames.length) {
210
+ const frame = frames[frameCursor];
211
+ consumedBytes += frame.length;
212
+ duration += frame.duration;
213
+ frameCursor++;
214
+ }
215
+ if (frameCursor === startFrameIndex) {
216
+ return;
217
+ }
218
+ const start = frames[startFrameIndex].offset;
219
+ const lastFrame = frames[frameCursor - 1];
220
+ const end = lastFrame.offset + lastFrame.length;
221
+ segments.push({
222
+ start,
223
+ end,
224
+ duration
225
+ });
226
+ };
227
+ targetSizes.forEach(consumeFrames);
228
+ while (frameCursor < frames.length) {
229
+ consumeFrames(this.segmentSize);
230
+ }
231
+ if (!segments.length) {
232
+ this.segments = this.buildSegmentsWithoutFrameTable(fileInfo);
233
+ }
234
+ else {
235
+ this.segments = segments;
236
+ }
237
+ return this.segments;
238
+ }
239
+ computeTargetSizes(fileInfo) {
240
+ const totalBytes = fileInfo.audioDataSize || fileInfo.size;
241
+ return this.computeTargetSizesFromBytes(totalBytes);
242
+ }
243
+ computeTargetSizesFromBytes(totalBytes) {
244
+ if (totalBytes <= 0) {
245
+ return [];
246
+ }
247
+ const targets = [];
248
+ let remaining = totalBytes;
249
+ let index = 0;
250
+ while (remaining > 0) {
251
+ const targetSize = Math.min(this.calculateSegmentSize(index), remaining);
252
+ targets.push(targetSize);
253
+ remaining -= targetSize;
254
+ index++;
255
+ }
256
+ return targets.length ? targets : [totalBytes];
257
+ }
258
+ buildSegmentsWithoutFrameTable(fileInfo) {
259
+ const segments = [];
260
+ const totalBytes = Math.max(fileInfo.size, fileInfo.audioDataSize);
261
+ if (totalBytes <= 0) {
262
+ return segments;
263
+ }
264
+ const targets = this.computeTargetSizesFromBytes(totalBytes);
265
+ let start = 0;
266
+ targets.forEach((targetSize) => {
267
+ const end = Math.min(totalBytes, start + targetSize);
268
+ const duration = this.estimateSegmentDuration(end - start, fileInfo);
269
+ segments.push({ start, end, duration });
270
+ start = end;
271
+ });
272
+ if (start < totalBytes) {
273
+ const duration = this.estimateSegmentDuration(totalBytes - start, fileInfo);
274
+ segments.push({ start, end: totalBytes, duration });
182
275
  }
183
- const firstTwoSegmentsSize = this.calculatefirst2SegmentSize();
184
- const remainingSize = fileSize - firstTwoSegmentsSize;
185
- return Math.ceil(remainingSize / this.segmentSize) + 2;
276
+ return segments;
186
277
  }
187
- estimateSegmentDuration(segmentSize) {
188
- const estimatedBytesPerSecond = 16000;
278
+ estimateSegmentDuration(segmentSize, fileInfo) {
279
+ if (fileInfo.duration > 0 && fileInfo.audioDataSize > 0) {
280
+ const bytesPerSecond = fileInfo.audioDataSize / fileInfo.duration;
281
+ return segmentSize / bytesPerSecond;
282
+ }
283
+ const estimatedBytesPerSecond = fileInfo.averageBitrate
284
+ ? (fileInfo.averageBitrate * 1000) / 8
285
+ : 16000;
189
286
  return segmentSize / estimatedBytesPerSecond;
190
287
  }
191
288
  buildSegmentUrl(start, end, index) {
192
289
  const baseUrlPrefix = this.baseUrl ? `/${this.baseUrl}` : '';
193
- return `${baseUrlPrefix}/${start}/${end}/${this.fileName}${this.padNumber(index, 3)}.mp3`;
290
+ const ext = this.fileInfo?.format || node_path_1.default.extname(this.filePath).toLowerCase().slice(1) || 'mp3';
291
+ return `${baseUrlPrefix}/${start}/${end}/${this.fileName}${this.padNumber(index, 3)}.${ext}`;
194
292
  }
195
293
  calculateSegmentSize(segmentIndex) {
196
294
  if (!this.enableFastStart) {
@@ -205,94 +303,19 @@ class HlsStreamer {
205
303
  return this.segmentSize;
206
304
  }
207
305
  }
208
- calculateSegment(segmentIndex, fileSize) {
209
- let start = 0;
210
- if (this.enableFastStart && segmentIndex < 2) {
211
- start = segmentIndex * (this.segmentSize / 4);
212
- }
213
- else if (this.enableFastStart) {
214
- start = (3 * this.segmentSize) / 4 + (segmentIndex - 2) * this.segmentSize;
215
- }
216
- else {
217
- start = segmentIndex * this.segmentSize;
218
- }
219
- const segmentSize = this.calculateSegmentSize(segmentIndex);
220
- const end = Math.min(start + segmentSize, fileSize);
221
- return { start: Math.floor(start), end: Math.floor(end) };
222
- }
223
- async getFrameAlignedSegment(segmentIndex, fileSize) {
224
- if (this.frameAlignedSegments.has(segmentIndex)) {
225
- return this.frameAlignedSegments.get(segmentIndex);
226
- }
227
- const rawSegment = this.calculateSegment(segmentIndex, fileSize);
228
- if (segmentIndex === 0) {
229
- const fd = node_fs_1.default.openSync(this.filePath, "r");
230
- try {
231
- const headerBuffer = Buffer.alloc(Math.min(8192, fileSize));
232
- node_fs_1.default.readSync(fd, headerBuffer, 0, headerBuffer.length, 0);
233
- const frameStart = this.findNextMp3Frame(headerBuffer, 0);
234
- const alignedSegment = { start: frameStart, end: rawSegment.end };
235
- this.frameAlignedSegments.set(segmentIndex, alignedSegment);
236
- return alignedSegment;
237
- }
238
- finally {
239
- node_fs_1.default.closeSync(fd);
240
- }
241
- }
242
- this.frameAlignedSegments.set(segmentIndex, rawSegment);
243
- return rawSegment;
244
- }
245
- findNextMp3Frame(buffer, startPos) {
246
- let pos = startPos;
247
- if (pos === 0 && buffer.length > 10 && buffer.toString('ascii', 0, 3) === 'ID3') {
248
- const tagSize = ((buffer[6] & 0x7f) << 21) |
249
- ((buffer[7] & 0x7f) << 14) |
250
- ((buffer[8] & 0x7f) << 7) |
251
- (buffer[9] & 0x7f);
252
- pos = 10 + tagSize;
253
- }
254
- while (pos < buffer.length - 1) {
255
- if (buffer[pos] === 0xFF && (buffer[pos + 1] & 0xE0) === 0xE0) {
256
- if (pos + 3 < buffer.length) {
257
- const header = (buffer[pos] << 24) |
258
- (buffer[pos + 1] << 16) |
259
- (buffer[pos + 2] << 8) |
260
- buffer[pos + 3];
261
- const version = (header >> 19) & 0x3;
262
- const layer = (header >> 17) & 0x3;
263
- const bitrateIndex = (header >> 12) & 0xF;
264
- const sampleRateIndex = (header >> 10) & 0x3;
265
- if (version !== 1 && layer !== 0 && bitrateIndex !== 0 &&
266
- bitrateIndex !== 15 && sampleRateIndex !== 3) {
267
- return pos;
268
- }
269
- }
270
- }
271
- pos++;
272
- }
273
- return startPos;
274
- }
275
- calculatefirst2SegmentSize() {
276
- return (this.segmentSize / 4) * 3;
277
- }
278
306
  padNumber(value, padding) {
279
307
  return value.toString().padStart(padding, '0');
280
308
  }
281
309
  async getSegmentDuration(segmentIndex) {
282
- const cachedSegment = this.segmentCache.get(segmentIndex);
283
- if (cachedSegment) {
284
- return cachedSegment.duration;
310
+ if (!Number.isInteger(segmentIndex) || segmentIndex < 0) {
311
+ throw new HlsStreamerErrors_1.InvalidParameterError('segmentIndex', segmentIndex);
285
312
  }
286
- const fileInfo = await this.getFileInfo();
287
- const { start, end } = this.calculateSegment(segmentIndex, fileInfo.size);
288
- const segmentBuffer = await this.getFileBuffer(start, end);
289
- const duration = await FileLib_1.FileLib.getMP3DurationFromBuffer(segmentBuffer);
290
- this.segmentCache.set(segmentIndex, {
291
- start,
292
- end,
293
- duration
294
- });
295
- return duration;
313
+ const segments = await this.getSegments();
314
+ const segment = segments[segmentIndex];
315
+ if (!segment) {
316
+ throw new HlsStreamerErrors_1.InvalidParameterError('segmentIndex', segmentIndex);
317
+ }
318
+ return segment.duration;
296
319
  }
297
320
  }
298
321
  exports.HlsStreamer = HlsStreamer;