Mercurial
diff mrjunejune/test/hls_player_test.js @ 242:543df0fe7168
[tools] Add full HLS player support
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 03 Aug 2026 13:14:41 -0700 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/test/hls_player_test.js Mon Aug 03 13:14:41 2026 -0700 @@ -0,0 +1,86 @@ +const assert = require('node:assert/strict'); +const { + chooseVariant, + parseAttributeList, + parseMasterPlaylist, + parseMediaPlaylist, + normalizePlaylistUrl, + localRelativePath, + rewritePlaylistUris, +} = require('../src/tools/hls_player/hls-player.js'); + +const attributes = parseAttributeList( + 'BANDWIDTH=500000,RESOLUTION=640x360,CODECS="avc1.42c01e,mp4a.40.2"', +); +assert.equal(attributes.BANDWIDTH, '500000'); +assert.equal(attributes.RESOLUTION, '640x360'); +assert.equal(attributes.CODECS, 'avc1.42c01e,mp4a.40.2'); + +const variants = parseMasterPlaylist( + '#EXTM3U\n#EXT-X-STREAM-INF:BANDWIDTH=100\nlow.m3u8\n#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1.42c01e"\nhigh.m3u8\n', + 'https://example.com/master.m3u8', +); +assert.equal(variants.length, 2); +assert.equal(chooseVariant(variants).url, 'https://example.com/high.m3u8'); +assert.equal( + chooseVariant(variants, variant => variant.bandwidth < 200).url, + 'https://example.com/low.m3u8', +); +assert.equal(chooseVariant(variants, () => false), null); + +const media = parseMediaPlaylist( + '#EXTM3U\n#EXT-X-MAP:URI="init.mp4"\n#EXTINF:1.5,\nseg0.m4s\n#EXTINF:2.0,\nseg1.m4s\n#EXT-X-ENDLIST\n', + 'https://example.com/path/stream.m3u8', +); +assert.equal(media.initSegment, 'https://example.com/path/init.mp4'); +assert.equal(media.segments.length, 2); +assert.equal(media.segments[1].url, 'https://example.com/path/seg1.m4s'); +assert.equal(media.totalDuration, 3.5); +assert.equal(media.endList, true); + +assert.equal( + normalizePlaylistUrl('/stream.m3u8', 'https://example.com/tool'), + 'https://example.com/stream.m3u8', +); +assert.throws( + () => normalizePlaylistUrl('file:///tmp/stream.m3u8', 'https://example.com'), + /HTTP, HTTPS, or a local file/, +); +assert.equal( + localRelativePath('folder/master.m3u8', 'video/stream.m3u8'), + 'folder/video/stream.m3u8', +); +assert.equal( + localRelativePath('folder/video/stream.m3u8', '../init.mp4'), + 'folder/init.mp4', +); +assert.equal( + localRelativePath('folder/video/stream.m3u8', '/init.mp4'), + 'init.mp4', +); +assert.throws( + () => localRelativePath('master.m3u8', '../outside.m4s'), + /escapes/, +); + +assert.throws( + () => parseMediaPlaylist( + '#EXTM3U\n#EXT-X-KEY:METHOD=AES-128,URI="key"\n#EXTINF:1,\nseg.m4s\n', + 'https://example.com/stream.m3u8', + ), + /Encrypted HLS/, +); + +(async () => { + const rewritten = await rewritePlaylistUris( + '#EXTM3U\n#EXT-X-MEDIA:TYPE=AUDIO,URI="audio.m3u8"\n#EXT-X-MAP:URI="init.mp4"\n#EXT-X-KEY:METHOD=AES-128,URI="key.bin"\n#EXTINF:1,\nseg.m4s\n', + async reference => `blob:${reference}`, + ); + assert.match(rewritten, /URI="blob:audio\.m3u8"/); + assert.match(rewritten, /URI="blob:init\.mp4"/); + assert.match(rewritten, /URI="blob:key\.bin"/); + assert.match(rewritten, /blob:seg\.m4s/); +})().catch(error => { + console.error(error); + process.exitCode = 1; +});