hls-streamer 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +189 -190
- package/dist/Interfaces/HlsStreamer.d.ts +20 -0
- package/dist/Interfaces/HlsStreamer.d.ts.map +1 -1
- package/dist/Libs/FileLib.d.ts +22 -1
- package/dist/Libs/FileLib.d.ts.map +1 -1
- package/dist/Libs/FileLib.js +273 -108
- package/dist/Libs/FileLib.js.map +1 -1
- package/dist/Libs/FormatDetector.d.ts +8 -0
- package/dist/Libs/FormatDetector.d.ts.map +1 -0
- package/dist/Libs/FormatDetector.js +63 -0
- package/dist/Libs/FormatDetector.js.map +1 -0
- package/dist/Parsers/AacParser.d.ts +15 -0
- package/dist/Parsers/AacParser.d.ts.map +1 -0
- package/dist/Parsers/AacParser.js +246 -0
- package/dist/Parsers/AacParser.js.map +1 -0
- package/dist/Parsers/FlacParser.d.ts +13 -0
- package/dist/Parsers/FlacParser.d.ts.map +1 -0
- package/dist/Parsers/FlacParser.js +200 -0
- package/dist/Parsers/FlacParser.js.map +1 -0
- package/dist/Parsers/IAudioParser.d.ts +31 -0
- package/dist/Parsers/IAudioParser.d.ts.map +1 -0
- package/dist/Parsers/IAudioParser.js +2 -0
- package/dist/Parsers/IAudioParser.js.map +1 -0
- package/dist/Parsers/Mp3Parser.d.ts +19 -0
- package/dist/Parsers/Mp3Parser.d.ts.map +1 -0
- package/dist/Parsers/Mp3Parser.js +270 -0
- package/dist/Parsers/Mp3Parser.js.map +1 -0
- package/dist/Parsers/OggParser.d.ts +11 -0
- package/dist/Parsers/OggParser.d.ts.map +1 -0
- package/dist/Parsers/OggParser.js +160 -0
- package/dist/Parsers/OggParser.js.map +1 -0
- package/dist/Parsers/ParserFactory.d.ts +10 -0
- package/dist/Parsers/ParserFactory.d.ts.map +1 -0
- package/dist/Parsers/ParserFactory.js +43 -0
- package/dist/Parsers/ParserFactory.js.map +1 -0
- package/dist/Parsers/WavParser.d.ts +9 -0
- package/dist/Parsers/WavParser.d.ts.map +1 -0
- package/dist/Parsers/WavParser.js +135 -0
- package/dist/Parsers/WavParser.js.map +1 -0
- package/dist/cjs/Interfaces/HlsStreamer.d.ts +20 -0
- package/dist/cjs/Interfaces/HlsStreamer.d.ts.map +1 -1
- package/dist/cjs/Libs/FileLib.d.ts +22 -1
- package/dist/cjs/Libs/FileLib.d.ts.map +1 -1
- package/dist/cjs/Libs/FileLib.js +276 -108
- package/dist/cjs/Libs/FileLib.js.map +1 -1
- package/dist/cjs/Libs/FormatDetector.d.ts +8 -0
- package/dist/cjs/Libs/FormatDetector.d.ts.map +1 -0
- package/dist/cjs/Libs/FormatDetector.js +67 -0
- package/dist/cjs/Libs/FormatDetector.js.map +1 -0
- package/dist/cjs/Parsers/AacParser.d.ts +15 -0
- package/dist/cjs/Parsers/AacParser.d.ts.map +1 -0
- package/dist/cjs/Parsers/AacParser.js +250 -0
- package/dist/cjs/Parsers/AacParser.js.map +1 -0
- package/dist/cjs/Parsers/FlacParser.d.ts +13 -0
- package/dist/cjs/Parsers/FlacParser.d.ts.map +1 -0
- package/dist/cjs/Parsers/FlacParser.js +204 -0
- package/dist/cjs/Parsers/FlacParser.js.map +1 -0
- package/dist/cjs/Parsers/IAudioParser.d.ts +31 -0
- package/dist/cjs/Parsers/IAudioParser.d.ts.map +1 -0
- package/dist/cjs/Parsers/IAudioParser.js +3 -0
- package/dist/cjs/Parsers/IAudioParser.js.map +1 -0
- package/dist/cjs/Parsers/Mp3Parser.d.ts +19 -0
- package/dist/cjs/Parsers/Mp3Parser.d.ts.map +1 -0
- package/dist/cjs/Parsers/Mp3Parser.js +274 -0
- package/dist/cjs/Parsers/Mp3Parser.js.map +1 -0
- package/dist/cjs/Parsers/OggParser.d.ts +11 -0
- package/dist/cjs/Parsers/OggParser.d.ts.map +1 -0
- package/dist/cjs/Parsers/OggParser.js +164 -0
- package/dist/cjs/Parsers/OggParser.js.map +1 -0
- package/dist/cjs/Parsers/ParserFactory.d.ts +10 -0
- package/dist/cjs/Parsers/ParserFactory.d.ts.map +1 -0
- package/dist/cjs/Parsers/ParserFactory.js +47 -0
- package/dist/cjs/Parsers/ParserFactory.js.map +1 -0
- package/dist/cjs/Parsers/WavParser.d.ts +9 -0
- package/dist/cjs/Parsers/WavParser.d.ts.map +1 -0
- package/dist/cjs/Parsers/WavParser.js +139 -0
- package/dist/cjs/Parsers/WavParser.js.map +1 -0
- package/dist/cjs/errors/HlsStreamerErrors.d.ts +3 -0
- package/dist/cjs/errors/HlsStreamerErrors.d.ts.map +1 -1
- package/dist/cjs/errors/HlsStreamerErrors.js +8 -1
- package/dist/cjs/errors/HlsStreamerErrors.js.map +1 -1
- package/dist/cjs/index.d.ts +6 -7
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +161 -138
- package/dist/cjs/index.js.map +1 -1
- package/dist/errors/HlsStreamerErrors.d.ts +3 -0
- package/dist/errors/HlsStreamerErrors.d.ts.map +1 -1
- package/dist/errors/HlsStreamerErrors.js +6 -0
- package/dist/errors/HlsStreamerErrors.js.map +1 -1
- package/dist/index.d.ts +6 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +162 -139
- package/dist/index.js.map +1 -1
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -2,254 +2,253 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/js/hls-streamer)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
HLS Streamer turns any audio file (MP3, AAC, M4A, OGG Vorbis, FLAC, WAV) into an HTTP Live Streaming (HLS) playlist on the fly. It analyses the source audio in-memory, builds frame-aligned byte ranges, and streams them without temporary files, native bindings, or external binaries like ffmpeg.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- 🚀 **True Zero Dependencies** - No external dependencies, no ffmpeg, no native binaries
|
|
12
|
-
- 🎵 **Built-in MP3 Parser** - Custom MP3 duration parsing without external libraries
|
|
13
|
-
- 🔄 **Frame-Aligned Segments** - Ensures all MP3 segments start at valid frame boundaries for seamless playback
|
|
14
|
-
- 💾 **No Temporary Files** - Stream directly from source
|
|
15
|
-
- ⚡ **Fast Startup** - Optional smaller initial segments for quick playback start
|
|
16
|
-
- 🎯 **TypeScript Support** - Full type definitions included
|
|
17
|
-
- 🔧 **Configurable** - Customizable segment sizes and naming
|
|
18
|
-
- 📱 **Memory Efficient** - Byte-range streaming with minimal memory footprint
|
|
9
|
+
---
|
|
19
10
|
|
|
20
|
-
|
|
11
|
+
- [Why HLS Streamer?](#why-hls-streamer)
|
|
12
|
+
- [How It Works](#how-it-works)
|
|
13
|
+
- [Quick Start](#quick-start)
|
|
14
|
+
- [Serving Over HTTP](#serving-over-http)
|
|
15
|
+
- [Configuration Reference](#configuration-reference)
|
|
16
|
+
- [Playlist Anatomy](#playlist-anatomy)
|
|
17
|
+
- [Operational Tips](#operational-tips)
|
|
18
|
+
- [Development](#development)
|
|
19
|
+
- [Support](#support)
|
|
20
|
+
|
|
21
|
+
## Why HLS Streamer?
|
|
22
|
+
|
|
23
|
+
- **Multi-format support** – handles MP3, AAC, M4A, OGG Vorbis, FLAC, and WAV with automatic format detection.
|
|
24
|
+
- **Zero dependencies** – no shared libraries, no ffmpeg, no native compilation. Pure TypeScript parsers for all formats. Drop it into Docker, serverless, or edge runtimes.
|
|
25
|
+
- **Accurate segments** – real audio frame/packet parsing provides true durations, `#EXTINF` metadata, and target durations that match playback.
|
|
26
|
+
- **Frame-aligned byte ranges** – every segment begins and ends on verified frame/packet boundaries, preventing pops and clipped audio.
|
|
27
|
+
- **No temp files** – streams straight from the source audio file using byte-range reads.
|
|
28
|
+
- **Fast-start aware** – optional smaller first segments improve startup latency for constrained networks.
|
|
29
|
+
- **TypeScript first** – authored in TypeScript with full type definitions for your tooling and IDEs.
|
|
30
|
+
|
|
31
|
+
## How It Works
|
|
32
|
+
|
|
33
|
+
1. **Format detection** – automatically detects audio format from file content (magic bytes) or extension, with optional manual override.
|
|
34
|
+
2. **Metadata analysis** – format-specific parsers extract metadata (ID3/Vorbis comments/etc.), parse frame/packet headers, and produce a frame table with offsets, durations, and bitrates.
|
|
35
|
+
3. **Segment planning** – segment boundaries are calculated from the frame table so each segment contains whole frames/packets while matching your target sizes.
|
|
36
|
+
4. **Playlist generation** – `createM3U8()` emits an `#EXTM3U` playlist with accurate `#EXTINF` entries and `#EXT-X-TARGETDURATION` derived from the longest segment.
|
|
37
|
+
5. **On-demand byte ranges** – `getFileBuffer(start, end)` streams the exact bytes for a segment without reading the entire file into memory.
|
|
21
38
|
|
|
22
|
-
```bash
|
|
23
|
-
npm install hls-streamer
|
|
24
39
|
```
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
┌──────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────────┐
|
|
41
|
+
│ Audio Source │ ──▶ │ Format Detector │ ──▶ │ Format Parser │ ──▶ │ Segment Planner │
|
|
42
|
+
│ (any format) │ │ (magic bytes) │ │ (MP3/AAC/etc.) │ │ (frame-aligned) │
|
|
43
|
+
└──────────────┘ └─────────────────┘ └─────────────────┘ └──────────┬───────────┘
|
|
44
|
+
│
|
|
45
|
+
▼
|
|
46
|
+
┌──────────────────────┐
|
|
47
|
+
│ HLS Playlist & Bytes │
|
|
48
|
+
└──────────────────────┘
|
|
32
49
|
```
|
|
33
50
|
|
|
34
|
-
##
|
|
35
|
-
|
|
36
|
-
This package is **truly zero-dependency**, meaning:
|
|
37
|
-
|
|
38
|
-
- ✅ **No ffmpeg** - No external binary dependencies
|
|
39
|
-
- ✅ **No native modules** - Pure JavaScript/TypeScript implementation
|
|
40
|
-
- ✅ **No runtime dependencies** - Check `package.json` - completely empty dependencies
|
|
41
|
-
- ✅ **Custom MP3 parser** - Built-in MP3 header parsing and duration calculation
|
|
42
|
-
- ✅ **Frame-perfect segmentation** - MP3 segments start at valid frame boundaries
|
|
43
|
-
- ✅ **Cross-platform** - Works on any platform that supports Node.js
|
|
44
|
-
|
|
45
|
-
Perfect for:
|
|
46
|
-
- 🐳 **Docker containers** - No need to install ffmpeg
|
|
47
|
-
- 🌐 **Serverless functions** - Minimal package size and cold start time
|
|
48
|
-
- 📱 **Edge computing** - Lightweight deployment
|
|
49
|
-
- 🔒 **Security-conscious environments** - No external binaries to audit
|
|
51
|
+
## Quick Start
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
### Basic Usage
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
53
|
+
```ts
|
|
56
54
|
import { HlsStreamer } from 'hls-streamer';
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
// Works with any supported format - auto-detected!
|
|
57
|
+
const streamer = new HlsStreamer({
|
|
58
|
+
filePath: '/media/library/song.mp3', // or .aac, .m4a, .ogg, .flac, .wav
|
|
60
59
|
segmentSizeKB: 512,
|
|
61
|
-
fileName: '
|
|
62
|
-
baseUrl: '
|
|
63
|
-
enableFastStart: true
|
|
60
|
+
fileName: 'track',
|
|
61
|
+
baseUrl: 'audio/stream/session-42',
|
|
62
|
+
enableFastStart: true,
|
|
63
|
+
// format: 'mp3' // optional: override auto-detection
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
const playlist = await streamer.createM3U8();
|
|
67
|
+
console.log(playlist);
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
const buffer = await hls.getFileBuffer(startByte, endByte);
|
|
69
|
+
const firstSegmentBuffer = await streamer.getFileBuffer(0, 512 * 1024);
|
|
71
70
|
```
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
## Serving Over HTTP
|
|
74
73
|
|
|
75
|
-
|
|
74
|
+
Create playlists and segment endpoints with any HTTP framework. The example below shows an Express setup:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
76
77
|
import express from 'express';
|
|
77
78
|
import { HlsStreamer } from 'hls-streamer';
|
|
78
79
|
|
|
79
80
|
const app = express();
|
|
80
81
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
app.get('/streams/:id/playlist.m3u8', async (req, res, next) => {
|
|
83
|
+
try {
|
|
84
|
+
const streamer = new HlsStreamer({
|
|
85
|
+
filePath: resolveAudioPath(req.params.id),
|
|
86
|
+
baseUrl: `streams/${req.params.id}`,
|
|
87
|
+
enableFastStart: true,
|
|
88
|
+
});
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
res.type('application/vnd.apple.mpegurl');
|
|
91
|
+
res.send(await streamer.createM3U8());
|
|
92
|
+
} catch (error) {
|
|
93
|
+
next(error);
|
|
94
|
+
}
|
|
91
95
|
});
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
app.get('/streams/:id/:start/:end/:filename', async (req, res, next) => {
|
|
98
|
+
try {
|
|
99
|
+
const streamer = new HlsStreamer({
|
|
100
|
+
filePath: resolveAudioPath(req.params.id),
|
|
101
|
+
baseUrl: `streams/${req.params.id}`,
|
|
102
|
+
});
|
|
99
103
|
|
|
100
|
-
|
|
101
|
-
|
|
104
|
+
const start = Number(req.params.start);
|
|
105
|
+
const end = Number(req.params.end);
|
|
102
106
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
107
|
+
res.type('audio/mpeg');
|
|
108
|
+
res.set('Accept-Ranges', 'bytes');
|
|
109
|
+
res.send(await streamer.getFileBuffer(start, end));
|
|
110
|
+
} catch (error) {
|
|
111
|
+
next(error);
|
|
112
|
+
}
|
|
106
113
|
});
|
|
107
114
|
```
|
|
108
115
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
### HlsStreamer
|
|
116
|
+
### Segment URL Contract
|
|
112
117
|
|
|
113
|
-
|
|
118
|
+
Generated playlists follow the pattern below:
|
|
114
119
|
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
/** Path to the MP3 file */
|
|
118
|
-
filePath: string;
|
|
119
|
-
/** Segment size in KB (default: 512) */
|
|
120
|
-
segmentSizeKB?: number;
|
|
121
|
-
/** Base filename for segments (default: "file") */
|
|
122
|
-
fileName?: string;
|
|
123
|
-
/** Base URL path for segment URLs */
|
|
124
|
-
baseUrl?: string;
|
|
125
|
-
/** Enable smaller initial segments for faster startup */
|
|
126
|
-
enableFastStart?: boolean;
|
|
127
|
-
}
|
|
120
|
+
```
|
|
121
|
+
/{baseUrl}/{startByte}/{endByte}/{fileName}{index}.mp3
|
|
128
122
|
```
|
|
129
123
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
124
|
+
- `startByte` is inclusive, `endByte` is exclusive.
|
|
125
|
+
- `index` is zero-padded to three digits (`000`, `001`, ...).
|
|
126
|
+
- Use the provided byte range as-is when serving `audio/mpeg` responses.
|
|
127
|
+
|
|
128
|
+
## Configuration Reference
|
|
129
|
+
|
|
130
|
+
| Option | Type | Default | Description |
|
|
131
|
+
| ------------------- | --------- | ------- | ----------- |
|
|
132
|
+
| `filePath` | `string` | — | Absolute or relative path to the audio file. Supports: MP3, AAC, M4A, OGG, FLAC, WAV. |
|
|
133
|
+
| `segmentSizeKB` | `number` | `512` | Target segment size in kilobytes. Fast-start mode splits the first two segments into quarters/halves of this value. |
|
|
134
|
+
| `fileName` | `string` | `"file"` | Base name used in generated segment URLs (the index is appended automatically). |
|
|
135
|
+
| `baseUrl` | `string` | `""` | URL prefix inserted before each segment path. Useful when mounting under a route or CDN prefix. |
|
|
136
|
+
| `enableFastStart` | `boolean` | `false` | When true, the first two segments are smaller to reduce initial buffering time. |
|
|
137
|
+
| `format` | `AudioFormat` | auto-detect | Optional format override ('mp3', 'aac', 'm4a', 'ogg', 'flac', 'wav'). Auto-detected from file if not specified. |
|
|
138
|
+
|
|
139
|
+
### API Surface
|
|
140
|
+
|
|
141
|
+
- `createM3U8(): Promise<string>` – Returns a full playlist with frame-accurate durations.
|
|
142
|
+
- `getFileBuffer(start: number, end: number): Promise<Buffer>` – Streams a byte range from the audio file.
|
|
143
|
+
- `getSegmentDuration(index: number): Promise<number>` – Reads the cached segment table to return the duration of a segment in seconds.
|
|
144
|
+
|
|
145
|
+
Custom error classes are exported to help with error handling: `FileNotFoundError`, `InvalidFileError`, `InvalidRangeError`, `InvalidParameterError`, and `UnsupportedFormatError`.
|
|
146
|
+
|
|
147
|
+
### Supported Formats
|
|
148
|
+
|
|
149
|
+
| Format | Extensions | Container | Codec | Frame Parsing |
|
|
150
|
+
|--------|-----------|-----------|-------|---------------|
|
|
151
|
+
| **MP3** | `.mp3` | — | MPEG-1/2 Layer III | ✅ Full frame table |
|
|
152
|
+
| **AAC** | `.aac` | ADTS | AAC | ✅ ADTS frames |
|
|
153
|
+
| **M4A** | `.m4a`, `.m4b` | MP4 | AAC | ✅ MP4 box structure |
|
|
154
|
+
| **OGG** | `.ogg`, `.oga` | OGG | Vorbis | ✅ OGG pages |
|
|
155
|
+
| **FLAC** | `.flac` | — | FLAC | ✅ FLAC frames |
|
|
156
|
+
| **WAV** | `.wav` | RIFF | PCM | ⚠️ Synthetic 1-second frames |
|
|
157
|
+
|
|
158
|
+
## Playlist Anatomy
|
|
159
|
+
|
|
160
|
+
```m3u8
|
|
161
|
+
#EXTM3U
|
|
162
|
+
#EXT-X-VERSION:6
|
|
163
|
+
#EXT-X-PLAYLIST-TYPE:VOD
|
|
164
|
+
#EXT-X-TARGETDURATION:6
|
|
165
|
+
#EXT-X-MEDIA-SEQUENCE:0
|
|
166
|
+
#EXTINF:5.973,
|
|
167
|
+
/audio/session/0/260736/track000.mp3
|
|
168
|
+
#EXTINF:5.994,
|
|
169
|
+
/audio/session/260736/521472/track001.mp3
|
|
170
|
+
...
|
|
171
|
+
#EXT-X-ENDLIST
|
|
172
|
+
```
|
|
136
173
|
|
|
137
|
-
|
|
138
|
-
|
|
174
|
+
- `#EXT-X-TARGETDURATION` is rounded up from the longest real segment duration.
|
|
175
|
+
- `#EXTINF` entries retain millisecond precision for smooth playback on strict clients.
|
|
176
|
+
- Segment paths directly encode the byte ranges your route must return.
|
|
139
177
|
|
|
140
|
-
|
|
141
|
-
- `startByte` - Starting byte position (inclusive)
|
|
142
|
-
- `endByte` - Ending byte position (exclusive)
|
|
178
|
+
## Operational Tips
|
|
143
179
|
|
|
144
|
-
**
|
|
180
|
+
- **Caching** – Construct the streamer once per unique audio file and reuse it. Segment planning caches the metadata, so repeated calls to `createM3U8()` or `getSegmentDuration()` are cheap.
|
|
181
|
+
- **CDN friendliness** – Because segment URLs are deterministic byte ranges, edge caches can serve them efficiently. Configure consistent caching headers (e.g. `Cache-Control: public, max-age=86400`).
|
|
182
|
+
- **Serverless** – The zero-dependency design works well in Lambda/Cloud Functions. For large files, prefer streaming reads (`getFileBuffer`) instead of loading entire files into memory.
|
|
183
|
+
- **Monitoring** – Log segment `start`/`end` pairs and durations to correlate playback issues with specific byte ranges or frame parsing warnings.
|
|
184
|
+
- **Troubleshooting** – For corrupted files, inspect `FileLib.analyzeAudioFile()` to review parsing warnings and format-specific metadata.
|
|
185
|
+
- **Format selection** – All formats work seamlessly with HLS, but consider:
|
|
186
|
+
- **MP3/AAC/M4A**: Best compatibility with HLS players
|
|
187
|
+
- **FLAC**: Lossless quality but larger segments
|
|
188
|
+
- **OGG**: Open-source, good compression
|
|
189
|
+
- **WAV**: Uncompressed, very large segments (consider smaller `segmentSizeKB`)
|
|
145
190
|
|
|
146
|
-
|
|
147
|
-
Gets the accurate duration of a specific segment.
|
|
191
|
+
## Development
|
|
148
192
|
|
|
149
|
-
|
|
150
|
-
- `segmentIndex` - Zero-based segment index
|
|
193
|
+
Clone the repo, install dependencies, and run the usual scripts:
|
|
151
194
|
|
|
152
|
-
|
|
195
|
+
```bash
|
|
196
|
+
npm install
|
|
197
|
+
npm test -- --runInBand --watchman=false
|
|
198
|
+
npm run build
|
|
199
|
+
```
|
|
153
200
|
|
|
154
|
-
|
|
201
|
+
The Jest flag `--watchman=false` avoids macOS sandbox issues when running in restricted environments.
|
|
155
202
|
|
|
156
|
-
|
|
203
|
+
To explore the example playlist generator, see `example/test-hls-generation.js` and the bundled `example/sample.mp3` fixture.
|
|
157
204
|
|
|
158
|
-
|
|
159
|
-
import {
|
|
160
|
-
FileNotFoundError,
|
|
161
|
-
InvalidFileError,
|
|
162
|
-
InvalidRangeError,
|
|
163
|
-
InvalidParameterError
|
|
164
|
-
} from 'hls-streamer';
|
|
205
|
+
## Support
|
|
165
206
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (error instanceof FileNotFoundError) {
|
|
170
|
-
console.error('File not found:', error.message);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
```
|
|
207
|
+
- 🐛 Bug reports: [GitHub Issues](https://github.com/LordVersA/hls-streamer/issues)
|
|
208
|
+
- 💬 Questions & ideas: [GitHub Discussions](https://github.com/LordVersA/hls-streamer/discussions)
|
|
209
|
+
- 📦 npm registry: [hls-streamer](https://www.npmjs.com/package/hls-streamer)
|
|
174
210
|
|
|
175
|
-
##
|
|
211
|
+
## Contributing
|
|
176
212
|
|
|
177
|
-
|
|
178
|
-
|--------|------|---------|-------------|
|
|
179
|
-
| `filePath` | `string` | **required** | Path to the MP3 file |
|
|
180
|
-
| `segmentSizeKB` | `number` | `512` | Size of each segment in KB |
|
|
181
|
-
| `fileName` | `string` | `"file"` | Base name for segment files |
|
|
182
|
-
| `baseUrl` | `string` | `""` | Base URL path for segment URLs |
|
|
183
|
-
| `enableFastStart` | `boolean` | `false` | Use smaller initial segments for faster startup |
|
|
213
|
+
Contributions are welcome! Please open an issue to discuss substantial changes before submitting a pull request. Make sure `npm test -- --runInBand --watchman=false` and `npm run build` pass prior to filing the PR.
|
|
184
214
|
|
|
185
|
-
|
|
215
|
+
---
|
|
186
216
|
|
|
187
|
-
|
|
188
|
-
- **Podcast Platforms** - On-demand episode streaming
|
|
189
|
-
- **Educational Platforms** - Stream lecture recordings
|
|
190
|
-
- **Voice Message Systems** - Real-time audio message playback
|
|
191
|
-
- **Audio Books** - Chapter-based streaming
|
|
217
|
+
## Version 3.0.0 Breaking Changes
|
|
192
218
|
|
|
193
|
-
|
|
219
|
+
This major release adds multi-format support with pure TypeScript parsers. While most code remains backward compatible, please note:
|
|
194
220
|
|
|
195
|
-
###
|
|
221
|
+
### What Changed
|
|
222
|
+
- **New formats**: Added AAC, M4A, OGG Vorbis, FLAC, and WAV support
|
|
223
|
+
- **Format detection**: Files are now validated against all supported formats, not just MP3
|
|
224
|
+
- **Error types**: `InvalidFileError` for non-MP3 files is now `UnsupportedFormatError` for unsupported formats
|
|
225
|
+
- **Internal architecture**: MP3 parsing refactored into modular `Parsers/` directory
|
|
196
226
|
|
|
227
|
+
### Migration Guide
|
|
197
228
|
```typescript
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
segmentSizeKB: 1024, // 1MB segments for better quality
|
|
201
|
-
enableFastStart: true // First segments will be 256KB and 512KB
|
|
202
|
-
});
|
|
203
|
-
```
|
|
229
|
+
// v2.x - Only MP3 supported
|
|
230
|
+
const streamer = new HlsStreamer({ filePath: 'song.mp3' });
|
|
204
231
|
|
|
205
|
-
|
|
232
|
+
// v3.x - All formats work the same way!
|
|
233
|
+
const streamer = new HlsStreamer({ filePath: 'song.mp3' }); // ✅ Still works
|
|
234
|
+
const streamer = new HlsStreamer({ filePath: 'song.ogg' }); // ✅ Now supported
|
|
235
|
+
const streamer = new HlsStreamer({ filePath: 'song.flac' }); // ✅ Now supported
|
|
206
236
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
filePath,
|
|
214
|
-
baseUrl: `audio/${userId}/${audioId}`,
|
|
215
|
-
fileName: `audio-${audioId}`,
|
|
216
|
-
segmentSizeKB: 256 // Smaller segments for mobile
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
return hls.createM3U8();
|
|
220
|
-
}
|
|
237
|
+
// Error handling update
|
|
238
|
+
try {
|
|
239
|
+
new HlsStreamer({ filePath: 'document.pdf' });
|
|
240
|
+
} catch (err) {
|
|
241
|
+
// v2.x: InvalidFileError
|
|
242
|
+
// v3.x: UnsupportedFormatError
|
|
221
243
|
}
|
|
222
244
|
```
|
|
223
245
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
npm run test:watch
|
|
229
|
-
npm run test:coverage
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
## 🏗️ Building
|
|
233
|
-
|
|
234
|
-
```bash
|
|
235
|
-
npm run build # Build both ESM and CJS
|
|
236
|
-
npm run build:esm # Build ES modules
|
|
237
|
-
npm run build:cjs # Build CommonJS
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
## 📄 License
|
|
241
|
-
|
|
242
|
-
MIT License - see [LICENSE](LICENSE) file for details.
|
|
243
|
-
|
|
244
|
-
## 🤝 Contributing
|
|
245
|
-
|
|
246
|
-
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.
|
|
247
|
-
|
|
248
|
-
## 📞 Support
|
|
246
|
+
### Deprecated APIs
|
|
247
|
+
- `Mp3FileInfo` → Use `AudioFileInfo` (backward compatible, but deprecated)
|
|
248
|
+
- `Mp3FrameInfo` → Use `AudioFrameInfo` (backward compatible, but deprecated)
|
|
249
|
+
- `FileLib.analyzeMP3File()` → Use `FileLib.analyzeAudioFile()` (old method still works)
|
|
249
250
|
|
|
250
|
-
|
|
251
|
-
- 💬 **Questions:** [GitHub Discussions](https://github.com/LordVersA/hls-streamer/discussions)
|
|
252
|
-
- 📦 **NPM:** [hls-streamer](https://www.npmjs.com/package/hls-streamer)
|
|
251
|
+
All deprecated APIs remain functional for backward compatibility but will be removed in v4.0.0.
|
|
253
252
|
|
|
254
253
|
---
|
|
255
254
|
|
|
@@ -1,17 +1,37 @@
|
|
|
1
|
+
import { AudioFormat } from '../Parsers/IAudioParser';
|
|
1
2
|
export interface HlsStreamerOptions {
|
|
2
3
|
filePath: string;
|
|
3
4
|
segmentSizeKB?: number;
|
|
4
5
|
fileName?: string;
|
|
5
6
|
baseUrl?: string;
|
|
6
7
|
enableFastStart?: boolean;
|
|
8
|
+
format?: AudioFormat;
|
|
7
9
|
}
|
|
8
10
|
export interface SegmentInfo {
|
|
9
11
|
start: number;
|
|
10
12
|
end: number;
|
|
11
13
|
duration: number;
|
|
12
14
|
}
|
|
15
|
+
export type { AudioFormat, AudioFrameInfo, AudioFileInfo } from '../Parsers/IAudioParser';
|
|
16
|
+
export interface Mp3FrameInfo {
|
|
17
|
+
index: number;
|
|
18
|
+
offset: number;
|
|
19
|
+
length: number;
|
|
20
|
+
duration: number;
|
|
21
|
+
samples: number;
|
|
22
|
+
sampleRate: number;
|
|
23
|
+
bitrate: number;
|
|
24
|
+
padding?: 0 | 1;
|
|
25
|
+
}
|
|
13
26
|
export interface Mp3FileInfo {
|
|
14
27
|
size: number;
|
|
15
28
|
duration: number;
|
|
29
|
+
audioDataSize: number;
|
|
30
|
+
sampleRate?: number;
|
|
31
|
+
averageBitrate?: number;
|
|
32
|
+
id3v2Size?: number;
|
|
33
|
+
id3v1Size?: number;
|
|
34
|
+
frames: Mp3FrameInfo[];
|
|
35
|
+
warnings?: string[];
|
|
16
36
|
}
|
|
17
37
|
//# sourceMappingURL=HlsStreamer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HlsStreamer.d.ts","sourceRoot":"","sources":["../../src/Interfaces/HlsStreamer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"HlsStreamer.d.ts","sourceRoot":"","sources":["../../src/Interfaces/HlsStreamer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAKtD,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;IAE1B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAKD,MAAM,WAAW,WAAW;IAE1B,KAAK,EAAE,MAAM,CAAC;IAEd,GAAG,EAAE,MAAM,CAAC;IAEZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAM1F,MAAM,WAAW,YAAY;IAI3B,KAAK,EAAE,MAAM,CAAC;IAEd,MAAM,EAAE,MAAM,CAAC;IAEf,MAAM,EAAE,MAAM,CAAC;IAEf,QAAQ,EAAE,MAAM,CAAC;IAEjB,OAAO,EAAE,MAAM,CAAC;IAEhB,UAAU,EAAE,MAAM,CAAC;IAEnB,OAAO,EAAE,MAAM,CAAC;IAEhB,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;CACjB;AAMD,MAAM,WAAW,WAAW;IAE1B,IAAI,EAAE,MAAM,CAAC;IAEb,QAAQ,EAAE,MAAM,CAAC;IAEjB,aAAa,EAAE,MAAM,CAAC;IAEtB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,MAAM,EAAE,YAAY,EAAE,CAAC;IAEvB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB"}
|
package/dist/Libs/FileLib.d.ts
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
|
+
import { Mp3FileInfo, AudioFileInfo } from '../Interfaces/HlsStreamer';
|
|
2
|
+
import { AudioFormat } from '../Parsers/IAudioParser';
|
|
1
3
|
export declare class FileLib {
|
|
4
|
+
private static readonly BITRATE_INDEX;
|
|
5
|
+
private static readonly SAMPLE_RATE_INDEX;
|
|
2
6
|
static getFileSizeInBytes(buffer: Buffer): number;
|
|
3
7
|
static getMP3DurationFromBuffer(mp3Buffer: Buffer): Promise<number>;
|
|
4
|
-
|
|
8
|
+
static analyzeMP3Buffer(buffer: Buffer, opts?: {
|
|
9
|
+
fileSize?: number;
|
|
10
|
+
}): Mp3FileInfo;
|
|
11
|
+
static analyzeMP3File(filePath: string): Promise<Mp3FileInfo>;
|
|
12
|
+
static analyzeAudioFile(filePath: string, format?: AudioFormat): Promise<AudioFileInfo>;
|
|
13
|
+
static analyzeAudioBuffer(buffer: Buffer, opts?: {
|
|
14
|
+
fileSize?: number;
|
|
15
|
+
filePath?: string;
|
|
16
|
+
format?: AudioFormat;
|
|
17
|
+
}): AudioFileInfo;
|
|
18
|
+
private static calculateFrameLength;
|
|
19
|
+
private static getId3Offsets;
|
|
20
|
+
private static syncSafeInteger;
|
|
21
|
+
private static isFrameSync;
|
|
22
|
+
private static parseFrameHeader;
|
|
23
|
+
private static decodeVersion;
|
|
24
|
+
private static decodeLayer;
|
|
25
|
+
private static getSamplesPerFrame;
|
|
5
26
|
}
|
|
6
27
|
//# sourceMappingURL=FileLib.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileLib.d.ts","sourceRoot":"","sources":["../../src/Libs/FileLib.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FileLib.d.ts","sourceRoot":"","sources":["../../src/Libs/FileLib.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAgB,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAGrF,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAqBtD,qBAAa,OAAO;IAClB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAgBnC;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAIvC;IAKF,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;WAWpC,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQzE,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,WAAW;WA8HzE,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;WAetD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC;IAwB7F,MAAM,CAAC,kBAAkB,CACvB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACxE,aAAa;IA6BhB,OAAO,CAAC,MAAM,CAAC,oBAAoB;IASnC,OAAO,CAAC,MAAM,CAAC,aAAa;IA8B5B,OAAO,CAAC,MAAM,CAAC,eAAe;IAO9B,OAAO,CAAC,MAAM,CAAC,WAAW;IAW1B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAqC/B,OAAO,CAAC,MAAM,CAAC,aAAa;IAa5B,OAAO,CAAC,MAAM,CAAC,WAAW;IAa1B,OAAO,CAAC,MAAM,CAAC,kBAAkB;CAWlC"}
|