Skip to content

Instantly share code, notes, and snippets.

@private-face
Created October 28, 2012 05:49
Show Gist options
  • Save private-face/3967789 to your computer and use it in GitHub Desktop.
Save private-face/3967789 to your computer and use it in GitHub Desktop.
Postnauka video downloader
const INCOMPLETE_DOWNLOADS_FOLDER_PATH = '/Users/vz/Downloads/';
const COMPLETE_DOWNLOADS_FOLDER_PATH = '/Users/vz/Music/iTunes/iTunes Media/Automatically Add to iTunes/';
const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.52 Safari/537.11';
const VIDEO_INFO_URL = 'http://player.vimeo.com/config/';
const VIDEO_DOWNLOAD_URL = 'http://player.vimeo.com/play_redirect?clip_id={{id}}&sig={{signature}}&time={{timestamp}}&quality={{quality}}&codecs=H264&type=moogaloop&embed_location={{location}}';
const RECHECK_PERIOD = 2 * 60 * 60 * 1000;
var fs = require("fs"),
xml2js = require('xml2js'),
request = require('request'),
db = new require('dirty')('data.db.json'),
ProgressBar = require('progress'),
growl = require('growl'),
videos = {};
function stripHTML(s) {
return s.replace(/<[^>]+>/gm, ' ');
}
function createVideo(rawVideoData) {
return {
title: rawVideoData.title[0],
description: stripHTML(rawVideoData.description[0]),
pageUrl: rawVideoData.link[0],
date: +new Date(rawVideoData.pubDate[0]),
category: rawVideoData.category
};
}
function getVimeoId(url, callback) {
request(url, function(error, response, body) {
if (!error && response.statusCode == 200) {
var match = body.match(/https?:\/\/player\.vimeo\.com\/video\/(\d+)/i);
callback(match && match[1] || null);
} else {
callback(null);
}
});
}
function getDownloadUrl(id, location, callback) {
request({
url: VIDEO_INFO_URL + id,
headers: {
'User-Agent': USER_AGENT,
'Referer': location
}
}, function(error, response, body) {
if (!error && response.statusCode == 200) {
try {
var data = JSON.parse(body);
var url = VIDEO_DOWNLOAD_URL
.replace('{{id}}', data.video.id)
.replace('{{signature}}', data.request.signature)
.replace('{{timestamp}}', data.request.timestamp)
.replace('{{quality}}', data.video.files.h264[0]) // best format always goes first
.replace('{{location}}', location);
callback(url);
} catch(e) {
callback(null);
}
} else {
callback(null)
}
});
}
function downloadVideo(videoUrl, name, callback) {
var req = request({
url: videoUrl,
headers: {
'User-Agent': USER_AGENT
}
});
req.on('response', function(res) {
var file = fs.createWriteStream(INCOMPLETE_DOWNLOADS_FOLDER_PATH + name),
len = parseInt(res.headers['content-length'], 10),
bar = new ProgressBar('[:bar] :percent :etas', {
complete: '=',
incomplete: ' ',
width: 40,
total: len
});
res.on('data', function(chunk){
bar.tick(chunk.length);
file.write(chunk, encoding='binary');
});
res.on('end', function(){
console.log('\n');
file.end();
fs.rename(INCOMPLETE_DOWNLOADS_FOLDER_PATH + name, COMPLETE_DOWNLOADS_FOLDER_PATH + name);
growl(name + ' has been successfully downloaded.', {title: 'Postnauka'});
callback(true);
});
res.on('error', function() {
console.log('\n');
file.end();
growl(name + ' has failed to download.', {title: 'Postnauka'});
callback(null);
});
});
req.end();
}
function downloadVideos(items, callback) {
var queue = [];
for(var url in items) {
if (!items[url].done) {
queue.push(items[url]);
}
}
queue.sort(function(a, b) {
return a.date - b.date;
});
processQueue(queue, callback);
}
function processQueue(items, callback) {
var item = items.shift();
if (!item) {
return callback();
}
console.log(item.title);
console.log('* geting vimeo id...');
getVimeoId(item.pageUrl, function(id) {
if (id === null) {
console.log('! could not get vimeo id, file skipped.');
return processQueue(items, callback);
}
console.log('* geting download url...');
getDownloadUrl(id, item.pageUrl, function(url) {
if (url === null) {
console.log('! could not get download url, file skipped.');
return processQueue(items, callback);
}
downloadVideo(url, item.title + '.mp4', function(result) {
if (result) {
item.done = true;
db.set('rss_items', videos);
processQueue(items, callback);
} else {
console.log('! download failed.');
}
})
});
});
}
function checkForNewVideos() {
request('http://postnauka.ru/video/feed', function (error, response, body) {
if (!error && response.statusCode == 200) {
var parser = new xml2js.Parser;
parser.parseString(body, function (err, result) {
var c = 0;
result.rss.channel[0].item.forEach(function(item) {
var pageUrl = item.link[0];
if (!videos[pageUrl]) {
c++;
videos[pageUrl] = createVideo(item);
}
});
if (c) {
var msg = c + ' new video' + (c * 1 ? 's' : '') + ' found.';
growl(msg, {title: 'Postnauka'});
console.log(msg + ' found.');
}
downloadVideos(videos, function() {
setTimeout(checkForNewVideos, RECHECK_PERIOD);
});
});
}
});
}
db.on('load', function() {
videos = db.get('rss_items') || {};
checkForNewVideos();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment