// ==UserScript==
// @name monkey-videos
// @description 播放网页里的视频, 不再需要Adobe Flash Player
// @version 1.0.7
// @license GPLv3
// @author LiuLang
// @email [email protected]
// @include http://www.56.com/u*
// @include http://www.56.com/w*
// @include http://www.acfun.tv/v/ac*
// @include http://www.bilibili.com/video/av*
// @include http://tv.cntv.cn/video/*
// @include http://search.cctv.com/playVideo.php*
// @include http://www.fun.tv/vplay/*
// @include http://fun.tv/vplay/*
// @include http://phtv.ifeng.com/program/*
// @include http://v.ifeng.com/*
// @include http://www.iqiyi.com/v_*
// @include http://www.iqiyi.com/jilupian/*
// @include http://www.letv.com/ptv/vplay/*
// @include http://www.justing.com.cn/page/*
// @include http://v.ku6.com/*
// @include http://v.163.com/*
// @include http://open.163.com/*
// @include http://v.pps.tv/play_*
// @include http://ipd.pps.tv/play_*
// @include http://video.sina.com.cn/*
// @include http://open.sina.com.cn/course/*
// @include http://tv.sohu.com/*
// @include http://www.tucao.cc/play/*
// @include http://www.tudou.com/albumplay/*
// @include http://www.tudou.com/listplay/*
// @include http://www.tudou.com/programs/view/*
// @include http://www.wasu.cn/Play/show/id/*
// @include http://www.wasu.cn/play/show/id/*
// @include http://www.wasu.cn/wap/Play/show/id/*
// @include http://www.wasu.cn/wap/play/show/id/*
// @include http://www.weiqitv.com/index/live_back?videoId=*
// @include http://www.weiqitv.com/index/video_play?videoId=*
// @include http://v.youku.com/v_show/id_*
// @include http://v.youku.com/v_playlist/*
// @include http://www.youtube.com/watch?v=*
// @include https://www.youtube.com/watch?v=*
// @include http://www.youtube.com/embed/*
// @include https://www.youtube.com/embed/*
// @run-at document-end
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @namespace https://greasyfork.org/users/7422
// ==/UserScript==
(function() {
////////////////////////////////////////////////////////////////////////
//// Router
////////////////////////////////////////////////////////////////////////
var monkey = {
pathnames: {},
handlers: {},
run: function() {
console.log('monkey.run: ', this);
var handler = this.matchHandler();
console.log('handler:', handler);
if (handler !== null) {
handler.run();
}
},
extend: function(hostname, pathnameList, handler) {
this.pathnames[hostname] = pathnameList;
this.handlers[hostname] = handler;
},
matchHandler: function() {
console.log('matchHandler() --');
var host = location.host,
url = location.href,
pathnames;
if (host in this.pathnames) {
pathnames = this.pathnames[host];
if (pathnames.some(function(pathname) {
return url.startsWith(pathname);
})) {
return this.handlers[host];
}
}
return null;
},
};
////////////////////////////////////////////////////////////////////////
//// Utils
////////////////////////////////////////////////////////////////////////
/*
* JavaScript MD5 1.0.1
* https://github.com/blueimp/JavaScript-MD5
*
* Copyright 2011, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*
* Based on
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for more info.
*/
/*jslint bitwise: true */
/*global unescape, define */
(function ($) {
'use strict';
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF),
msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function bit_rol(num, cnt) {
return (num << cnt) | (num >>> (32 - cnt));
}
/*
* These functions implement the four basic operations the algorithm uses.
*/
function md5_cmn(q, a, b, x, s, t) {
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
}
function md5_ff(a, b, c, d, x, s, t) {
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t) {
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t) {
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t) {
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}
/*
* Calculate the MD5 of an array of little-endian words, and a bit length.
*/
function binl_md5(x, len) {
/* append padding */
x[len >> 5] |= 0x80 << (len % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;
var i, olda, oldb, oldc, oldd,
a = 1732584193,
b = -271733879,
c = -1732584194,
d = 271733878;
for (i = 0; i < x.length; i += 16) {
olda = a;
oldb = b;
oldc = c;
oldd = d;
a = md5_ff(a, b, c, d, x[i], 7, -680876936);
d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i], 20, -373897302);
a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
d = md5_hh(d, a, b, c, x[i], 11, -358537222);
c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
a = md5_ii(a, b, c, d, x[i], 6, -198630844);
d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
}
return [a, b, c, d];
}
/*
* Convert an array of little-endian words to a string
*/
function binl2rstr(input) {
var i,
output = '';
for (i = 0; i < input.length * 32; i += 8) {
output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
}
return output;
}
/*
* Convert a raw string to an array of little-endian words
* Characters >255 have their high-byte silently ignored.
*/
function rstr2binl(input) {
var i,
output = [];
output[(input.length >> 2) - 1] = undefined;
for (i = 0; i < output.length; i += 1) {
output[i] = 0;
}
for (i = 0; i < input.length * 8; i += 8) {
output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
}
return output;
}
/*
* Calculate the MD5 of a raw string
*/
function rstr_md5(s) {
return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
}
/*
* Calculate the HMAC-MD5, of a key and some data (raw strings)
*/
function rstr_hmac_md5(key, data) {
var i,
bkey = rstr2binl(key),
ipad = [],
opad = [],
hash;
ipad[15] = opad[15] = undefined;
if (bkey.length > 16) {
bkey = binl_md5(bkey, key.length * 8);
}
for (i = 0; i < 16; i += 1) {
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
}
/*
* Convert a raw string to a hex string
*/
function rstr2hex(input) {
var hex_tab = '0123456789abcdef',
output = '',
x,
i;
for (i = 0; i < input.length; i += 1) {
x = input.charCodeAt(i);
output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
}
return output;
}
/*
* Encode a string as utf-8
*/
function str2rstr_utf8(input) {
return unescape(encodeURIComponent(input));
}
/*
* Take string arguments and return either raw or hex encoded strings
*/
function raw_md5(s) {
return rstr_md5(str2rstr_utf8(s));
}
function hex_md5(s) {
return rstr2hex(raw_md5(s));
}
function raw_hmac_md5(k, d) {
return rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d));
}
function hex_hmac_md5(k, d) {
return rstr2hex(raw_hmac_md5(k, d));
}
function md5(string, key, raw) {
if (!key) {
if (!raw) {
return hex_md5(string);
}
return raw_md5(string);
}
if (!raw) {
return hex_hmac_md5(key, string);
}
return raw_hmac_md5(key, string);
}
if (typeof define === 'function' && define.amd) {
define(function () {
return md5;
});
} else {
$.md5 = md5;
}
}(this));
/**
* base64 function wrap
* usage: base64.encode(str); base64.decode(base64_str);
*/
var base64 = {
encodeChars : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs' +
'tuvwxyz0123456789+/',
decodeChars : [
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1],
encodeFunction: function(str) {
var out = '',
len = str.length,
i = 0,
c1,
c2,
c3;
while(i < len) {
c1 = str.charCodeAt(i++) & 0xff;
if(i === len) {
out += this.encodeChars.charAt(c1 >> 2);
out += this.encodeChars.charAt((c1 & 0x3) << 4);
out += "==";
break;
}
c2 = str.charCodeAt(i++);
if(i === len) {
out += this.encodeChars.charAt(c1 >> 2);
out += this.encodeChars.charAt(((c1 & 0x3)<< 4) |
((c2 & 0xF0) >> 4));
out += this.encodeChars.charAt((c2 & 0xF) << 2);
out += "=";
break;
}
c3 = str.charCodeAt(i++);
out += this.encodeChars.charAt(c1 >> 2);
out += this.encodeChars.charAt(((c1 & 0x3)<< 4) |
((c2 & 0xF0) >> 4));
out += this.encodeChars.charAt(((c2 & 0xF) << 2) |
((c3 & 0xC0) >>6));
out += this.encodeChars.charAt(c3 & 0x3F);
}
return out;
},
decodeFunction: function(str) {
var c1,
c2,
c3,
c4,
len = str.length,
out = '',
i = 0;
while(i < len) {
do {
c1 = this.decodeChars[str.charCodeAt(i++) & 0xff];
} while(i < len && c1 === -1);
if(c1 === -1) {
break;
}
do {
c2 = this.decodeChars[str.charCodeAt(i++) & 0xff];
} while(i < len && c2 === -1);
if(c2 === -1) {
break;
}
out += String.fromCharCode((c1 << 2) |
((c2 & 0x30) >> 4));
do {
c3 = str.charCodeAt(i++) & 0xff;
if(c3 === 61) {
return out;
}
c3 = this.decodeChars[c3];
} while(i < len && c3 === -1);
if(c3 === -1) {
break;
}
out += String.fromCharCode(((c2 & 0XF) << 4) |
((c3 & 0x3C) >> 2));
do {
c4 = str.charCodeAt(i++) & 0xff;
if(c4 === 61) {
return out;
}
c4 = this.decodeChars[c4];
} while(i < len && c4 === -1);
if(c4 === -1) {
break;
}
out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
};
return out;
},
utf16to8: function(str) {
var out = '',
len = str.length,
i,
c;
for(i = 0; i < len; i++) {
c = str.charCodeAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
out += str.charAt(i);
} else if (c > 0x07FF) {
out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
} else {
out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
}
}
return out;
},
utf8to16: function(str) {
var out = '',
len = str.length,
i = 0,
c,
char2,
char3;
while(i < len) {
c = str.charCodeAt(i++);
switch(c >> 4) {
// 0xxxxxxx
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
out += str.charAt(i - 1);
break;
// 110x xxxx 10xx xxxx
case 12: case 13:
char2 = str.charCodeAt(i++);
out += String.fromCharCode(((c & 0x1F) << 6) |
(char2 & 0x3F));
break;
// 1110 xxxx 10xx xxxx 10xx xxxx
case 14:
char2 = str.charCodeAt(i++);
char3 = str.charCodeAt(i++);
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
break;
}
}
return out;
},
// This is encode/decode wrap, which convert chars between UTF-8
// and UTF-16;
encode: function(str) {
return this.encodeFunction(this.utf16to8(str));
},
decode: function(str) {
return this.utf8to16(this.decodeFunction(str));
},
};
/**
* Create a new <style> tag with str as its content.
* @param string styleText
* - The <style> tag content.
*/
var addStyle = function(styleText) {
console.log('addStyle() --');
var style = document.createElement('style');
if (document.head) {
document.head.appendChild(style);
style.innerHTML = styleText;
}
};
/**
* Split query parameters in url and convert to object
*/
var getQueryVariable = function(query) {
var vars = query.split('&'),
params = {},
param,
i;
for (i = 0; i < vars.length; i += 1) {
param = vars[i].split('=');
params[param[0]] = param[1];
}
return params;
};
/**
* Convert string to xml
* @param string str
* - the string to be converted.
* @return object xml
* - the converted xml object.
*/
var parseXML = function(str) {
if (document.implementation &&
document.implementation.createDocument) {
xmlDoc = new DOMParser().parseFromString(str, 'text/xml');
} else {
console.log('parseXML() error: not support current web browser!');
return null;
}
return xmlDoc;
};
/**
* UI functions.
* create UI which has multiples files per video.
*/
var multiFiles = {
// videos is an object containing these fields:
// title, video title
// formats, title of each video format
// links, list containing video links of each duration
videos: null,
run: function(videos) {
console.log('multiFiles.run() --');
this.videos = videos;
if ((!videos.formats) || (videos.formats.length === 0)) {
console.error('Error: no video formats specified!');
return;
}
this.removeOldPanels();
this.createPanel();
},
removeOldPanels: function() {
console.log('removeOldPanels() --');
var panels = document.querySelectorAll('.monkey-videos-panel'),
panel,
i;
for (i = 0; panel = panels[i]; i += 1) {
panel.parentElement.removeChild(panel);
}
},
/**
* Create the control panel.
*/
createPanel: function() {
console.log('createPanel() --');
var panel = document.createElement('div'),
div,
form,
label,
input,
span,
a,
i,
playlistWrap,
playlistToggle,
that = this;
addStyle([
'.monkey-videos-panel {',
'position: fixed;',
'right: 10px;',
'bottom: 0px;',
'z-index: 99999;',
'border: 2px solid #ccc;',
'border-top-left-radius: 14px;',
'margin: 10px 0px 0px 0px;',
'padding: 10px 10px 0px 10px;',
'background-color: #fff;',
'overflow-y: hidden;',
'max-height: 90%;',
'min-width: 100px;',
'text-align: left;',
'}',
'.monkey-videos-panel:hover {',
'overflow-y: auto;',
'}',
'.monkey-videos-panel label {',
'margin-right: 10px;',
'}',
'.monkey-videos-panel .playlist-item {',
'display: block;',
'margin-top: 8px;',
'}',
'.monkey-videos-panel #playlist-toggle {',
'height: 10px;',
'margin-top: 10px;',
'}',
'.monkey-videos-panel #playlist-toggle:hover {',
'cursor: pointer;',
'}',
'.monkey-videos-panel .playlist-show {',
'background-color: #8b82a2;',
//'border-radius: 0px 0px 5px 5px;',
'}',
'.monkey-videos-panel .playlist-hide {',
'background-color: #462093;',
//'border-radius: 5px 5px 0px 0px;',
'}',
].join(''));
panel.className = 'monkey-videos-panel';
document.body.appendChild(panel);
playlistWrap = document.createElement('div');
playlistWrap.className = 'playlist-wrap';
panel.appendChild(playlistWrap);
div = document.createElement('div');
div.className = 'playlist-nav';
playlistWrap.appendChild(div);
form = document.createElement('form');
form.className = 'playlist-format';
playlistWrap.appendChild(form);
for (i = 0; i < this.videos.formats.length; i += 1) {
label = document.createElement('label');
form.appendChild(label);
input = document.createElement('input');
label.appendChild(input);
input.type = 'radio';
input.name = 'monkey-videos-format';
span = document.createElement('span');
label.appendChild(span);
span.innerHTML = this.videos.formats[i];
(function(input, pos) {
input.addEventListener('change', function() {
that.modifyList(pos);
GM_setValue('format', pos);
}, false);
})(input, i);
}
// playlist m3u (with url data schema)
a = document.createElement('a');
a.className = 'playlist-m3u';
a.innerHTML = '播放列表';
a.title = a.innerHTML;
a.download = this.videos.title + '.m3u';
a.href = '';
form.appendChild(a);
div = document.createElement('div');
div.className = 'playlist';
playlistWrap.appendChild(div);
playlistToggle = document.createElement('div');
playlistToggle.id = 'playlist-toggle';
playlistToggle.title = '隐藏';
playlistToggle.className = 'playlist-show';
panel.appendChild(playlistToggle);
playlistToggle.addEventListener('click', function(event) {
var wrap = document.querySelector('.monkey-videos-panel .playlist-wrap');
if (wrap.style.display === 'none') {
wrap.style.display = 'block';
event.target.className = 'playlist-show';
event.target.title = '隐藏';
GM_setValue('hidePlaylist', false);
} else {
wrap.style.display = 'none';
event.target.title = '显示';
event.target.className = 'playlist-hide';
GM_setValue('hidePlaylist', true);
}
}, false);
if (GM_getValue('hidePlaylist', false)) {
playlistToggle.click();
}
this.loadDefault();
},
loadDefault: function() {
console.log('loadDefault() --');
// Load default type of playlist.
var currPos = GM_getValue('format', 0),
formats = this.videos.formats,
currPlaylist;
console.log('currPos: ', currPos);
if (formats.length <= currPos) {
currPos = formats.length - 1;
}
console.log('currPos: ', currPos);
currPlaylist = document.querySelectorAll(
'.monkey-videos-panel .playlist-format input')[currPos];
if (currPlaylist) {
currPlaylist.checked = true;
this.modifyList(currPos);
}
},
/**
* Modify the playlist content.
*
* Empty playlist first, and add new links of specific video format.
*/
modifyList: function(pos) {
console.log('modifyList(), pos = ', pos);
var playlist = document.querySelector('.monkey-videos-panel .playlist'),
url,
a,
i;
// Clear its content first
playlist.innerHTML = '';
for (i = 0; url = this.videos.links[pos][i]; i += 1) {
a = document.createElement('a');
playlist.appendChild(a);
a.className = 'playlist-item',
a.href = url;
if (this.videos.links[pos].length == 1) {
a.innerHTML = this.videos.title;
}
else if (i < 9) {
a.innerHTML = this.videos.title + '(0' + String(i + 1) + ')';
} else {
a.innerHTML = this.videos.title + '(' + String(i + 1) + ')';
}
a.title = a.innerHTML;
}
// Refresh m3u playlist file.
document.querySelector('.playlist-m3u').href = this.plsDataScheme();
},
/**
* Generate Playlist using base64 and Data URI scheme.
* So that we can download directly and same it as a pls file using HTML.
* URL:http://en.wikipedia.org/wiki/Data_URI_scheme
* @return string
* - Data scheme containting playlist.
*/
plsDataScheme: function() {
console.log('plsDataSchema() --');
return 'data:audio/x-m3u;charset=UTF-8;base64,' +
base64.encode(this.generatePls());
},
/**
* Generate pls - a multimedia playlist file, like m3u.
* @return string
* - playlist content.
*/
generatePls: function() {
console.log('generatePls() --');
var output = [],
links = document.querySelectorAll('.monkey-videos-panel .playlist-item'),
a,
i;
output.push('#EXTM3U');
for (i = 0; a = links[i]; i += 1) {
output.push('#EXTINF:81, ' + a.innerHTML);
output.push(a.href);
}
return output.join('\n');
},
};
var singleFile = {
// videos is an object containing video info.
//
// @title, string, video title
// @formats, string list, format name of each video
// @links, string list, video link
// @msg, string
// @ok, bool, is ok is false, @msg will be displayed on playlist-panel
videos: null,
run: function(videos) {
console.log('run() -- ');
this.videos = videos;
this.createPanel();
this.createPlaylist();
},
createPanel: function() {
console.log('createPanel() --');
var panel = document.createElement('div'),
playlist = document.createElement('div'),
playlistToggle = document.createElement('div');
addStyle([
'.monkey-videos-panel {',
'position: fixed;',
'right: 10px;',
'bottom: 0px;',
'z-index: 99999;',
'border: 2px solid #ccc;',
'border-top-left-radius: 14px;',
'margin: 10px 0px 0px 0px;',
'padding: 10px 10px 0px 10px;',
'background-color: #fff;',
'overflow-y: hidden;',
'max-height: 90%;',
'min-width: 100px;',
'}',
'.monkey-videos-panel:hover {',
'overflow-y: auto;',
'}',
'.monkey-videos-panel label {',
'margin-right: 10px;',
'}',
'.monkey-videos-panel .playlist-item {',
'display: block;',
'}',
'.monkey-videos-panel #playlist-toggle {',
'height: 10px;',
'width: 100%;',
'margin-top: 10px;',
'}',
'.monkey-videos-panel #playlist-toggle:hover {',
'cursor: pointer;',
'}',
'.monkey-videos-panel .playlist-show {',
'background-color: #8b82a2;',
//'border-radius: 0px 0px 5px 5px;',
'}',
'.monkey-videos-panel .playlist-hide {',
'background-color: #462093;',
//'border-radius: 5px 5px 0px 0px;',
'}',
].join(''));
panel.className = 'monkey-videos-panel';
document.body.appendChild(panel);
playlist= document.createElement('div');
playlist.className = 'playlist-wrap';
panel.appendChild(playlist);
playlistToggle = document.createElement('div');
playlistToggle.id = 'playlist-toggle';
playlistToggle.title = '隐藏';
playlistToggle.className = 'playlist-show';
panel.appendChild(playlistToggle);
playlistToggle.addEventListener('click', function(event) {
var wrap = document.querySelector('.monkey-videos-panel .playlist-wrap');
if (wrap.style.display === 'none') {
wrap.style.display = 'block';
event.target.className = 'playlist-show';
event.target.title = '隐藏';
GM_setValue('hidePlaylist', false);
} else {
wrap.style.display = 'none';
event.target.title = '显示';
event.target.className = 'playlist-hide';
GM_setValue('hidePlaylist', true);
}
}, false);
if (GM_getValue('hidePlaylist', false)) {
playlistToggle.click();
}
},
createPlaylist: function() {
console.log('createPlayList() -- ');
var playlist = document.querySelector('.monkey-videos-panel .playlist-wrap'),
a,
i;
if (!this.videos.ok) {
console.error(this.videos.msg);
a = document.createElement('span');
a.title = this.videos.msg;
a.innerHTML = this.videos.msg;
playlist.appendChild(a);
return;
}
for (i = 0; i < this.videos.links.length; i += 1) {
a = document.createElement('a');
a.className = 'playlist-item';
a.innerHTML = this.videos.title + '(' + this.videos.formats[i] + ')';
a.title = a.innerHTML;
a.href = this.videos.links[i];
playlist.appendChild(a);
}
},
};
////////////////////////////////////////////////////////////////////////
//// Define video handlers
////////////////////////////////////////////////////////////////////////
/**
* 56.com
*/
var monkey_56 = {
title: '',
id: '',
json: null,
videos: {
'normal': '',
'clear': '',
'super': '',
},
run: function() {
console.log('run() --');
this.getID();
if (this.id.length > 0) {
this.getPlaylist();
} else {
console.error('Failed to get video id!');
return;
}
},
/**
* Get video id
*/
getID: function() {
console.log('getID() --');
var url = location.href,
idReg = /\/v_(\w+)\.html/,
idMatch = idReg.exec(url),
albumIDReg = /_vid-(\w+)\.html/,
albumIDMatch = albumIDReg.exec(url);
console.log(idMatch);
console.log(albumIDMatch);
if (idMatch && idMatch.length === 2) {
this.id = idMatch[1];
} else if (albumIDMatch && albumIDMatch.length === 2) {
this.id = albumIDMatch[1];
}
console.log(this);
},
/**
* Get video playlist from a json object
*/
getPlaylist: function() {
console.log('getPlaylist() --');
var url = 'http://vxml.56.com/json/' + this.id + '/?src=out',
that = this;
console.log('url: ', url);
GM_xmlhttpRequest({
method: 'get',
url: url,
onload: function(response) {
console.log('response:', response);
var txt = response.responseText,
json = JSON.parse(txt),
video,
i;
that.json = json;
if (json.msg == 'ok' && json.status == '1') {
that.title = json.info.Subject;
for (i = 0; video = json.info.rfiles[i]; i = i + 1) {
that.videos[video.type] = video.url;
}
}
that.createUI();
},
});
},
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: this.title,
formats: [],
links: [],
ok: true,
msg: '',
},
formats = ['normal', 'clear', 'super'],
format_names = ['标清', '高清', '超清'],
format,
link,
i;
for (i = 0; format = formats[i]; i += 1) {
if (format in this.videos && this.videos[format].length > 0) {
videos.links.push([this.videos[format]]);
videos.formats.push(format_names[i]);
} else {
console.error('This video type is not supported: ', format);
}
}
console.log(videos);
multiFiles.run(videos);
},
}
monkey.extend('www.56.com', [
'http://www.56.com/u',
'http://www.56.com/w',
], monkey_56);
/**
* acfun.tv
*/
var monkey_acfun = {
vid: '',
origUrl: '',
run: function() {
this.getVid();
if (this.vid.length === 0) {
console.error('Failed to get video id!');
this.createUI();
} else {
this.getVideoLink();
}
},
getVid: function() {
console.log('getVid()');
var videos = document.querySelectorAll(
'div#area-part-view div.l a'),
video,
i;
console.log('videos: ', videos);
for (i = 0; video = videos[i]; i += 1) {
if (video.className.search('active') > 0) {
this.vid = video.getAttribute('data-vid');
return;
}
}
console.error('Failed to get vid');
},
/**
* Get video link from a json object
*/
getVideoLink: function() {
console.log('getVideoLink()');
console.log(this);
//var url = 'http://www.acfun.tv/api/getVideoByID.aspx?vid=' + this.vid,
var url = 'http://www.acfun.tv/video/getVideo.aspx?id=' + this.vid,
that = this;
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
console.log('response:', response);
var json = JSON.parse(response.responseText);
if (json.success && json.sourceUrl.startsWith('http')) {
that.origUrl = json.sourceUrl;
}
that.createUI();
},
});
},
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: '原始地址',
formats: [],
links: [],
ok: true,
msg: '',
};
if (this.origUrl.length === 0) {
videos.ok = false;
videos.msg = '暂不支持';
singleFile.run(videos);
} else {
videos.formats.push(' ');
videos.links.push(this.origUrl);
singleFile.run(videos);
}
},
}
monkey.extend('www.acfun.tv', [
'http://www.acfun.tv/v/ac',
], monkey_acfun);
/**
* bilibili.tv
*/
var monkey_bili = {
cid: '',
title: '',
oriurl: '',
run: function() {
console.log('run() --');
this.getTitle();
this.getCid();
},
/**
* Get video title
*/
getTitle: function() {
console.log('getTitle()');
var metas = document.querySelectorAll('meta'),
meta,
i;
for (i = 0; meta = metas[i]; i += 1) {
if (meta.hasAttribute('name') &&
meta.getAttribute('name') === 'title') {
this.title = meta.getAttribute('content');
return;
}
}
this.title = document.title;
},
/**
* 获取 content ID.
*/
getCid: function() {
console.log('getCid()');
var iframe = document.querySelector('iframe'),
flashvar = document.querySelector('div#bofqi embed'),
reg = /cid=(\d+)&aid=(\d+)/,
match;
if (iframe) {
match = reg.exec(iframe.src);
} else if (flashvar) {
console.log(flashvar.getAttribute('flashvars'));
match = reg.exec(flashvar.getAttribute('flashvars'));
}
console.log('match:', match);
if (match && match.length === 3) {
this.cid = match[1];
this.getVideos();
} else {
console.error('Failed to get cid!');
}
},
/**
* Get original video links from interface.bilibili.cn
*/
getVideos: function() {
console.log('getVideos() -- ');
var url = 'http://interface.bilibili.cn/player?cid=' + this.cid,
that = this;
console.log('url:', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
var reg = /<oriurl>(.+)<\/oriurl>/g,
txt = response.responseText,
match = reg.exec(txt);
if (match && match.length === 2) {
that.oriurl = match[1];
that.createUI();
}
},
});
},
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: '视频的原始地址',
formats: [''],
links: [],
ok: true,
msg: '',
};
videos.formats.push('');
videos.links.push(this.oriurl);
singleFile.run(videos);
},
}
monkey.extend('www.bilibili.com', [
'http://www.bilibili.com/video/av',
], monkey_bili);
/**
* cntv.cn
*/
var monkey_cntv = {
pid: '',
title: '',
json: [],
videos: {
chapters: [],
chapters2: [],
},
run: function() {
console.log('run() --');
this.router();
},
router: function() {
console.log('router() --');
var href = location.href,
schema;
if (href.search('search.cctv.com/playVideo.php?') > -1) {
schema = this.hashToObject(location.search.substr(1));
this.pid = schema.detailsid;
this.title = decodeURI(schema.title);
this.getVideoInfo();
} else if (href.search('tv.cntv.cn/video/') > -1) {
this.pid = href.match(/\/([^\/]+)$/)[1];
this.title = document.title.substring(0, document.title.length - 8);
this.getVideoInfo();
} else {
this.getPidFromSource();
}
},
/**
* Get video pid from html source file
*/
getPidFromSource: function() {
console.log('getPidFromSource() --');
var that = this;
GM_xmlhttpRequest({
url: location.href,
method: 'GET',
onload: function(response) {
console.log('response:', response);
that.parsePid(response.responseText);
},
});
},
/**
* Parse txt and get pid of video
*/
parsePid: function(txt) {
console.log('parsePid() --');
var pidReg = /code\.begin-->([^<]+)/,
pidMatch = pidReg.exec(txt),
titleReg = /title\.begin-->([^<]+)/,
titleMatch = titleReg.exec(txt);
if (titleMatch && titleMatch.length === 2) {
this.title = titleMatch[1];
} else {
this.title = document.title;
}
console.log('pidMatch:', pidMatch);
if (pidMatch && pidMatch.length === 2) {
this.pid = pidMatch[1];
this.getVideoInfo();
} else {
console.error('Failed to get Pid');
return;
}
},
/**
* Get video info, including formats and uri
*/
getVideoInfo: function() {
console.log('getVideoInfo() --');
var url = [
'http://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?',
'tz=-8&from=000tv&idlr=32&modified=false&idl=32&pid=',
this.pid,
'&url=',
location.href,
].join(''),
that = this;
console.log('url:', url);
GM_xmlhttpRequest({
url: url,
method: 'GET',
onload: function(response) {
console.log('response: ', response);
that.json = JSON.parse(response.responseText);
console.log('that: ', that);
that.parseVideos();
},
});
},
/**
* Parse video info from json object.
*/
parseVideos: function() {
console.log('parseVideos() --');
var chapter;
for (chapter in this.json.video) {
if (chapter.startsWith('chapters')) {
this.parseChapter(chapter);
}
}
this.createUI();
},
/**
* Parse specified chapter, list of video links.
*/
parseChapter: function(chapter) {
console.log('parseChapter() --');
var item,
i;
for (i = 0; item = this.json.video[chapter][i]; i += 1) {
if (this.videos[chapter] === undefined) {
this.videos[chapter] = [];
}
this.videos[chapter].push(item.url);
}
},
/**
* Call multiFiles.js to construct UI widgets.
*/
createUI: function() {
console.log('createUI() --');
console.log('this: ', this);
var videos = {
title: this.title,
formats: [],
links: [],
};
if (this.videos.chapters.length > 0) {
videos.formats.push('标清');
videos.links.push(this.videos.chapters);
}
if (this.videos.chapters2.length > 0) {
videos.formats.push('高清');
videos.links.push(this.videos.chapters2);
}
multiFiles.run(videos);
},
/**
* Create a new <style> tag with str as its content.
* @param string styleText
* - The <style> tag content.
*/
addStyle: function(styleText) {
var style = document.createElement('style');
if (document.head) {
document.head.appendChild(style);
style.innerHTML = styleText;
}
},
/**
* 将URL中的hash转为了个对象/字典, 用于解析链接;
*/
hashToObject: function(hashTxt) {
var list = hashTxt.split('&'),
output = {},
len = list.length,
i = 0,
tmp = '';
for (i = 0; i < len; i += 1) {
tmp = list[i].split('=')
output[tmp[0]] = tmp[1];
}
return output;
},
}
monkey.extend('tv.cntv.cn', [
'http://tv.cntv.cn/video/',
], monkey_cntv);
monkey.extend('search.cctv.com', [
'http://search.cctv.com/playVideo.php',
], monkey_cntv);
/**
* funshion.com
*/
var monkey_funshion = {
title: '',
mediaid: '', // 专辑ID;
number: '', // 第几集, 从1计数;
jobs: 0,
formats: {
327680: '标清版',
491520: '高清版',
737280: '超清版',
},
videos: {
327680: '',
491520: '',
737280: '',
},
run: function() {
console.log('run() --');
this.router();
},
/**
* router control
*/
router: function() {
var url = location.href;
if (url.search('subject/play/') > 1 ||
url.search('/vplay/') > 1 ) {
this.getVid();
} else if (url.search('subject/') > 1) {
this.addLinks();
} else if (url.search('uvideo/play/') > 1) {
this.getUGCID();
} else {
console.error('Error: current page is not supported!');
}
},
/**
* Get UGC video ID.
* For uvideo/play/'.
*/
getUGCID: function() {
console.log('getUGCID() --');
var urlReg = /uvideo\/play\/(\d+)$/,
urlMatch = urlReg.exec(location.href);
console.log('urlMatch: ', urlMatch);
if (urlMatch.length === 2) {
this.mediaid = urlMatch[1];
this.getUGCVideoInfo();
} else {
console.error('Failed to parse video ID!');
}
},
getUGCVideoInfo: function() {
console.log('getUGCVideoInfo() --');
var url = 'http://api.funshion.com/ajax/get_media_data/ugc/' + this.mediaid,
that = this;
console.log('url: ', url);
GM_xmlhttpRequest({
url: url,
method: 'GET',
onload: function(response) {
console.log('response: ', response);
that.json = JSON.parse(response.responseText);
console.log('json: ', that.json);
that.decodeUGCVideoInfo();
},
});
},
decodeUGCVideoInfo: function() {
console.log('decodeUGCVideoInfo() --');
var url = [
'http://jobsfe.funshion.com/query/v1/mp4/',
this.json.data.hashid,
'.json?file=',
this.json.data.filename,
].join(''),
that = this;
console.log('url: ', url);
GM_xmlhttpRequest({
url: url,
method: 'GET',
onload: function(response) {
console.log('response: ', response);
that.appendUGCVideo(JSON.parse(response.responseText));
},
});
},
appendUGCVideo: function(videoJson) {
console.log('appendUGCVideo() --');
console.log('this: ', this);
console.log('videoJson:', videoJson);
var fileformat = this.fileformats[videoJson.playlist[0].bits];
info = {
title: this.json.data.name_cn,
href: videoJson.playlist[0].urls[0],
};
console.log('info: ', info);
this._appendVideo(info);
},
/**
* Get video ID.
* For subject/play/'.
*/
getVid: function() {
console.log('getVid() --');
var url = location.href,
urlReg = /subject\/play\/(\d+)\/(\d+)$/,
urlMatch = urlReg.exec(url),
urlReg2 = /\/vplay\/m-(\d+)/,
urlMatch2 = urlReg2.exec(url);
console.log('urlMatch: ', urlMatch);
console.log('urlMatch2: ', urlMatch2);
if (urlMatch && urlMatch.length === 3) {
this.mediaid = urlMatch[1];
this.number = parseInt(urlMatch[2]);
} else if (urlMatch2 && urlMatch2.length === 2) {
this.mediaid = urlMatch2[1];
this.number = 1;
} else {
console.error('Failed to parse video ID!');
return;
}
this.getVideoInfo();
},
/**
* Download a json file containing video info
*/
getVideoInfo: function() {
console.log('getVideoInfo() --');
var url = [
'http://api.funshion.com/ajax/get_web_fsp/',
this.mediaid,
'/mp4',
].join(''),
that = this;
console.log('url: ', url);
GM_xmlhttpRequest({
url: url,
method: 'GET',
onload: function(response) {
console.log('response: ', response);
var json = JSON.parse(response.responseText),
format;
console.log('json: ', json);
that.title = json.data.name_cn || that.getTitle();
if ((! json.data.fsps) || (! json.data.fsps.mult) ||
(json.data.fsps.mult.length === 0) ||
(! json.data.fsps.mult[0].cid)) {
that.createUI();
}
that.mediaid = json.data.fsps.mult[0].cid;
for (format in that.formats) {
that.jobs = that.jobs + 1;
that.getVideoLink(format);
}
},
});
},
/**
* Get title from document.tiel
*/
getTitle: function() {
console.log('getTitle() --');
var title = document.title,
online = title.search(' - 在线观看');
if (online > -1) {
return title.substr(0, online);
} else {
return title.substr(0, 12) + '..';
}
},
/**
* Get Video source link.
*/
getVideoLink: function(format) {
console.log('getVideoLink() --');
var url = [
'http://jobsfe.funshion.com/query/v1/mp4/',
this.mediaid,
'.json?bits=',
format,
].join(''),
that = this;
console.log('url: ', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
console.log('response: ', response);
var json = JSON.parse(response.responseText);
console.log('json: ', json);
that.videos[format] = json.playlist[0].urls[0];
that.jobs = that.jobs - 1;
if (that.jobs === 0) {
that.createUI();
}
},
});
},
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: this.title,
formats: [],
links: [],
ok: true,
msg: '',
},
format;
if (this.videos[327680].length > 0) {
videos.links.push([this.videos[327680]]);
videos.formats.push(this.formats[327680]);
}
if (this.videos[491520].length > 0) {
videos.links.push([this.videos[491520]]);
videos.formats.push(this.formats[491520]);
}
if (this.videos[737280].length > 0) {
videos.links.push([this.videos[737280]]);
videos.formats.push(this.formats[737280]);
}
if (videos.links.length === 0) {
videos.ok = false;
videos.msg = 'Video source is not available.';
}
multiFiles.run(videos);
},
}
monkey.extend('fun.tv', [
'http://fun.tv/vplay/',
], monkey_funshion);
monkey.extend('www.fun.tv', [
'http://www.fun.tv/vplay/',
], monkey_funshion);
/**
* ifeng.com
*/
var monkey_ifeng = {
id: '',
title: '',
links: [],
run: function() {
console.log('run() --');
this.getId();
},
getId: function() {
console.log('getId() --');
var reg = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/,
match = reg.exec(location.href);
console.log('match: ', match);
if (match && match.length === 2) {
this.id = match[1];
this.downloadById();
} else {
console.error('Failed to get video id');
}
},
downloadById: function() {
console.log('downloadById() --');
var length = this.id.length,
url = [
'http://v.ifeng.com/video_info_new/',
this.id[length-2],
'/',
this.id.substr(length-2),
'/',
this.id,
'.xml'
].join(''),
that = this;
console.log('url:', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
var xml = new DOMParser().parseFromString(response.responseText,
'text/xml'),
item = xml.querySelector('item');
that.title = item.getAttribute('Name');
that.links.push(item.getAttribute('VideoPlayUrl'));
that.createUI();
},
});
},
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: this.title,
formats: [],
links: [],
ok: true,
msg: '',
};
videos.formats.push('标清');
videos.links.push([this.links]);
multiFiles.run(videos);
},
};
monkey.extend('v.ifeng.com', [
'http://v.ifeng.com/',
], monkey_ifeng);
monkey.extend('phtv.ifeng.com', [
'http://phtv.ifeng.com/program/',
], monkey_ifeng);
/**
* iqiyi.com
*/
var monkey_iqiyi = {
title: '',
vid: '', // default vid, data-player-videoid
uid: '', // generated uuid/user id
aid: '', // album id, data-player-albumid
tvid: '', // data-player-tvid
type: 0, // default type
rcOrder: [96, 1, 2, 3, 4, 5, 10],
vip: false, // this video is for VIP only
rc: {
96: {name: '240P', links: []},
1: {name: '320P', links: []},
2: {name: '480P', links: []},
3: {name: 'super', links: []},
4: {name: '720P', links: []},
5: {name: '1080P', links: []},
10: {name: '4K', links: []},
},
jobs: 0,
run: function() {
console.log('run() --');
this.getTitle();
this.getVid();
},
getTitle: function() {
console.log('getTitle() --');
var nav = unsafeWindow.document.querySelector('#navbar em'),
id,
title;
if (nav) {
title = nav.innerHTML;
} else {
title = unsafeWindow.document.title.split('-')[0];
}
this.title = title.trim();
},
getVid: function() {
console.log('getVid() --');
var videoPlay = unsafeWindow.document.querySelector('div#flashbox');
if (videoPlay && videoPlay.hasAttribute('data-player-videoid')) {
this.vid = videoPlay.getAttribute('data-player-videoid');
this.aid = videoPlay.getAttribute('data-player-aid');
this.tvid = videoPlay.getAttribute('data-player-tvid');
this.uid = this.hex_guid();
this.getVideoUrls();
} else {
console.error('Error: failed to get video id');
return;
}
},
getVideoUrls: function() {
console.log('getVideoUrls() --');
var tm = this.randint(1000, 2000),
enc = md5('ts56gh' + tm + this.tvid),
url = [
'http://cache.video.qiyi.com/vms?key=fvip&src=p',
'&tvId=', this.tvid,
'&vid=', this.vid,
'&vinfo=1&tm=', tm,
'&enc=', enc,
'&qyid=', this.uid,
'&tn=', Math.random(),
].join(''),
that = this;
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
var json = JSON.parse(response.responseText),
formats,
format,
vlink,
vlink_parts,
key,
url,
i,
j;
that.title = json.data.vi.vn;
if (! json.data.vp.tkl) {
that.vip = true;
that.createUI();
}
formats = json.data.vp.tkl[0].vs;
for (i = 0; format = formats[i]; i += 1) {
if (! that.rc[format.bid]) {
console.error('Current video type not supported: ', format.bid);
continue;
}
for (j = 0; j < format.fs.length; j += 1) {
vlink = format.fs[j].l;
if (! vlink.startsWith('/')) {
vlink = that.getVrsEncodeCode(vlink);
}
vlink_parts = vlink.split('/');
that.getDispathKey(
vlink_parts[vlink_parts.length - 1].split('.')[0],
format.bid, vlink, j);
}
}
},
});
},
getVrsEncodeCode: function(vlink) {
var loc6 = 0,
loc2 = [],
loc3 = vlink.split('-'),
loc4 = loc3.length,
i;
for (i = loc4 - 1; i >= 0; i -= 1) {
loc6 = this.getVRSXORCode(parseInt(loc3[loc4 - i - 1], 16), i);
loc2.push(String.fromCharCode(loc6));
}
return loc2.reverse().join('');
},
getVRSXORCode: function(arg1, arg2) {
var loc3 = arg2 % 3;
if (loc3 === 1) {
return arg1 ^ 121;
} else if (loc3 === 2) {
return arg1 ^ 72
} else {
return arg1 ^ 103;
}
},
getDispathKey: function(rid, bid, vlink, i) {
var tp = ")(*&^flash@#$%a",
that = this;
GM_xmlhttpRequest({
method: 'GET',
url: 'http://data.video.qiyi.com/t?tn=' + Math.random(),
onload: function(response) {
var json = JSON.parse(response.responseText),
time = json.t,
t = Math.floor(parseInt(time) / 600.0).toString(),
key = md5(t + tp + rid),
url = [
'http://data.video.qiyi.com/', key, '/videos', vlink,
'?su=', that.uid,
'&client=&z=&bt=&ct=&tn=', that.randint(10000, 20000),
].join('');
that.rc[bid].links.push('');
that.jobs += 1;
that.getFinalURL(bid, i, url);
},
});
},
getFinalURL: function(bid, i, url) {
var that = this;
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
var json = JSON.parse(response.responseText);
that.rc[bid].links[i] = json.l;
that.jobs -= 1;
if (that.jobs === 0) {
that.createUI();
}
},
});
},
createUI: function() {
console.log('createUI() --');
console.log(this);
var i,
video,
videos = {
title: this.title,
formats: [],
links: [],
},
links,
link,
j;
if (this.vip) {
unsafeWindow.alert('VIP only!');
return;
}
for (i = 0; i < this.rcOrder.length; i += 1) {
video = this.rc[this.rcOrder[i]];
if (video.links.length > 0) {
videos.formats.push(video.name);
// append KEY/UUID string to end of each video link
links = [];
for (j = 0; j < video.links.length; j += 1) {
link = [video.links[j], '?', video.key].join('');
links.push(link);
}
videos.links.push(links);
}
}
multiFiles.run(videos);
},
/**
* Convert string to xml
* @param string str
* - the string to be converted.
* @return object xml
* - the converted xml object.
*/
parseXML: function(str) {
if (unsafeWindow.document.implementation &&
unsafeWindow.document.implementation.createDocument) {
xmlDoc = new DOMParser().parseFromString(str, 'text/xml');
} else {
console.log('parseXML() error: not support current web browser!');
return null;
}
return xmlDoc;
},
/**
* Generate a UUID string
*/
hex_guid: function() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return [s4(), s4(), s4(), s4(), s4(), s4(), s4(), s4()].join('');
},
randint: function(start, stop) {
return parseInt(Math.random() * (stop - start)) + start;
},
};
monkey.extend('www.iqiyi.com', [
'http://www.iqiyi.com/v_',
'http://www.iqiyi.com/jilupian/',
], monkey_iqiyi);
/**
* letv.com
*/
var monkey_letv = {
pid: '',
vid: '', // video id
title: '',
stime: 0, // server timestamp
tkey: 0, // time key
jobs: 0,
videoUrl: {
'9': '',
'21': '',
'13': '',
},
videoFormats: {
'9': '流畅',
'13': '超清',
'21': '高清',
},
run: function() {
console.log('run() -- ');
var url = location.href;
this.title = document.title.substr(0, document.title.length-12);
if (url.search('yuanxian.letv') !== -1) {
// movie info page.
this.addLinkToYuanxian();
} else if (url.search('ptv/pplay/') > 1 ||
url.search('ptv/vplay/' > 1)) {
this.getVid();
} else {
console.error('I do not know what to do!');
}
},
/**
* Show original video link in video index page.
*/
addLinkToYuanxian: function() {
console.log('addLinkToYuanxian() --');
var pid = __INFO__.video.pid,
url = 'http://www.letv.com/ptv/pplay/' + pid + '.html',
titleLink = document.querySelector('dl.w424 dt a');
titleLink.href = url;
},
/**
* Get video id
*/
getVid: function() {
console.log('getVid() --')
var input = document.querySelector('.add input'),
vidReg = /\/(\d+)\.html$/,
vidMatch;
console.log(input);
if (input && input.hasAttribute('value')) {
vidMatch = vidReg.exec(input.getAttribute('value'));
} else {
console.error('Failed to get input element');
return;
}
console.log('vidMatch: ', vidMatch);
if (vidMatch && vidMatch.length === 2) {
this.vid = vidMatch[1];
this.getTimestamp();
} else {
console.error('Failed to get video ID!');
return;
}
},
/**
* Get timestamp from server
*/
getTimestamp: function() {
console.log('getTimestamp() --');
var tn = Math.random(),
url = 'http://api.letv.com/time?tn=' + tn.toString(),
that = this;
console.log('url:', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
console.log('response:', response);
var obj = JSON.parse(response.responseText);
that.stime = parseInt(obj.stime);
that.tkey = that.getKey(that.stime);
that.getVideoXML();
},
});
},
/**
* Get time key
* @param integer t, server time
*/
getKey: function(t) {
console.log('getKey() --', t);
for(var e = 0, s = 0; s < 8; s += 1){
e = 1 & t;
t >>= 1;
e <<= 31;
t += e;
}
return t ^ 185025305;
},
/**
* Get video info from an xml file
*/
getVideoXML: function() {
console.log('getVideoXML() --');
var url = [
'http://api.letv.com/mms/out/common/geturl?',
'platid=3&splatid=301&playid=0&vtype=9,13,21,28&version=2.0',
'&tss=no',
'&vid=', this.vid,
'&tkey=', this.tkey,
'&domain=http%3A%2F%2Fwww.letv.com'
].join(''),
that = this;
console.log('videoXML url: ', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
console.log('response: ', response);
var json = JSON.parse(response.responseText);
that.getVideoUrl(json.data[0].infos);
},
});
},
/**
* Parse video url
*/
getVideoUrl: function(videos) {
console.log('getVideoUrl() --');
var video,
i,
url;
for (i = 0; video = videos[i]; i = i + 1) {
url = [
video.mainUrl,
'&ctv=pc&m3v=1&termid=1&format=1&hwtype=un&ostype=Linux&tag=letv',
'&sign=letv&expect=3&pay=0&iscpn=f9051&rateid=1300',
'&tn=', Math.random()
].join('');
this.getFinalUrl(url, video.vtype);
this.jobs += 1;
}
},
/**
* Get final video links
* @param url, video link,
* @param type, video type
*/
getFinalUrl: function(url, type) {
console.log('getFinalUrl() --', type);
var that = this;
GM_xmlhttpRequest({
url: url,
method: 'GET',
onload: function(response) {
var json = JSON.parse(response.responseText);
that.videoUrl[type] = json.location;
that.jobs -= 1;
if (that.jobs === 0) {
that.createUI();
}
},
});
},
/**
* construct ui widgets.
*/
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: this.title,
formats: [],
links: [],
ok: true,
msg: '',
},
types = ['9', '21', '13'],
type,
url,
i;
for (i = 0; type = types[i]; i += 1) {
url = this.videoUrl[type];
if (url) {
videos.links.push([this.videoUrl[type]]);
videos.formats.push(this.videoFormats[type]);
}
}
multiFiles.run(videos);
},
};
monkey.extend('www.letv.com', [
'http://www.letv.com/ptv/vplay/',
], monkey_letv);
/**
* justing.com.cn
*/
var monkey_justing = {
title: '',
link: '',
run: function() {
console.log('run() --');
this.getTitle();
this.createUI();
},
getTitle: function() {
var titleElem = unsafeWindow.document.querySelector('div#title');
if (titleElem) {
this.title = titleElem.innerHTML;
this.link = encodeURI([
'http://dl.justing.com.cn/page/',
this.title,
'.mp3'].join(''));
}
},
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: this.title,
links: [],
formats: [],
ok: true,
msg: '',
};
if (this.title.length === 0) {
videos.ok = false;
videos.msg = 'Failed to get mp3 link';
} else {
videos.links.push([this.link]);
videos.formats.push('mp3');
}
multiFiles.run(videos);
},
};
monkey.extend('www.justing.com.cn', [
'http://www.justing.com.cn/page/',
], monkey_justing);
/**
* ku6.com
*/
var monkey_ku6 = {
vid: '',
title: '',
links: [],
type: '标清',
run: function() {
console.log('run() --');
this.getVid();
},
/**
* 获取video id, 用于构建下载链接.
*/
getVid: function() {
console.log('getVid() --');
var url = location.href,
vid_reg = /\/([^\/]+)\.html/,
vid_match = vid_reg.exec(url);
console.log(vid_match);
if (vid_match && vid_match.length == 2) {
this.vid = vid_match[1];
this.getVideo();
}
},
getVideo: function() {
console.log('getVideo() --');
var url = 'http://v.ku6.com/fetchVideo4Player/' + this.vid + '.html',
that = this;
console.log('url:', url);
GM_xmlhttpRequest({
url: url,
method: 'GET',
onload: function(response) {
console.log('response:', response);
var video_obj = JSON.parse(response.responseText);
console.log(video_obj);
that.title = video_obj.data.t;
that.links = video_obj.data.f.split(',');
that.createUI();
},
});
},
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: this.title,
formats: [],
links: [],
};
if (this.links.length > 0) {
videos.formats.push(this.type);
videos.links.push(this.links);
multiFiles.run(videos);
} else {
console.error('this.video is empty');
}
},
};
monkey.extend('v.ku6.com', [
'http://v.ku6.com/',
], monkey_ku6);
/**
* 163.com
*/
var monkey_netease = {
plid: '', // playlist id
mid: '', // video id
raw_vid: '',
title: '',
pl_title: '', // playlist title
videos: {
sd: '',
hd: '',
shd: '',
},
types: {
sd: '标清',
hd: '高清',
shd: '超清',
},
subs: {
},
run: function() {
console.log('run() --');
this.getTitle();
if (document.title.search('网易公开课') > -1) {
this.getOpenCourseSource();
} else {
this.getSource();
}
},
getTitle: function() {
console.log('getTitle() --');
this.title = document.title;
},
getOpenCourseSource: function() {
console.log('getOpenCourseSource() --');
var url = document.location.href.split('/'),
urlMatch = /([A-Z0-9]{9})_([A-Z0-9]{9})/,
match = urlMatch.exec(url),
length = url.length,
xmlUrl,
that = this;
if (! match || match.length !== 3) {
console.error('Failed to get mid!', match);
return;
}
this.raw_vid = match[0];
this.plid = match[1];
this.mid = match[2];
xmlUrl = [
'http://live.ws.126.net/movie',
url[length - 3],
url[length - 2],
'2_' + this.raw_vid + '.xml',
].join('/');
console.log('xmlUrl: ', xmlUrl);
GM_xmlhttpRequest({
method: 'GET',
url: xmlUrl,
onload: function(response) {
var xml = parseXML(response.responseText),
type,
video,
subs,
sub,
subName,
i;
//that.title = xml.querySelector('all title').innerHTML;
that.title = that.title.replace('_网易公开课', '');
for (type in that.videos) {
video = xml.querySelector('playurl ' + type +' mp4');
if (video) {
that.videos[type] = video.firstChild.data;
continue;
}
video = xml.querySelector('playurl ' + type.toUpperCase() +' mp4');
if (video) {
that.videos[type] = video.firstChild.data;
}
}
subs = xml.querySelectorAll('subs sub');
for (i = 0; sub = subs[i]; i += 1) {
subName = sub.querySelector('name').innerHTML + '字幕';
that.subs[subName] = sub.querySelector('url').innerHTML;
}
that.getMobileOpenCourse();
},
});
},
/**
* AES ECB decrypt is too large to embed, so use another way.
*/
getMobileOpenCourse: function() {
console.log('getMobileOpenCourse() --');
var url = 'http://mobile.open.163.com/movie/' + this.plid + '/getMoviesForAndroid.htm',
that = this;
console.log('url: ', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
var json = JSON.parse(response.responseText),
video,
i;
for (i = 0; i < json.videoList.length; i += 1) {
video = json.videoList[i];
console.log('video:', video);
if (video.mid === that.mid) {
that.videos.sd = video.repovideourlmp4Origin;
that.videos.hd = video.repovideourl;
that.title = video.title;
that.pl_title = json.title;
break;
}
}
that.createUI();
},
});
},
getSource: function() {
console.log('getSource() --');
var scripts = document.querySelectorAll('script'),
script,
reg = /<source[\s\S]+src="([^"]+)"/,
match,
m3u8Reg = /appsrc\:\s*'([\s\S]+)\.m3u8'/,
m3u8Match,
i;
for (i = 0; script = scripts[i]; i += 1) {
match = reg.exec(script.innerHTML);
console.log(match);
if (match && match.length > 1) {
this.videos.sd = match[1].replace('-mobile.mp4', '.flv');
this.createUI();
return true;
}
m3u8Match = m3u8Reg.exec(script.innerHTML);
console.log(m3u8Match);
if (m3u8Match && m3u8Match.length > 1) {
this.videos.sd = m3u8Match[1].replace('-list', '') + '.mp4';
this.createUI();
return true;
}
}
},
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: this.title,
formats: [],
links: [],
ok: true,
msg: '',
},
formats = ['sd', 'hd', 'shd'],
format,
url,
subName,
i;
if (this.pl_title.length > 0) {
videos.title = this.title;
}
for (i = 0; format = formats[i]; i += 1) {
url = this.videos[format];
if (url.length > 0) {
videos.links.push([url]);
videos.formats.push(this.types[format]);
}
}
for (subName in this.subs) {
videos.links.push([this.subs[subName]]);
videos.formats.push(subName);
}
multiFiles.run(videos);
},
};
monkey.extend('v.163.com', [
'http://v.163.com/',
], monkey_netease);
monkey.extend('open.163.com', [
'http://open.163.com/',
], monkey_netease);
/**
* pps.tv
*/
var monkey_pps = {
vid: '',
title: '',
types: {
1: '高清',
2: '标清',
3: '流畅',
},
videoUrl: {
1: '',
2: '',
3: '',
},
jobs: 3,
fromIqiyi: false,
run: function() {
console.log('run()');
if (location.href.search('pps.tv/play_') !== -1) {
this.getId();
} else {
console.error('Failed to get vid!');
}
},
getId: function() {
console.log('getId() -- ');
var vidReg = /play_([\s\S]+)\.html/,
vidMatch = vidReg.exec(document.location.href),
titleReg = /([\s\S]+)-在线观看/,
titleMatch = titleReg.exec(document.title);
if (vidMatch) {
this.vid = vidMatch[1];
}
if (titleMatch) {
this.title = titleMatch[1];
}
if (this.vid.length > 0) {
this.getUrl(1); // 高清
this.getUrl(2); // 标清
this.getUrl(3); // 流畅
}
},
getUrl: function(type) {
console.log('getUrl()');
var url = [
'http://dp.ppstv.com/get_play_url_cdn.php?sid=',
this.vid,
'&flash_type=1&type=',
type,
].join(''),
that = this;
console.log('url: ', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
console.log(response);
var txt = response.responseText;
if (txt.search('.pfv?') > 0) {
that.videoUrl[type] = txt.substr(0, txt.search('.pfv?') + 4);
}
that.jobs -= 1;
if (that.jobs === 0) {
that.createUI();
}
},
});
},
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: this.title,
formats: [],
links: [],
},
types = [3, 2, 1],
type,
i;
for (i = 0; type = types[i]; i += 1) {
if (this.videoUrl[type]) {
videos.links.push([this.videoUrl[type]]);
videos.formats.push(this.types[type]);
}
}
multiFiles.run(videos);
},
}
monkey.extend('v.pps.tv', [
'http://v.pps.tv/play_',
], monkey_pps);
monkey.extend('ipd.pps.tv', [
'http://ipd.pps.tv/play_',
], monkey_pps);
/**
* sina.com.cn
*/
var monkey_sina = {
title: '',
jobs: 0,
video: {
format: '标清',
vid: '',
url: '',
links: [],
},
hdVideo: {
format: '高清',
vid: '',
url: '',
links: [],
},
run: function() {
var loc = location.href;
if (loc.search('/vlist/') > -1) {
this.getVlist();
} else if (loc.search('video.sina.com.cn') > -1 ||
loc.search('open.sina.com.cn') > -1) {
this.getVid(loc);
} else {
console.error('This page is not supported!');
return;
}
},
/**
* e.g.
* http://video.sina.com.cn/vlist/news/zt/topvideos1/?opsubject_id=top12#118295074
* http://video.sina.com.cn/news/vlist/zt/chczlj2013/?opsubject_id=top12#109873117
*/
getVlist: function() {
console.log('getVlist() --');
var h4s = document.querySelectorAll('h4.video_btn'),
h4,
i,
lis = document.querySelectorAll('ul#video_list li'),
li,
As,
A,
j,
that = this;
if (h4s && h4s.length > 0) {
this.getVlistItem(h4s[0].parentElement);
for (i = 0; i < h4s.length; i += 1) {
h4 = h4s[i];
h4.addEventListener('click', function(event) {
that.getVlistItem(event.target.parentElement);
}, false);
}
} else if (lis && lis.length > 0) {
this.getVlistItem(lis[0]);
for (i = 0; i < lis.length; i += 1) {
li = lis[i];
As = li.querySelectorAll('a.btn_play');
for (j = 0; A = As[j]; j += 1) {
A.href= li.getAttribute('vurl');
}
li.addEventListener('click', function(event) {
that.getVlistItem(event.target);
event.preventDefault();
return;
}, false);
}
}
},
getVlistItem: function(div) {
console.log('getVlistItem() --', div);
if (div.hasAttribute('data-url')) {
this.getVid(div.getAttribute('data-url'));
} else if (div.nodeName === 'A' && div.className === 'btn_play') {
this.getVid(div.parentElement.parentElement.parentElement.getAttribute('vurl'));
} else if (div.nodeName === 'IMG') {
this.getVid(div.parentElement.parentElement.parentElement.parentElement.getAttribute('vurl'));
} else if (div.hasAttribute('vurl')) {
this.getVid(div.getAttribute('vurl'));
} else {
console.error('Failed to get vid!', div);
return;
}
},
/**
* Get Video vid and hdVid.
*/
getVid: function(url) {
console.log('getVid() --', url);
var that = this;
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
console.log(response);
var reg = /vid:['"](\d{5,})['"]/,
txt = response.responseText,
match = reg.exec(txt),
hdReg = /hd_vid:'(\d{5,})'/,
hdMatch = hdReg.exec(txt),
titleReg = /\s+title:'([^']+)'/,
titleMatch = titleReg.exec(txt);
title2Reg = /VideoTitle : "([^"]+)"/,
title2Match = title2Reg.exec(txt);
if (titleMatch) {
that.title = titleMatch[1];
} else if (title2Match) {
that.title = title2Match[1];
}
if (hdMatch && hdMatch.length > 1) {
that.hdVideo.vid = hdMatch[1];
that.jobs += 1;
that.getVideoByVid(that.hdVideo);
}
if (match && match.length > 1) {
that.video.vid = match[1];
that.jobs += 1;
that.getVideoByVid(that.video);
}
},
});
},
/**
* Calcuate video information url
*/
getURLByVid: function(vid) {
console.log('getURLByVid() -- ', vid);
var randInt = parseInt(Math.random() * 1000),
time = parseInt(Date.now() / 1000) >> 6,
key = '';
key = [
vid,
'Z6prk18aWxP278cVAH',
time,
randInt,
].join('');
key = md5(key);
console.log('key: ', key);
key = key.substring(0, 16) + time;
console.log('key: ', key);
return [
'http://v.iask.com/v_play.php?',
'vid=', vid,
'&uid=null',
'&pid=null',
'&tid=undefined',
'&plid=4001',
'&prid=ja_7_4993252847',
'&referer=',
'&ran=', randInt,
'&r=video.sina.com.cn',
'&v=4.1.42.35',
'&p=i',
'&k=', key,
].join('');
},
/**
* Get video info specified by vid.
*/
getVideoByVid: function(container) {
console.log('getVideoByVid() --', container);
console.log(this);
var that = this;
container.url = this.getURLByVid(container.vid),
GM_xmlhttpRequest({
method: 'GET',
url: container.url,
onload: function(response) {
console.log('response: ', response);
var reg = /<url>.{9}([^\]]+)/g,
txt = response.responseText,
match = reg.exec(txt);
while (match) {
container.links.push(match[1]);
match = reg.exec(txt);
}
that.jobs -= 1;
if (that.jobs === 0) {
that.createUI();
}
},
});
},
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
formats: [],
links: [],
title: this.title,
};
if (this.video.links.length > 0) {
videos.formats.push(this.video.format);
videos.links.push(this.video.links);
}
if (this.hdVideo.links.length > 0) {
videos.formats.push(this.hdVideo.format);
videos.links.push(this.hdVideo.links);
}
console.log('videos: ', videos);
multiFiles.run(videos);
},
}
monkey.extend('video.sina.com.cn', [
'http://video.sina.com.cn/',
], monkey_sina);
monkey.extend('open.sina.com.cn', [
'http://open.sina.com.cn/course/',
], monkey_sina);
/**
* sohu.com
*/
var monkey_sohu = {
title: '',
vid: '',
plid: '',
referer: '',
jobs: 0,
formats: {
p1: '标清',
p2: '高清',
p3: '超清',
p4: '原画质'
},
p1: {
json: [],
su: [],
clipsURL: [],
ip: '',
vid: 0,
reserveIp: [],
videos: [],
params: [],
},
p2: {
json: [],
su: [],
vid: 0,
clipsURL: [],
ip: '',
reserveIp: [],
videos: [],
params: [],
},
p3: {
json: [],
su: [],
clipsURL: [],
vid: 0,
ip: '',
reserveIp: [],
videos: [],
params: [],
},
p4: {
json: [],
su: [],
clipsURL: [],
vid: 0,
ip: '',
reserveIp: [],
videos: [],
params: [],
},
run: function() {
console.log('run() --');
this.router();
},
router: function() {
console.log('router() -- ');
var host = document.location.hostname;
if (host === 'my.tv.sohu.com') {
this.getUGCId();
} else if (host === 'tv.sohu.com') {
this.getId();
} else {
console.error('Error: this page is not supported');
}
},
/**
* Get video id for UGC video
*/
getUGCId: function() {
console.log('getUGCId() -- ');
var scripts = document.querySelectorAll('script'),
script,
vidReg = /var vid\s+=\s+'(\d+)'/,
vidMatch,
titleReg = /,title:\s+'([^']+)'/,
titleMatch,
txt,
i;
for (i = 0; script = scripts[i]; i += 1) {
if (script.innerHTML.search('var vid') > -1) {
txt = script.innerHTML;
vidMatch = vidReg.exec(txt);
console.log('vidMatch: ', vidMatch);
if (vidMatch && vidMatch.length === 2) {
this.vid = vidMatch[1];
}
console.log('titleMatch: ', titleMatch);
titleMatch = titleReg.exec(txt);
if (titleMatch && titleMatch.length === 2) {
this.title = titleMatch[1];
}
break;
}
}
if (this.vid.length > 0) {
this.referer = escape(location.href);
this.p2.vid = this.vid;
this.getUGCVideoJSON('p2');
} else {
console.error('Error: failed to get video id!');
}
},
/**
* Get UGC video info
*/
getUGCVideoJSON: function(fmt) {
console.log('getUGCVideoJSON() -- ');
var that = this,
url = 'http://my.tv.sohu.com/videinfo.jhtml?m=viewtv&vid=' + this.vid;
console.log('url: ', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
var json = JSON.parse(response.responseText);
console.log('json: ', json);
that[fmt].json = json;
that[fmt].su = json.data.su;
that[fmt].clipsURL = json.data.clipsURL;
if (fmt === 'p2') {
if (json.data.norVid) {
that.p1.vid = json.data.norVid;
that.getUGCVideoJSON('p1');
}
if (json.data.superVid) {
that.p3.vid = json.data.superVid;
that.getUGCVideoJSON('p3');
}
if (json.data.oriVid) {
that.p4.vid = json.data.oriVid;
that.getUGCVideoJSON('p4');
}
}
that.decUGCVideo(fmt);
},
});
},
/**
* Decode UGC video url
*/
decUGCVideo: function(fmt) {
console.log('decUGCVideo() -- ');
var url,
json = this[fmt].json,
i;
for (i = 0; i < json.data.clipsURL.length; i += 1) {
url = [
'http://', json.allot, '/' ,
'?prot=', json.prot,
'&file=', json.data.clipsURL[i],
'&new=', json.data.su[i],
].join('');
console.log('url: ', url);
this[fmt].videos.push('');
this.jobs += 1;
this.decUGCVideo2(fmt, url, i);
}
},
decUGCVideo2: function(fmt, url, i) {
console.log('decUGCVideo2() -- ');
var that = this;
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
console.log('response:', response);
var params = response.responseText.split('|');
that[fmt].params = params;
that[fmt].videos[i] = [
params[0],
that[fmt].su[i],
'?key=',
params[3],
].join('');
that.jobs -= 1;
if (that.jobs === 0) {
that.createUI();
}
},
});
},
/**
* Get video id
*/
getId: function() {
console.log('getId() --');
var scripts = document.querySelectorAll('script'),
script,
vidReg = /var vid="(\d+)";/,
vidMatch,
playlistReg = /var playlistId="(\d+)";/,
playlistMatch,
i;
for (i = 0; script = scripts[i]; i += 1) {
if (script.innerHTML.search('var vid') > -1) {
txt = script.innerHTML;
vidMatch = vidReg.exec(txt);
console.log('vidMatch: ', vidMatch);
if (vidMatch && vidMatch.length === 2) {
this.vid = vidMatch[1];
}
playlistMatch = playlistReg.exec(txt);
if (playlistMatch && playlistMatch.length === 2) {
this.plid = playlistMatch[1];
break;
}
}
}
if (this.vid) {
this.p2.vid = this.vid;
this.title = document.title.split('-')[0].trim();
this.referer = escape(location.href);
this.jobs += 1;
this.getVideoJSON('p2');
} else {
console.error('Failed to get vid!');
}
},
/**
* Get video info.
* e.g. http://hot.vrs.sohu.com/vrs_flash.action?vid=1109268&plid=5028903&referer=http%3A//tv.sohu.com/20130426/n374150509.shtml
*/
getVideoJSON: function(fmt) {
console.log('getVideoJSON() --');
console.log('fmt: ', fmt);
var pref = 'http://hot.vrs.sohu.com/vrs_flash.action',
url = '',
that = this;
// If vid is unset, just return it.
if (this[fmt].vid === 0) {
return;
}
url = [
pref,
'?vid=', this[fmt].vid,
'&plid=', this.plid,
'&out=0',
'&g=8',
'&referer=', this.referer,
'&r=1',
].join('');
console.log('url: ', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
console.log('response: ', response);
var i = 0;
console.log(that);
that.jobs -= 1;
that[fmt].json = JSON.parse(response.responseText);
//that.title = that[fmt].json.data.tvName;
that[fmt].clipsURL = that[fmt].json.data.clipsURL;
that[fmt].su = that[fmt].json.data.su;
that.p1.vid = that[fmt].json.data.norVid;
that.p2.vid = that[fmt].json.data.highVid;
that.p3.vid = that[fmt].json.data.superVid;
that.p4.vid = that[fmt].json.data.oriVid;
that[fmt].ip = that[fmt].json.allot;
that[fmt].reserveIp = that[fmt].json.reserveIp.split(';');
for (i in that[fmt].clipsURL) {
url = [
'http://', that[fmt].ip, '/',
'?prot=', that[fmt].json.prot,
'&file=', that[fmt].clipsURL[i],
'&new=', that[fmt].su[i],
].join('');
that.jobs += 1;
that[fmt].videos.push('');
that.getRealUrl(fmt, url, that[fmt].su[i], i);
}
if (fmt === 'p2') {
if (that.p1.vid > 0) {
that.jobs += 1;
that.getVideoJSON('p1');
}
if (that.p3.vid > 0) {
that.jobs += 1;
that.getVideoJSON('p3');
}
if (that.p4.vid > 0) {
that.jobs += 1;
that.getVideoJSON('p4');
}
}
},
});
},
/**
* Get final url from content like this:
* http://61.54.26.168/sohu/s26h23eab6/6/|145|182.112.229.55|G69w1ucoqFNj8ww74DxHN-6ZQTkgJZ90FlnqBA..|1|0|2|1518|1
*/
getRealUrl: function(fmt, url, new_, i) {
console.log('getRealUrl() --', fmt, url, new_, i);
var that = this;
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
var txt = response.responseText,
params = txt.split('|'),
start = params[0],
key = params[3],
url = [
start.substr(0, start.length-1), new_,
'?key=', key,
].join('');
that[fmt].videos[i] = url;
that.jobs -= 1;
// Display UI when all processes ended
if (that.jobs === 0) {
that.createUI();
}
},
});
},
/**
* Construct UI widgets
*/
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: this.title,
links: [],
formats: [],
},
type,
i;
for (type in this.formats) {
console.log('type: ', type);
if (this[type].videos.length > 0) {
videos.links.push(this[type].videos);
videos.formats.push(this.formats[type]);
}
}
if (videos.formats.length > 0) {
multiFiles.run(videos);
}
},
};
monkey.extend('tv.sohu.com', [
'http://tv.sohu.com/',
], monkey_sohu);
/**
* tucao.cc
*/
var monkey_tucao = {
url: '',
title: '',
playerId: '',
key: '',
timestamp: '',
vid: '',
type: '',
vids: [],
pos: 0,
videos: [],
format: '标清',
redirect: false,
types: {
sina: 'sina',
tudou: false, // redirect to original url
youku: false, // redirect to original url
},
run: function() {
console.log('run()');
this.getVid();
},
/**
* Get video id
*/
getVid: function() {
console.log('getVid() -- ');
var playerCode = document.querySelectorAll(
'ul#player_code li');
if (playerCode && playerCode.length === 2) {
this.vids = playerCode[0].firstChild.nodeValue.split('**');
if (this.vids[this.vids.length - 1] == '') {
// remove empty vid
this.vids.pop();
}
this.playerId = playerCode[1].innerHTML;
this.getTitle();
}
},
/**
* Get video title
*/
getTitle: function() {
console.log('getTitle()');
var params;
if (this.vids.length === 1 || location.hash === '') {
this.pos = 0;
this.url = location.href;
} else {
// hash starts with 1, not 0
this.pos = parseInt(location.hash.replace('#', '')) - 1;
this.url = location.href.replace(location.hash, '');
}
params = getQueryVariable(this.vids[this.pos].split('|')[0]);
this.vid = params.vid;
this.type = params.type;
if (this.vids.length === 1) {
this.title = document.title.substr(0, document.title.length - 16);
} else {
this.title = this.vids[this.pos].split('|')[1];
}
this.getUrl();
},
/**
* Get original url
*/
getUrl: function(type) {
console.log('getUrl()');
var url,
params,
that = this;
if (this.types[this.type] === false) {
this.redirectTo();
return;
}
this.calcKey();
url = [
'http://www.tucao.cc/api/playurl.php',
'?type=',
this.type,
'&vid=',
this.vid,
'&key=', this.key,
'&r=', this.timestamp
].join('');
console.log('url: ', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
console.log(response);
var xml = parseXML(response.responseText),
durls = xml.querySelectorAll('durl'),
durl,
url,
i;
for (i = 0; durl = durls[i]; i += 1) {
url = durl.querySelector('url');
that.videos.push(
url.innerHTML.replace('<![CDATA[', '').replace(']]>', ''));
}
that.createUI();
},
});
},
/**
* 计算这个请求的授权key.
* 算法来自于: http://www.cnbeining.com/2014/05/serious-businesstucao-cc-c-video-resolution/
* @return [key, timestamp]
*/
calcKey: function() {
console.log('calcKey () --');
var time = new Date().getTime();
this.timestamp = Math.round(time / 1000);
var local3 = this.timestamp ^ 2774181285;
var local4 = parseInt(this.vid, 10);
var local5 = local3 + local4;
local5 = (local5 < 0) ? (-(local5) >> 0) : (local5 >> 0);
this.key = 'tucao' + local5.toString(16) + '.cc';
},
/**
* Redirect to original url
*/
redirectTo: function() {
console.log('redirectTo() --');
console.log(this);
var urls = {
tudou: function(vid) {
return 'http://www.tudou.com/programs/view/' + vid + '/';
},
youku: function(vid) {
return 'http://v.youku.com/v_show/id_' + vid + '.html';
},
};
this.redirect = true;
this.videos.push(urls[this.type](this.vid));
this.formats.push('原始地址');
this.createUI();
},
/**
* Construct ui widgets
*/
createUI: function() {
console.log('createUI() -- ');
console.log(this);
var videos = {
title: this.title,
formats: [],
links: [],
ok: true,
msg: '',
},
video,
i;
videos.links.push(this.videos);
videos.formats.push(this.format);
multiFiles.run(videos);
},
}
monkey.extend('www.tucao.cc', [
'http://www.tucao.cc/play/',
], monkey_tucao);
/**
* tudou.com
*/
var monkey_tudou = {
url:'', // document.location.href
title: '',
iid: '',
vcode: '',
segs: {},
totalJobs: 0,
formats: {
2: '240P', // 流畅
3: '360P', // 清晰
4: '480P', // 高清
5: '720P', // 超清
52: '240P(mp4)',
53: '360P(mp4)',
54: '480P(mp4)',
99: '原画质' // 原画质
},
links: {
},
run: function() {
console.log('run() --');
this.router();
},
/**
* Page router control
*/
router: function() {
console.log('router() --');
var scripts = document.querySelectorAll('script'),
script,
titleReg = /kw:\s*['"]([^'"]+)['"]/,
titleMatch,
iidReg = /iid\s*[:=]\s*(\d+)/,
iidMatch,
vcodeReg = /vcode: '([^']+)'/,
vcodeMatch,
i;
for (i = 0; script = scripts[i]; i += 1) {
if (this.vcode.length === 0) {
vcodeMatch = vcodeReg.exec(script.innerHTML);
console.log('vcodeMatch:', vcodeMatch);
if (vcodeMatch && vcodeMatch.length > 1) {
this.vcode = vcodeMatch[1];
this.redirectToYouku();
return;
}
}
if (this.title.length === 0) {
titleMatch = titleReg.exec(script.innerHTML);
console.log('titleMatch:', titleMatch);
if (titleMatch) {
this.title = titleMatch[1];
}
}
if (this.iid.length === 0) {
iidMatch = iidReg.exec(script.innerHTML);
console.log('iidMatch:', iidMatch);
if (iidMatch) {
this.iid = iidMatch[1];
this.getByIid();
return;
}
}
}
//this.getPlayList();
},
/**
* Get video info by vid
*/
getByIid: function() {
console.log('getByIid()');
console.log(this);
var that = this,
url = 'http://www.tudou.com/outplay/goto/getItemSegs.action?iid=' +
this.iid;
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
console.log('respone:', response);
that.segs = JSON.parse(response.responseText);
that.getAllVideos();
},
});
},
// getPlayList: function() {
// console.log('getPlayList()');
// console.log(this);
// },
/**
* Get all video links
*/
getAllVideos: function() {
console.log('getAllVideos() --');
console.log(this);
var key,
videos,
video,
i;
for (key in this.segs) {
videos = this.segs[key];
for (i = 0; video = videos[i]; i += 1) {
console.log(key, video);
this.links[key] = [];
this.totalJobs += 1;
this.getVideoUrl(key, video['k'], video['no']);
}
}
},
/**
* Get video url
*/
getVideoUrl: function(key, k, num) {
console.log('getVideoUrl() --');
var url = 'http://ct.v2.tudou.com/f?id=' + k,
that = this;
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
console.log('response:', response);
var reg = /<f[^>]+>([^<]+)</,
match = reg.exec(response.responseText);
if (match && match.length > 1) {
that.links[key][num] = match[1];
that.totalJobs -= 1;
if (that.totalJobs === 0) {
that.createUI();
}
}
},
});
},
/**
* Redirect url to youku.com.
* Because tudou.com use youku.com as video source on /albumplay/ page.
*/
redirectToYouku: function() {
var url = 'http://v.youku.com/v_show/id_' + this.vcode + '.html';
console.log('redirectToYouku:', url);
this.createUI();
},
/**
* Construct UI widgets
*/
createUI: function() {
console.log('createUI()');
console.log(this);
var videos = {
title: this.title,
formats: [],
links: [],
ok: true,
msg: '',
},
type,
i;
if (this.vcode.length > 0) {
videos.title = '原始地址';
videos.links.push('http://v.youku.com/v_show/id_' + this.vcode + '.html');
videos.formats.push('');
singleFile.run(videos);
} else {
for (type in this.links) {
videos.links.push(this.links[type]);
videos.formats.push(this.formats[type]);
}
console.log('videos: ', videos);
multiFiles.run(videos);
}
},
};
monkey.extend('www.tudou.com', [
'http://www.tudou.com/albumplay/',
'http://www.tudou.com/listplay/',
'http://www.tudou.com/programs/view/',
], monkey_tudou);
/**
* wasu.cn
*/
var monkey_wasu = {
id: '',
key: '',
url: '',
title: '',
link: '',
format: '高清',
run: function() {
console.log('run() --');
this.getTitle();
},
getTitle: function() {
console.log('getTitle() --');
var h3 = document.querySelector('div.play_movie div.play_site div.l h3');
if (h3) {
this.title = h3.innerHTML;
} else {
this.title = document.title.replace(
'高清电影全集在线观看-正版高清电影-华数TV', '').replace(
' 正版高清电影', '');
}
this.getVid();
},
/**
* Get video id
*/
getVid: function() {
console.log('getVid()--');
var reg = /show\/id\/(\d+)/,
match = reg.exec(location.href),
url,
that = this;
if (!match || match.length !== 2) {
console.error('Failed to get vid!');
return
}
this.vid = match[1];
url = 'http://www.wasu.cn/wap/play/show/id/' + this.vid,
console.log('url: ', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
var txt = response.responseText,
keyReg = /'key'\s*:\s*'([^']+)'/,
urlReg = /'url'\s*:\s*'([^']+)'/,
keyMatch,
urlMatch;
keyMatch = keyReg.exec(txt);
if (! keyMatch || keyMatch.length !== 2) {
console.error('Failed to get key: ', keyMatch);
return;
}
that.key = keyMatch[1];
urlMatch = urlReg.exec(txt);
that.url = urlMatch[1];
that.getVideoInfo();
},
});
},
/**
* Get video information
*/
getVideoInfo: function() {
console.log('getVideoInfo() --');
var url = [
'http://www.wasu.cn/wap/Api/getVideoUrl/id/', this.vid,
'/key/', this.key,
'/url/', this.url,
'/type/txt',
].join(''),
that = this;
console.log('video info link: ', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
that.link = response.responseText;
that.createUI();
},
});
},
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: this.title,
formats: [],
links: [],
ok: true,
msg: '',
};
if (this.link.length === 0) {
videos.ok = false;
videos.msg = 'Failed to get video link';
} else {
videos.formats.push(this.format);
videos.links.push([this.link]);
}
multiFiles.run(videos);
},
};
monkey.extend('www.wasu.cn', [
'http://www.wasu.cn/Play/show/id/',
'http://www.wasu.cn/play/show/id/',
'http://www.wasu.cn/wap/Play/show/id/',
'http://www.wasu.cn/wap/play/show/id/',
], monkey_wasu);
/**
* weiqitv.com
*/
var monkey_weiqitv = {
sid: '',
vid: '',
title: '',
videos: {},
formats: {
'default': '标清flv', // 640x360
'2': '高清flv', // 960x540
'3': '超清flv', // 1280x720
'4': '高清mp4', // 850x480
'5': '超清mp4', // 1280x720
},
run: function() {
console.log('run() -- ');
this.title = document.title.replace('围棋TV - ', '');
this.getVid();
},
getVid: function() {
console.log('getVid() --');
var vidReg = /vid:(\d+),/,
vidMatch,
sidReg = /sid:(\d+)\s*/,
sidMatch,
scripts = document.querySelectorAll('script'),
script,
i;
for (i = 0; i < scripts.length; i += 1) {
script = scripts[i];
vidMatch = vidReg.exec(script.innerHTML);
if (vidMatch && vidMatch.length === 2) {
this.vid = vidMatch[1];
sidMatch = sidReg.exec(script.innerHTML);
this.sid = sidMatch[1];
break;
}
}
if (this.vid.length === 0) {
console.error('Failed to get vid!');
} else {
this.getVideoInfo();
}
},
getVideoInfo: function() {
var that = this,
url = [
'http://www.yunsp.com.cn:8080/dispatch/videoPlay/getInfo?',
'vid=', this.vid,
'&sid=', this.sid,
'&isList=0&ecode=notexist',
].join('');
console.log('url: ', url);
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
var json = JSON.parse(response.responseText),
videoInfo = json[0].videoInfo,
format;
for (format in that.formats) {
if (format in videoInfo) {
that.videos[format] = videoInfo[format].url;
}
}
that.createUI();
},
});
},
/**
* construct ui widgets.
*/
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: this.title,
formats: [],
links: [],
ok: true,
msg: '',
},
types = ['default', '4', '2', '5', '3'],
type,
url,
i;
for (i = 0; type = types[i]; i += 1) {
url = this.videos[type];
if (url && url.length > 0) {
videos.links.push([url]);
videos.formats.push(this.formats[type]);
}
}
multiFiles.run(videos);
},
};
monkey.extend('www.weiqitv.com', [
'http://www.weiqitv.com/index/live_back?videoId=',
'http://www.weiqitv.com/index/video_play?videoId=',
], monkey_weiqitv);
/**
* youku.com
*/
var monkey_youku = {
// store xhr result, with json format
rs: null,
brs: null,
// store video formats and its urls
data: null,
title: '',
videoId: '',
run: function() {
console.log('run() --');
this.getVideoId();
},
/**
* Get video id, and stored in yk.videoId.
*
* Page url for playing page almost like this:
* http://v.youku.com/v_show/id_XMjY1OTk1ODY0.html
* http://v.youku.com/v_playlist/f17273995o1p0.html
*/
getVideoId: function() {
console.log('getVideoId() --');
var url = location.href,
idReg = /(?:id_)(.*)(?:.html)/,
idMatch = idReg.exec(url),
idReg2 = /(?:v_playlist\/f)(.*)(?:o1p\d.html)/,
idMatch2 = idReg2.exec(url);
console.log('idMatch: ', idMatch);
console.log('idMatch2: ', idMatch2);
if (idMatch && idMatch.length === 2) {
this.videoId = idMatch[1];
this.getPlayListMeta();
} else if (idMatch2 && idMatch2.length === 2) {
this.videoId = idMatch2[1];
this.getPlayListMeta();
} else {
error('Failed to get video id!');
}
},
/**
* Get metadata of video playlist
*/
getPlayListMeta: function() {
console.log('getPlaylistMeta() --');
var url = 'http://v.youku.com/player/getPlayList/VideoIDS/' + this.videoId,
url2 = url + '/Pf/4/ctype/12/ev/1',
that = this;
console.log('url2:', url2);
GM_xmlhttpRequest({
method: 'GET',
url: url2,
onload: function(response) {
var json = JSON.parse(response.responseText);
if (json.data.length === 0) {
console.error('Error: video not found!');
return;
}
that.rs = json.data[0];
that.title = that.rs.title;
that.parseVideo();
}
});
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
var json = JSON.parse(response.responseText);
if (json.data.length === 0) {
console.error('Error: video not found!');
return;
}
that.brs = json.data[0];
that.parseVideo();
}
});
},
parseVideo: function() {
console.log('parseVideo() --');
if (! this.rs || ! this.brs) {
return;
}
var streamtypes = this.rs.streamtypes,
streamfileids = this.rs.streamfileids,
data = {},
seed = this.rs.seed,
segs = this.rs.segs,
key,
value,
k,
v,
ip = this.rs.ip,
bsegs = this.brs.segs,
sid,
token,
i,
k,
number,
fileId0,
fileId,
ep,
pass1 = 'becaf9be',
pass2 = 'bf7e5f01',
typeArray = {
'flv': 'flv', 'mp4': 'mp4', 'hd2': 'flv', '3gphd': 'mp4',
'3gp': 'flv', 'hd3': 'flv'
},
sharpness = {
'flv': 'normal', 'flvhd': 'normal', 'mp4': 'high',
'hd2': 'super', '3gphd': 'high', '3g': 'normal',
'hd3': 'original'
},
filetype;
[sid, token] = this.yk_e(pass1, this.yk_na(this.rs.ep)).split('_');
for (key in segs) {
value = segs[key];
if (streamtypes.indexOf(key) > -1) {
for (i in value) {
v = value[i];
number = parseInt(v.no, 10).toString(16).toUpperCase();
if (number.length === 1) {
number = '0'.concat(number);
}
// 构建视频地址K值
k = v.k;
if (!k || k === -1) {
console.log(bsegs, bsegs[key], bsegs[key][i]);
k = bsegs[key][i]['k'];
}
fileId0 = this.getFileId(streamfileids[key], seed);
fileId = fileId0.substr(0, 8) + number + fileId0.substr(10);
ep = encodeURIComponent(this.yk_d(
this.yk_e(pass2, [sid, fileId, token].join('_'))));
// 判断后缀类型, 获得后缀
filetype = typeArray[key];
data[key] = data[key] || [];
data[key].push([
'http://k.youku.com/player/getFlvPath/sid/', sid,
'_00/st/', filetype,
'/fileid/', fileId,
'?K=', k,
'&ctype=12&ev=1&token=', token,
'&oip=', ip,
'&ep=', ep,
].join(''));
}
}
}
this.data = data;
this.createUI();
},
/**
* Get file id of each video file.
*
* @param string seed
* - the file seed number.
* @param string fileId
* - file Id.
* @return string
* - return decrypted file id.
*/
getFileId: function(fileId, seed) {
console.log('getFileId() --');
function getFileIdMixed(seed) {
var mixed = [],
source = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP' +
'QRSTUVWXYZ/\\:._-1234567890',
len = source.length,
index,
i;
for (i = 0; i < len; i += 1) {
seed = (seed * 211 + 30031) % 65536;
index = Math.floor(seed / 65536 * source.length);
mixed.push(source.charAt(index));
source = source.replace(source.charAt(index), '');
}
return mixed;
}
var mixed = getFileIdMixed(seed),
ids = fileId.split('\*'),
len = ids.length - 1,
realId = '',
idx,
i;
for (i = 0; i < len; i += 1) {
idx = parseInt(ids[i]);
realId += mixed[idx];
}
return realId;
},
/**
* Timestamp
*/
getSid: function() {
return String((new Date()).getTime()) + '01';
},
/**
* Decryption
*/
yk_d: function(s) {
var len = s.length,
i = 0,
result = [],
e = 0,
g = 0,
h,
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
if (len === 0) {
return '';
}
while (i < len) {
e = s.charCodeAt(i) & 255;
i = i + 1;
if (i === len) {
result.push(chars.charAt(e >> 2));
result.push(chars.charAt((e & 3) << 4));
result.push('==');
break
}
g = s.charCodeAt(i);
i = i + 1;
if (i === len) {
result.push(chars.charAt(e >> 2));
result.push(chars.charAt((e & 3) << 4 | (g & 240) >> 4));
result.push(chars.charAt((g & 15) << 2));
result.push('=');
break
}
h = s.charCodeAt(i);
i = i + 1;
result.push(chars.charAt(e >> 2));
result.push(chars.charAt((e & 3) << 4 | (g & 240) >> 4));
result.push(chars.charAt((g & 15) << 2 | (h & 192) >> 6));
result.push(chars.charAt(h & 63));
}
return result.join('');
},
yk_na: function(a) {
if (! a) {
return '';
}
var h = [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1],
i = a.length,
e = [],
f = 0,
b,
c;
while (f < i) {
do {
c = h[a.charCodeAt(f++) & 255];
} while (f < i && c === -1);
if (c === -1) {
break;
}
do {
b = h[a.charCodeAt(f++) & 255];
} while (f < i && b === -1);
if (b === -1) {
break;
}
e.push(String.fromCharCode(c << 2 | (b & 48) >> 4));
do {
c = a.charCodeAt(f++) & 255;
if (c === 61) {
return e.join('');
}
c = h[c];
} while (f < i && c === -1);
if (c === -1) {
break;
}
e.push(String.fromCharCode((b & 15) << 4 | (c & 60) >> 2));
do {
b = a.charCodeAt(f) & 255;
f = f + 1;
if (b === 61) {
return e.join('');
}
b = h[b];
} while (f < i && b === -1);
if (b === -1) {
break;
}
e.push(String.fromCharCode((c & 3) << 6 | b));
}
return e.join('');
},
yk_e: function(a, c) {
var f = 0,
i = '',
e = [],
q = 0,
h = 0,
b = {};
for (h = 0; h < 256; h = h + 1) {
b[h] = h;
}
for (h = 0; h < 256; h = h + 1) {
f = ((f + b[h]) + a.charCodeAt(h % a.length)) % 256;
i = b[h];
b[h] = b[f];
b[f] = i;
}
for (q = 0, f = 0, h = 0; q < c.length; q = q + 1) {
h = (h + 1) % 256;
f = (f + b[h]) % 256;
i = b[h];
b[h] = b[f];
b[f] = i;
e.push(String.fromCharCode(c.charCodeAt(q) ^ b[(b[h] + b[f]) % 256]));
}
return e.join('');
},
/**
* construct video data and create UI widgets.
*/
createUI: function() {
console.log('createUI() --');
console.log(this);
var videos = {
title: this.title,
formats: [],
links: [],
},
types = {
'3gp': '3G',
'3gphd': '3G高清',
flv: '标清',
flvhd: '高清Flv',
mp4: '高清',
hd2: '超清',
hd3: '1080P',
},
type;
for(type in types) {
if (type in this.data) {
videos.formats.push(types[type]);
videos.links.push(this.data[type]);
}
}
multiFiles.run(videos);
},
};
monkey.extend('v.youku.com', [
'http://v.youku.com/v_show/id_',
'http://v.youku.com/v_playlist/',
], monkey_youku);
/**
* youtube.com
*/
var monkey_youtube = {
videoId: '',
videoInfoUrl: null,
videoTitle: '',
stream: null,
adaptive_fmts: null,
urlInfo: false,
// format list comes from https://github.com/rg3/youtube-dl
formats: {
'5': {ext: 'flv', width: 400, height: 240, resolution: '240p'},
'6': {ext: 'flv', width: 450, height: 270, resolution: '270p'},
'13': {ext: '3gp', resolution: 'unknown'},
'17': {ext: '3gp', width: 176, height: 144, resolution: '144p'},
'18': {ext: 'mp4', width: 640, height: 360, resolution: '360p'},
'22': {ext: 'mp4', width: 1280, height: 720, resolution: '720p'},
'34': {ext: 'flv', width: 640, height: 360, resolution: '360p'},
'35': {ext: 'flv', width: 854, height: 480, resolution: '720p'},
'36': {ext: '3gp', width: 320, height: 240, resolution: '240p'},
'37': {ext: 'mp4', width: 1920, height: 1080, resolution: '1080p'},
'38': {ext: 'mp4', width: 4096, height: 3072, resolution: '4k'},
'43': {ext: 'webm', width: 640, height: 360, resolution: '360p'},
'44': {ext: 'webm', width: 854, height: 480, resolution: '480p'},
'45': {ext: 'webm', width: 1280, height: 720, resolution: '720p'},
'46': {ext: 'webm', width: 1920, height: 1080, resolution: '1080p'},
// 3d videos
'82': {'ext': 'mp4', 'height': 360, 'resolution': '360p', 'format_note': '3D', 'preference': -20},
'83': {'ext': 'mp4', 'height': 480, 'resolution': '480p', 'format_note': '3D', 'preference': -20},
'84': {'ext': 'mp4', 'height': 720, 'resolution': '720p', 'format_note': '3D', 'preference': -20},
'85': {'ext': 'mp4', 'height': 1080, 'resolution': '1080p', 'format_note': '3D', 'preference': -20},
'100': {'ext': 'webm', 'height': 360, 'resolution': '360p', 'format_note': '3D', 'preference': -20},
'101': {'ext': 'webm', 'height': 480, 'resolution': '480p', 'format_note': '3D', 'preference': -20},
'102': {'ext': 'webm', 'height': 720, 'resolution': '720p', 'format_note': '3D', 'preference': -20},
// Apple HTTP Live Streaming
'92': {'ext': 'mp4', 'height': 240, 'resolution': '240p', 'format_note': 'HLS', 'preference': -10},
'93': {'ext': 'mp4', 'height': 360, 'resolution': '360p', 'format_note': 'HLS', 'preference': -10},
'94': {'ext': 'mp4', 'height': 480, 'resolution': '480p', 'format_note': 'HLS', 'preference': -10},
'95': {'ext': 'mp4', 'height': 720, 'resolution': '720p', 'format_note': 'HLS', 'preference': -10},
'96': {'ext': 'mp4', 'height': 1080, 'resolution': '1080p', 'format_note': 'HLS', 'preference': -10},
'132': {'ext': 'mp4', 'height': 240, 'resolution': '240p', 'format_note': 'HLS', 'preference': -10},
'151': {'ext': 'mp4', 'height': 72, 'resolution': '72p', 'format_note': 'HLS', 'preference': -10},
// DASH mp4 video
'133': {'ext': 'mp4', 'width': 400, 'height': 240, 'resolution': '240p', 'format_note': 'DASH video', 'preference': -40},
'134': {'ext': 'mp4', 'width': 640, 'height': 360, 'resolution': '360p', 'format_note': 'DASH video', 'preference': -40},
'135': {'ext': 'mp4', 'width': 854, 'height': 480, 'resolution': '480p', 'format_note': 'DASH video', 'preference': -40},
'136': {'ext': 'mp4', 'width': 1280, 'height': 720, 'resolution': '720p', 'format_note': 'DASH video', 'preference': -40},
'137': {'ext': 'mp4', 'width': 1920, 'height': 1080, 'resolution': '1080p', 'format_note': 'DASH video', 'preference': -40},
'138': {'ext': 'mp4', 'width': 1921, 'height': 1081, 'resolution': '>1080p', 'format_note': 'DASH video', 'preference': -40},
'160': {'ext': 'mp4', 'width': 256, 'height': 192, 'resolution': '192p', 'format_note': 'DASH video', 'preference': -40},
'264': {'ext': 'mp4', 'width': 1920, 'height': 1080, 'resolution': '1080p', 'format_note': 'DASH video', 'preference': -40},
// Dash mp4 audio
'139': {'ext': 'm4a', 'format_note': 'DASH audio', 'vcodec': 'none', 'abr': 48, 'preference': -50},
'140': {'ext': 'm4a', 'format_note': 'DASH audio', 'vcodec': 'none', 'abr': 128, 'preference': -50},
'141': {'ext': 'm4a', 'format_note': 'DASH audio', 'vcodec': 'none', 'abr': 256, 'preference': -50},
// Dash webm
'167': {'ext': 'webm', 'height': 360, 'width': 640, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40, resolution: '360p'},
'168': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40, resolution: '480p'},
'169': {'ext': 'webm', 'height': 720, 'width': 1280, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40, resolution: '720p'},
'170': {'ext': 'webm', 'height': 1080, 'width': 1920, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40, resolution: '1080p'},
'218': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40, resolution: '480p'},
'219': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40, resolution: '480p'},
'242': {'ext': 'webm', 'height': 240, 'resolution': '240p', 'format_note': 'DASH webm', 'preference': -40},
'243': {'ext': 'webm', 'height': 360, 'resolution': '360p', 'format_note': 'DASH webm', 'preference': -40},
'244': {'ext': 'webm', 'height': 480, 'resolution': '480p', 'format_note': 'DASH webm', 'preference': -40},
'245': {'ext': 'webm', 'height': 480, 'resolution': '480p', 'format_note': 'DASH webm', 'preference': -40},
'246': {'ext': 'webm', 'height': 480, 'resolution': '480p', 'format_note': 'DASH webm', 'preference': -40},
'247': {'ext': 'webm', 'height': 720, 'resolution': '720p', 'format_note': 'DASH webm', 'preference': -40},
'248': {'ext': 'webm', 'height': 1080, 'resolution': '1080p', 'format_note': 'DASH webm', 'preference': -40},
// Dash webm audio
'171': {'ext': 'webm', 'vcodec': 'none', 'format_note': 'DASH webm audio', 'abr': 48, 'preference': -50},
'172': {'ext': 'webm', 'vcodec': 'none', 'format_note': 'DASH webm audio', 'abr': 256, 'preference': -50},
// RTMP (unnamed)
'_rtmp': {'protocol': 'rtmp'},
},
run: function() {
console.log('run() --');
this.getURLInfo();
},
/**
* parse location.href
*/
getURLInfo: function() {
this.urlInfo = this.parseURI(unsafeWindow.location.href);
if (document.location.href.contains('/embed/')) {
window.location.href = this.urlInfo.replace('/embed/', '/watch?v=');
} else {
this.getVideo();
}
},
/**
* Get video url info:
*/
getVideo: function () {
console.log('getVideo()--');
var that = this;
if (!this.urlInfo.params['v']) {
return;
}
this.videoId = this.urlInfo.params['v'];
this.videoInfoUrl = [
'/get_video_info',
'?video_id=', this.videoId,
//'&el=player_embeded&hl=en&gl=US',
'&el=html5&hl=en&gl=US',
'&eurl=https://youtube.googleapis.com/v/', this.videoId,
].join('');
this.videoTitle = unsafeWindow.document.title.substr(
0, unsafeWindow.document.title.length - 10);
GM_xmlhttpRequest({
method: 'GET',
url: this.videoInfoUrl,
onload: function(response) {
console.log('xhr response: ', response);
that.parseStream(response.responseText);
},
});
},
/**
* Parse stream info from xhr text:
*/
parseStream: function(rawVideoInfo) {
console.log('parseStream() ---');
var that = this;
/**
* Parse the stream text to Object
*/
function _parseStream(rawStream){
var a = decodeURIComponent(rawStream).split(',');
return a.map(that.urlHashToObject);
}
this.videoInfo = this.urlHashToObject(rawVideoInfo);
this.stream = _parseStream(this.videoInfo.url_encoded_fmt_stream_map);
this.adaptive_fmts = _parseStream(this.videoInfo.adaptive_fmts)
this.createUI();
},
/**
* Create download list:
*/
createUI: function() {
console.log('createUI() -- ');
console.log('this: ', this);
var videos = {
title: this.videoTitle,
formats: [],
links: [],
ok: true,
msg: '',
},
video,
format,
formatName,
url,
streams = this.stream.concat(this.adaptive_fmts),
i;
for (i = 0; video = streams[i]; i += 1) {
format = this.formats[video['itag']];
if (! format) {
console.error('current format not supported: ', video);
continue;
}
formatName = []
if ('format_note' in format) {
formatName.push(format.format_note);
}
if ('resolution' in format) {
if (formatName.length > 0) {
formatName.push('-');
}
formatName.push(format.resolution);
}
if ('ext' in format) {
formatName.push('.');
formatName.push(format.ext);
}
formatName = formatName.join('');
if (videos.formats.indexOf(formatName) >= 0) {
continue;
}
videos.formats.push(formatName);
url = decodeURIComponent(video.url);
if ('sig' in video) {
url = url + '&signature=' + video.sig
}
videos.links.push(url);
}
if (videos.links.length === 0) {
videos.ok = false;
videos.msg = 'This video does not allowed to download';
}
singleFile.run(videos);
},
/**
* Parse URL hash and convert to Object.
*/
urlHashToObject: function(hashText) {
var list = hashText.split('&'),
output = {},
len = list.length,
i = 0,
tmp = '';
for (i = 0; i < len; i += 1) {
tmp = list[i].split('=')
output[tmp[0]] = tmp[1];
}
return output;
},
/**
* FROM: http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
* This function creates a new anchor element and uses location
* properties (inherent) to get the desired URL data. Some String
* operations are used (to normalize results across browsers).
*/
parseURI: function(url) {
var a = unsafeWindow.document.createElement('a');
a.href = url;
return {
source: url,
protocol: a.protocol.replace(':',''),
host: a.hostname,
port: a.port,
query: a.search,
params: (function(){
var ret = {},
seg = a.search.replace(/^\?/,'').split('&'),
len = seg.length,
i = 0,
s;
for (i = 0; i< len; i += 1) {
if (seg[i]) {
s = seg[i].split('=');
ret[s[0]] = s[1];
}
}
return ret;
})(),
file: (a.pathname.match(/\/([^\/?#]+)$/i) || [,''])[1],
hash: a.hash.replace('#',''),
path: a.pathname.replace(/^([^\/])/,'/$1'),
relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [,''])[1],
segments: a.pathname.replace(/^\//,'').split('/')
};
},
};
monkey.extend('www.youtube.com', [
'http://www.youtube.com/watch?v=',
'https://www.youtube.com/watch?v=',
'http://www.youtube.com/embed/',
'https://www.youtube.com/embed/',
], monkey_youtube);
// In the end, get video handler and call it
monkey.run();
}).call(this);