[1] Youtube Proxy
The following script1 runs on vanilla Node.js and requires an executable of
youtube-dl
. If it’s not located in the same directory as the script, change
the line
var job = spawn('./youtube-dl', args);
to something like
var job = spawn('~/path/to/youtube-dl', args);
Usage examples
Download a video in MP4 from YouTube:
http://example.com:8567/https://www.youtube.com/watch?v=XXXXXXX
Download a video from YouTube and extract audio in M4A format:
http://example.com:8567/https://www.youtube.com/watch?v=XXXXXXX.m4a
Currently, the headers it sets are designed for downloading. You can change them so that it plays in your browser, but I mainly wanted it for downloading videos on my phone and Firefox on Android wouldn’t download it without them.
Also, be aware that it needs a “downloads
” directory that is not created
automatically.
The Code
var spawn = require('child_process').spawn,
fs = require('fs'),
http = require('http'),
mime = require('mime');
var server = http.createServer(function(req, res) {
var m = req.url.match(/\/(.*)\.(.*?)/),
format = m[2],
url = m[1];
if (!/^.*\.[a-z0-9]{3,5}$/.test(req.url)) {
format = 'mp4';
url = req.url.match(/\/(.*)/)[1];
}
var file = url.replace(/[\/:?!&=\.]/g, '') + '.' + format,
mimeType = mime.lookup(file),
args = ['-o', 'downloads/' + file];
if (['m4a', 'mp3', 'opus'].indexOf(format) !== -1) {
args = args.concat(['-x', '--audio-format']);
} else {
args.push('-f');
}
args.push(format);
args.push(url);
console.log(args.join(' '));
var job = spawn('./youtube-dl', args);
job.on('close', function(code, signal) {
var fileStream = fs.createReadStream('downloads/' + file);
pipeReadstream(req, res, fileStream, mimeType, file, function(err) {
console.log('error: ' + err);
});
});
job.stdout.on('data', function(data) {
console.log('stdout: ' + data);
});
job.stderr.on('data', function(data) {
console.log('stderr: ' + data);
});
});
server.listen(8567);
// Pipe some stream as HTTP response
function pipeReadstream(req, res, readStream, mimeType, filename, cb) {
var headWritten = false;
readStream.on('data', function(data) {
if (!headWritten) {
res.writeHead(200, {
'Content-Disposition': 'attachment; filename=' + filename,
'Content-Type': mimeType
});
headWritten = true;
}
var flushed = res.write(data);
// Pause the read stream when the write stream gets saturated
if (!flushed) {
readStream.pause();
}
});
res.on('drain', function() {
// Resume the read stream when the write stream gets hungry
readStream.resume();
});
readStream.on('end', function() { res.end(); });
readStream.on('error', function(err) { cb(err); });
}
-
Original comment on /tech/: https://lainchan.org/tech/res/5644.html#5658,
original source code at https://pastebin.com/raw.php?i=amxBtmmj ↩︎