hls-streamer 2.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 (35) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +237 -0
  3. package/dist/Interfaces/HlsStreamer.d.ts +17 -0
  4. package/dist/Interfaces/HlsStreamer.d.ts.map +1 -0
  5. package/dist/Interfaces/HlsStreamer.js +2 -0
  6. package/dist/Interfaces/HlsStreamer.js.map +1 -0
  7. package/dist/Libs/FileLib.d.ts +5 -0
  8. package/dist/Libs/FileLib.d.ts.map +1 -0
  9. package/dist/Libs/FileLib.js +24 -0
  10. package/dist/Libs/FileLib.js.map +1 -0
  11. package/dist/cjs/Interfaces/HlsStreamer.d.ts +17 -0
  12. package/dist/cjs/Interfaces/HlsStreamer.d.ts.map +1 -0
  13. package/dist/cjs/Interfaces/HlsStreamer.js +3 -0
  14. package/dist/cjs/Interfaces/HlsStreamer.js.map +1 -0
  15. package/dist/cjs/Libs/FileLib.d.ts +5 -0
  16. package/dist/cjs/Libs/FileLib.d.ts.map +1 -0
  17. package/dist/cjs/Libs/FileLib.js +31 -0
  18. package/dist/cjs/Libs/FileLib.js.map +1 -0
  19. package/dist/cjs/errors/HlsStreamerErrors.d.ts +16 -0
  20. package/dist/cjs/errors/HlsStreamerErrors.d.ts.map +1 -0
  21. package/dist/cjs/errors/HlsStreamerErrors.js +39 -0
  22. package/dist/cjs/errors/HlsStreamerErrors.js.map +1 -0
  23. package/dist/cjs/index.d.ts +27 -0
  24. package/dist/cjs/index.d.ts.map +1 -0
  25. package/dist/cjs/index.js +228 -0
  26. package/dist/cjs/index.js.map +1 -0
  27. package/dist/errors/HlsStreamerErrors.d.ts +16 -0
  28. package/dist/errors/HlsStreamerErrors.d.ts.map +1 -0
  29. package/dist/errors/HlsStreamerErrors.js +31 -0
  30. package/dist/errors/HlsStreamerErrors.js.map +1 -0
  31. package/dist/index.d.ts +27 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +207 -0
  34. package/dist/index.js.map +1 -0
  35. package/package.json +69 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 LordVersA
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,237 @@
1
+ # HLS Streamer
2
+
3
+ [![npm version](https://badge.fury.io/js/hls-streamer.svg)](https://badge.fury.io/js/hls-streamer)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)
6
+
7
+ **HLS Streamer** is a lightweight npm package that creates and streams HLS (HTTP Live Streaming) from MP3 files on demand, without storing temporary files or requiring ffmpeg.
8
+
9
+ ## ✨ Features
10
+
11
+ - 🚀 **Zero Dependencies** - No ffmpeg required
12
+ - 💾 **No Temporary Files** - Stream directly from source
13
+ - ⚡ **Fast Startup** - Optional smaller initial segments for quick playback start
14
+ - 🎯 **TypeScript Support** - Full type definitions included
15
+ - 🔧 **Configurable** - Customizable segment sizes and naming
16
+ - 📱 **Memory Efficient** - Byte-range streaming with minimal memory footprint
17
+
18
+ ## 📦 Installation
19
+
20
+ ```bash
21
+ npm install hls-streamer
22
+ ```
23
+
24
+ ```bash
25
+ yarn add hls-streamer
26
+ ```
27
+
28
+ ```bash
29
+ pnpm add hls-streamer
30
+ ```
31
+
32
+ ## 🚀 Quick Start
33
+
34
+ ### Basic Usage
35
+
36
+ ```typescript
37
+ import { HlsStreamer } from 'hls-streamer';
38
+
39
+ const hls = new HlsStreamer({
40
+ filePath: 'path/to/audio.mp3',
41
+ segmentSizeKB: 512,
42
+ fileName: 'segment',
43
+ baseUrl: 'segments/session-123',
44
+ enableFastStart: true
45
+ });
46
+
47
+ // Generate M3U8 playlist
48
+ const playlist = await hls.createM3U8();
49
+
50
+ // Get file buffer for specific byte range
51
+ const buffer = await hls.getFileBuffer(startByte, endByte);
52
+ ```
53
+
54
+ ### Express.js Integration
55
+
56
+ ```typescript
57
+ import express from 'express';
58
+ import { HlsStreamer } from 'hls-streamer';
59
+
60
+ const app = express();
61
+
62
+ // Serve M3U8 playlist
63
+ app.get('/stream/:sessionId/playlist.m3u8', async (req, res) => {
64
+ const hls = new HlsStreamer({
65
+ filePath: getFilePath(req.params.sessionId),
66
+ baseUrl: `stream/${req.params.sessionId}`,
67
+ enableFastStart: true
68
+ });
69
+
70
+ res.setHeader('Content-Type', 'application/vnd.apple.mpegurl');
71
+ res.send(await hls.createM3U8());
72
+ });
73
+
74
+ // Serve segment files
75
+ app.get('/stream/:sessionId/:start/:end/:filename', async (req, res) => {
76
+ const hls = new HlsStreamer({
77
+ filePath: getFilePath(req.params.sessionId),
78
+ baseUrl: `stream/${req.params.sessionId}`
79
+ });
80
+
81
+ const start = parseInt(req.params.start);
82
+ const end = parseInt(req.params.end);
83
+
84
+ res.setHeader('Content-Type', 'audio/mpeg');
85
+ res.setHeader('Accept-Ranges', 'bytes');
86
+ res.send(await hls.getFileBuffer(start, end));
87
+ });
88
+ ```
89
+
90
+ ## 🛠️ API Reference
91
+
92
+ ### HlsStreamer
93
+
94
+ #### Constructor Options
95
+
96
+ ```typescript
97
+ interface HlsStreamerOptions {
98
+ /** Path to the MP3 file */
99
+ filePath: string;
100
+ /** Segment size in KB (default: 512) */
101
+ segmentSizeKB?: number;
102
+ /** Base filename for segments (default: "file") */
103
+ fileName?: string;
104
+ /** Base URL path for segment URLs */
105
+ baseUrl?: string;
106
+ /** Enable smaller initial segments for faster startup */
107
+ enableFastStart?: boolean;
108
+ }
109
+ ```
110
+
111
+ #### Methods
112
+
113
+ ##### `createM3U8(): Promise<string>`
114
+ Generates an HLS M3U8 playlist file content.
115
+
116
+ **Returns:** M3U8 playlist as a string
117
+
118
+ ##### `getFileBuffer(startByte: number, endByte: number): Promise<Buffer>`
119
+ Retrieves a specific byte range from the MP3 file.
120
+
121
+ **Parameters:**
122
+ - `startByte` - Starting byte position (inclusive)
123
+ - `endByte` - Ending byte position (exclusive)
124
+
125
+ **Returns:** Buffer containing the requested byte range
126
+
127
+ ##### `getSegmentDuration(segmentIndex: number): Promise<number>`
128
+ Gets the accurate duration of a specific segment.
129
+
130
+ **Parameters:**
131
+ - `segmentIndex` - Zero-based segment index
132
+
133
+ **Returns:** Duration in seconds
134
+
135
+ ### Error Handling
136
+
137
+ The package includes custom error types for better error handling:
138
+
139
+ ```typescript
140
+ import {
141
+ FileNotFoundError,
142
+ InvalidFileError,
143
+ InvalidRangeError,
144
+ InvalidParameterError
145
+ } from 'hls-streamer';
146
+
147
+ try {
148
+ const hls = new HlsStreamer({ filePath: 'nonexistent.mp3' });
149
+ } catch (error) {
150
+ if (error instanceof FileNotFoundError) {
151
+ console.error('File not found:', error.message);
152
+ }
153
+ }
154
+ ```
155
+
156
+ ## 📋 Configuration Options
157
+
158
+ | Option | Type | Default | Description |
159
+ |--------|------|---------|-------------|
160
+ | `filePath` | `string` | **required** | Path to the MP3 file |
161
+ | `segmentSizeKB` | `number` | `512` | Size of each segment in KB |
162
+ | `fileName` | `string` | `"file"` | Base name for segment files |
163
+ | `baseUrl` | `string` | `""` | Base URL path for segment URLs |
164
+ | `enableFastStart` | `boolean` | `false` | Use smaller initial segments for faster startup |
165
+
166
+ ## 🎯 Use Cases
167
+
168
+ - **Audio Streaming Services** - Stream music without pre-processing
169
+ - **Podcast Platforms** - On-demand episode streaming
170
+ - **Educational Platforms** - Stream lecture recordings
171
+ - **Voice Message Systems** - Real-time audio message playback
172
+ - **Audio Books** - Chapter-based streaming
173
+
174
+ ## 🔧 Advanced Examples
175
+
176
+ ### Custom Segment Sizing
177
+
178
+ ```typescript
179
+ const hls = new HlsStreamer({
180
+ filePath: 'large-audio-file.mp3',
181
+ segmentSizeKB: 1024, // 1MB segments for better quality
182
+ enableFastStart: true // First segments will be 256KB and 512KB
183
+ });
184
+ ```
185
+
186
+ ### Dynamic File Paths
187
+
188
+ ```typescript
189
+ class AudioStreamer {
190
+ async streamAudio(userId: string, audioId: string) {
191
+ const filePath = await this.getAudioPath(userId, audioId);
192
+
193
+ const hls = new HlsStreamer({
194
+ filePath,
195
+ baseUrl: `audio/${userId}/${audioId}`,
196
+ fileName: `audio-${audioId}`,
197
+ segmentSizeKB: 256 // Smaller segments for mobile
198
+ });
199
+
200
+ return hls.createM3U8();
201
+ }
202
+ }
203
+ ```
204
+
205
+ ## 🧪 Testing
206
+
207
+ ```bash
208
+ npm test
209
+ npm run test:watch
210
+ npm run test:coverage
211
+ ```
212
+
213
+ ## 🏗️ Building
214
+
215
+ ```bash
216
+ npm run build # Build both ESM and CJS
217
+ npm run build:esm # Build ES modules
218
+ npm run build:cjs # Build CommonJS
219
+ ```
220
+
221
+ ## 📄 License
222
+
223
+ MIT License - see [LICENSE](LICENSE) file for details.
224
+
225
+ ## 🤝 Contributing
226
+
227
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
228
+
229
+ ## 📞 Support
230
+
231
+ - 🐛 **Bug Reports:** [GitHub Issues](https://github.com/LordVersA/hls-streamer/issues)
232
+ - 💬 **Questions:** [GitHub Discussions](https://github.com/LordVersA/hls-streamer/discussions)
233
+ - 📦 **NPM:** [hls-streamer](https://www.npmjs.com/package/hls-streamer)
234
+
235
+ ---
236
+
237
+ Made with ❤️ by [LordVersA](https://github.com/LordVersA)
@@ -0,0 +1,17 @@
1
+ export interface HlsStreamerOptions {
2
+ filePath: string;
3
+ segmentSizeKB?: number;
4
+ fileName?: string;
5
+ baseUrl?: string;
6
+ enableFastStart?: boolean;
7
+ }
8
+ export interface SegmentInfo {
9
+ start: number;
10
+ end: number;
11
+ duration: number;
12
+ }
13
+ export interface Mp3FileInfo {
14
+ size: number;
15
+ duration: number;
16
+ }
17
+ //# sourceMappingURL=HlsStreamer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HlsStreamer.d.ts","sourceRoot":"","sources":["../../src/Interfaces/HlsStreamer.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IAEjC,QAAQ,EAAE,MAAM,CAAC;IAEjB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAKD,MAAM,WAAW,WAAW;IAE1B,KAAK,EAAE,MAAM,CAAC;IAEd,GAAG,EAAE,MAAM,CAAC;IAEZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAKD,MAAM,WAAW,WAAW;IAE1B,IAAI,EAAE,MAAM,CAAC;IAEb,QAAQ,EAAE,MAAM,CAAC;CAClB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=HlsStreamer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HlsStreamer.js","sourceRoot":"","sources":["../../src/Interfaces/HlsStreamer.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export declare class FileLib {
2
+ static getFileSizeInBytes(buffer: Buffer): number;
3
+ static getMP3DurationFromBuffer(mp3Buffer: Buffer): Promise<number>;
4
+ }
5
+ //# sourceMappingURL=FileLib.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileLib.d.ts","sourceRoot":"","sources":["../../src/Libs/FileLib.ts"],"names":[],"mappings":"AAKA,qBAAa,OAAO;IAIlB,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAWjD,MAAM,CAAC,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAWpE"}
@@ -0,0 +1,24 @@
1
+ import mp3Duration from "mp3-duration";
2
+ export class FileLib {
3
+ static getFileSizeInBytes(buffer) {
4
+ if (Buffer.isBuffer(buffer)) {
5
+ return buffer.length;
6
+ }
7
+ else {
8
+ throw new Error("Input is not a buffer");
9
+ }
10
+ }
11
+ static getMP3DurationFromBuffer(mp3Buffer) {
12
+ return new Promise((resolve, reject) => {
13
+ mp3Duration(mp3Buffer, (err, duration) => {
14
+ if (err) {
15
+ reject(err);
16
+ }
17
+ else {
18
+ resolve(duration);
19
+ }
20
+ });
21
+ });
22
+ }
23
+ }
24
+ //# sourceMappingURL=FileLib.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileLib.js","sourceRoot":"","sources":["../../src/Libs/FileLib.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,cAAc,CAAC;AAKvC,MAAM,OAAO,OAAO;IAIlB,MAAM,CAAC,kBAAkB,CAAC,MAAc;QACtC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAC,MAAM,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAKD,MAAM,CAAC,wBAAwB,CAAC,SAAiB;QAC/C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,WAAW,CAAC,SAAS,EAAE,CAAC,GAAiB,EAAE,QAAgB,EAAE,EAAE;gBAC7D,IAAI,GAAG,EAAE,CAAC;oBACR,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACpB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,17 @@
1
+ export interface HlsStreamerOptions {
2
+ filePath: string;
3
+ segmentSizeKB?: number;
4
+ fileName?: string;
5
+ baseUrl?: string;
6
+ enableFastStart?: boolean;
7
+ }
8
+ export interface SegmentInfo {
9
+ start: number;
10
+ end: number;
11
+ duration: number;
12
+ }
13
+ export interface Mp3FileInfo {
14
+ size: number;
15
+ duration: number;
16
+ }
17
+ //# sourceMappingURL=HlsStreamer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HlsStreamer.d.ts","sourceRoot":"","sources":["../../../src/Interfaces/HlsStreamer.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IAEjC,QAAQ,EAAE,MAAM,CAAC;IAEjB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAKD,MAAM,WAAW,WAAW;IAE1B,KAAK,EAAE,MAAM,CAAC;IAEd,GAAG,EAAE,MAAM,CAAC;IAEZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAKD,MAAM,WAAW,WAAW;IAE1B,IAAI,EAAE,MAAM,CAAC;IAEb,QAAQ,EAAE,MAAM,CAAC;CAClB"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=HlsStreamer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HlsStreamer.js","sourceRoot":"","sources":["../../../src/Interfaces/HlsStreamer.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export declare class FileLib {
2
+ static getFileSizeInBytes(buffer: Buffer): number;
3
+ static getMP3DurationFromBuffer(mp3Buffer: Buffer): Promise<number>;
4
+ }
5
+ //# sourceMappingURL=FileLib.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileLib.d.ts","sourceRoot":"","sources":["../../../src/Libs/FileLib.ts"],"names":[],"mappings":"AAKA,qBAAa,OAAO;IAIlB,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAWjD,MAAM,CAAC,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAWpE"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FileLib = void 0;
7
+ const mp3_duration_1 = __importDefault(require("mp3-duration"));
8
+ class FileLib {
9
+ static getFileSizeInBytes(buffer) {
10
+ if (Buffer.isBuffer(buffer)) {
11
+ return buffer.length;
12
+ }
13
+ else {
14
+ throw new Error("Input is not a buffer");
15
+ }
16
+ }
17
+ static getMP3DurationFromBuffer(mp3Buffer) {
18
+ return new Promise((resolve, reject) => {
19
+ (0, mp3_duration_1.default)(mp3Buffer, (err, duration) => {
20
+ if (err) {
21
+ reject(err);
22
+ }
23
+ else {
24
+ resolve(duration);
25
+ }
26
+ });
27
+ });
28
+ }
29
+ }
30
+ exports.FileLib = FileLib;
31
+ //# sourceMappingURL=FileLib.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileLib.js","sourceRoot":"","sources":["../../../src/Libs/FileLib.ts"],"names":[],"mappings":";;;;;;AAAA,gEAAuC;AAKvC,MAAa,OAAO;IAIlB,MAAM,CAAC,kBAAkB,CAAC,MAAc;QACtC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAC,MAAM,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAKD,MAAM,CAAC,wBAAwB,CAAC,SAAiB;QAC/C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAA,sBAAW,EAAC,SAAS,EAAE,CAAC,GAAiB,EAAE,QAAgB,EAAE,EAAE;gBAC7D,IAAI,GAAG,EAAE,CAAC;oBACR,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACpB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AA1BD,0BA0BC"}
@@ -0,0 +1,16 @@
1
+ export declare class HlsStreamerError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class FileNotFoundError extends HlsStreamerError {
5
+ constructor(filePath: string);
6
+ }
7
+ export declare class InvalidFileError extends HlsStreamerError {
8
+ constructor(message: string);
9
+ }
10
+ export declare class InvalidRangeError extends HlsStreamerError {
11
+ constructor(startByte: number, endByte: number);
12
+ }
13
+ export declare class InvalidParameterError extends HlsStreamerError {
14
+ constructor(parameter: string, value: any);
15
+ }
16
+ //# sourceMappingURL=HlsStreamerErrors.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InvalidParameterError = exports.InvalidRangeError = exports.InvalidFileError = exports.FileNotFoundError = exports.HlsStreamerError = void 0;
4
+ class HlsStreamerError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = 'HlsStreamerError';
8
+ }
9
+ }
10
+ exports.HlsStreamerError = HlsStreamerError;
11
+ class FileNotFoundError extends HlsStreamerError {
12
+ constructor(filePath) {
13
+ super(`File not found: ${filePath}`);
14
+ this.name = 'FileNotFoundError';
15
+ }
16
+ }
17
+ exports.FileNotFoundError = FileNotFoundError;
18
+ class InvalidFileError extends HlsStreamerError {
19
+ constructor(message) {
20
+ super(`Invalid file: ${message}`);
21
+ this.name = 'InvalidFileError';
22
+ }
23
+ }
24
+ exports.InvalidFileError = InvalidFileError;
25
+ class InvalidRangeError extends HlsStreamerError {
26
+ constructor(startByte, endByte) {
27
+ super(`Invalid range: start=${startByte}, end=${endByte}`);
28
+ this.name = 'InvalidRangeError';
29
+ }
30
+ }
31
+ exports.InvalidRangeError = InvalidRangeError;
32
+ class InvalidParameterError extends HlsStreamerError {
33
+ constructor(parameter, value) {
34
+ super(`Invalid parameter '${parameter}': ${value}`);
35
+ this.name = 'InvalidParameterError';
36
+ }
37
+ }
38
+ exports.InvalidParameterError = InvalidParameterError;
39
+ //# sourceMappingURL=HlsStreamerErrors.js.map
@@ -0,0 +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"}
@@ -0,0 +1,27 @@
1
+ import { HlsStreamerOptions } from "./Interfaces/HlsStreamer";
2
+ export declare class HlsStreamer {
3
+ private readonly filePath;
4
+ private readonly segmentSize;
5
+ private readonly fileName;
6
+ private readonly baseUrl;
7
+ private readonly enableFastStart;
8
+ private fileInfo?;
9
+ private segmentCache;
10
+ constructor(options: HlsStreamerOptions);
11
+ private validateOptions;
12
+ private validateFile;
13
+ private getFileInfo;
14
+ getFileBuffer(startByte: number, endByte: number): Promise<Buffer>;
15
+ createM3U8(): Promise<string>;
16
+ private calculateSegmentCount;
17
+ private estimateSegmentDuration;
18
+ private buildSegmentUrl;
19
+ private calculateSegmentSize;
20
+ private calculateSegment;
21
+ private calculatefirst2SegmentSize;
22
+ private padNumber;
23
+ getSegmentDuration(segmentIndex: number): Promise<number>;
24
+ }
25
+ export * from './Interfaces/HlsStreamer';
26
+ export * from './errors/HlsStreamerErrors';
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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;gBAK1C,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;IAyBlE,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;IAoBxB,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"}
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.HlsStreamer = void 0;
21
+ const node_fs_1 = __importDefault(require("node:fs"));
22
+ const node_path_1 = __importDefault(require("node:path"));
23
+ const FileLib_1 = require("./Libs/FileLib");
24
+ const HlsStreamerErrors_1 = require("./errors/HlsStreamerErrors");
25
+ class HlsStreamer {
26
+ constructor(options) {
27
+ Object.defineProperty(this, "filePath", {
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true,
31
+ value: void 0
32
+ });
33
+ Object.defineProperty(this, "segmentSize", {
34
+ enumerable: true,
35
+ configurable: true,
36
+ writable: true,
37
+ value: void 0
38
+ });
39
+ Object.defineProperty(this, "fileName", {
40
+ enumerable: true,
41
+ configurable: true,
42
+ writable: true,
43
+ value: void 0
44
+ });
45
+ Object.defineProperty(this, "baseUrl", {
46
+ enumerable: true,
47
+ configurable: true,
48
+ writable: true,
49
+ value: void 0
50
+ });
51
+ Object.defineProperty(this, "enableFastStart", {
52
+ enumerable: true,
53
+ configurable: true,
54
+ writable: true,
55
+ value: void 0
56
+ });
57
+ Object.defineProperty(this, "fileInfo", {
58
+ enumerable: true,
59
+ configurable: true,
60
+ writable: true,
61
+ value: void 0
62
+ });
63
+ Object.defineProperty(this, "segmentCache", {
64
+ enumerable: true,
65
+ configurable: true,
66
+ writable: true,
67
+ value: new Map()
68
+ });
69
+ this.validateOptions(options);
70
+ this.validateFile(options.filePath);
71
+ this.filePath = options.filePath;
72
+ this.segmentSize = (options.segmentSizeKB ?? 512) * 1024;
73
+ this.fileName = options.fileName ?? "file";
74
+ this.baseUrl = options.baseUrl ?? "";
75
+ this.enableFastStart = options.enableFastStart ?? false;
76
+ }
77
+ validateOptions(options) {
78
+ if (!options.filePath || typeof options.filePath !== 'string') {
79
+ throw new HlsStreamerErrors_1.InvalidParameterError('filePath', options.filePath);
80
+ }
81
+ if (options.segmentSizeKB !== undefined &&
82
+ (typeof options.segmentSizeKB !== 'number' || options.segmentSizeKB <= 0)) {
83
+ throw new HlsStreamerErrors_1.InvalidParameterError('segmentSizeKB', options.segmentSizeKB);
84
+ }
85
+ if (options.fileName !== undefined && typeof options.fileName !== 'string') {
86
+ throw new HlsStreamerErrors_1.InvalidParameterError('fileName', options.fileName);
87
+ }
88
+ }
89
+ validateFile(filePath) {
90
+ if (!node_fs_1.default.existsSync(filePath)) {
91
+ throw new HlsStreamerErrors_1.FileNotFoundError(filePath);
92
+ }
93
+ const stat = node_fs_1.default.statSync(filePath);
94
+ if (!stat.isFile()) {
95
+ throw new HlsStreamerErrors_1.InvalidFileError('Path is not a file');
96
+ }
97
+ const ext = node_path_1.default.extname(filePath).toLowerCase();
98
+ if (ext !== '.mp3') {
99
+ throw new HlsStreamerErrors_1.InvalidFileError('Only MP3 files are supported');
100
+ }
101
+ }
102
+ async getFileInfo() {
103
+ if (!this.fileInfo) {
104
+ const stat = await node_fs_1.default.promises.stat(this.filePath);
105
+ const size = stat.size;
106
+ if (size <= 0) {
107
+ throw new HlsStreamerErrors_1.InvalidFileError('File is empty');
108
+ }
109
+ if (this.segmentSize > size) {
110
+ throw new HlsStreamerErrors_1.InvalidFileError('Segment size is larger than file size');
111
+ }
112
+ this.fileInfo = {
113
+ size,
114
+ duration: 0
115
+ };
116
+ }
117
+ return this.fileInfo;
118
+ }
119
+ async getFileBuffer(startByte, endByte) {
120
+ if (isNaN(startByte) || isNaN(endByte) || startByte < 0 || endByte < startByte) {
121
+ throw new HlsStreamerErrors_1.InvalidRangeError(startByte, endByte);
122
+ }
123
+ const fileInfo = await this.getFileInfo();
124
+ if (endByte > fileInfo.size) {
125
+ throw new HlsStreamerErrors_1.InvalidRangeError(startByte, endByte);
126
+ }
127
+ const bufferLength = endByte - startByte;
128
+ const fd = node_fs_1.default.openSync(this.filePath, "r");
129
+ try {
130
+ const buffer = Buffer.alloc(bufferLength);
131
+ const bytesRead = node_fs_1.default.readSync(fd, buffer, 0, bufferLength, startByte);
132
+ return buffer.subarray(0, bytesRead);
133
+ }
134
+ finally {
135
+ node_fs_1.default.closeSync(fd);
136
+ }
137
+ }
138
+ async createM3U8() {
139
+ const fileInfo = await this.getFileInfo();
140
+ const segmentCount = this.calculateSegmentCount(fileInfo.size);
141
+ const m3u8 = [
142
+ '#EXTM3U',
143
+ '#EXT-X-VERSION:6',
144
+ '#EXT-X-PLAYLIST-TYPE:VOD',
145
+ '#EXT-X-TARGETDURATION:14',
146
+ '#EXT-X-MEDIA-SEQUENCE:0',
147
+ ];
148
+ for (let i = 0; i < segmentCount; i++) {
149
+ const { start, end } = this.calculateSegment(i, fileInfo.size);
150
+ const estimatedDuration = this.estimateSegmentDuration(end - start);
151
+ const segmentUrl = this.buildSegmentUrl(start, end, i);
152
+ m3u8.push(`#EXTINF:${estimatedDuration.toFixed(3)}`);
153
+ m3u8.push(segmentUrl);
154
+ }
155
+ m3u8.push('#EXT-X-ENDLIST');
156
+ return m3u8.join('\n');
157
+ }
158
+ calculateSegmentCount(fileSize) {
159
+ if (!this.enableFastStart) {
160
+ return Math.ceil(fileSize / this.segmentSize);
161
+ }
162
+ const firstTwoSegmentsSize = this.calculatefirst2SegmentSize();
163
+ const remainingSize = fileSize - firstTwoSegmentsSize;
164
+ return Math.ceil(remainingSize / this.segmentSize) + 2;
165
+ }
166
+ estimateSegmentDuration(segmentSize) {
167
+ const estimatedBytesPerSecond = 16000;
168
+ return segmentSize / estimatedBytesPerSecond;
169
+ }
170
+ buildSegmentUrl(start, end, index) {
171
+ const baseUrlPrefix = this.baseUrl ? `/${this.baseUrl}` : '';
172
+ return `${baseUrlPrefix}/${start}/${end}/${this.fileName}${this.padNumber(index, 3)}.mp3`;
173
+ }
174
+ calculateSegmentSize(segmentIndex) {
175
+ if (!this.enableFastStart) {
176
+ return this.segmentSize;
177
+ }
178
+ switch (segmentIndex) {
179
+ case 0:
180
+ return this.segmentSize / 4;
181
+ case 1:
182
+ return this.segmentSize / 2;
183
+ default:
184
+ return this.segmentSize;
185
+ }
186
+ }
187
+ calculateSegment(segmentIndex, fileSize) {
188
+ let start = 0;
189
+ if (this.enableFastStart && segmentIndex < 2) {
190
+ start = segmentIndex * (this.segmentSize / 4);
191
+ }
192
+ else if (this.enableFastStart) {
193
+ start = (3 * this.segmentSize) / 4 + (segmentIndex - 2) * this.segmentSize;
194
+ }
195
+ else {
196
+ start = segmentIndex * this.segmentSize;
197
+ }
198
+ const segmentSize = this.calculateSegmentSize(segmentIndex);
199
+ const end = Math.min(start + segmentSize, fileSize);
200
+ return { start: Math.floor(start), end: Math.floor(end) };
201
+ }
202
+ calculatefirst2SegmentSize() {
203
+ return (this.segmentSize / 4) * 3;
204
+ }
205
+ padNumber(value, padding) {
206
+ return value.toString().padStart(padding, '0');
207
+ }
208
+ async getSegmentDuration(segmentIndex) {
209
+ const cachedSegment = this.segmentCache.get(segmentIndex);
210
+ if (cachedSegment) {
211
+ return cachedSegment.duration;
212
+ }
213
+ const fileInfo = await this.getFileInfo();
214
+ const { start, end } = this.calculateSegment(segmentIndex, fileInfo.size);
215
+ const segmentBuffer = await this.getFileBuffer(start, end);
216
+ const duration = await FileLib_1.FileLib.getMP3DurationFromBuffer(segmentBuffer);
217
+ this.segmentCache.set(segmentIndex, {
218
+ start,
219
+ end,
220
+ duration
221
+ });
222
+ return duration;
223
+ }
224
+ }
225
+ exports.HlsStreamer = HlsStreamer;
226
+ __exportStar(require("./Interfaces/HlsStreamer"), exports);
227
+ __exportStar(require("./errors/HlsStreamerErrors"), exports);
228
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export declare class HlsStreamerError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class FileNotFoundError extends HlsStreamerError {
5
+ constructor(filePath: string);
6
+ }
7
+ export declare class InvalidFileError extends HlsStreamerError {
8
+ constructor(message: string);
9
+ }
10
+ export declare class InvalidRangeError extends HlsStreamerError {
11
+ constructor(startByte: number, endByte: number);
12
+ }
13
+ export declare class InvalidParameterError extends HlsStreamerError {
14
+ constructor(parameter: string, value: any);
15
+ }
16
+ //# sourceMappingURL=HlsStreamerErrors.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,31 @@
1
+ export class HlsStreamerError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = 'HlsStreamerError';
5
+ }
6
+ }
7
+ export class FileNotFoundError extends HlsStreamerError {
8
+ constructor(filePath) {
9
+ super(`File not found: ${filePath}`);
10
+ this.name = 'FileNotFoundError';
11
+ }
12
+ }
13
+ export class InvalidFileError extends HlsStreamerError {
14
+ constructor(message) {
15
+ super(`Invalid file: ${message}`);
16
+ this.name = 'InvalidFileError';
17
+ }
18
+ }
19
+ export class InvalidRangeError extends HlsStreamerError {
20
+ constructor(startByte, endByte) {
21
+ super(`Invalid range: start=${startByte}, end=${endByte}`);
22
+ this.name = 'InvalidRangeError';
23
+ }
24
+ }
25
+ export class InvalidParameterError extends HlsStreamerError {
26
+ constructor(parameter, value) {
27
+ super(`Invalid parameter '${parameter}': ${value}`);
28
+ this.name = 'InvalidParameterError';
29
+ }
30
+ }
31
+ //# sourceMappingURL=HlsStreamerErrors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HlsStreamerErrors.js","sourceRoot":"","sources":["../../src/errors/HlsStreamerErrors.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAKD,MAAM,OAAO,iBAAkB,SAAQ,gBAAgB;IACrD,YAAY,QAAgB;QAC1B,KAAK,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAKD,MAAM,OAAO,gBAAiB,SAAQ,gBAAgB;IACpD,YAAY,OAAe;QACzB,KAAK,CAAC,iBAAiB,OAAO,EAAE,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAKD,MAAM,OAAO,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;AAKD,MAAM,OAAO,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"}
@@ -0,0 +1,27 @@
1
+ import { HlsStreamerOptions } from "./Interfaces/HlsStreamer";
2
+ export declare class HlsStreamer {
3
+ private readonly filePath;
4
+ private readonly segmentSize;
5
+ private readonly fileName;
6
+ private readonly baseUrl;
7
+ private readonly enableFastStart;
8
+ private fileInfo?;
9
+ private segmentCache;
10
+ constructor(options: HlsStreamerOptions);
11
+ private validateOptions;
12
+ private validateFile;
13
+ private getFileInfo;
14
+ getFileBuffer(startByte: number, endByte: number): Promise<Buffer>;
15
+ createM3U8(): Promise<string>;
16
+ private calculateSegmentCount;
17
+ private estimateSegmentDuration;
18
+ private buildSegmentUrl;
19
+ private calculateSegmentSize;
20
+ private calculateSegment;
21
+ private calculatefirst2SegmentSize;
22
+ private padNumber;
23
+ getSegmentDuration(segmentIndex: number): Promise<number>;
24
+ }
25
+ export * from './Interfaces/HlsStreamer';
26
+ export * from './errors/HlsStreamerErrors';
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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;gBAK1C,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;IAyBlE,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;IAoBxB,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"}
package/dist/index.js ADDED
@@ -0,0 +1,207 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { FileLib } from "./Libs/FileLib";
4
+ import { FileNotFoundError, InvalidFileError, InvalidRangeError, InvalidParameterError } from "./errors/HlsStreamerErrors";
5
+ export class HlsStreamer {
6
+ constructor(options) {
7
+ Object.defineProperty(this, "filePath", {
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true,
11
+ value: void 0
12
+ });
13
+ Object.defineProperty(this, "segmentSize", {
14
+ enumerable: true,
15
+ configurable: true,
16
+ writable: true,
17
+ value: void 0
18
+ });
19
+ Object.defineProperty(this, "fileName", {
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true,
23
+ value: void 0
24
+ });
25
+ Object.defineProperty(this, "baseUrl", {
26
+ enumerable: true,
27
+ configurable: true,
28
+ writable: true,
29
+ value: void 0
30
+ });
31
+ Object.defineProperty(this, "enableFastStart", {
32
+ enumerable: true,
33
+ configurable: true,
34
+ writable: true,
35
+ value: void 0
36
+ });
37
+ Object.defineProperty(this, "fileInfo", {
38
+ enumerable: true,
39
+ configurable: true,
40
+ writable: true,
41
+ value: void 0
42
+ });
43
+ Object.defineProperty(this, "segmentCache", {
44
+ enumerable: true,
45
+ configurable: true,
46
+ writable: true,
47
+ value: new Map()
48
+ });
49
+ this.validateOptions(options);
50
+ this.validateFile(options.filePath);
51
+ this.filePath = options.filePath;
52
+ this.segmentSize = (options.segmentSizeKB ?? 512) * 1024;
53
+ this.fileName = options.fileName ?? "file";
54
+ this.baseUrl = options.baseUrl ?? "";
55
+ this.enableFastStart = options.enableFastStart ?? false;
56
+ }
57
+ validateOptions(options) {
58
+ if (!options.filePath || typeof options.filePath !== 'string') {
59
+ throw new InvalidParameterError('filePath', options.filePath);
60
+ }
61
+ if (options.segmentSizeKB !== undefined &&
62
+ (typeof options.segmentSizeKB !== 'number' || options.segmentSizeKB <= 0)) {
63
+ throw new InvalidParameterError('segmentSizeKB', options.segmentSizeKB);
64
+ }
65
+ if (options.fileName !== undefined && typeof options.fileName !== 'string') {
66
+ throw new InvalidParameterError('fileName', options.fileName);
67
+ }
68
+ }
69
+ validateFile(filePath) {
70
+ if (!fs.existsSync(filePath)) {
71
+ throw new FileNotFoundError(filePath);
72
+ }
73
+ const stat = fs.statSync(filePath);
74
+ if (!stat.isFile()) {
75
+ throw new InvalidFileError('Path is not a file');
76
+ }
77
+ const ext = path.extname(filePath).toLowerCase();
78
+ if (ext !== '.mp3') {
79
+ throw new InvalidFileError('Only MP3 files are supported');
80
+ }
81
+ }
82
+ async getFileInfo() {
83
+ if (!this.fileInfo) {
84
+ const stat = await fs.promises.stat(this.filePath);
85
+ const size = stat.size;
86
+ if (size <= 0) {
87
+ throw new InvalidFileError('File is empty');
88
+ }
89
+ if (this.segmentSize > size) {
90
+ throw new InvalidFileError('Segment size is larger than file size');
91
+ }
92
+ this.fileInfo = {
93
+ size,
94
+ duration: 0
95
+ };
96
+ }
97
+ return this.fileInfo;
98
+ }
99
+ async getFileBuffer(startByte, endByte) {
100
+ if (isNaN(startByte) || isNaN(endByte) || startByte < 0 || endByte < startByte) {
101
+ throw new InvalidRangeError(startByte, endByte);
102
+ }
103
+ const fileInfo = await this.getFileInfo();
104
+ if (endByte > fileInfo.size) {
105
+ throw new InvalidRangeError(startByte, endByte);
106
+ }
107
+ const bufferLength = endByte - startByte;
108
+ const fd = fs.openSync(this.filePath, "r");
109
+ try {
110
+ const buffer = Buffer.alloc(bufferLength);
111
+ const bytesRead = fs.readSync(fd, buffer, 0, bufferLength, startByte);
112
+ return buffer.subarray(0, bytesRead);
113
+ }
114
+ finally {
115
+ fs.closeSync(fd);
116
+ }
117
+ }
118
+ async createM3U8() {
119
+ const fileInfo = await this.getFileInfo();
120
+ const segmentCount = this.calculateSegmentCount(fileInfo.size);
121
+ const m3u8 = [
122
+ '#EXTM3U',
123
+ '#EXT-X-VERSION:6',
124
+ '#EXT-X-PLAYLIST-TYPE:VOD',
125
+ '#EXT-X-TARGETDURATION:14',
126
+ '#EXT-X-MEDIA-SEQUENCE:0',
127
+ ];
128
+ for (let i = 0; i < segmentCount; i++) {
129
+ const { start, end } = this.calculateSegment(i, fileInfo.size);
130
+ const estimatedDuration = this.estimateSegmentDuration(end - start);
131
+ const segmentUrl = this.buildSegmentUrl(start, end, i);
132
+ m3u8.push(`#EXTINF:${estimatedDuration.toFixed(3)}`);
133
+ m3u8.push(segmentUrl);
134
+ }
135
+ m3u8.push('#EXT-X-ENDLIST');
136
+ return m3u8.join('\n');
137
+ }
138
+ calculateSegmentCount(fileSize) {
139
+ if (!this.enableFastStart) {
140
+ return Math.ceil(fileSize / this.segmentSize);
141
+ }
142
+ const firstTwoSegmentsSize = this.calculatefirst2SegmentSize();
143
+ const remainingSize = fileSize - firstTwoSegmentsSize;
144
+ return Math.ceil(remainingSize / this.segmentSize) + 2;
145
+ }
146
+ estimateSegmentDuration(segmentSize) {
147
+ const estimatedBytesPerSecond = 16000;
148
+ return segmentSize / estimatedBytesPerSecond;
149
+ }
150
+ buildSegmentUrl(start, end, index) {
151
+ const baseUrlPrefix = this.baseUrl ? `/${this.baseUrl}` : '';
152
+ return `${baseUrlPrefix}/${start}/${end}/${this.fileName}${this.padNumber(index, 3)}.mp3`;
153
+ }
154
+ calculateSegmentSize(segmentIndex) {
155
+ if (!this.enableFastStart) {
156
+ return this.segmentSize;
157
+ }
158
+ switch (segmentIndex) {
159
+ case 0:
160
+ return this.segmentSize / 4;
161
+ case 1:
162
+ return this.segmentSize / 2;
163
+ default:
164
+ return this.segmentSize;
165
+ }
166
+ }
167
+ calculateSegment(segmentIndex, fileSize) {
168
+ let start = 0;
169
+ if (this.enableFastStart && segmentIndex < 2) {
170
+ start = segmentIndex * (this.segmentSize / 4);
171
+ }
172
+ else if (this.enableFastStart) {
173
+ start = (3 * this.segmentSize) / 4 + (segmentIndex - 2) * this.segmentSize;
174
+ }
175
+ else {
176
+ start = segmentIndex * this.segmentSize;
177
+ }
178
+ const segmentSize = this.calculateSegmentSize(segmentIndex);
179
+ const end = Math.min(start + segmentSize, fileSize);
180
+ return { start: Math.floor(start), end: Math.floor(end) };
181
+ }
182
+ calculatefirst2SegmentSize() {
183
+ return (this.segmentSize / 4) * 3;
184
+ }
185
+ padNumber(value, padding) {
186
+ return value.toString().padStart(padding, '0');
187
+ }
188
+ async getSegmentDuration(segmentIndex) {
189
+ const cachedSegment = this.segmentCache.get(segmentIndex);
190
+ if (cachedSegment) {
191
+ return cachedSegment.duration;
192
+ }
193
+ const fileInfo = await this.getFileInfo();
194
+ const { start, end } = this.calculateSegment(segmentIndex, fileInfo.size);
195
+ const segmentBuffer = await this.getFileBuffer(start, end);
196
+ const duration = await FileLib.getMP3DurationFromBuffer(segmentBuffer);
197
+ this.segmentCache.set(segmentIndex, {
198
+ start,
199
+ end,
200
+ duration
201
+ });
202
+ return duration;
203
+ }
204
+ }
205
+ export * from './Interfaces/HlsStreamer';
206
+ export * from './errors/HlsStreamerErrors';
207
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "hls-streamer",
3
+ "version": "2.0.0",
4
+ "description": "Lightweight HLS streaming from MP3 files on demand, without temporary files or ffmpeg dependency",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/cjs/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "rm -rf dist/ && npm run build:esm && npm run build:cjs",
22
+ "build:esm": "tsc",
23
+ "build:cjs": "tsc --module CommonJS --outDir dist/cjs",
24
+ "test": "jest",
25
+ "test:watch": "jest --watch",
26
+ "test:coverage": "jest --coverage",
27
+ "prepublishOnly": "npm run build && npm test",
28
+ "lint": "tsc --noEmit",
29
+ "clean": "rm -rf dist/"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/LordVersA/hls-streamer.git"
34
+ },
35
+ "keywords": [
36
+ "hls",
37
+ "streaming",
38
+ "mp3",
39
+ "audio",
40
+ "http-live-streaming",
41
+ "media",
42
+ "ffmpeg-free",
43
+ "on-demand",
44
+ "typescript",
45
+ "nodejs"
46
+ ],
47
+ "author": {
48
+ "name": "LordVersA",
49
+ "url": "https://github.com/LordVersA"
50
+ },
51
+ "license": "MIT",
52
+ "bugs": {
53
+ "url": "https://github.com/LordVersA/hls-streamer/issues"
54
+ },
55
+ "homepage": "https://github.com/LordVersA/hls-streamer#readme",
56
+ "engines": {
57
+ "node": ">=14.0.0"
58
+ },
59
+ "devDependencies": {
60
+ "@types/jest": "^30.0.0",
61
+ "@types/node": "^20.8.3",
62
+ "jest": "^30.1.3",
63
+ "ts-jest": "^29.4.4",
64
+ "typescript": "^5.9.2"
65
+ },
66
+ "dependencies": {
67
+ "mp3-duration": "^1.1.0"
68
+ }
69
+ }