import {test} from 'node:test'; import assert from 'node:assert/strict'; import {readFile} from 'node:fs/promises'; import vm from 'node:vm'; const fullBuild=await readFile(new URL('../node_modules/hls.js/dist/hls.js',import.meta.url),'utf8'); const variants=[ {codec:'avc1.42E01E',path:'h264.m3u8'}, {codec:'hvc1.1.6.L93.B0',path:'h265-hvc1.m3u8'}, {codec:'hev1.1.6.L93.B0',path:'h265-hev1.m3u8'}, ]; // Run the real bundled hls.js parser/controller with deterministic platform capabilities. // The loader only returns this local manifest; no network or browser session is used. async function parseManifest(supported,items=variants){ const manifest='#EXTM3U\n'+items.map((item,index)=>`#EXT-X-STREAM-INF:BANDWIDTH=${(index+1)*500000},CODECS="${item.codec},mp4a.40.2"\n${item.path}\n`).join(''); const sandbox={module:{exports:{}},exports:{},navigator:{userAgent:'Chrome/130'},performance,URL,URLSearchParams,TextDecoder,TextEncoder,setTimeout,clearTimeout,setInterval,clearInterval,queueMicrotask}; sandbox.self=sandbox;sandbox.window=sandbox;sandbox.location={href:'https://example.test/'}; sandbox.MediaSource=class { static isTypeSupported(type){ const codecs=type.match(/codecs=["']?([^"']+)/)?.[1]?.split(',')||[]; return codecs.length>0&&codecs.every(codec=>codec.startsWith('mp4a')||supported.some(prefix=>codec.startsWith(prefix))); } }; vm.runInNewContext(fullBuild,sandbox,{timeout:1000}); const Hls=sandbox.module.exports; class ManifestLoader { constructor(){this.stats={aborted:false,loaded:manifest.length,total:manifest.length,retry:0,chunkCount:1,loading:{start:1,first:2,end:3},parsing:{start:0,end:0},buffering:{start:0,first:0,end:0}};} load(context,_config,callbacks){ assert.equal(context.url,'https://example.test/master.m3u8'); this.context=context; queueMicrotask(()=>callbacks.onSuccess({url:context.url,data:manifest},this.stats,context,null)); } abort(){}destroy(){}getCacheAge(){return 0;} } const hls=new Hls({autoStartLoad:false,enableWorker:false,pLoader:ManifestLoader}); const done=new Promise(resolve=>{ hls.on(Hls.Events.MANIFEST_PARSED,(_event,data)=>resolve({codecs:Array.from(data.levels,level=>level.videoCodec)})); hls.on(Hls.Events.ERROR,(_event,data)=>{if(data.fatal)resolve({error:data.details});}); }); hls.loadSource('https://example.test/master.m3u8'); try{return await done;}finally{hls.destroy();} } test('real HLS manifest filtering retains H.264 when a device cannot decode HEVC',{timeout:2000},async()=>{ assert.deepEqual(await parseManifest(['avc1']),{codecs:['avc1.42E01E']}); }); test('real HLS manifest filtering retains AVC, hvc1 and hev1 when all are supported',{timeout:2000},async()=>{ assert.deepEqual(await parseManifest(['avc1','hvc1','hev1']),{codecs:variants.map(item=>item.codec)}); }); test('HEVC-only playlists are accepted on capable devices and report incompatibility elsewhere',{timeout:2000},async()=>{ assert.deepEqual(await parseManifest(['hvc1'],[variants[1]]),{codecs:['hvc1.1.6.L93.B0']}); assert.deepEqual(await parseManifest(['avc1'],[variants[1]]),{error:'manifestIncompatibleCodecsError'}); });