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.
- package/LICENSE +21 -0
- package/README.md +237 -0
- package/dist/Interfaces/HlsStreamer.d.ts +17 -0
- package/dist/Interfaces/HlsStreamer.d.ts.map +1 -0
- package/dist/Interfaces/HlsStreamer.js +2 -0
- package/dist/Interfaces/HlsStreamer.js.map +1 -0
- package/dist/Libs/FileLib.d.ts +5 -0
- package/dist/Libs/FileLib.d.ts.map +1 -0
- package/dist/Libs/FileLib.js +24 -0
- package/dist/Libs/FileLib.js.map +1 -0
- package/dist/cjs/Interfaces/HlsStreamer.d.ts +17 -0
- package/dist/cjs/Interfaces/HlsStreamer.d.ts.map +1 -0
- package/dist/cjs/Interfaces/HlsStreamer.js +3 -0
- package/dist/cjs/Interfaces/HlsStreamer.js.map +1 -0
- package/dist/cjs/Libs/FileLib.d.ts +5 -0
- package/dist/cjs/Libs/FileLib.d.ts.map +1 -0
- package/dist/cjs/Libs/FileLib.js +31 -0
- package/dist/cjs/Libs/FileLib.js.map +1 -0
- package/dist/cjs/errors/HlsStreamerErrors.d.ts +16 -0
- package/dist/cjs/errors/HlsStreamerErrors.d.ts.map +1 -0
- package/dist/cjs/errors/HlsStreamerErrors.js +39 -0
- package/dist/cjs/errors/HlsStreamerErrors.js.map +1 -0
- package/dist/cjs/index.d.ts +27 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +228 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/errors/HlsStreamerErrors.d.ts +16 -0
- package/dist/errors/HlsStreamerErrors.d.ts.map +1 -0
- package/dist/errors/HlsStreamerErrors.js +31 -0
- package/dist/errors/HlsStreamerErrors.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +207 -0
- package/dist/index.js.map +1 -0
- 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
|
+
[](https://badge.fury.io/js/hls-streamer)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](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 @@
|
|
|
1
|
+
{"version":3,"file":"HlsStreamer.js","sourceRoot":"","sources":["../../src/Interfaces/HlsStreamer.ts"],"names":[],"mappings":""}
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"HlsStreamer.js","sourceRoot":"","sources":["../../../src/Interfaces/HlsStreamer.ts"],"names":[],"mappings":""}
|
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|