Skip to content

Instantly share code, notes, and snippets.

@emsifa
Last active August 4, 2016 05:09
Show Gist options
  • Save emsifa/c78cecbf7b734f7210d3bdaf7aef0f1a to your computer and use it in GitHub Desktop.
Save emsifa/c78cecbf7b734f7210d3bdaf7aef0f1a to your computer and use it in GitHub Desktop.
Simple node.js command line vidio.com downloader
{
"dependencies": {
"bluebird": "^3.3.5",
"chalk": "^1.1.3",
"concat-files": "^0.1.0",
"inquirer": "^1.0.2",
"progress": "^1.1.8"
}
}
var url = process.argv[2];
// url harus dari vidio.com
if (!url) {
return console.error("URL cannot be empty");
}
if (!(/^http(s)?:\/\/www\.vidio\.com\/watch\//).test(url)) {
return console.error("URL is not vidio.com video page");
}
var Promise = require('bluebird');
var ProgressBar = require('progress');
var inquirer = require('inquirer');
var https = require('https');
var fs = require('fs');
var chalk = require('chalk');
var concat = require('concat-files');
var video_resolutions = {};
var temp_files = [];
var result_filename;
function grab(url, message) {
return new Promise(function(resolve, reject) {
var req = https.request(url);
var result = '';
req.on('response', function(res) {
if (res.statusCode < 200 || res.statusCode >= 300) {
reject(new Error("Cannot grab content from statusCode " + res.statusCode));
}
if (message) {
var len = parseInt(res.headers['content-length'], 10);
var bar = new ProgressBar(message, {
complete: chalk.green('.'),
incomplete: ' ',
width: 3,
total: len
});
}
res.on('data', function(chunk) {
if (message) {
bar.tick(chunk.length);
}
result += chunk;
});
res.on('end', function() {
resolve(result);
});
});
req.end();
});
}
function download(url, file_stream, message) {
return new Promise(function(resolve, reject) {
var req = https.request(url);
req.on('response', function(res) {
if (res.statusCode < 200 || res.statusCode >= 300) {
reject(new Error("Cannot download file, statusCode = " + res.statusCode));
}
var len = parseInt(res.headers['content-length'], 10);
var bar = new ProgressBar(message, {
complete: chalk.green('='),
incomplete: ' ',
width: 20,
total: len
});
res.on('data', function(chunk) {
bar.tick(chunk.length);
});
res.on('end', function() {
resolve(file_stream);
});
res.pipe(file_stream);
});
req.end();
});
}
function removeTempFiles() {
for (i in temp_files) {
var tmp_file = temp_files[i];
if (fs.existsSync(tmp_file)) {
fs.unlinkSync(tmp_file);
}
}
}
// remove temp files jika terjadi interupsi (misal: ctrl+C)
process.on('SIGINT', function() {
removeTempFiles();
console.log("Caught interrupt signal");
});
// grab url
grab(url, chalk.cyan(">") + " Mengambil informasi halaman :bar")
// scrap url playlist
.then(function(content) {
return new Promise(function(resolve, reject) {
var url_data_clip = content.match(/https:\/\/www\.vidio\.com\/videos\/\d+\/playlist\.m3u8/);
if (!url_data_clip.length) {
reject(new Error("Cannot find data clip url at that page"));
} else {
resolve(url_data_clip[0]);
}
})
})
// grab url playlist dan scrap metadata (resolusi video)
.then(function(url_data_clip) {
return new Promise(function(resolve, reject) {
grab(url_data_clip).then(function(content) {
var list_metadata = content.match(/(\#[^\n]+[\n\r\t]*)(https[^\n]+)/g).map(function(match) {
var split = match.split("\n");
var data = {
url: split[1]
};
split[0].replace(/^\#/, '').split(',').map(function(metadata) {
var s = metadata.split('=');
data[s[0].toLowerCase()] = (s[1] || '').trim().replace(/(^\"|\"$)/g, '');
});
return data;
});
var resolutions = {};
list_metadata.forEach(function(metadata) {
resolutions[metadata.name + " (" + metadata.resolution + ")"] = metadata.url;
});
resolve(resolutions);
})
});
})
// Propmpt/tanya mau resolusi yang mana?
.then(function(resolutions) {
return inquirer.prompt({
type: 'list',
name: 'resolution',
message: 'Pilih resolusi video',
choices: Object.keys(resolutions)
}).then(function(answer) {
return Promise.resolve(resolutions[answer.resolution]);
});
})
// grab dan scrap list url transport stream (.ts)
.then(function(url_video_resolution) {
return new Promise(function(resolve, reject) {
grab(url_video_resolution, chalk.cyan(">") + " Mengambil daftar url stream").then(function(result) {
var urls = result.match(/https:[^\n]+/g);
// jika urls yang match dengan https tidak ada, mungkin hanya berisi url videonya saja
if (!urls) {
var split = url_video_resolution.split('/');
var file_pattern = new RegExp(split.pop().substr(0, 10) + '[^\n]+', 'g');
var url_video_directory = split.join('/');
urls = (result.match(file_pattern) || []).map(function(url) {
return url_video_directory + '/' + url;
});
}
resolve(urls);
});
});
})
// Download semua file .ts ke folder ./temp
.then(function(stream_urls) {
var count = stream_urls.length;
var x = 0;
result_filename = stream_urls[0].split('/').pop().replace(/-\d+\.ts$/, '');
// var dest = __dirname+'/temp/'+result_filename;
return Promise.each(stream_urls, function(stream_url) {
var flags = !fs.existsSync(dest) ? 'w' : 'r+';
var chunk_filename = stream_url.split('/').pop();
var message = chalk.cyan("[") + (++x) + chalk.cyan("/") + count + chalk.cyan("] ") + chunk_filename + " [:bar] :percent";
var sort = '0'.repeat(5 - ('' + x).length) + x;
var dest = __dirname + '/temp/' + sort + '_' + chunk_filename;
var file_stream = fs.createWriteStream(dest);
temp_files.push(dest);
return download(stream_url, file_stream, message);
});
})
// concat semua file .ts jadi 1 file
.then(function(videos) {
console.log(chalk.cyan(">") + " Menyatukan potongan-potongan video ...");
concat(temp_files, __dirname + '/' + result_filename, function() {
console.log(chalk.green("SELESAI!") + " " + result_filename);
removeTempFiles(); // kalo udah remove temp files
});
});
@emsifa
Copy link
Author

emsifa commented Aug 4, 2016

penggunaan: node vidio-dl <url-vidio-nya>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment