summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore19
-rw-r--r--LICENSE24
-rw-r--r--README.md0
-rw-r--r--bin/mongo-edu3
-rw-r--r--lib/login.js160
-rw-r--r--lib/validate.js31
-rw-r--r--lib/videos.js120
-rw-r--r--mongo-edu.js103
-rw-r--r--package.json48
9 files changed, 508 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..33d7898
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+lib-cov
+lcov.info
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+
+pids
+logs
+results
+build
+livegit
+.grunt
+.DS_Store
+
+node_modules
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0c0e69d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+(The MIT License)
+
+Copyright (c) 2014 Przemyslaw Pluta <przemyslawplutadev@gmail.com>
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/README.md
diff --git a/bin/mongo-edu b/bin/mongo-edu
new file mode 100644
index 0000000..e1558e3
--- /dev/null
+++ b/bin/mongo-edu
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+
+require('../mongo-edu').create(); \ No newline at end of file
diff --git a/lib/login.js b/lib/login.js
new file mode 100644
index 0000000..c28c334
--- /dev/null
+++ b/lib/login.js
@@ -0,0 +1,160 @@
+/*
+ * mongo-edu
+ *
+ * Copyright (c) 2014 Przemyslaw Pluta
+ * Licensed under the MIT license.
+ * https://github.com/przemyslawpluta/mongo-edu/blob/master/LICENSE
+ */
+
+var request = require('request'),
+ cheerio = require('cheerio'),
+ ProgressBar = require('progress'),
+ _ = require('lodash');
+
+var jar = request.jar();
+
+function addCookies(cookies, url) {
+ _.each(cookies, function cookies(cookie) { jar.setCookie(cookie, url + '/login'); });
+}
+
+module.exports = {
+
+ init: function init(opt, callback) {
+
+ var url = 'https://education.mongodb.com',
+ bar = new ProgressBar('[' + '>'.magenta + '] Searching [:bar] :percent', { complete: '=', incomplete: ' ', width: 20, total: 5 }),
+ CSRFTokenCookie = '',
+
+ login = function login(token) {
+
+ request({
+ uri: url + '/login',
+ method: 'POST',
+ jar: jar,
+ headers: { 'X-CSRFToken': token[0].split('=')[1] },
+ form: { 'email': opt.user, 'password': opt.password }
+ }, function post(err, res, body) {
+
+ if (err !== null) { return callback(err, null); }
+
+ if (res.statusCode === 200) {
+ var status = JSON.parse(body);
+ if (status.success !== true) { return callback(new Error(status.value)); }
+
+ bar.tick();
+
+ return checkCourses(bar);
+
+ }
+
+ callback(new Error(res.statusCode));
+
+ });
+ },
+
+ checkCourses = function checkCourses(bar) {
+
+ bar.tick();
+
+ request({ url: url + '/dashboard', jar: jar }, function get(err, res, body) {
+ if (err !== null) { return callback(err, null); }
+ bar.tick();
+ if (res.statusCode === 200) {
+
+ var list = [], getCourses = [], $ = cheerio.load(body),
+
+ current = $('article.my-course.panel').children(),
+
+ link = $(current).children().filter('.span-7.offset-1').children().find('h3').children();
+
+ bar.tick();
+
+ $(link).each(function each(i, item) {
+ list.push({ name: $(item).text(), value: url + $(item).attr('href').replace('about', '').replace('info', '') + 'course_wiki' });
+ });
+
+ bar.tick();
+
+ callback(null, list);
+ }
+
+ });
+ };
+
+ request(url, function (err, res, body) {
+
+ if (err !== null) { return callback(err, null); }
+
+ if (res.statusCode === 200) {
+
+ addCookies(res.headers['set-cookie'], url);
+
+ CSRFTokenCookie = res.headers['set-cookie'][0].split(';');
+
+ return login(CSRFTokenCookie);
+
+ }
+
+ callback(new Error(res.statusCode));
+
+ });
+ },
+
+ getList: function getList(opt, callback) {
+
+ request({ url: opt.url, jar: jar }, function get(err, res, body) {
+ if (err !== null) { return callback(err, null); }
+ if (res.statusCode === 200) {
+
+ var list = [], getCourses = [], $ = cheerio.load(body), tag = opt.url.replace('course_', '');
+
+ $('div.wiki-article p').children().filter('a').map(function map(i, item) {
+ var current = $(item);
+ if (current.text().indexOf('Video') !== -1) { list.push(current.attr('href')); }
+ });
+
+ getCourses = _.filter(list, function map(item) { return (item.indexOf('wiki/M101') !== -1); });
+
+ callback(null, _.map(getCourses, function map(item) { return { name: item.replace(tag, '').slice(0, -1).slice(1).replace(/-/g, ' ').replace(/\//g, ': '), value: item }; }));
+ }
+
+ });
+
+ },
+
+ listVideos: function listVideos(opt, callback) {
+
+ var videos = [], state = false, list, $, current;
+
+ request({ url: opt.url, jar: jar }, function get(err, res, body) {
+ if (err !== null) { return callback(err, null); }
+ if (res.statusCode === 200) {
+ $ = cheerio.load(body);
+ list = $('div.wiki-article');
+
+ current = list.find('code').text();
+
+ if ((current.indexOf('http://') !== -1) || (current.indexOf('https://') !== -1)) {
+ videos = _.compact(current.split('\n'));
+ }
+
+ if (!videos.length) {
+
+ list.children('p').children().each(function each(i, item) {
+ var current = $(item);
+ if (current.attr('href')) {
+ videos.push({ name: current.text().split('-')[0], value: current.attr('href') });
+ }
+ });
+
+ if (videos.length) { videos.unshift({name: 'All', value: 'all', checked: true}, {name: 'Cancel', value: 'cancel'}); }
+ state = true;
+ }
+
+ return callback(null, videos, state);
+ }
+
+ callback(new Error(res.statusCode));
+ });
+ }
+}; \ No newline at end of file
diff --git a/lib/validate.js b/lib/validate.js
new file mode 100644
index 0000000..1480b11
--- /dev/null
+++ b/lib/validate.js
@@ -0,0 +1,31 @@
+/*
+ * mongo-edu
+ *
+ * Copyright (c) 2014 Przemyslaw Pluta
+ * Licensed under the MIT license.
+ * https://github.com/przemyslawpluta/mongo-edu/blob/master/LICENSE
+ */
+
+var mkdirp = require('mkdirp');
+
+module.exports.init = function init(opt, callback) {
+
+ var profile = [
+ { type: 'input', name: 'user', message: 'MongoDB Uni User Name', default: '', validate: function(value) {
+ if (value !== '') { return true; }
+ return "Please enter your MongoDB Uni user name - email address";
+ }},
+ { type: 'password', message: 'MongoDB Uni Password', name: 'password', validate: function(value) {
+ if (value !== '') { return true; }
+ return "Please enter your MongoDB Uni password";
+ }}
+ ];
+
+ if (opt.u !== '') { profile[0].default = opt.u; }
+
+ mkdirp(opt.d, function mkdirp(err) {
+ if (err !== null) { return callback(err); }
+ callback(null, profile);
+ });
+
+};
diff --git a/lib/videos.js b/lib/videos.js
new file mode 100644
index 0000000..44bfd7e
--- /dev/null
+++ b/lib/videos.js
@@ -0,0 +1,120 @@
+/*
+ * mongo-edu
+ *
+ * Copyright (c) 2014 Przemyslaw Pluta
+ * Licensed under the MIT license.
+ * https://github.com/przemyslawpluta/mongo-edu/blob/master/LICENSE
+ */
+
+var youtubedl = require('youtube-dl'),
+ ProgressBar = require('progress'),
+ colors = require('colors'),
+ _ = require('lodash');
+
+var downloadPath = '';
+
+var handleList = function handleList(list) {
+
+ var currentList = list,
+
+ getVideos = function getVideos(item) {
+
+ var dl = youtubedl.download(item, downloadPath, ['--max-quality=18']), bar;
+
+ dl.on('download', function(data) {
+ console.log('[' + 'i'.magenta + '] Downloading: ' + data.filename.cyan + ' > ' + item);
+ bar = new ProgressBar('[' + '>'.green + '] ' + data.size + ' [:bar] :percent :etas', { complete: '=', incomplete: ' ', width: 20, total: 100.0 });
+ });
+
+ dl.on('progress', function(data) {
+ if (!bar.complete) { bar.tick(parseInt(data.percent, 10)); }
+ });
+
+ dl.on('error', function(err) {
+ return console.log(err.stack);
+ });
+
+ dl.on('end', function(data) {
+ var left = (currentList.length)? ' ' + currentList.length + ' left ...' : '';
+ if (data.filename) {
+ if (data.filename.indexOf('has already been downloaded') !== -1) {
+ console.log('[' + '>'.magenta + '] ' + data.filename + '.' + left);
+ } else {
+ console.log('[' + 'i'.green + '] Done in ' + data.timeTakenms + 'ms.' + left);
+
+ }
+ } else {
+ console.log('[' + 'i'.red + '] Download Issues');
+ }
+ handleList(currentList);
+ });
+ };
+
+ if (currentList.length) { return getVideos(currentList.shift()); }
+ console.log('[ Finished ]'.green);
+
+};
+
+module.exports = {
+
+ details: function details(opt, callback) {
+
+ var bar = new ProgressBar('[' + '>'.magenta + '] Collecting [:bar] :percent', { complete: '=', incomplete: ' ', width: 20, total: opt.length });
+
+ var getDetails = function getDetails(item, i) {
+
+ function getInfo() {
+ youtubedl.getInfo(item, function(err, info) {
+
+ items.push((!err)?{name: info.title + ' - ' + info.resolution, value: item, id: i}:{name: 'No info: ' + item, value: item, id: i});
+
+ count = count - 1;
+
+ bar.tick();
+
+ if (count === 0) {
+
+ var sortedList = _.map(_.sortBy(items, 'id'));
+
+ sortedList.unshift({name: 'All', value: 'all', checked: true}, {name: 'Cancel', value: 'cancel'});
+ callback(null, sortedList);
+ }
+ });
+ }
+
+ setTimeout(function timeout(){ getInfo(); }, 100 * i);
+
+ }, items = [], list = [], i, count;
+
+ list = _.filter(opt, function filter(item) { return (item.indexOf('http://') !== -1) || (item.indexOf('https://') !== -1); });
+
+ if (!list.length) { return callback(null, list); }
+
+ count = list.length;
+
+ for (i = 0; i < list.length; i++) {
+ getDetails(list[i], i);
+ }
+ },
+
+ download: function download(opt, list, argv) {
+
+ var options = opt.videos, fullList = [], selected = [];
+
+ downloadPath = argv.d;
+
+ if (options.length === 1 && options[0] === 'cancel') { return console.log('Cancel'); }
+
+ if (options.length === 1 && options[0] === 'all') {
+ fullList = _.pull(_.map(list, function map(item) { return item.value; }), 'all', 'cancel');
+ return handleList(fullList);
+ }
+
+ if (options.length >= 1) {
+ selected = _.pull(options, 'all', 'cancel');
+
+ if (selected.length) { return handleList(selected); }
+ }
+
+ }
+}; \ No newline at end of file
diff --git a/mongo-edu.js b/mongo-edu.js
new file mode 100644
index 0000000..71652f0
--- /dev/null
+++ b/mongo-edu.js
@@ -0,0 +1,103 @@
+/*
+ * mongo-edu
+ *
+ * Copyright (c) 2014 Przemyslaw Pluta
+ * Licensed under the MIT license.
+ * https://github.com/przemyslawpluta/mongo-edu/blob/master/LICENSE
+ */
+
+var mdbvideos = require('./lib/login'),
+ videoHandler = require('./lib/videos'),
+ validate = require('./lib/validate'),
+ colors = require('colors'),
+ inquirer = require('inquirer'),
+ argv = require('optimist')
+ .usage('Usage: $0 -u [user name] -d [download path]')
+ .describe('d', 'download path').describe('u', 'email address')
+ .demand('d').argv;
+
+exports.create = function start() {
+
+ validate.init(argv, function (err, profile) {
+ if (err !== null) { throw err; }
+ run(profile);
+ });
+
+ function run(profile) {
+
+ inquirer.prompt(profile, function promt(answers) {
+
+ var list = [{ type: 'list', name: 'url', message: '', choices: [] }], classes = list,
+
+ check = [{ type: 'checkbox', message: '', name: 'videos', choices: [],
+ validate: function validate(answer) {
+ if ( answer.length < 1 ) { return 'You must choose at least one option.'; }
+ return true;
+ }
+ }];
+
+ mdbvideos.init(answers, function get(err, data) {
+ if (err !== null) { throw err; }
+ if (data.length) {
+
+ classes[0].message = 'Found ' + data.length + ' Course'+ ((data.length > 1)? 's' : '') + '. Select:';
+ classes[0].choices = data;
+ return currentList();
+
+ }
+
+ });
+
+ function currentList() {
+ inquirer.prompt(classes, function prompt(answers) {
+
+ mdbvideos.getList(answers, function get(err, data) {
+ if (err !== null) { throw err; }
+
+ if (data.length) {
+
+ list[0].message = 'Found ' + data.length + ' List'+ ((data.length > 1)? 's' : '') + '. Select:';
+ list[0].choices = data;
+
+ return currentVideos();
+
+ }
+
+ return console.log('[' + 'i'.red + '] Unable to locate any lists in the wiki. Is course available?');
+ });
+
+ });
+ }
+
+ function currentVideos() {
+ inquirer.prompt(list, function prompt(answers) {
+
+ mdbvideos.listVideos(answers, function get(err, data, pass) {
+ if (err !== null) { throw err; }
+ if (!pass) { return videoHandler.details(data, showDetails); }
+ showDetails(err, data);
+ });
+
+ });
+ }
+
+ function showDetails(err, data) {
+ if (err !== null) { throw err; }
+ if (data.length) {
+ check[0].message = 'Select From ' + (data.length - 2) + ' Videos. Download:';
+ check[0].choices = data;
+
+ return inquirer.prompt(check, function prompt(answers) {
+ videoHandler.download(answers, data, argv);
+ });
+
+ }
+
+ console.log('[' + 'i'.red + '] Could not locate any videos.'); process.exit(0);
+ }
+
+ });
+
+ }
+
+}; \ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..0c82b36
--- /dev/null
+++ b/package.json
@@ -0,0 +1,48 @@
+{
+ "name": "mongo-edu",
+ "preferGlobal": true,
+ "version": "0.1.0",
+ "author": "Przemyslaw Pluta <przemyslawplutadev@gmail.com> (http://przemyslawpluta.com)",
+ "description": "Select and download video courses from education.mongodb.com",
+ "main": "./mongo-edu",
+ "scripts": {
+ "start": "node ./bin/mongo-edu"
+ },
+ "bin": {
+ "mongo-edu": "./bin/mongo-edu"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/przemyslawpluta/mongo-edu.git"
+ },
+ "keywords": [
+ "mongodb",
+ "download",
+ "videos",
+ "youtube",
+ "course",
+ "education",
+ "edu"
+ ],
+ "analyze": false,
+ "license": "MIT",
+ "dependencies": {
+ "request": "~2.33.0",
+ "youtube-dl": "~1.3.2",
+ "colors": "~0.6.2",
+ "lodash": "~2.4.1",
+ "cheerio": "~0.13.1",
+ "inquirer": "~0.4.0",
+ "progress": "~1.1.3",
+ "mkdirp": "~0.3.5",
+ "optimist": "~0.6.1"
+ },
+ "engines": {
+ "node": ">= 0.8.x"
+ },
+ "bugs": {
+ "url": "https://github.com/przemyslawpluta/mongo-edu/issues"
+ },
+ "homepage": "https://github.com/przemyslawpluta/mongo-edu",
+ "devDependencies": {}
+}