您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
播放网页里的视频, 不再需要Adobe Flash Player
// ==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://gf.qytechs.cn/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);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址