Skip to content

Instantly share code, notes, and snippets.

@ToadKing
Last active February 14, 2024 22:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ToadKing/013bba2337318ce022efa19fc2c0cf7a to your computer and use it in GitHub Desktop.
Save ToadKing/013bba2337318ce022efa19fc2c0cf7a to your computer and use it in GitHub Desktop.
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