const sourceUrl = "https://m3u8-0.c-spanvideo.org/clip/clip.5081597.576.tsc.m3u8" const source = await fetch(sourceUrl) .then(res => res.ok ? res.text() : undefined) if (!source) throw new Error("it's over") const tsBlobs = await Promise.all( source .split("\n") .flatMap(line => { const trimmed = line.trim() if (trimmed.startsWith("#")) return [] try { return [new URL(trimmed, sourceUrl)] } catch { return [] } }) .filter(url => url.pathname.endsWith(".ts")) .map(url => fetch(url).then(res => { if (!res.ok) throw new Error("Failed to download", url) return res.blob() }) ) ) const tsByteArrays = await Promise.all( tsBlobs.map(async blob => new Uint8Array(await blob.arrayBuffer())) ) const { zip } = await import("https://esm.sh/fflate@0.8.2") const indexFile = tsBlobs.map((blob, i) => `file 'chunks/${i}.ts'`).join("\n") const zipFile = await new Promise(async (resolve, reject) => { zip( { "index.txt": new TextEncoder().encode(indexFile), chunks: Object.fromEntries( tsByteArrays.map((byteArray, i) => [`${i}.ts`, byteArray]) ) }, { level: 0 }, (error, data) => { if (error) reject(error) else resolve(data) } ) }) const downloadLink = document.createElement("a") downloadLink.href = URL.createObjectURL( new Blob([zipFile], { type: "application/zip" }) ) downloadLink.download = "video-source.zip" downloadLink.click() console.log("cd Downloads/video-source") console.log("ffmpeg -f concat -i index.txt -c copy video.mp4")