serde-m3u
v0.1.2
Published
serde-m3u
Downloads
260
Readme
serde-m3u
M3U / M3U8 playlist parser with HLS support. Available for both Rust and TypeScript.
Features
- Parse
#EXTINF,#EXTVLCOPT(standard M3U) - Parse HLS tags:
#EXT-X-MEDIA,#EXT-X-STREAM-INF,#EXT-X-I-FRAME-STREAM-INF,#EXT-X-KEY,#EXT-X-MAP,#EXT-X-BYTERANGE - Round-trip serialization (parse → stringify → parse)
- Serde
Serialize/Deserialize(Rust) - Zero dependencies (TypeScript)
TypeScript
Install
npm install serde-m3uUsage
import { Playlist } from "serde-m3u";
const m3u = `
#EXTM3U
#EXTINF:419,Alice in Chains - Rotten Apple
Alice in Chains_Jar of Flies_01_Rotten Apple.mp3
#EXTINF:260,Alice in Chains - Nutshell
Alice in Chains_Jar of Flies_02_Nutshell.mp3
`;
const playlist = Playlist.fromString(m3u);
console.log(playlist.list.length); // 2
console.log(playlist.list[0].title); // "Alice in Chains - Rotten Apple"VLC options (#EXTVLCOPT)
const m3u = `
#EXTM3U
#EXTINF:-1,My Video
#EXTVLCOPT:sub-file=./subtitles.en.srt
#EXTVLCOPT:subsdec-encoding=UTF-8
./video.webm
`;
const playlist = Playlist.fromString(m3u);
for (const [key, value] of playlist.list[0].vlc_opt) {
console.log(`${key} = ${value}`);
}
// sub-file = ./subtitles.en.srt
// subsdec-encoding = UTF-8HLS media entries (#EXT-X-MEDIA)
const m3u = `
#EXTM3U
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English",LANGUAGE="en",URI="subs-en.m3u8",DEFAULT=YES
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="English",LANGUAGE="en",URI="audio-en.m3u8",DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=1280000,RESOLUTION=720x480
video.m3u8
`;
const playlist = Playlist.fromString(m3u);
// Iterate all media entries
for (const media of playlist.media) {
const type = Playlist.getMediaAttr(media, "TYPE");
const uri = Playlist.getMediaAttr(media, "URI");
console.log(`${type}: ${uri}`);
}
// Filter by type
const subtitles = playlist.findMedia((attrs) =>
attrs.some(([k, v]) => k === "TYPE" && v === "SUBTITLES"),
);HLS stream tags
const m3u = `
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=1280000,RESOLUTION=720x480,CODECS="avc1.42e01e,mp4a.40.2"
video-720p.m3u8
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=86000,URI="iframes.m3u8"
`;
const playlist = Playlist.fromString(m3u);
// STREAM-INF attributes on the entry
console.log(playlist.list[0].getAttr("BANDWIDTH")); // "1280000"
console.log(playlist.list[0].getAttr("RESOLUTION")); // "720x480"
// I-FRAME-STREAM-INF is a self-contained entry
console.log(playlist.list[1].getAttr("URI")); // "iframes.m3u8"API
Playlist
| Member | Type | Description |
| -------------------- | ------------ | -------------------------------- |
| list | Entry[] | Parsed media entries |
| media | HlsAttrs[] | #EXT-X-MEDIA entries |
| fromString(s) | static | Parse an M3U string |
| toString() | string | Serialize back to M3U |
| findMedia(fn) | HlsAttrs[] | Filter media entries |
| getMediaAttr(m, k) | static | Get attribute from a media entry |
Entry
| Member | Type | Description |
| -------------- | ---------------------- | --------------------------------- |
| url | string | Media URL / path |
| title | string? | Title from #EXTINF |
| time | number? | Duration from #EXTINF |
| vlc_opt | [string, string][] | #EXTVLCOPT key-value pairs |
| hls_tags | [string, HlsAttrs][] | HLS tags with their attributes |
| getAttr(key) | string? | Get an attribute from any HLS tag |
Rust
Install
[dependencies]
serde-m3u = "0.1"
serde = { version = "1", features = ["derive"] }Usage
use serde_m3u::Playlist;
let m3u = r#"
#EXTM3U
#EXTINF:419,Alice in Chains - Rotten Apple
Alice in Chains_Jar of Flies_01_Rotten Apple.mp3
"#.trim();
let playlist = Playlist::from(m3u);
assert_eq!(playlist.list.len(), 1);
assert_eq!(playlist.list[0].title.as_deref(), Some("Alice in Chains - Rotten Apple"));
// Serde support
let json = serde_json::to_string(&playlist).unwrap();
let playlist2: Playlist = serde_json::from_str(&json).unwrap();HLS
let m3u = r#"
#EXTM3U
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English",LANGUAGE="en",URI="subs-en.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=1280000,RESOLUTION=720x480
video.m3u8
"#.trim();
let playlist = Playlist::from(m3u);
assert_eq!(playlist.media.len(), 1);
assert_eq!(
Playlist::get_media_attr(&playlist.media[0], "URI"),
Some("subs-en.m3u8")
);
// Stream attributes
assert_eq!(playlist.list[0].get_attr("BANDWIDTH"), Some("1280000"));License
MIT
