Last active
February 14, 2024 22:46
-
-
Save ToadKing/013bba2337318ce022efa19fc2c0cf7a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { readFileSync, writeFileSync } from 'fs' | |
async function get(url, options) { | |
const controller = new AbortController() | |
const { signal } = controller | |
const res = await fetch(url, { ...options, signal }) | |
if (!res.ok) { | |
controller.abort() | |
throw new Error(`bad request (${res.status} ${res.statusText})`) | |
} | |
return await res.arrayBuffer() | |
} | |
const playlistFilename = process.argv[2] | |
const requestOptions = JSON.parse(process.argv[3] || null) | |
const playlist = readFileSync(playlistFilename, { encoding: 'utf-8' }).replaceAll('\r\n', '\n') | |
let key | |
let iv | |
let newPlaylist = '' | |
for (const line of playlist.split('\n')) { | |
if (line[0] === '#') { | |
const sepIdx = line.indexOf(':') | |
const ext = line.substring(1, sepIdx) | |
if (ext === 'EXT-X-KEY') { | |
const params = line.substring(sepIdx + 1).split(',').map(p => { let idx = p.indexOf('='); return [p.substring(0, idx), p.substring(idx + 1)] }) | |
for (const [k, v] of params) { | |
switch (k) { | |
case 'METHOD': | |
if (v !== 'AES-128') { | |
console.error(`unknown encryption method: ${v}`) | |
process.exit(1) | |
} | |
break | |
case 'URI': | |
const uri = JSON.parse(v) | |
console.log('getting key') | |
const rawKey = await get(uri, requestOptions) | |
key = await crypto.subtle.importKey('raw', rawKey, 'AES-CBC', false, ['decrypt']) | |
break | |
case 'IV': | |
iv = Buffer.from(v.substring(2), 'hex') | |
break | |
default: | |
console.error(`unknown parameter ${k}`) | |
process.exit(1) | |
} | |
} | |
} else { | |
newPlaylist += line + '\n' | |
} | |
} else { | |
if (!line) { | |
continue | |
} | |
if (!!key !== !!iv) { | |
console.error('need both secret key and IV') | |
process.exit(1) | |
} | |
const url = new URL(line) | |
const filename = url.pathname.split('/').pop() | |
console.log(`getting chunk (${filename})`) | |
const buffer = await get(url, requestOptions) | |
let out | |
if (key) { | |
out = await crypto.subtle.decrypt({ name: "AES-CBC", iv }, key, buffer) | |
} else { | |
out = buffer | |
} | |
writeFileSync(filename, Buffer.from(out)) | |
newPlaylist += filename + '\n' | |
} | |
} | |
writeFileSync('new.m3u8', newPlaylist, { encoding: 'utf-8' }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment