Skip to content

Instantly share code, notes, and snippets.

@adammw
Last active September 14, 2023 01:29
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save adammw/6257550 to your computer and use it in GitHub Desktop.
Save adammw/6257550 to your computer and use it in GitHub Desktop.
HLS / M3U8 Downloader for AES-128 Encrypted Streams
#!/usr/bin/env node
// M3U8 Downloader (not working)
var fs = require('fs');
var http = require('http');
var https = require('https');
var crypto = require('crypto');
var program = require('commander');
var ee = require('streamee');
var urlparse = require('url').parse;
var PassThrough = require('stream').PassThrough;
var WritableBufferStream = require('buffertools').WritableBufferStream;
program
.version('0.0.1')
.usage('[options] <file ...>')
.parse(process.argv);
var file = program.args[0];
var rendition = fs.readFileSync(file, {encoding: 'utf8'});
var keyid = 0;
var keycbs = {};
var id = 0;
var request = function(url, cb) {
var url = urlparse(url);
var module = ('https:' == url.protocol) ? https : http;
url.agent = module.globalAgent;
return module.get(url, function(res) {
if (res.statusCode != 200) throw new Error(res.statusCode + ': ' + http.STATUS_CODES[res.statusCode]);
cb(res);
});
}
var bufferedRequest = function(url, cb) {
return request(url, function(res) {
var ostream = new WritableBufferStream();
res.pipe(ostream);
res.on('end', function() {
cb(ostream.getBuffer());
});
});
};
var dataStreams = [];
rendition.split('\n').forEach(function (line) {
if (line.substr(0,1) == '#') {
if (/^#EXT-X-KEY/.test(line)) {
keyid++;
var mykeyid = keyid;
keycbs[mykeyid] = [];
var url = /URI="(.+?)"/.exec(line)[1];
bufferedRequest(url, function(key) {
keycbs[mykeyid].forEach(function(cb) {
cb(key);
})
});
} else {
//console.log('Unhandled: %s', line);
}
} else if (/^http/.test(line)) {
var url = line;
var segid = id;
var dataStream = new PassThrough();
dataStream.on('end', function() {
console.log('Segement #%d finsihed', segid);
})
dataStreams.push(dataStream);
console.log('Queuing download of segment #%d', segid);
keycbs[keyid].push(function(key) {
var iv = new Buffer(16);
iv.fill(0);
var decipher = crypto.createDecipheriv('aes128', key, iv);
request(url, function(res) {
res.pipe(decipher).pipe(dataStream);
}).on('socket', function() {
console.log('Starting download of segment #%d', segid);
});
});
id++;
}
});
console.log('%d segments',dataStreams.length);
var finalStream = ee.concatenate(dataStreams);
var outfile = fs.createWriteStream('output.ts');
finalStream.pipe(outfile);
finalStream.on('end', function() {
console.log('Download completed.');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment