monkey-videos

播放网页里的视频, 不再需要Adobe Flash Player

  1. // ==UserScript==
  2. // @name monkey-videos
  3. // @description 播放网页里的视频, 不再需要Adobe Flash Player
  4. // @version 1.0.7
  5. // @license GPLv3
  6. // @author LiuLang
  7. // @email gsushzhsosgsu@gmail.com
  8. // @include http://www.56.com/u*
  9. // @include http://www.56.com/w*
  10. // @include http://www.acfun.tv/v/ac*
  11. // @include http://www.bilibili.com/video/av*
  12. // @include http://tv.cntv.cn/video/*
  13. // @include http://search.cctv.com/playVideo.php*
  14. // @include http://www.fun.tv/vplay/*
  15. // @include http://fun.tv/vplay/*
  16. // @include http://phtv.ifeng.com/program/*
  17. // @include http://v.ifeng.com/*
  18. // @include http://www.iqiyi.com/v_*
  19. // @include http://www.iqiyi.com/jilupian/*
  20. // @include http://www.letv.com/ptv/vplay/*
  21. // @include http://www.justing.com.cn/page/*
  22. // @include http://v.ku6.com/*
  23. // @include http://v.163.com/*
  24. // @include http://open.163.com/*
  25. // @include http://v.pps.tv/play_*
  26. // @include http://ipd.pps.tv/play_*
  27. // @include http://video.sina.com.cn/*
  28. // @include http://open.sina.com.cn/course/*
  29. // @include http://tv.sohu.com/*
  30. // @include http://www.tucao.cc/play/*
  31. // @include http://www.tudou.com/albumplay/*
  32. // @include http://www.tudou.com/listplay/*
  33. // @include http://www.tudou.com/programs/view/*
  34. // @include http://www.wasu.cn/Play/show/id/*
  35. // @include http://www.wasu.cn/play/show/id/*
  36. // @include http://www.wasu.cn/wap/Play/show/id/*
  37. // @include http://www.wasu.cn/wap/play/show/id/*
  38. // @include http://www.weiqitv.com/index/live_back?videoId=*
  39. // @include http://www.weiqitv.com/index/video_play?videoId=*
  40. // @include http://v.youku.com/v_show/id_*
  41. // @include http://v.youku.com/v_playlist/*
  42. // @include http://www.youtube.com/watch?v=*
  43. // @include https://www.youtube.com/watch?v=*
  44. // @include http://www.youtube.com/embed/*
  45. // @include https://www.youtube.com/embed/*
  46. // @run-at document-end
  47. // @grant GM_getValue
  48. // @grant GM_setValue
  49. // @grant GM_xmlhttpRequest
  50. // @namespace https://gf.qytechs.cn/users/7422
  51. // ==/UserScript==
  52.  
  53. (function() {
  54.  
  55. ////////////////////////////////////////////////////////////////////////
  56. //// Router
  57. ////////////////////////////////////////////////////////////////////////
  58. var monkey = {
  59. pathnames: {},
  60. handlers: {},
  61.  
  62. run: function() {
  63. console.log('monkey.run: ', this);
  64. var handler = this.matchHandler();
  65. console.log('handler:', handler);
  66. if (handler !== null) {
  67. handler.run();
  68. }
  69. },
  70.  
  71. extend: function(hostname, pathnameList, handler) {
  72. this.pathnames[hostname] = pathnameList;
  73. this.handlers[hostname] = handler;
  74. },
  75.  
  76. matchHandler: function() {
  77. console.log('matchHandler() --');
  78. var host = location.host,
  79. url = location.href,
  80. pathnames;
  81. if (host in this.pathnames) {
  82. pathnames = this.pathnames[host];
  83. if (pathnames.some(function(pathname) {
  84. return url.startsWith(pathname);
  85. })) {
  86. return this.handlers[host];
  87. }
  88. }
  89. return null;
  90. },
  91. };
  92.  
  93. ////////////////////////////////////////////////////////////////////////
  94. //// Utils
  95. ////////////////////////////////////////////////////////////////////////
  96.  
  97. /*
  98. * JavaScript MD5 1.0.1
  99. * https://github.com/blueimp/JavaScript-MD5
  100. *
  101. * Copyright 2011, Sebastian Tschan
  102. * https://blueimp.net
  103. *
  104. * Licensed under the MIT license:
  105. * http://www.opensource.org/licenses/MIT
  106. *
  107. * Based on
  108. * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
  109. * Digest Algorithm, as defined in RFC 1321.
  110. * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
  111. * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
  112. * Distributed under the BSD License
  113. * See http://pajhome.org.uk/crypt/md5 for more info.
  114. */
  115.  
  116. /*jslint bitwise: true */
  117. /*global unescape, define */
  118.  
  119. (function ($) {
  120. 'use strict';
  121.  
  122. /*
  123. * Add integers, wrapping at 2^32. This uses 16-bit operations internally
  124. * to work around bugs in some JS interpreters.
  125. */
  126. function safe_add(x, y) {
  127. var lsw = (x & 0xFFFF) + (y & 0xFFFF),
  128. msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  129. return (msw << 16) | (lsw & 0xFFFF);
  130. }
  131.  
  132. /*
  133. * Bitwise rotate a 32-bit number to the left.
  134. */
  135. function bit_rol(num, cnt) {
  136. return (num << cnt) | (num >>> (32 - cnt));
  137. }
  138.  
  139. /*
  140. * These functions implement the four basic operations the algorithm uses.
  141. */
  142. function md5_cmn(q, a, b, x, s, t) {
  143. return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
  144. }
  145. function md5_ff(a, b, c, d, x, s, t) {
  146. return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
  147. }
  148. function md5_gg(a, b, c, d, x, s, t) {
  149. return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
  150. }
  151. function md5_hh(a, b, c, d, x, s, t) {
  152. return md5_cmn(b ^ c ^ d, a, b, x, s, t);
  153. }
  154. function md5_ii(a, b, c, d, x, s, t) {
  155. return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
  156. }
  157.  
  158. /*
  159. * Calculate the MD5 of an array of little-endian words, and a bit length.
  160. */
  161. function binl_md5(x, len) {
  162. /* append padding */
  163. x[len >> 5] |= 0x80 << (len % 32);
  164. x[(((len + 64) >>> 9) << 4) + 14] = len;
  165.  
  166. var i, olda, oldb, oldc, oldd,
  167. a = 1732584193,
  168. b = -271733879,
  169. c = -1732584194,
  170. d = 271733878;
  171.  
  172. for (i = 0; i < x.length; i += 16) {
  173. olda = a;
  174. oldb = b;
  175. oldc = c;
  176. oldd = d;
  177.  
  178. a = md5_ff(a, b, c, d, x[i], 7, -680876936);
  179. d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
  180. c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
  181. b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
  182. a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
  183. d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
  184. c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
  185. b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
  186. a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
  187. d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
  188. c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
  189. b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
  190. a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
  191. d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
  192. c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
  193. b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
  194.  
  195. a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
  196. d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
  197. c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
  198. b = md5_gg(b, c, d, a, x[i], 20, -373897302);
  199. a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
  200. d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
  201. c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
  202. b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
  203. a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
  204. d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
  205. c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
  206. b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
  207. a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
  208. d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
  209. c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
  210. b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
  211.  
  212. a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
  213. d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
  214. c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
  215. b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
  216. a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
  217. d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
  218. c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
  219. b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
  220. a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
  221. d = md5_hh(d, a, b, c, x[i], 11, -358537222);
  222. c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
  223. b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
  224. a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
  225. d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
  226. c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
  227. b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
  228.  
  229. a = md5_ii(a, b, c, d, x[i], 6, -198630844);
  230. d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
  231. c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
  232. b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
  233. a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
  234. d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
  235. c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
  236. b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
  237. a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
  238. d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
  239. c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
  240. b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
  241. a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
  242. d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
  243. c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
  244. b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
  245.  
  246. a = safe_add(a, olda);
  247. b = safe_add(b, oldb);
  248. c = safe_add(c, oldc);
  249. d = safe_add(d, oldd);
  250. }
  251. return [a, b, c, d];
  252. }
  253.  
  254. /*
  255. * Convert an array of little-endian words to a string
  256. */
  257. function binl2rstr(input) {
  258. var i,
  259. output = '';
  260. for (i = 0; i < input.length * 32; i += 8) {
  261. output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
  262. }
  263. return output;
  264. }
  265.  
  266. /*
  267. * Convert a raw string to an array of little-endian words
  268. * Characters >255 have their high-byte silently ignored.
  269. */
  270. function rstr2binl(input) {
  271. var i,
  272. output = [];
  273. output[(input.length >> 2) - 1] = undefined;
  274. for (i = 0; i < output.length; i += 1) {
  275. output[i] = 0;
  276. }
  277. for (i = 0; i < input.length * 8; i += 8) {
  278. output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
  279. }
  280. return output;
  281. }
  282.  
  283. /*
  284. * Calculate the MD5 of a raw string
  285. */
  286. function rstr_md5(s) {
  287. return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
  288. }
  289.  
  290. /*
  291. * Calculate the HMAC-MD5, of a key and some data (raw strings)
  292. */
  293. function rstr_hmac_md5(key, data) {
  294. var i,
  295. bkey = rstr2binl(key),
  296. ipad = [],
  297. opad = [],
  298. hash;
  299. ipad[15] = opad[15] = undefined;
  300. if (bkey.length > 16) {
  301. bkey = binl_md5(bkey, key.length * 8);
  302. }
  303. for (i = 0; i < 16; i += 1) {
  304. ipad[i] = bkey[i] ^ 0x36363636;
  305. opad[i] = bkey[i] ^ 0x5C5C5C5C;
  306. }
  307. hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
  308. return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
  309. }
  310.  
  311. /*
  312. * Convert a raw string to a hex string
  313. */
  314. function rstr2hex(input) {
  315. var hex_tab = '0123456789abcdef',
  316. output = '',
  317. x,
  318. i;
  319. for (i = 0; i < input.length; i += 1) {
  320. x = input.charCodeAt(i);
  321. output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
  322. }
  323. return output;
  324. }
  325.  
  326. /*
  327. * Encode a string as utf-8
  328. */
  329. function str2rstr_utf8(input) {
  330. return unescape(encodeURIComponent(input));
  331. }
  332.  
  333. /*
  334. * Take string arguments and return either raw or hex encoded strings
  335. */
  336. function raw_md5(s) {
  337. return rstr_md5(str2rstr_utf8(s));
  338. }
  339. function hex_md5(s) {
  340. return rstr2hex(raw_md5(s));
  341. }
  342. function raw_hmac_md5(k, d) {
  343. return rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d));
  344. }
  345. function hex_hmac_md5(k, d) {
  346. return rstr2hex(raw_hmac_md5(k, d));
  347. }
  348.  
  349. function md5(string, key, raw) {
  350. if (!key) {
  351. if (!raw) {
  352. return hex_md5(string);
  353. }
  354. return raw_md5(string);
  355. }
  356. if (!raw) {
  357. return hex_hmac_md5(key, string);
  358. }
  359. return raw_hmac_md5(key, string);
  360. }
  361.  
  362. if (typeof define === 'function' && define.amd) {
  363. define(function () {
  364. return md5;
  365. });
  366. } else {
  367. $.md5 = md5;
  368. }
  369. }(this));
  370.  
  371. /**
  372. * base64 function wrap
  373. * usage: base64.encode(str); base64.decode(base64_str);
  374. */
  375. var base64 = {
  376. encodeChars : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs' +
  377. 'tuvwxyz0123456789+/',
  378. decodeChars : [
  379.   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  380.   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  381.   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
  382.   52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
  383.   -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  384.   15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
  385.   -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
  386.   41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1],
  387.  
  388. encodeFunction: function(str) {
  389.   var out = '',
  390. len = str.length,
  391. i = 0,
  392. c1,
  393. c2,
  394. c3;
  395. while(i < len) {
  396. c1 = str.charCodeAt(i++) & 0xff;
  397. if(i === len) {
  398. out += this.encodeChars.charAt(c1 >> 2);
  399. out += this.encodeChars.charAt((c1 & 0x3) << 4);
  400. out += "==";
  401. break;
  402. }
  403. c2 = str.charCodeAt(i++);
  404. if(i === len) {
  405. out += this.encodeChars.charAt(c1 >> 2);
  406. out += this.encodeChars.charAt(((c1 & 0x3)<< 4) |
  407. ((c2 & 0xF0) >> 4));
  408. out += this.encodeChars.charAt((c2 & 0xF) << 2);
  409. out += "=";
  410. break;
  411. }
  412. c3 = str.charCodeAt(i++);
  413. out += this.encodeChars.charAt(c1 >> 2);
  414. out += this.encodeChars.charAt(((c1 & 0x3)<< 4) |
  415. ((c2 & 0xF0) >> 4));
  416. out += this.encodeChars.charAt(((c2 & 0xF) << 2) |
  417. ((c3 & 0xC0) >>6));
  418. out += this.encodeChars.charAt(c3 & 0x3F);
  419. }
  420. return out;
  421. },
  422.  
  423. decodeFunction: function(str) {
  424. var c1,
  425. c2,
  426. c3,
  427. c4,
  428. len = str.length,
  429. out = '',
  430. i = 0;
  431.  
  432. while(i < len) {
  433. do {
  434. c1 = this.decodeChars[str.charCodeAt(i++) & 0xff];
  435. } while(i < len && c1 === -1);
  436. if(c1 === -1) {
  437. break;
  438. }
  439.  
  440. do {
  441. c2 = this.decodeChars[str.charCodeAt(i++) & 0xff];
  442. } while(i < len && c2 === -1);
  443. if(c2 === -1) {
  444. break;
  445. }
  446. out += String.fromCharCode((c1 << 2) |
  447. ((c2 & 0x30) >> 4));
  448. do {
  449. c3 = str.charCodeAt(i++) & 0xff;
  450. if(c3 === 61) {
  451. return out;
  452. }
  453. c3 = this.decodeChars[c3];
  454. } while(i < len && c3 === -1);
  455. if(c3 === -1) {
  456. break;
  457. }
  458. out += String.fromCharCode(((c2 & 0XF) << 4) |
  459. ((c3 & 0x3C) >> 2));
  460.  
  461. do {
  462. c4 = str.charCodeAt(i++) & 0xff;
  463. if(c4 === 61) {
  464. return out;
  465. }
  466. c4 = this.decodeChars[c4];
  467. } while(i < len && c4 === -1);
  468. if(c4 === -1) {
  469. break;
  470. }
  471. out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
  472. };
  473. return out;
  474. },
  475.  
  476. utf16to8: function(str) {
  477. var out = '',
  478. len = str.length,
  479. i,
  480. c;
  481.  
  482. for(i = 0; i < len; i++) {
  483. c = str.charCodeAt(i);
  484. if ((c >= 0x0001) && (c <= 0x007F)) {
  485. out += str.charAt(i);
  486. } else if (c > 0x07FF) {
  487. out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
  488. out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
  489. out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
  490. } else {
  491. out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
  492. out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
  493. }
  494. }
  495. return out;
  496. },
  497.  
  498. utf8to16: function(str) {
  499.   var out = '',
  500. len = str.length,
  501. i = 0,
  502. c,
  503. char2,
  504. char3;
  505. while(i < len) {
  506. c = str.charCodeAt(i++);
  507. switch(c >> 4) {
  508. // 0xxxxxxx
  509. case 0:
  510. case 1:
  511. case 2:
  512. case 3:
  513. case 4:
  514. case 5:
  515. case 6:
  516. case 7:
  517. out += str.charAt(i - 1);
  518. break;
  519. // 110x xxxx  10xx xxxx
  520. case 12: case 13:
  521. char2 = str.charCodeAt(i++);
  522. out += String.fromCharCode(((c & 0x1F) << 6) |
  523. (char2 & 0x3F));
  524. break;
  525. // 1110 xxxx 10xx xxxx 10xx xxxx
  526. case 14:
  527. char2 = str.charCodeAt(i++);
  528. char3 = str.charCodeAt(i++);
  529. out += String.fromCharCode(((c & 0x0F) << 12) |
  530. ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
  531. break;
  532. }
  533. }
  534. return out;
  535. },
  536.  
  537. // This is encode/decode wrap, which convert chars between UTF-8
  538. // and UTF-16;
  539. encode: function(str) {
  540. return this.encodeFunction(this.utf16to8(str));
  541. },
  542.  
  543. decode: function(str) {
  544. return this.utf8to16(this.decodeFunction(str));
  545. },
  546. };
  547.  
  548.  
  549. /**
  550. * Create a new <style> tag with str as its content.
  551. * @param string styleText
  552. * - The <style> tag content.
  553. */
  554. var addStyle = function(styleText) {
  555. console.log('addStyle() --');
  556. var style = document.createElement('style');
  557. if (document.head) {
  558. document.head.appendChild(style);
  559. style.innerHTML = styleText;
  560. }
  561. };
  562.  
  563. /**
  564. * Split query parameters in url and convert to object
  565. */
  566. var getQueryVariable = function(query) {
  567. var vars = query.split('&'),
  568. params = {},
  569. param,
  570. i;
  571.  
  572. for (i = 0; i < vars.length; i += 1) {
  573. param = vars[i].split('=');
  574. params[param[0]] = param[1];
  575. }
  576. return params;
  577. };
  578.  
  579. /**
  580. * Convert string to xml
  581. * @param string str
  582. * - the string to be converted.
  583. * @return object xml
  584. * - the converted xml object.
  585. */
  586. var parseXML = function(str) {
  587. if (document.implementation &&
  588. document.implementation.createDocument) {
  589. xmlDoc = new DOMParser().parseFromString(str, 'text/xml');
  590. } else {
  591. console.log('parseXML() error: not support current web browser!');
  592. return null;
  593. }
  594. return xmlDoc;
  595. };
  596.  
  597. /**
  598. * UI functions.
  599. * create UI which has multiples files per video.
  600. */
  601. var multiFiles = {
  602.  
  603. // videos is an object containing these fields:
  604. // title, video title
  605. // formats, title of each video format
  606. // links, list containing video links of each duration
  607. videos: null,
  608.  
  609. run: function(videos) {
  610. console.log('multiFiles.run() --');
  611. this.videos = videos;
  612. if ((!videos.formats) || (videos.formats.length === 0)) {
  613. console.error('Error: no video formats specified!');
  614. return;
  615. }
  616. this.removeOldPanels();
  617. this.createPanel();
  618. },
  619.  
  620. removeOldPanels: function() {
  621. console.log('removeOldPanels() --');
  622. var panels = document.querySelectorAll('.monkey-videos-panel'),
  623. panel,
  624. i;
  625.  
  626. for (i = 0; panel = panels[i]; i += 1) {
  627. panel.parentElement.removeChild(panel);
  628. }
  629. },
  630.  
  631. /**
  632. * Create the control panel.
  633. */
  634. createPanel: function() {
  635. console.log('createPanel() --');
  636. var panel = document.createElement('div'),
  637. div,
  638. form,
  639. label,
  640. input,
  641. span,
  642. a,
  643. i,
  644. playlistWrap,
  645. playlistToggle,
  646. that = this;
  647.  
  648. addStyle([
  649. '.monkey-videos-panel {',
  650. 'position: fixed;',
  651. 'right: 10px;',
  652. 'bottom: 0px;',
  653. 'z-index: 99999;',
  654. 'border: 2px solid #ccc;',
  655. 'border-top-left-radius: 14px;',
  656. 'margin: 10px 0px 0px 0px;',
  657. 'padding: 10px 10px 0px 10px;',
  658. 'background-color: #fff;',
  659. 'overflow-y: hidden;',
  660. 'max-height: 90%;',
  661. 'min-width: 100px;',
  662. 'text-align: left;',
  663. '}',
  664. '.monkey-videos-panel:hover {',
  665. 'overflow-y: auto;',
  666. '}',
  667. '.monkey-videos-panel label {',
  668. 'margin-right: 10px;',
  669. '}',
  670. '.monkey-videos-panel .playlist-item {',
  671. 'display: block;',
  672. 'margin-top: 8px;',
  673. '}',
  674. '.monkey-videos-panel #playlist-toggle {',
  675. 'height: 10px;',
  676. 'margin-top: 10px;',
  677. '}',
  678. '.monkey-videos-panel #playlist-toggle:hover {',
  679. 'cursor: pointer;',
  680. '}',
  681. '.monkey-videos-panel .playlist-show {',
  682. 'background-color: #8b82a2;',
  683. //'border-radius: 0px 0px 5px 5px;',
  684. '}',
  685. '.monkey-videos-panel .playlist-hide {',
  686. 'background-color: #462093;',
  687. //'border-radius: 5px 5px 0px 0px;',
  688. '}',
  689. ].join(''));
  690.  
  691. panel.className = 'monkey-videos-panel';
  692. document.body.appendChild(panel);
  693.  
  694. playlistWrap = document.createElement('div');
  695. playlistWrap.className = 'playlist-wrap';
  696. panel.appendChild(playlistWrap);
  697.  
  698. div = document.createElement('div');
  699. div.className = 'playlist-nav';
  700. playlistWrap.appendChild(div);
  701.  
  702. form = document.createElement('form');
  703. form.className = 'playlist-format';
  704. playlistWrap.appendChild(form);
  705. for (i = 0; i < this.videos.formats.length; i += 1) {
  706. label = document.createElement('label');
  707. form.appendChild(label);
  708. input = document.createElement('input');
  709. label.appendChild(input);
  710. input.type = 'radio';
  711. input.name = 'monkey-videos-format';
  712. span = document.createElement('span');
  713. label.appendChild(span);
  714. span.innerHTML = this.videos.formats[i];
  715.  
  716. (function(input, pos) {
  717. input.addEventListener('change', function() {
  718. that.modifyList(pos);
  719. GM_setValue('format', pos);
  720. }, false);
  721. })(input, i);
  722. }
  723. // playlist m3u (with url data schema)
  724. a = document.createElement('a');
  725. a.className = 'playlist-m3u';
  726. a.innerHTML = '播放列表';
  727. a.title = a.innerHTML;
  728. a.download = this.videos.title + '.m3u';
  729. a.href = '';
  730. form.appendChild(a);
  731.  
  732. div = document.createElement('div');
  733. div.className = 'playlist';
  734. playlistWrap.appendChild(div);
  735.  
  736. playlistToggle = document.createElement('div');
  737. playlistToggle.id = 'playlist-toggle';
  738. playlistToggle.title = '隐藏';
  739. playlistToggle.className = 'playlist-show';
  740. panel.appendChild(playlistToggle);
  741. playlistToggle.addEventListener('click', function(event) {
  742. var wrap = document.querySelector('.monkey-videos-panel .playlist-wrap');
  743. if (wrap.style.display === 'none') {
  744. wrap.style.display = 'block';
  745. event.target.className = 'playlist-show';
  746. event.target.title = '隐藏';
  747. GM_setValue('hidePlaylist', false);
  748. } else {
  749. wrap.style.display = 'none';
  750. event.target.title = '显示';
  751. event.target.className = 'playlist-hide';
  752. GM_setValue('hidePlaylist', true);
  753. }
  754. }, false);
  755.  
  756. if (GM_getValue('hidePlaylist', false)) {
  757. playlistToggle.click();
  758. }
  759.  
  760. this.loadDefault();
  761. },
  762.  
  763. loadDefault: function() {
  764. console.log('loadDefault() --');
  765. // Load default type of playlist.
  766. var currPos = GM_getValue('format', 0),
  767. formats = this.videos.formats,
  768. currPlaylist;
  769.  
  770. console.log('currPos: ', currPos);
  771. if (formats.length <= currPos) {
  772. currPos = formats.length - 1;
  773. }
  774. console.log('currPos: ', currPos);
  775.  
  776. currPlaylist = document.querySelectorAll(
  777. '.monkey-videos-panel .playlist-format input')[currPos];
  778.  
  779. if (currPlaylist) {
  780. currPlaylist.checked = true;
  781. this.modifyList(currPos);
  782. }
  783. },
  784.  
  785. /**
  786. * Modify the playlist content.
  787. *
  788. * Empty playlist first, and add new links of specific video format.
  789. */
  790. modifyList: function(pos) {
  791. console.log('modifyList(), pos = ', pos);
  792. var playlist = document.querySelector('.monkey-videos-panel .playlist'),
  793. url,
  794. a,
  795. i;
  796. // Clear its content first
  797. playlist.innerHTML = '';
  798.  
  799. for (i = 0; url = this.videos.links[pos][i]; i += 1) {
  800. a = document.createElement('a');
  801. playlist.appendChild(a);
  802. a.className = 'playlist-item',
  803. a.href = url;
  804. if (this.videos.links[pos].length == 1) {
  805. a.innerHTML = this.videos.title;
  806. }
  807. else if (i < 9) {
  808. a.innerHTML = this.videos.title + '(0' + String(i + 1) + ')';
  809. } else {
  810. a.innerHTML = this.videos.title + '(' + String(i + 1) + ')';
  811. }
  812. a.title = a.innerHTML;
  813. }
  814.  
  815. // Refresh m3u playlist file.
  816. document.querySelector('.playlist-m3u').href = this.plsDataScheme();
  817. },
  818.  
  819. /**
  820. * Generate Playlist using base64 and Data URI scheme.
  821. * So that we can download directly and same it as a pls file using HTML.
  822. * URL:http://en.wikipedia.org/wiki/Data_URI_scheme
  823. * @return string
  824. * - Data scheme containting playlist.
  825. */
  826. plsDataScheme: function() {
  827. console.log('plsDataSchema() --');
  828. return 'data:audio/x-m3u;charset=UTF-8;base64,' +
  829. base64.encode(this.generatePls());
  830. },
  831.  
  832. /**
  833. * Generate pls - a multimedia playlist file, like m3u.
  834. * @return string
  835. * - playlist content.
  836. */
  837. generatePls: function() {
  838. console.log('generatePls() --');
  839. var output = [],
  840. links = document.querySelectorAll('.monkey-videos-panel .playlist-item'),
  841. a,
  842. i;
  843.  
  844. output.push('#EXTM3U');
  845. for (i = 0; a = links[i]; i += 1) {
  846. output.push('#EXTINF:81, ' + a.innerHTML);
  847. output.push(a.href);
  848. }
  849. return output.join('\n');
  850. },
  851. };
  852.  
  853.  
  854. var singleFile = {
  855. // videos is an object containing video info.
  856. //
  857. // @title, string, video title
  858. // @formats, string list, format name of each video
  859. // @links, string list, video link
  860. // @msg, string
  861. // @ok, bool, is ok is false, @msg will be displayed on playlist-panel
  862. videos: null,
  863.  
  864. run: function(videos) {
  865. console.log('run() -- ');
  866. this.videos = videos;
  867. this.createPanel();
  868. this.createPlaylist();
  869. },
  870.  
  871. createPanel: function() {
  872. console.log('createPanel() --');
  873. var panel = document.createElement('div'),
  874. playlist = document.createElement('div'),
  875. playlistToggle = document.createElement('div');
  876.  
  877. addStyle([
  878. '.monkey-videos-panel {',
  879. 'position: fixed;',
  880. 'right: 10px;',
  881. 'bottom: 0px;',
  882. 'z-index: 99999;',
  883. 'border: 2px solid #ccc;',
  884. 'border-top-left-radius: 14px;',
  885. 'margin: 10px 0px 0px 0px;',
  886. 'padding: 10px 10px 0px 10px;',
  887. 'background-color: #fff;',
  888. 'overflow-y: hidden;',
  889. 'max-height: 90%;',
  890. 'min-width: 100px;',
  891. '}',
  892. '.monkey-videos-panel:hover {',
  893. 'overflow-y: auto;',
  894. '}',
  895. '.monkey-videos-panel label {',
  896. 'margin-right: 10px;',
  897. '}',
  898. '.monkey-videos-panel .playlist-item {',
  899. 'display: block;',
  900. '}',
  901. '.monkey-videos-panel #playlist-toggle {',
  902. 'height: 10px;',
  903. 'width: 100%;',
  904. 'margin-top: 10px;',
  905. '}',
  906. '.monkey-videos-panel #playlist-toggle:hover {',
  907. 'cursor: pointer;',
  908. '}',
  909. '.monkey-videos-panel .playlist-show {',
  910. 'background-color: #8b82a2;',
  911. //'border-radius: 0px 0px 5px 5px;',
  912. '}',
  913. '.monkey-videos-panel .playlist-hide {',
  914. 'background-color: #462093;',
  915. //'border-radius: 5px 5px 0px 0px;',
  916. '}',
  917. ].join(''));
  918.  
  919. panel.className = 'monkey-videos-panel';
  920. document.body.appendChild(panel);
  921.  
  922. playlist= document.createElement('div');
  923. playlist.className = 'playlist-wrap';
  924. panel.appendChild(playlist);
  925.  
  926. playlistToggle = document.createElement('div');
  927. playlistToggle.id = 'playlist-toggle';
  928. playlistToggle.title = '隐藏';
  929. playlistToggle.className = 'playlist-show';
  930. panel.appendChild(playlistToggle);
  931. playlistToggle.addEventListener('click', function(event) {
  932. var wrap = document.querySelector('.monkey-videos-panel .playlist-wrap');
  933.  
  934. if (wrap.style.display === 'none') {
  935. wrap.style.display = 'block';
  936. event.target.className = 'playlist-show';
  937. event.target.title = '隐藏';
  938. GM_setValue('hidePlaylist', false);
  939. } else {
  940. wrap.style.display = 'none';
  941. event.target.title = '显示';
  942. event.target.className = 'playlist-hide';
  943. GM_setValue('hidePlaylist', true);
  944. }
  945. }, false);
  946.  
  947. if (GM_getValue('hidePlaylist', false)) {
  948. playlistToggle.click();
  949. }
  950. },
  951.  
  952. createPlaylist: function() {
  953. console.log('createPlayList() -- ');
  954. var playlist = document.querySelector('.monkey-videos-panel .playlist-wrap'),
  955. a,
  956. i;
  957.  
  958. if (!this.videos.ok) {
  959. console.error(this.videos.msg);
  960. a = document.createElement('span');
  961. a.title = this.videos.msg;
  962. a.innerHTML = this.videos.msg;
  963. playlist.appendChild(a);
  964. return;
  965. }
  966.  
  967. for (i = 0; i < this.videos.links.length; i += 1) {
  968. a = document.createElement('a');
  969. a.className = 'playlist-item';
  970. a.innerHTML = this.videos.title + '(' + this.videos.formats[i] + ')';
  971. a.title = a.innerHTML;
  972. a.href = this.videos.links[i];
  973. playlist.appendChild(a);
  974. }
  975. },
  976. };
  977.  
  978. ////////////////////////////////////////////////////////////////////////
  979. //// Define video handlers
  980. ////////////////////////////////////////////////////////////////////////
  981.  
  982.  
  983. /**
  984. * 56.com
  985. */
  986. var monkey_56 = {
  987. title: '',
  988. id: '',
  989. json: null,
  990. videos: {
  991. 'normal': '',
  992. 'clear': '',
  993. 'super': '',
  994. },
  995.  
  996. run: function() {
  997. console.log('run() --');
  998. this.getID();
  999. if (this.id.length > 0) {
  1000. this.getPlaylist();
  1001. } else {
  1002. console.error('Failed to get video id!');
  1003. return;
  1004. }
  1005. },
  1006.  
  1007. /**
  1008. * Get video id
  1009. */
  1010. getID: function() {
  1011. console.log('getID() --');
  1012. var url = location.href,
  1013. idReg = /\/v_(\w+)\.html/,
  1014. idMatch = idReg.exec(url),
  1015. albumIDReg = /_vid-(\w+)\.html/,
  1016. albumIDMatch = albumIDReg.exec(url);
  1017.  
  1018. console.log(idMatch);
  1019. console.log(albumIDMatch);
  1020. if (idMatch && idMatch.length === 2) {
  1021. this.id = idMatch[1];
  1022. } else if (albumIDMatch && albumIDMatch.length === 2) {
  1023. this.id = albumIDMatch[1];
  1024. }
  1025. console.log(this);
  1026. },
  1027.  
  1028. /**
  1029. * Get video playlist from a json object
  1030. */
  1031. getPlaylist: function() {
  1032. console.log('getPlaylist() --');
  1033. var url = 'http://vxml.56.com/json/' + this.id + '/?src=out',
  1034. that = this;
  1035.  
  1036. console.log('url: ', url);
  1037. GM_xmlhttpRequest({
  1038. method: 'get',
  1039. url: url,
  1040. onload: function(response) {
  1041. console.log('response:', response);
  1042. var txt = response.responseText,
  1043. json = JSON.parse(txt),
  1044. video,
  1045. i;
  1046.  
  1047. that.json = json;
  1048. if (json.msg == 'ok' && json.status == '1') {
  1049. that.title = json.info.Subject;
  1050. for (i = 0; video = json.info.rfiles[i]; i = i + 1) {
  1051. that.videos[video.type] = video.url;
  1052. }
  1053. }
  1054. that.createUI();
  1055. },
  1056. });
  1057. },
  1058.  
  1059. createUI: function() {
  1060. console.log('createUI() --');
  1061. console.log(this);
  1062. var videos = {
  1063. title: this.title,
  1064. formats: [],
  1065. links: [],
  1066. ok: true,
  1067. msg: '',
  1068. },
  1069. formats = ['normal', 'clear', 'super'],
  1070. format_names = ['标清', '高清', '超清'],
  1071. format,
  1072. link,
  1073. i;
  1074.  
  1075. for (i = 0; format = formats[i]; i += 1) {
  1076. if (format in this.videos && this.videos[format].length > 0) {
  1077. videos.links.push([this.videos[format]]);
  1078. videos.formats.push(format_names[i]);
  1079. } else {
  1080. console.error('This video type is not supported: ', format);
  1081. }
  1082. }
  1083. console.log(videos);
  1084. multiFiles.run(videos);
  1085. },
  1086. }
  1087.  
  1088.  
  1089. monkey.extend('www.56.com', [
  1090. 'http://www.56.com/u',
  1091. 'http://www.56.com/w',
  1092. ], monkey_56);
  1093.  
  1094. /**
  1095. * acfun.tv
  1096. */
  1097. var monkey_acfun = {
  1098. vid: '',
  1099. origUrl: '',
  1100.  
  1101. run: function() {
  1102. this.getVid();
  1103. if (this.vid.length === 0) {
  1104. console.error('Failed to get video id!');
  1105. this.createUI();
  1106. } else {
  1107. this.getVideoLink();
  1108. }
  1109. },
  1110.  
  1111. getVid: function() {
  1112. console.log('getVid()');
  1113. var videos = document.querySelectorAll(
  1114. 'div#area-part-view div.l a'),
  1115. video,
  1116. i;
  1117.  
  1118. console.log('videos: ', videos);
  1119. for (i = 0; video = videos[i]; i += 1) {
  1120. if (video.className.search('active') > 0) {
  1121. this.vid = video.getAttribute('data-vid');
  1122. return;
  1123. }
  1124. }
  1125. console.error('Failed to get vid');
  1126. },
  1127.  
  1128. /**
  1129. * Get video link from a json object
  1130. */
  1131. getVideoLink: function() {
  1132. console.log('getVideoLink()');
  1133. console.log(this);
  1134. //var url = 'http://www.acfun.tv/api/getVideoByID.aspx?vid=' + this.vid,
  1135. var url = 'http://www.acfun.tv/video/getVideo.aspx?id=' + this.vid,
  1136. that = this;
  1137.  
  1138. GM_xmlhttpRequest({
  1139. method: 'GET',
  1140. url: url,
  1141. onload: function(response) {
  1142. console.log('response:', response);
  1143. var json = JSON.parse(response.responseText);
  1144.  
  1145. if (json.success && json.sourceUrl.startsWith('http')) {
  1146. that.origUrl = json.sourceUrl;
  1147. }
  1148. that.createUI();
  1149. },
  1150. });
  1151. },
  1152.  
  1153. createUI: function() {
  1154. console.log('createUI() --');
  1155. console.log(this);
  1156. var videos = {
  1157. title: '原始地址',
  1158. formats: [],
  1159. links: [],
  1160. ok: true,
  1161. msg: '',
  1162. };
  1163.  
  1164. if (this.origUrl.length === 0) {
  1165. videos.ok = false;
  1166. videos.msg = '暂不支持';
  1167. singleFile.run(videos);
  1168. } else {
  1169. videos.formats.push(' ');
  1170. videos.links.push(this.origUrl);
  1171. singleFile.run(videos);
  1172. }
  1173. },
  1174. }
  1175.  
  1176. monkey.extend('www.acfun.tv', [
  1177. 'http://www.acfun.tv/v/ac',
  1178. ], monkey_acfun);
  1179.  
  1180. /**
  1181. * bilibili.tv
  1182. */
  1183. var monkey_bili = {
  1184. cid: '',
  1185. title: '',
  1186. oriurl: '',
  1187.  
  1188. run: function() {
  1189. console.log('run() --');
  1190. this.getTitle();
  1191. this.getCid();
  1192. },
  1193.  
  1194. /**
  1195. * Get video title
  1196. */
  1197. getTitle: function() {
  1198. console.log('getTitle()');
  1199. var metas = document.querySelectorAll('meta'),
  1200. meta,
  1201. i;
  1202.  
  1203. for (i = 0; meta = metas[i]; i += 1) {
  1204. if (meta.hasAttribute('name') &&
  1205. meta.getAttribute('name') === 'title') {
  1206. this.title = meta.getAttribute('content');
  1207. return;
  1208. }
  1209. }
  1210. this.title = document.title;
  1211. },
  1212.  
  1213. /**
  1214. * 获取 content ID.
  1215. */
  1216. getCid: function() {
  1217. console.log('getCid()');
  1218. var iframe = document.querySelector('iframe'),
  1219. flashvar = document.querySelector('div#bofqi embed'),
  1220. reg = /cid=(\d+)&aid=(\d+)/,
  1221. match;
  1222.  
  1223.  
  1224. if (iframe) {
  1225. match = reg.exec(iframe.src);
  1226. } else if (flashvar) {
  1227. console.log(flashvar.getAttribute('flashvars'));
  1228. match = reg.exec(flashvar.getAttribute('flashvars'));
  1229. }
  1230. console.log('match:', match);
  1231. if (match && match.length === 3) {
  1232. this.cid = match[1];
  1233. this.getVideos();
  1234. } else {
  1235. console.error('Failed to get cid!');
  1236. }
  1237. },
  1238.  
  1239. /**
  1240. * Get original video links from interface.bilibili.cn
  1241. */
  1242. getVideos: function() {
  1243. console.log('getVideos() -- ');
  1244. var url = 'http://interface.bilibili.cn/player?cid=' + this.cid,
  1245. that = this;
  1246.  
  1247. console.log('url:', url);
  1248. GM_xmlhttpRequest({
  1249. method: 'GET',
  1250. url: url,
  1251. onload: function(response) {
  1252. var reg = /<oriurl>(.+)<\/oriurl>/g,
  1253. txt = response.responseText,
  1254. match = reg.exec(txt);
  1255.  
  1256. if (match && match.length === 2) {
  1257. that.oriurl = match[1];
  1258. that.createUI();
  1259. }
  1260. },
  1261. });
  1262. },
  1263.  
  1264. createUI: function() {
  1265. console.log('createUI() --');
  1266. console.log(this);
  1267. var videos = {
  1268. title: '视频的原始地址',
  1269. formats: [''],
  1270. links: [],
  1271. ok: true,
  1272. msg: '',
  1273. };
  1274.  
  1275. videos.formats.push('');
  1276. videos.links.push(this.oriurl);
  1277.  
  1278. singleFile.run(videos);
  1279. },
  1280. }
  1281.  
  1282. monkey.extend('www.bilibili.com', [
  1283. 'http://www.bilibili.com/video/av',
  1284. ], monkey_bili);
  1285.  
  1286.  
  1287. /**
  1288. * cntv.cn
  1289. */
  1290. var monkey_cntv = {
  1291. pid: '',
  1292. title: '',
  1293. json: [],
  1294. videos: {
  1295. chapters: [],
  1296. chapters2: [],
  1297. },
  1298.  
  1299. run: function() {
  1300. console.log('run() --');
  1301. this.router();
  1302. },
  1303.  
  1304. router: function() {
  1305. console.log('router() --');
  1306. var href = location.href,
  1307. schema;
  1308. if (href.search('search.cctv.com/playVideo.php?') > -1) {
  1309. schema = this.hashToObject(location.search.substr(1));
  1310. this.pid = schema.detailsid;
  1311. this.title = decodeURI(schema.title);
  1312. this.getVideoInfo();
  1313. } else if (href.search('tv.cntv.cn/video/') > -1) {
  1314. this.pid = href.match(/\/([^\/]+)$/)[1];
  1315. this.title = document.title.substring(0, document.title.length - 8);
  1316. this.getVideoInfo();
  1317. } else {
  1318. this.getPidFromSource();
  1319. }
  1320. },
  1321.  
  1322. /**
  1323. * Get video pid from html source file
  1324. */
  1325. getPidFromSource: function() {
  1326. console.log('getPidFromSource() --');
  1327. var that = this;
  1328.  
  1329. GM_xmlhttpRequest({
  1330. url: location.href,
  1331. method: 'GET',
  1332. onload: function(response) {
  1333. console.log('response:', response);
  1334. that.parsePid(response.responseText);
  1335. },
  1336. });
  1337. },
  1338.  
  1339. /**
  1340. * Parse txt and get pid of video
  1341. */
  1342. parsePid: function(txt) {
  1343. console.log('parsePid() --');
  1344. var pidReg = /code\.begin-->([^<]+)/,
  1345. pidMatch = pidReg.exec(txt),
  1346. titleReg = /title\.begin-->([^<]+)/,
  1347. titleMatch = titleReg.exec(txt);
  1348.  
  1349. if (titleMatch && titleMatch.length === 2) {
  1350. this.title = titleMatch[1];
  1351. } else {
  1352. this.title = document.title;
  1353. }
  1354.  
  1355. console.log('pidMatch:', pidMatch);
  1356. if (pidMatch && pidMatch.length === 2) {
  1357. this.pid = pidMatch[1];
  1358. this.getVideoInfo();
  1359. } else {
  1360. console.error('Failed to get Pid');
  1361. return;
  1362. }
  1363. },
  1364.  
  1365. /**
  1366. * Get video info, including formats and uri
  1367. */
  1368. getVideoInfo: function() {
  1369. console.log('getVideoInfo() --');
  1370. var url = [
  1371. 'http://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?',
  1372. 'tz=-8&from=000tv&idlr=32&modified=false&idl=32&pid=',
  1373. this.pid,
  1374. '&url=',
  1375. location.href,
  1376. ].join(''),
  1377. that = this;
  1378.  
  1379. console.log('url:', url);
  1380. GM_xmlhttpRequest({
  1381. url: url,
  1382. method: 'GET',
  1383. onload: function(response) {
  1384. console.log('response: ', response);
  1385. that.json = JSON.parse(response.responseText);
  1386. console.log('that: ', that);
  1387. that.parseVideos();
  1388. },
  1389. });
  1390. },
  1391.  
  1392. /**
  1393. * Parse video info from json object.
  1394. */
  1395. parseVideos: function() {
  1396. console.log('parseVideos() --');
  1397. var chapter;
  1398.  
  1399. for (chapter in this.json.video) {
  1400. if (chapter.startsWith('chapters')) {
  1401. this.parseChapter(chapter);
  1402. }
  1403. }
  1404.  
  1405. this.createUI();
  1406. },
  1407.  
  1408. /**
  1409. * Parse specified chapter, list of video links.
  1410. */
  1411. parseChapter: function(chapter) {
  1412. console.log('parseChapter() --');
  1413. var item,
  1414. i;
  1415.  
  1416. for (i = 0; item = this.json.video[chapter][i]; i += 1) {
  1417. if (this.videos[chapter] === undefined) {
  1418. this.videos[chapter] = [];
  1419. }
  1420. this.videos[chapter].push(item.url);
  1421. }
  1422. },
  1423.  
  1424. /**
  1425. * Call multiFiles.js to construct UI widgets.
  1426. */
  1427. createUI: function() {
  1428. console.log('createUI() --');
  1429. console.log('this: ', this);
  1430. var videos = {
  1431. title: this.title,
  1432. formats: [],
  1433. links: [],
  1434. };
  1435.  
  1436. if (this.videos.chapters.length > 0) {
  1437. videos.formats.push('标清');
  1438. videos.links.push(this.videos.chapters);
  1439. }
  1440. if (this.videos.chapters2.length > 0) {
  1441. videos.formats.push('高清');
  1442. videos.links.push(this.videos.chapters2);
  1443. }
  1444.  
  1445. multiFiles.run(videos);
  1446. },
  1447.  
  1448. /**
  1449. * Create a new <style> tag with str as its content.
  1450. * @param string styleText
  1451. * - The <style> tag content.
  1452. */
  1453. addStyle: function(styleText) {
  1454. var style = document.createElement('style');
  1455. if (document.head) {
  1456. document.head.appendChild(style);
  1457. style.innerHTML = styleText;
  1458. }
  1459. },
  1460.  
  1461. /**
  1462. * 将URL中的hash转为了个对象/字典, 用于解析链接;
  1463. */
  1464. hashToObject: function(hashTxt) {
  1465. var list = hashTxt.split('&'),
  1466. output = {},
  1467. len = list.length,
  1468. i = 0,
  1469. tmp = '';
  1470.  
  1471. for (i = 0; i < len; i += 1) {
  1472. tmp = list[i].split('=')
  1473. output[tmp[0]] = tmp[1];
  1474. }
  1475. return output;
  1476. },
  1477. }
  1478.  
  1479. monkey.extend('tv.cntv.cn', [
  1480. 'http://tv.cntv.cn/video/',
  1481. ], monkey_cntv);
  1482.  
  1483. monkey.extend('search.cctv.com', [
  1484. 'http://search.cctv.com/playVideo.php',
  1485. ], monkey_cntv);
  1486.  
  1487. /**
  1488. * funshion.com
  1489. */
  1490. var monkey_funshion = {
  1491. title: '',
  1492. mediaid: '', // 专辑ID;
  1493. number: '', // 第几集, 从1计数;
  1494. jobs: 0,
  1495.  
  1496. formats: {
  1497. 327680: '标清版',
  1498. 491520: '高清版',
  1499. 737280: '超清版',
  1500. },
  1501. videos: {
  1502. 327680: '',
  1503. 491520: '',
  1504. 737280: '',
  1505. },
  1506.  
  1507. run: function() {
  1508. console.log('run() --');
  1509. this.router();
  1510. },
  1511. /**
  1512. * router control
  1513. */
  1514. router: function() {
  1515. var url = location.href;
  1516.  
  1517. if (url.search('subject/play/') > 1 ||
  1518. url.search('/vplay/') > 1 ) {
  1519. this.getVid();
  1520. } else if (url.search('subject/') > 1) {
  1521. this.addLinks();
  1522. } else if (url.search('uvideo/play/') > 1) {
  1523. this.getUGCID();
  1524. } else {
  1525. console.error('Error: current page is not supported!');
  1526. }
  1527. },
  1528.  
  1529. /**
  1530. * Get UGC video ID.
  1531. * For uvideo/play/'.
  1532. */
  1533. getUGCID: function() {
  1534. console.log('getUGCID() --');
  1535. var urlReg = /uvideo\/play\/(\d+)$/,
  1536. urlMatch = urlReg.exec(location.href);
  1537.  
  1538. console.log('urlMatch: ', urlMatch);
  1539. if (urlMatch.length === 2) {
  1540. this.mediaid = urlMatch[1];
  1541. this.getUGCVideoInfo();
  1542. } else {
  1543. console.error('Failed to parse video ID!');
  1544. }
  1545. },
  1546.  
  1547. getUGCVideoInfo: function() {
  1548. console.log('getUGCVideoInfo() --');
  1549. var url = 'http://api.funshion.com/ajax/get_media_data/ugc/' + this.mediaid,
  1550. that = this;
  1551.  
  1552. console.log('url: ', url);
  1553. GM_xmlhttpRequest({
  1554. url: url,
  1555. method: 'GET',
  1556. onload: function(response) {
  1557. console.log('response: ', response);
  1558. that.json = JSON.parse(response.responseText);
  1559. console.log('json: ', that.json);
  1560. that.decodeUGCVideoInfo();
  1561. },
  1562. });
  1563. },
  1564.  
  1565. decodeUGCVideoInfo: function() {
  1566. console.log('decodeUGCVideoInfo() --');
  1567. var url = [
  1568. 'http://jobsfe.funshion.com/query/v1/mp4/',
  1569. this.json.data.hashid,
  1570. '.json?file=',
  1571. this.json.data.filename,
  1572. ].join(''),
  1573. that = this;
  1574.  
  1575. console.log('url: ', url);
  1576. GM_xmlhttpRequest({
  1577. url: url,
  1578. method: 'GET',
  1579. onload: function(response) {
  1580. console.log('response: ', response);
  1581. that.appendUGCVideo(JSON.parse(response.responseText));
  1582. },
  1583. });
  1584. },
  1585.  
  1586. appendUGCVideo: function(videoJson) {
  1587. console.log('appendUGCVideo() --');
  1588. console.log('this: ', this);
  1589. console.log('videoJson:', videoJson);
  1590. var fileformat = this.fileformats[videoJson.playlist[0].bits];
  1591.  
  1592. info = {
  1593. title: this.json.data.name_cn,
  1594. href: videoJson.playlist[0].urls[0],
  1595. };
  1596. console.log('info: ', info);
  1597.  
  1598. this._appendVideo(info);
  1599. },
  1600.  
  1601.  
  1602. /**
  1603. * Get video ID.
  1604. * For subject/play/'.
  1605. */
  1606. getVid: function() {
  1607. console.log('getVid() --');
  1608. var url = location.href,
  1609. urlReg = /subject\/play\/(\d+)\/(\d+)$/,
  1610. urlMatch = urlReg.exec(url),
  1611. urlReg2 = /\/vplay\/m-(\d+)/,
  1612. urlMatch2 = urlReg2.exec(url);
  1613.  
  1614. console.log('urlMatch: ', urlMatch);
  1615. console.log('urlMatch2: ', urlMatch2);
  1616. if (urlMatch && urlMatch.length === 3) {
  1617. this.mediaid = urlMatch[1];
  1618. this.number = parseInt(urlMatch[2]);
  1619. } else if (urlMatch2 && urlMatch2.length === 2) {
  1620. this.mediaid = urlMatch2[1];
  1621. this.number = 1;
  1622. } else {
  1623. console.error('Failed to parse video ID!');
  1624. return;
  1625. }
  1626. this.getVideoInfo();
  1627. },
  1628.  
  1629. /**
  1630. * Download a json file containing video info
  1631. */
  1632. getVideoInfo: function() {
  1633. console.log('getVideoInfo() --');
  1634. var url = [
  1635. 'http://api.funshion.com/ajax/get_web_fsp/',
  1636. this.mediaid,
  1637. '/mp4',
  1638. ].join(''),
  1639. that = this;
  1640.  
  1641. console.log('url: ', url);
  1642. GM_xmlhttpRequest({
  1643. url: url,
  1644. method: 'GET',
  1645. onload: function(response) {
  1646. console.log('response: ', response);
  1647. var json = JSON.parse(response.responseText),
  1648. format;
  1649. console.log('json: ', json);
  1650. that.title = json.data.name_cn || that.getTitle();
  1651. if ((! json.data.fsps) || (! json.data.fsps.mult) ||
  1652. (json.data.fsps.mult.length === 0) ||
  1653. (! json.data.fsps.mult[0].cid)) {
  1654. that.createUI();
  1655. }
  1656.  
  1657. that.mediaid = json.data.fsps.mult[0].cid;
  1658. for (format in that.formats) {
  1659. that.jobs = that.jobs + 1;
  1660. that.getVideoLink(format);
  1661. }
  1662. },
  1663. });
  1664. },
  1665.  
  1666. /**
  1667. * Get title from document.tiel
  1668. */
  1669. getTitle: function() {
  1670. console.log('getTitle() --');
  1671. var title = document.title,
  1672. online = title.search(' - 在线观看');
  1673.  
  1674. if (online > -1) {
  1675. return title.substr(0, online);
  1676. } else {
  1677. return title.substr(0, 12) + '..';
  1678. }
  1679. },
  1680.  
  1681. /**
  1682. * Get Video source link.
  1683. */
  1684. getVideoLink: function(format) {
  1685. console.log('getVideoLink() --');
  1686. var url = [
  1687. 'http://jobsfe.funshion.com/query/v1/mp4/',
  1688. this.mediaid,
  1689. '.json?bits=',
  1690. format,
  1691. ].join(''),
  1692. that = this;
  1693.  
  1694. console.log('url: ', url);
  1695. GM_xmlhttpRequest({
  1696. method: 'GET',
  1697. url: url,
  1698. onload: function(response) {
  1699. console.log('response: ', response);
  1700. var json = JSON.parse(response.responseText);
  1701. console.log('json: ', json);
  1702. that.videos[format] = json.playlist[0].urls[0];
  1703. that.jobs = that.jobs - 1;
  1704. if (that.jobs === 0) {
  1705. that.createUI();
  1706. }
  1707. },
  1708. });
  1709. },
  1710.  
  1711. createUI: function() {
  1712. console.log('createUI() --');
  1713. console.log(this);
  1714. var videos = {
  1715. title: this.title,
  1716. formats: [],
  1717. links: [],
  1718. ok: true,
  1719. msg: '',
  1720. },
  1721. format;
  1722.  
  1723. if (this.videos[327680].length > 0) {
  1724. videos.links.push([this.videos[327680]]);
  1725. videos.formats.push(this.formats[327680]);
  1726. }
  1727. if (this.videos[491520].length > 0) {
  1728. videos.links.push([this.videos[491520]]);
  1729. videos.formats.push(this.formats[491520]);
  1730. }
  1731. if (this.videos[737280].length > 0) {
  1732. videos.links.push([this.videos[737280]]);
  1733. videos.formats.push(this.formats[737280]);
  1734. }
  1735.  
  1736. if (videos.links.length === 0) {
  1737. videos.ok = false;
  1738. videos.msg = 'Video source is not available.';
  1739. }
  1740. multiFiles.run(videos);
  1741. },
  1742. }
  1743.  
  1744. monkey.extend('fun.tv', [
  1745. 'http://fun.tv/vplay/',
  1746. ], monkey_funshion);
  1747.  
  1748. monkey.extend('www.fun.tv', [
  1749. 'http://www.fun.tv/vplay/',
  1750. ], monkey_funshion);
  1751.  
  1752. /**
  1753. * ifeng.com
  1754. */
  1755. var monkey_ifeng = {
  1756. id: '',
  1757. title: '',
  1758. links: [],
  1759.  
  1760. run: function() {
  1761. console.log('run() --');
  1762. this.getId();
  1763. },
  1764.  
  1765. getId: function() {
  1766. console.log('getId() --');
  1767. var reg = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/,
  1768. match = reg.exec(location.href);
  1769.  
  1770. console.log('match: ', match);
  1771. if (match && match.length === 2) {
  1772. this.id = match[1];
  1773. this.downloadById();
  1774. } else {
  1775. console.error('Failed to get video id');
  1776. }
  1777. },
  1778.  
  1779. downloadById: function() {
  1780. console.log('downloadById() --');
  1781. var length = this.id.length,
  1782. url = [
  1783. 'http://v.ifeng.com/video_info_new/',
  1784. this.id[length-2],
  1785. '/',
  1786. this.id.substr(length-2),
  1787. '/',
  1788. this.id,
  1789. '.xml'
  1790. ].join(''),
  1791. that = this;
  1792.  
  1793. console.log('url:', url);
  1794. GM_xmlhttpRequest({
  1795. method: 'GET',
  1796. url: url,
  1797. onload: function(response) {
  1798. var xml = new DOMParser().parseFromString(response.responseText,
  1799. 'text/xml'),
  1800. item = xml.querySelector('item');
  1801.  
  1802. that.title = item.getAttribute('Name');
  1803. that.links.push(item.getAttribute('VideoPlayUrl'));
  1804. that.createUI();
  1805. },
  1806. });
  1807. },
  1808.  
  1809. createUI: function() {
  1810. console.log('createUI() --');
  1811. console.log(this);
  1812. var videos = {
  1813. title: this.title,
  1814. formats: [],
  1815. links: [],
  1816. ok: true,
  1817. msg: '',
  1818. };
  1819. videos.formats.push('标清');
  1820. videos.links.push([this.links]);
  1821. multiFiles.run(videos);
  1822. },
  1823. };
  1824.  
  1825. monkey.extend('v.ifeng.com', [
  1826. 'http://v.ifeng.com/',
  1827. ], monkey_ifeng);
  1828.  
  1829. monkey.extend('phtv.ifeng.com', [
  1830. 'http://phtv.ifeng.com/program/',
  1831. ], monkey_ifeng);
  1832.  
  1833. /**
  1834. * iqiyi.com
  1835. */
  1836. var monkey_iqiyi = {
  1837. title: '',
  1838. vid: '', // default vid, data-player-videoid
  1839. uid: '', // generated uuid/user id
  1840. aid: '', // album id, data-player-albumid
  1841. tvid: '', // data-player-tvid
  1842. type: 0, // default type
  1843. rcOrder: [96, 1, 2, 3, 4, 5, 10],
  1844. vip: false, // this video is for VIP only
  1845. rc: {
  1846. 96: {name: '240P', links: []},
  1847. 1: {name: '320P', links: []},
  1848. 2: {name: '480P', links: []},
  1849. 3: {name: 'super', links: []},
  1850. 4: {name: '720P', links: []},
  1851. 5: {name: '1080P', links: []},
  1852. 10: {name: '4K', links: []},
  1853. },
  1854. jobs: 0,
  1855.  
  1856. run: function() {
  1857. console.log('run() --');
  1858. this.getTitle();
  1859. this.getVid();
  1860. },
  1861.  
  1862. getTitle: function() {
  1863. console.log('getTitle() --');
  1864. var nav = unsafeWindow.document.querySelector('#navbar em'),
  1865. id,
  1866. title;
  1867.  
  1868. if (nav) {
  1869. title = nav.innerHTML;
  1870. } else {
  1871. title = unsafeWindow.document.title.split('-')[0];
  1872. }
  1873. this.title = title.trim();
  1874. },
  1875.  
  1876. getVid: function() {
  1877. console.log('getVid() --');
  1878. var videoPlay = unsafeWindow.document.querySelector('div#flashbox');
  1879. if (videoPlay && videoPlay.hasAttribute('data-player-videoid')) {
  1880. this.vid = videoPlay.getAttribute('data-player-videoid');
  1881. this.aid = videoPlay.getAttribute('data-player-aid');
  1882. this.tvid = videoPlay.getAttribute('data-player-tvid');
  1883. this.uid = this.hex_guid();
  1884. this.getVideoUrls();
  1885. } else {
  1886. console.error('Error: failed to get video id');
  1887. return;
  1888. }
  1889. },
  1890.  
  1891. getVideoUrls: function() {
  1892. console.log('getVideoUrls() --');
  1893. var tm = this.randint(1000, 2000),
  1894. enc = md5('ts56gh' + tm + this.tvid),
  1895. url = [
  1896. 'http://cache.video.qiyi.com/vms?key=fvip&src=p',
  1897. '&tvId=', this.tvid,
  1898. '&vid=', this.vid,
  1899. '&vinfo=1&tm=', tm,
  1900. '&enc=', enc,
  1901. '&qyid=', this.uid,
  1902. '&tn=', Math.random(),
  1903. ].join(''),
  1904. that = this;
  1905.  
  1906. GM_xmlhttpRequest({
  1907. method: 'GET',
  1908. url: url,
  1909. onload: function(response) {
  1910. var json = JSON.parse(response.responseText),
  1911. formats,
  1912. format,
  1913. vlink,
  1914. vlink_parts,
  1915. key,
  1916. url,
  1917. i,
  1918. j;
  1919.  
  1920. that.title = json.data.vi.vn;
  1921. if (! json.data.vp.tkl) {
  1922. that.vip = true;
  1923. that.createUI();
  1924. }
  1925.  
  1926. formats = json.data.vp.tkl[0].vs;
  1927. for (i = 0; format = formats[i]; i += 1) {
  1928. if (! that.rc[format.bid]) {
  1929. console.error('Current video type not supported: ', format.bid);
  1930. continue;
  1931. }
  1932. for (j = 0; j < format.fs.length; j += 1) {
  1933. vlink = format.fs[j].l;
  1934. if (! vlink.startsWith('/')) {
  1935. vlink = that.getVrsEncodeCode(vlink);
  1936. }
  1937. vlink_parts = vlink.split('/');
  1938. that.getDispathKey(
  1939. vlink_parts[vlink_parts.length - 1].split('.')[0],
  1940. format.bid, vlink, j);
  1941. }
  1942. }
  1943. },
  1944. });
  1945. },
  1946.  
  1947. getVrsEncodeCode: function(vlink) {
  1948. var loc6 = 0,
  1949. loc2 = [],
  1950. loc3 = vlink.split('-'),
  1951. loc4 = loc3.length,
  1952. i;
  1953.  
  1954. for (i = loc4 - 1; i >= 0; i -= 1) {
  1955. loc6 = this.getVRSXORCode(parseInt(loc3[loc4 - i - 1], 16), i);
  1956. loc2.push(String.fromCharCode(loc6));
  1957. }
  1958. return loc2.reverse().join('');
  1959. },
  1960.  
  1961. getVRSXORCode: function(arg1, arg2) {
  1962. var loc3 = arg2 % 3;
  1963. if (loc3 === 1) {
  1964. return arg1 ^ 121;
  1965. } else if (loc3 === 2) {
  1966. return arg1 ^ 72
  1967. } else {
  1968. return arg1 ^ 103;
  1969. }
  1970. },
  1971.  
  1972. getDispathKey: function(rid, bid, vlink, i) {
  1973. var tp = ")(*&^flash@#$%a",
  1974. that = this;
  1975.  
  1976. GM_xmlhttpRequest({
  1977. method: 'GET',
  1978. url: 'http://data.video.qiyi.com/t?tn=' + Math.random(),
  1979. onload: function(response) {
  1980. var json = JSON.parse(response.responseText),
  1981. time = json.t,
  1982. t = Math.floor(parseInt(time) / 600.0).toString(),
  1983. key = md5(t + tp + rid),
  1984. url = [
  1985. 'http://data.video.qiyi.com/', key, '/videos', vlink,
  1986. '?su=', that.uid,
  1987. '&client=&z=&bt=&ct=&tn=', that.randint(10000, 20000),
  1988. ].join('');
  1989.  
  1990. that.rc[bid].links.push('');
  1991. that.jobs += 1;
  1992. that.getFinalURL(bid, i, url);
  1993. },
  1994. });
  1995. },
  1996.  
  1997. getFinalURL: function(bid, i, url) {
  1998. var that = this;
  1999. GM_xmlhttpRequest({
  2000. method: 'GET',
  2001. url: url,
  2002. onload: function(response) {
  2003. var json = JSON.parse(response.responseText);
  2004.  
  2005. that.rc[bid].links[i] = json.l;
  2006. that.jobs -= 1;
  2007. if (that.jobs === 0) {
  2008. that.createUI();
  2009. }
  2010. },
  2011. });
  2012. },
  2013.  
  2014. createUI: function() {
  2015. console.log('createUI() --');
  2016. console.log(this);
  2017. var i,
  2018. video,
  2019. videos = {
  2020. title: this.title,
  2021. formats: [],
  2022. links: [],
  2023. },
  2024. links,
  2025. link,
  2026. j;
  2027.  
  2028. if (this.vip) {
  2029. unsafeWindow.alert('VIP only!');
  2030. return;
  2031. }
  2032. for (i = 0; i < this.rcOrder.length; i += 1) {
  2033. video = this.rc[this.rcOrder[i]];
  2034. if (video.links.length > 0) {
  2035. videos.formats.push(video.name);
  2036.  
  2037. // append KEY/UUID string to end of each video link
  2038. links = [];
  2039. for (j = 0; j < video.links.length; j += 1) {
  2040. link = [video.links[j], '?', video.key].join('');
  2041. links.push(link);
  2042. }
  2043. videos.links.push(links);
  2044. }
  2045. }
  2046. multiFiles.run(videos);
  2047. },
  2048.  
  2049. /**
  2050. * Convert string to xml
  2051. * @param string str
  2052. * - the string to be converted.
  2053. * @return object xml
  2054. * - the converted xml object.
  2055. */
  2056. parseXML: function(str) {
  2057. if (unsafeWindow.document.implementation &&
  2058. unsafeWindow.document.implementation.createDocument) {
  2059. xmlDoc = new DOMParser().parseFromString(str, 'text/xml');
  2060. } else {
  2061. console.log('parseXML() error: not support current web browser!');
  2062. return null;
  2063. }
  2064. return xmlDoc;
  2065. },
  2066.  
  2067. /**
  2068. * Generate a UUID string
  2069. */
  2070. hex_guid: function() {
  2071. function s4() {
  2072. return Math.floor((1 + Math.random()) * 0x10000)
  2073. .toString(16)
  2074. .substring(1);
  2075. }
  2076. return [s4(), s4(), s4(), s4(), s4(), s4(), s4(), s4()].join('');
  2077. },
  2078.  
  2079. randint: function(start, stop) {
  2080. return parseInt(Math.random() * (stop - start)) + start;
  2081. },
  2082. };
  2083.  
  2084. monkey.extend('www.iqiyi.com', [
  2085. 'http://www.iqiyi.com/v_',
  2086. 'http://www.iqiyi.com/jilupian/',
  2087. ], monkey_iqiyi);
  2088.  
  2089. /**
  2090. * letv.com
  2091. */
  2092. var monkey_letv = {
  2093. pid: '',
  2094. vid: '', // video id
  2095. title: '',
  2096. stime: 0, // server timestamp
  2097. tkey: 0, // time key
  2098. jobs: 0,
  2099.  
  2100. videoUrl: {
  2101. '9': '',
  2102. '21': '',
  2103. '13': '',
  2104. },
  2105. videoFormats: {
  2106. '9': '流畅',
  2107. '13': '超清',
  2108. '21': '高清',
  2109. },
  2110.  
  2111. run: function() {
  2112. console.log('run() -- ');
  2113. var url = location.href;
  2114. this.title = document.title.substr(0, document.title.length-12);
  2115.  
  2116. if (url.search('yuanxian.letv') !== -1) {
  2117. // movie info page.
  2118. this.addLinkToYuanxian();
  2119. } else if (url.search('ptv/pplay/') > 1 ||
  2120. url.search('ptv/vplay/' > 1)) {
  2121. this.getVid();
  2122. } else {
  2123. console.error('I do not know what to do!');
  2124. }
  2125. },
  2126.  
  2127. /**
  2128. * Show original video link in video index page.
  2129. */
  2130. addLinkToYuanxian: function() {
  2131. console.log('addLinkToYuanxian() --');
  2132. var pid = __INFO__.video.pid,
  2133. url = 'http://www.letv.com/ptv/pplay/' + pid + '.html',
  2134. titleLink = document.querySelector('dl.w424 dt a');
  2135.  
  2136. titleLink.href = url;
  2137. },
  2138.  
  2139. /**
  2140. * Get video id
  2141. */
  2142. getVid: function() {
  2143. console.log('getVid() --')
  2144. var input = document.querySelector('.add input'),
  2145. vidReg = /\/(\d+)\.html$/,
  2146. vidMatch;
  2147.  
  2148. console.log(input);
  2149. if (input && input.hasAttribute('value')) {
  2150. vidMatch = vidReg.exec(input.getAttribute('value'));
  2151. } else {
  2152. console.error('Failed to get input element');
  2153. return;
  2154. }
  2155.  
  2156. console.log('vidMatch: ', vidMatch);
  2157. if (vidMatch && vidMatch.length === 2) {
  2158. this.vid = vidMatch[1];
  2159. this.getTimestamp();
  2160. } else {
  2161. console.error('Failed to get video ID!');
  2162. return;
  2163. }
  2164. },
  2165.  
  2166. /**
  2167. * Get timestamp from server
  2168. */
  2169. getTimestamp: function() {
  2170. console.log('getTimestamp() --');
  2171. var tn = Math.random(),
  2172. url = 'http://api.letv.com/time?tn=' + tn.toString(),
  2173. that = this;
  2174.  
  2175. console.log('url:', url);
  2176. GM_xmlhttpRequest({
  2177. method: 'GET',
  2178. url: url,
  2179. onload: function(response) {
  2180. console.log('response:', response);
  2181. var obj = JSON.parse(response.responseText);
  2182. that.stime = parseInt(obj.stime);
  2183. that.tkey = that.getKey(that.stime);
  2184. that.getVideoXML();
  2185. },
  2186. });
  2187. },
  2188.  
  2189. /**
  2190. * Get time key
  2191. * @param integer t, server time
  2192. */
  2193. getKey: function(t) {
  2194. console.log('getKey() --', t);
  2195. for(var e = 0, s = 0; s < 8; s += 1){
  2196. e = 1 & t;
  2197. t >>= 1;
  2198. e <<= 31;
  2199. t += e;
  2200. }
  2201. return t ^ 185025305;
  2202. },
  2203.  
  2204. /**
  2205. * Get video info from an xml file
  2206. */
  2207. getVideoXML: function() {
  2208. console.log('getVideoXML() --');
  2209. var url = [
  2210. 'http://api.letv.com/mms/out/common/geturl?',
  2211. 'platid=3&splatid=301&playid=0&vtype=9,13,21,28&version=2.0',
  2212. '&tss=no',
  2213. '&vid=', this.vid,
  2214. '&tkey=', this.tkey,
  2215. '&domain=http%3A%2F%2Fwww.letv.com'
  2216. ].join(''),
  2217. that = this;
  2218.  
  2219. console.log('videoXML url: ', url);
  2220. GM_xmlhttpRequest({
  2221. method: 'GET',
  2222. url: url,
  2223. onload: function(response) {
  2224. console.log('response: ', response);
  2225. var json = JSON.parse(response.responseText);
  2226. that.getVideoUrl(json.data[0].infos);
  2227. },
  2228. });
  2229. },
  2230.  
  2231. /**
  2232. * Parse video url
  2233. */
  2234. getVideoUrl: function(videos) {
  2235. console.log('getVideoUrl() --');
  2236. var video,
  2237. i,
  2238. url;
  2239.  
  2240.  
  2241. for (i = 0; video = videos[i]; i = i + 1) {
  2242. url = [
  2243. video.mainUrl,
  2244. '&ctv=pc&m3v=1&termid=1&format=1&hwtype=un&ostype=Linux&tag=letv',
  2245. '&sign=letv&expect=3&pay=0&iscpn=f9051&rateid=1300',
  2246. '&tn=', Math.random()
  2247. ].join('');
  2248. this.getFinalUrl(url, video.vtype);
  2249. this.jobs += 1;
  2250. }
  2251. },
  2252.  
  2253. /**
  2254. * Get final video links
  2255. * @param url, video link,
  2256. * @param type, video type
  2257. */
  2258. getFinalUrl: function(url, type) {
  2259. console.log('getFinalUrl() --', type);
  2260. var that = this;
  2261.  
  2262. GM_xmlhttpRequest({
  2263. url: url,
  2264. method: 'GET',
  2265. onload: function(response) {
  2266. var json = JSON.parse(response.responseText);
  2267. that.videoUrl[type] = json.location;
  2268. that.jobs -= 1;
  2269. if (that.jobs === 0) {
  2270. that.createUI();
  2271. }
  2272. },
  2273. });
  2274. },
  2275.  
  2276. /**
  2277. * construct ui widgets.
  2278. */
  2279. createUI: function() {
  2280. console.log('createUI() --');
  2281. console.log(this);
  2282. var videos = {
  2283. title: this.title,
  2284. formats: [],
  2285. links: [],
  2286. ok: true,
  2287. msg: '',
  2288. },
  2289. types = ['9', '21', '13'],
  2290. type,
  2291. url,
  2292. i;
  2293. for (i = 0; type = types[i]; i += 1) {
  2294. url = this.videoUrl[type];
  2295. if (url) {
  2296. videos.links.push([this.videoUrl[type]]);
  2297. videos.formats.push(this.videoFormats[type]);
  2298. }
  2299. }
  2300.  
  2301. multiFiles.run(videos);
  2302. },
  2303. };
  2304.  
  2305. monkey.extend('www.letv.com', [
  2306. 'http://www.letv.com/ptv/vplay/',
  2307. ], monkey_letv);
  2308.  
  2309.  
  2310. /**
  2311. * justing.com.cn
  2312. */
  2313. var monkey_justing = {
  2314. title: '',
  2315. link: '',
  2316.  
  2317. run: function() {
  2318. console.log('run() --');
  2319. this.getTitle();
  2320. this.createUI();
  2321. },
  2322.  
  2323. getTitle: function() {
  2324. var titleElem = unsafeWindow.document.querySelector('div#title');
  2325. if (titleElem) {
  2326. this.title = titleElem.innerHTML;
  2327. this.link = encodeURI([
  2328. 'http://dl.justing.com.cn/page/',
  2329. this.title,
  2330. '.mp3'].join(''));
  2331. }
  2332. },
  2333.  
  2334. createUI: function() {
  2335. console.log('createUI() --');
  2336. console.log(this);
  2337. var videos = {
  2338. title: this.title,
  2339. links: [],
  2340. formats: [],
  2341. ok: true,
  2342. msg: '',
  2343. };
  2344. if (this.title.length === 0) {
  2345. videos.ok = false;
  2346. videos.msg = 'Failed to get mp3 link';
  2347. } else {
  2348. videos.links.push([this.link]);
  2349. videos.formats.push('mp3');
  2350. }
  2351. multiFiles.run(videos);
  2352. },
  2353.  
  2354.  
  2355. };
  2356.  
  2357. monkey.extend('www.justing.com.cn', [
  2358. 'http://www.justing.com.cn/page/',
  2359. ], monkey_justing);
  2360.  
  2361. /**
  2362. * ku6.com
  2363. */
  2364. var monkey_ku6 = {
  2365.  
  2366. vid: '',
  2367. title: '',
  2368. links: [],
  2369. type: '标清',
  2370.  
  2371. run: function() {
  2372. console.log('run() --');
  2373. this.getVid();
  2374. },
  2375.  
  2376. /**
  2377. * 获取video id, 用于构建下载链接.
  2378. */
  2379. getVid: function() {
  2380. console.log('getVid() --');
  2381. var url = location.href,
  2382. vid_reg = /\/([^\/]+)\.html/,
  2383. vid_match = vid_reg.exec(url);
  2384.  
  2385. console.log(vid_match);
  2386. if (vid_match && vid_match.length == 2) {
  2387. this.vid = vid_match[1];
  2388. this.getVideo();
  2389. }
  2390. },
  2391.  
  2392. getVideo: function() {
  2393. console.log('getVideo() --');
  2394. var url = 'http://v.ku6.com/fetchVideo4Player/' + this.vid + '.html',
  2395. that = this;
  2396.  
  2397. console.log('url:', url);
  2398. GM_xmlhttpRequest({
  2399. url: url,
  2400. method: 'GET',
  2401. onload: function(response) {
  2402. console.log('response:', response);
  2403. var video_obj = JSON.parse(response.responseText);
  2404. console.log(video_obj);
  2405. that.title = video_obj.data.t;
  2406. that.links = video_obj.data.f.split(',');
  2407. that.createUI();
  2408. },
  2409. });
  2410. },
  2411.  
  2412. createUI: function() {
  2413. console.log('createUI() --');
  2414. console.log(this);
  2415. var videos = {
  2416. title: this.title,
  2417. formats: [],
  2418. links: [],
  2419. };
  2420.  
  2421. if (this.links.length > 0) {
  2422. videos.formats.push(this.type);
  2423. videos.links.push(this.links);
  2424. multiFiles.run(videos);
  2425. } else {
  2426. console.error('this.video is empty');
  2427. }
  2428. },
  2429. };
  2430.  
  2431. monkey.extend('v.ku6.com', [
  2432. 'http://v.ku6.com/',
  2433. ], monkey_ku6);
  2434.  
  2435. /**
  2436. * 163.com
  2437. */
  2438. var monkey_netease = {
  2439.  
  2440. plid: '', // playlist id
  2441. mid: '', // video id
  2442. raw_vid: '',
  2443. title: '',
  2444. pl_title: '', // playlist title
  2445. videos: {
  2446. sd: '',
  2447. hd: '',
  2448. shd: '',
  2449. },
  2450. types: {
  2451. sd: '标清',
  2452. hd: '高清',
  2453. shd: '超清',
  2454. },
  2455. subs: {
  2456. },
  2457.  
  2458. run: function() {
  2459. console.log('run() --');
  2460.  
  2461. this.getTitle();
  2462. if (document.title.search('网易公开课') > -1) {
  2463. this.getOpenCourseSource();
  2464. } else {
  2465. this.getSource();
  2466. }
  2467. },
  2468.  
  2469. getTitle: function() {
  2470. console.log('getTitle() --');
  2471. this.title = document.title;
  2472. },
  2473.  
  2474. getOpenCourseSource: function() {
  2475. console.log('getOpenCourseSource() --');
  2476. var url = document.location.href.split('/'),
  2477. urlMatch = /([A-Z0-9]{9})_([A-Z0-9]{9})/,
  2478. match = urlMatch.exec(url),
  2479. length = url.length,
  2480. xmlUrl,
  2481. that = this;
  2482.  
  2483. if (! match || match.length !== 3) {
  2484. console.error('Failed to get mid!', match);
  2485. return;
  2486. }
  2487. this.raw_vid = match[0];
  2488. this.plid = match[1];
  2489. this.mid = match[2];
  2490. xmlUrl = [
  2491. 'http://live.ws.126.net/movie',
  2492. url[length - 3],
  2493. url[length - 2],
  2494. '2_' + this.raw_vid + '.xml',
  2495. ].join('/');
  2496. console.log('xmlUrl: ', xmlUrl);
  2497.  
  2498. GM_xmlhttpRequest({
  2499. method: 'GET',
  2500. url: xmlUrl,
  2501. onload: function(response) {
  2502. var xml = parseXML(response.responseText),
  2503. type,
  2504. video,
  2505. subs,
  2506. sub,
  2507. subName,
  2508. i;
  2509.  
  2510. //that.title = xml.querySelector('all title').innerHTML;
  2511. that.title = that.title.replace('_网易公开课', '');
  2512. for (type in that.videos) {
  2513. video = xml.querySelector('playurl ' + type +' mp4');
  2514. if (video) {
  2515. that.videos[type] = video.firstChild.data;
  2516. continue;
  2517. }
  2518. video = xml.querySelector('playurl ' + type.toUpperCase() +' mp4');
  2519. if (video) {
  2520. that.videos[type] = video.firstChild.data;
  2521. }
  2522. }
  2523. subs = xml.querySelectorAll('subs sub');
  2524. for (i = 0; sub = subs[i]; i += 1) {
  2525. subName = sub.querySelector('name').innerHTML + '字幕';
  2526. that.subs[subName] = sub.querySelector('url').innerHTML;
  2527. }
  2528. that.getMobileOpenCourse();
  2529. },
  2530. });
  2531. },
  2532.  
  2533. /**
  2534. * AES ECB decrypt is too large to embed, so use another way.
  2535. */
  2536. getMobileOpenCourse: function() {
  2537. console.log('getMobileOpenCourse() --');
  2538. var url = 'http://mobile.open.163.com/movie/' + this.plid + '/getMoviesForAndroid.htm',
  2539. that = this;
  2540.  
  2541. console.log('url: ', url);
  2542. GM_xmlhttpRequest({
  2543. method: 'GET',
  2544. url: url,
  2545. onload: function(response) {
  2546. var json = JSON.parse(response.responseText),
  2547. video,
  2548. i;
  2549.  
  2550. for (i = 0; i < json.videoList.length; i += 1) {
  2551. video = json.videoList[i];
  2552. console.log('video:', video);
  2553. if (video.mid === that.mid) {
  2554. that.videos.sd = video.repovideourlmp4Origin;
  2555. that.videos.hd = video.repovideourl;
  2556. that.title = video.title;
  2557. that.pl_title = json.title;
  2558. break;
  2559. }
  2560. }
  2561. that.createUI();
  2562. },
  2563. });
  2564. },
  2565.  
  2566. getSource: function() {
  2567. console.log('getSource() --');
  2568. var scripts = document.querySelectorAll('script'),
  2569. script,
  2570. reg = /<source[\s\S]+src="([^"]+)"/,
  2571. match,
  2572. m3u8Reg = /appsrc\:\s*'([\s\S]+)\.m3u8'/,
  2573. m3u8Match,
  2574. i;
  2575.  
  2576. for (i = 0; script = scripts[i]; i += 1) {
  2577. match = reg.exec(script.innerHTML);
  2578. console.log(match);
  2579. if (match && match.length > 1) {
  2580. this.videos.sd = match[1].replace('-mobile.mp4', '.flv');
  2581. this.createUI();
  2582. return true;
  2583. }
  2584. m3u8Match = m3u8Reg.exec(script.innerHTML);
  2585. console.log(m3u8Match);
  2586. if (m3u8Match && m3u8Match.length > 1) {
  2587. this.videos.sd = m3u8Match[1].replace('-list', '') + '.mp4';
  2588. this.createUI();
  2589. return true;
  2590. }
  2591. }
  2592. },
  2593.  
  2594. createUI: function() {
  2595. console.log('createUI() --');
  2596. console.log(this);
  2597. var videos = {
  2598. title: this.title,
  2599. formats: [],
  2600. links: [],
  2601. ok: true,
  2602. msg: '',
  2603. },
  2604. formats = ['sd', 'hd', 'shd'],
  2605. format,
  2606. url,
  2607. subName,
  2608. i;
  2609.  
  2610. if (this.pl_title.length > 0) {
  2611. videos.title = this.title;
  2612. }
  2613.  
  2614. for (i = 0; format = formats[i]; i += 1) {
  2615. url = this.videos[format];
  2616. if (url.length > 0) {
  2617. videos.links.push([url]);
  2618. videos.formats.push(this.types[format]);
  2619. }
  2620. }
  2621. for (subName in this.subs) {
  2622. videos.links.push([this.subs[subName]]);
  2623. videos.formats.push(subName);
  2624. }
  2625. multiFiles.run(videos);
  2626. },
  2627. };
  2628.  
  2629.  
  2630. monkey.extend('v.163.com', [
  2631. 'http://v.163.com/',
  2632. ], monkey_netease);
  2633.  
  2634. monkey.extend('open.163.com', [
  2635. 'http://open.163.com/',
  2636. ], monkey_netease);
  2637.  
  2638.  
  2639. /**
  2640. * pps.tv
  2641. */
  2642. var monkey_pps = {
  2643. vid: '',
  2644. title: '',
  2645. types: {
  2646. 1: '高清',
  2647. 2: '标清',
  2648. 3: '流畅',
  2649. },
  2650. videoUrl: {
  2651. 1: '',
  2652. 2: '',
  2653. 3: '',
  2654. },
  2655. jobs: 3,
  2656. fromIqiyi: false,
  2657.  
  2658. run: function() {
  2659. console.log('run()');
  2660. if (location.href.search('pps.tv/play_') !== -1) {
  2661. this.getId();
  2662. } else {
  2663. console.error('Failed to get vid!');
  2664. }
  2665. },
  2666.  
  2667. getId: function() {
  2668. console.log('getId() -- ');
  2669. var vidReg = /play_([\s\S]+)\.html/,
  2670. vidMatch = vidReg.exec(document.location.href),
  2671. titleReg = /([\s\S]+)-在线观看/,
  2672. titleMatch = titleReg.exec(document.title);
  2673. if (vidMatch) {
  2674. this.vid = vidMatch[1];
  2675. }
  2676. if (titleMatch) {
  2677. this.title = titleMatch[1];
  2678. }
  2679. if (this.vid.length > 0) {
  2680. this.getUrl(1); // 高清
  2681. this.getUrl(2); // 标清
  2682. this.getUrl(3); // 流畅
  2683. }
  2684. },
  2685.  
  2686. getUrl: function(type) {
  2687. console.log('getUrl()');
  2688. var url = [
  2689. 'http://dp.ppstv.com/get_play_url_cdn.php?sid=',
  2690. this.vid,
  2691. '&flash_type=1&type=',
  2692. type,
  2693. ].join(''),
  2694. that = this;
  2695.  
  2696. console.log('url: ', url);
  2697. GM_xmlhttpRequest({
  2698. method: 'GET',
  2699. url: url,
  2700. onload: function(response) {
  2701. console.log(response);
  2702. var txt = response.responseText;
  2703.  
  2704. if (txt.search('.pfv?') > 0) {
  2705. that.videoUrl[type] = txt.substr(0, txt.search('.pfv?') + 4);
  2706. }
  2707. that.jobs -= 1;
  2708. if (that.jobs === 0) {
  2709. that.createUI();
  2710. }
  2711. },
  2712. });
  2713. },
  2714.  
  2715. createUI: function() {
  2716. console.log('createUI() --');
  2717. console.log(this);
  2718. var videos = {
  2719. title: this.title,
  2720. formats: [],
  2721. links: [],
  2722. },
  2723. types = [3, 2, 1],
  2724. type,
  2725. i;
  2726.  
  2727. for (i = 0; type = types[i]; i += 1) {
  2728. if (this.videoUrl[type]) {
  2729. videos.links.push([this.videoUrl[type]]);
  2730. videos.formats.push(this.types[type]);
  2731. }
  2732. }
  2733. multiFiles.run(videos);
  2734. },
  2735. }
  2736.  
  2737. monkey.extend('v.pps.tv', [
  2738. 'http://v.pps.tv/play_',
  2739. ], monkey_pps);
  2740.  
  2741. monkey.extend('ipd.pps.tv', [
  2742. 'http://ipd.pps.tv/play_',
  2743. ], monkey_pps);
  2744.  
  2745.  
  2746. /**
  2747. * sina.com.cn
  2748. */
  2749. var monkey_sina = {
  2750. title: '',
  2751. jobs: 0,
  2752. video: {
  2753. format: '标清',
  2754. vid: '',
  2755. url: '',
  2756. links: [],
  2757. },
  2758. hdVideo: {
  2759. format: '高清',
  2760. vid: '',
  2761. url: '',
  2762. links: [],
  2763. },
  2764.  
  2765. run: function() {
  2766. var loc = location.href;
  2767. if (loc.search('/vlist/') > -1) {
  2768. this.getVlist();
  2769. } else if (loc.search('video.sina.com.cn') > -1 ||
  2770. loc.search('open.sina.com.cn') > -1) {
  2771. this.getVid(loc);
  2772. } else {
  2773. console.error('This page is not supported!');
  2774. return;
  2775. }
  2776. },
  2777.  
  2778. /**
  2779. * e.g.
  2780. * http://video.sina.com.cn/vlist/news/zt/topvideos1/?opsubject_id=top12#118295074
  2781. * http://video.sina.com.cn/news/vlist/zt/chczlj2013/?opsubject_id=top12#109873117
  2782. */
  2783. getVlist: function() {
  2784. console.log('getVlist() --');
  2785. var h4s = document.querySelectorAll('h4.video_btn'),
  2786. h4,
  2787. i,
  2788. lis = document.querySelectorAll('ul#video_list li'),
  2789. li,
  2790. As,
  2791. A,
  2792. j,
  2793. that = this;
  2794.  
  2795. if (h4s && h4s.length > 0) {
  2796. this.getVlistItem(h4s[0].parentElement);
  2797. for (i = 0; i < h4s.length; i += 1) {
  2798. h4 = h4s[i];
  2799. h4.addEventListener('click', function(event) {
  2800. that.getVlistItem(event.target.parentElement);
  2801. }, false);
  2802. }
  2803.  
  2804. } else if (lis && lis.length > 0) {
  2805. this.getVlistItem(lis[0]);
  2806. for (i = 0; i < lis.length; i += 1) {
  2807. li = lis[i];
  2808. As = li.querySelectorAll('a.btn_play');
  2809. for (j = 0; A = As[j]; j += 1) {
  2810. A.href= li.getAttribute('vurl');
  2811. }
  2812. li.addEventListener('click', function(event) {
  2813. that.getVlistItem(event.target);
  2814. event.preventDefault();
  2815. return;
  2816. }, false);
  2817. }
  2818. }
  2819. },
  2820.  
  2821. getVlistItem: function(div) {
  2822. console.log('getVlistItem() --', div);
  2823. if (div.hasAttribute('data-url')) {
  2824. this.getVid(div.getAttribute('data-url'));
  2825. } else if (div.nodeName === 'A' && div.className === 'btn_play') {
  2826. this.getVid(div.parentElement.parentElement.parentElement.getAttribute('vurl'));
  2827. } else if (div.nodeName === 'IMG') {
  2828. this.getVid(div.parentElement.parentElement.parentElement.parentElement.getAttribute('vurl'));
  2829. } else if (div.hasAttribute('vurl')) {
  2830. this.getVid(div.getAttribute('vurl'));
  2831. } else {
  2832. console.error('Failed to get vid!', div);
  2833. return;
  2834. }
  2835. },
  2836.  
  2837. /**
  2838. * Get Video vid and hdVid.
  2839. */
  2840. getVid: function(url) {
  2841. console.log('getVid() --', url);
  2842. var that = this;
  2843.  
  2844. GM_xmlhttpRequest({
  2845. method: 'GET',
  2846. url: url,
  2847. onload: function(response) {
  2848. console.log(response);
  2849. var reg = /vid:['"](\d{5,})['"]/,
  2850. txt = response.responseText,
  2851. match = reg.exec(txt),
  2852. hdReg = /hd_vid:'(\d{5,})'/,
  2853. hdMatch = hdReg.exec(txt),
  2854. titleReg = /\s+title:'([^']+)'/,
  2855. titleMatch = titleReg.exec(txt);
  2856. title2Reg = /VideoTitle : "([^"]+)"/,
  2857. title2Match = title2Reg.exec(txt);
  2858.  
  2859. if (titleMatch) {
  2860. that.title = titleMatch[1];
  2861. } else if (title2Match) {
  2862. that.title = title2Match[1];
  2863. }
  2864. if (hdMatch && hdMatch.length > 1) {
  2865. that.hdVideo.vid = hdMatch[1];
  2866. that.jobs += 1;
  2867. that.getVideoByVid(that.hdVideo);
  2868. }
  2869. if (match && match.length > 1) {
  2870. that.video.vid = match[1];
  2871. that.jobs += 1;
  2872. that.getVideoByVid(that.video);
  2873. }
  2874. },
  2875. });
  2876. },
  2877.  
  2878. /**
  2879. * Calcuate video information url
  2880. */
  2881. getURLByVid: function(vid) {
  2882. console.log('getURLByVid() -- ', vid);
  2883. var randInt = parseInt(Math.random() * 1000),
  2884. time = parseInt(Date.now() / 1000) >> 6,
  2885. key = '';
  2886.  
  2887. key = [
  2888. vid,
  2889. 'Z6prk18aWxP278cVAH',
  2890. time,
  2891. randInt,
  2892. ].join('');
  2893. key = md5(key);
  2894. console.log('key: ', key);
  2895. key = key.substring(0, 16) + time;
  2896. console.log('key: ', key);
  2897.  
  2898. return [
  2899. 'http://v.iask.com/v_play.php?',
  2900. 'vid=', vid,
  2901. '&uid=null',
  2902. '&pid=null',
  2903. '&tid=undefined',
  2904. '&plid=4001',
  2905. '&prid=ja_7_4993252847',
  2906. '&referer=',
  2907. '&ran=', randInt,
  2908. '&r=video.sina.com.cn',
  2909. '&v=4.1.42.35',
  2910. '&p=i',
  2911. '&k=', key,
  2912. ].join('');
  2913. },
  2914.  
  2915. /**
  2916. * Get video info specified by vid.
  2917. */
  2918. getVideoByVid: function(container) {
  2919. console.log('getVideoByVid() --', container);
  2920. console.log(this);
  2921. var that = this;
  2922. container.url = this.getURLByVid(container.vid),
  2923.  
  2924. GM_xmlhttpRequest({
  2925. method: 'GET',
  2926. url: container.url,
  2927. onload: function(response) {
  2928. console.log('response: ', response);
  2929. var reg = /<url>.{9}([^\]]+)/g,
  2930. txt = response.responseText,
  2931. match = reg.exec(txt);
  2932.  
  2933. while (match) {
  2934. container.links.push(match[1]);
  2935. match = reg.exec(txt);
  2936. }
  2937.  
  2938. that.jobs -= 1;
  2939. if (that.jobs === 0) {
  2940. that.createUI();
  2941. }
  2942. },
  2943. });
  2944. },
  2945.  
  2946. createUI: function() {
  2947. console.log('createUI() --');
  2948. console.log(this);
  2949. var videos = {
  2950. formats: [],
  2951. links: [],
  2952. title: this.title,
  2953. };
  2954.  
  2955. if (this.video.links.length > 0) {
  2956. videos.formats.push(this.video.format);
  2957. videos.links.push(this.video.links);
  2958. }
  2959. if (this.hdVideo.links.length > 0) {
  2960. videos.formats.push(this.hdVideo.format);
  2961. videos.links.push(this.hdVideo.links);
  2962. }
  2963. console.log('videos: ', videos);
  2964. multiFiles.run(videos);
  2965. },
  2966. }
  2967.  
  2968.  
  2969. monkey.extend('video.sina.com.cn', [
  2970. 'http://video.sina.com.cn/',
  2971. ], monkey_sina);
  2972.  
  2973. monkey.extend('open.sina.com.cn', [
  2974. 'http://open.sina.com.cn/course/',
  2975. ], monkey_sina);
  2976.  
  2977. /**
  2978. * sohu.com
  2979. */
  2980. var monkey_sohu = {
  2981. title: '',
  2982. vid: '',
  2983. plid: '',
  2984. referer: '',
  2985. jobs: 0,
  2986. formats: {
  2987. p1: '标清',
  2988. p2: '高清',
  2989. p3: '超清',
  2990. p4: '原画质'
  2991. },
  2992.  
  2993. p1: {
  2994. json: [],
  2995. su: [],
  2996. clipsURL: [],
  2997. ip: '',
  2998. vid: 0,
  2999. reserveIp: [],
  3000. videos: [],
  3001. params: [],
  3002. },
  3003.  
  3004. p2: {
  3005. json: [],
  3006. su: [],
  3007. vid: 0,
  3008. clipsURL: [],
  3009. ip: '',
  3010. reserveIp: [],
  3011. videos: [],
  3012. params: [],
  3013. },
  3014.  
  3015. p3: {
  3016. json: [],
  3017. su: [],
  3018. clipsURL: [],
  3019. vid: 0,
  3020. ip: '',
  3021. reserveIp: [],
  3022. videos: [],
  3023. params: [],
  3024. },
  3025.  
  3026. p4: {
  3027. json: [],
  3028. su: [],
  3029. clipsURL: [],
  3030. vid: 0,
  3031. ip: '',
  3032. reserveIp: [],
  3033. videos: [],
  3034. params: [],
  3035. },
  3036.  
  3037. run: function() {
  3038. console.log('run() --');
  3039. this.router();
  3040. },
  3041.  
  3042. router: function() {
  3043. console.log('router() -- ');
  3044. var host = document.location.hostname;
  3045. if (host === 'my.tv.sohu.com') {
  3046. this.getUGCId();
  3047. } else if (host === 'tv.sohu.com') {
  3048. this.getId();
  3049. } else {
  3050. console.error('Error: this page is not supported');
  3051. }
  3052. },
  3053.  
  3054. /**
  3055. * Get video id for UGC video
  3056. */
  3057. getUGCId: function() {
  3058. console.log('getUGCId() -- ');
  3059. var scripts = document.querySelectorAll('script'),
  3060. script,
  3061. vidReg = /var vid\s+=\s+'(\d+)'/,
  3062. vidMatch,
  3063. titleReg = /,title:\s+'([^']+)'/,
  3064. titleMatch,
  3065. txt,
  3066. i;
  3067.  
  3068. for (i = 0; script = scripts[i]; i += 1) {
  3069. if (script.innerHTML.search('var vid') > -1) {
  3070. txt = script.innerHTML;
  3071. vidMatch = vidReg.exec(txt);
  3072. console.log('vidMatch: ', vidMatch);
  3073. if (vidMatch && vidMatch.length === 2) {
  3074. this.vid = vidMatch[1];
  3075. }
  3076. console.log('titleMatch: ', titleMatch);
  3077. titleMatch = titleReg.exec(txt);
  3078. if (titleMatch && titleMatch.length === 2) {
  3079. this.title = titleMatch[1];
  3080. }
  3081. break;
  3082. }
  3083. }
  3084. if (this.vid.length > 0) {
  3085. this.referer = escape(location.href);
  3086. this.p2.vid = this.vid;
  3087. this.getUGCVideoJSON('p2');
  3088. } else {
  3089. console.error('Error: failed to get video id!');
  3090. }
  3091. },
  3092.  
  3093. /**
  3094. * Get UGC video info
  3095. */
  3096. getUGCVideoJSON: function(fmt) {
  3097. console.log('getUGCVideoJSON() -- ');
  3098. var that = this,
  3099. url = 'http://my.tv.sohu.com/videinfo.jhtml?m=viewtv&vid=' + this.vid;
  3100.  
  3101. console.log('url: ', url);
  3102. GM_xmlhttpRequest({
  3103. method: 'GET',
  3104. url: url,
  3105. onload: function(response) {
  3106. var json = JSON.parse(response.responseText);
  3107.  
  3108. console.log('json: ', json);
  3109. that[fmt].json = json;
  3110. that[fmt].su = json.data.su;
  3111. that[fmt].clipsURL = json.data.clipsURL;
  3112.  
  3113. if (fmt === 'p2') {
  3114. if (json.data.norVid) {
  3115. that.p1.vid = json.data.norVid;
  3116. that.getUGCVideoJSON('p1');
  3117. }
  3118. if (json.data.superVid) {
  3119. that.p3.vid = json.data.superVid;
  3120. that.getUGCVideoJSON('p3');
  3121. }
  3122. if (json.data.oriVid) {
  3123. that.p4.vid = json.data.oriVid;
  3124. that.getUGCVideoJSON('p4');
  3125. }
  3126. }
  3127. that.decUGCVideo(fmt);
  3128. },
  3129. });
  3130. },
  3131.  
  3132. /**
  3133. * Decode UGC video url
  3134. */
  3135. decUGCVideo: function(fmt) {
  3136. console.log('decUGCVideo() -- ');
  3137. var url,
  3138. json = this[fmt].json,
  3139. i;
  3140.  
  3141. for (i = 0; i < json.data.clipsURL.length; i += 1) {
  3142. url = [
  3143. 'http://', json.allot, '/' ,
  3144. '?prot=', json.prot,
  3145. '&file=', json.data.clipsURL[i],
  3146. '&new=', json.data.su[i],
  3147. ].join('');
  3148. console.log('url: ', url);
  3149. this[fmt].videos.push('');
  3150. this.jobs += 1;
  3151. this.decUGCVideo2(fmt, url, i);
  3152. }
  3153. },
  3154.  
  3155. decUGCVideo2: function(fmt, url, i) {
  3156. console.log('decUGCVideo2() -- ');
  3157. var that = this;
  3158.  
  3159. GM_xmlhttpRequest({
  3160. method: 'GET',
  3161. url: url,
  3162. onload: function(response) {
  3163. console.log('response:', response);
  3164. var params = response.responseText.split('|');
  3165.  
  3166. that[fmt].params = params;
  3167. that[fmt].videos[i] = [
  3168. params[0],
  3169. that[fmt].su[i],
  3170. '?key=',
  3171. params[3],
  3172. ].join('');
  3173. that.jobs -= 1;
  3174. if (that.jobs === 0) {
  3175. that.createUI();
  3176. }
  3177. },
  3178. });
  3179. },
  3180.  
  3181. /**
  3182. * Get video id
  3183. */
  3184. getId: function() {
  3185. console.log('getId() --');
  3186. var scripts = document.querySelectorAll('script'),
  3187. script,
  3188. vidReg = /var vid="(\d+)";/,
  3189. vidMatch,
  3190. playlistReg = /var playlistId="(\d+)";/,
  3191. playlistMatch,
  3192. i;
  3193. for (i = 0; script = scripts[i]; i += 1) {
  3194. if (script.innerHTML.search('var vid') > -1) {
  3195. txt = script.innerHTML;
  3196. vidMatch = vidReg.exec(txt);
  3197. console.log('vidMatch: ', vidMatch);
  3198. if (vidMatch && vidMatch.length === 2) {
  3199. this.vid = vidMatch[1];
  3200. }
  3201. playlistMatch = playlistReg.exec(txt);
  3202. if (playlistMatch && playlistMatch.length === 2) {
  3203. this.plid = playlistMatch[1];
  3204. break;
  3205. }
  3206. }
  3207. }
  3208.  
  3209. if (this.vid) {
  3210. this.p2.vid = this.vid;
  3211. this.title = document.title.split('-')[0].trim();
  3212. this.referer = escape(location.href);
  3213. this.jobs += 1;
  3214. this.getVideoJSON('p2');
  3215. } else {
  3216. console.error('Failed to get vid!');
  3217. }
  3218. },
  3219.  
  3220. /**
  3221. * Get video info.
  3222. * e.g. http://hot.vrs.sohu.com/vrs_flash.action?vid=1109268&plid=5028903&referer=http%3A//tv.sohu.com/20130426/n374150509.shtml
  3223. */
  3224. getVideoJSON: function(fmt) {
  3225. console.log('getVideoJSON() --');
  3226. console.log('fmt: ', fmt);
  3227. var pref = 'http://hot.vrs.sohu.com/vrs_flash.action',
  3228. url = '',
  3229. that = this;
  3230.  
  3231. // If vid is unset, just return it.
  3232. if (this[fmt].vid === 0) {
  3233. return;
  3234. }
  3235.  
  3236. url = [
  3237. pref,
  3238. '?vid=', this[fmt].vid,
  3239. '&plid=', this.plid,
  3240. '&out=0',
  3241. '&g=8',
  3242. '&referer=', this.referer,
  3243. '&r=1',
  3244. ].join('');
  3245. console.log('url: ', url);
  3246.  
  3247. GM_xmlhttpRequest({
  3248. method: 'GET',
  3249. url: url,
  3250. onload: function(response) {
  3251. console.log('response: ', response);
  3252. var i = 0;
  3253.  
  3254. console.log(that);
  3255. that.jobs -= 1;
  3256. that[fmt].json = JSON.parse(response.responseText);
  3257. //that.title = that[fmt].json.data.tvName;
  3258. that[fmt].clipsURL = that[fmt].json.data.clipsURL;
  3259. that[fmt].su = that[fmt].json.data.su;
  3260. that.p1.vid = that[fmt].json.data.norVid;
  3261. that.p2.vid = that[fmt].json.data.highVid;
  3262. that.p3.vid = that[fmt].json.data.superVid;
  3263. that.p4.vid = that[fmt].json.data.oriVid;
  3264. that[fmt].ip = that[fmt].json.allot;
  3265. that[fmt].reserveIp = that[fmt].json.reserveIp.split(';');
  3266. for (i in that[fmt].clipsURL) {
  3267. url = [
  3268. 'http://', that[fmt].ip, '/',
  3269. '?prot=', that[fmt].json.prot,
  3270. '&file=', that[fmt].clipsURL[i],
  3271. '&new=', that[fmt].su[i],
  3272. ].join('');
  3273. that.jobs += 1;
  3274. that[fmt].videos.push('');
  3275. that.getRealUrl(fmt, url, that[fmt].su[i], i);
  3276. }
  3277.  
  3278. if (fmt === 'p2') {
  3279. if (that.p1.vid > 0) {
  3280. that.jobs += 1;
  3281. that.getVideoJSON('p1');
  3282. }
  3283. if (that.p3.vid > 0) {
  3284. that.jobs += 1;
  3285. that.getVideoJSON('p3');
  3286. }
  3287. if (that.p4.vid > 0) {
  3288. that.jobs += 1;
  3289. that.getVideoJSON('p4');
  3290. }
  3291. }
  3292.  
  3293. },
  3294. });
  3295. },
  3296.  
  3297. /**
  3298. * Get final url from content like this:
  3299. * http://61.54.26.168/sohu/s26h23eab6/6/|145|182.112.229.55|G69w1ucoqFNj8ww74DxHN-6ZQTkgJZ90FlnqBA..|1|0|2|1518|1
  3300. */
  3301. getRealUrl: function(fmt, url, new_, i) {
  3302. console.log('getRealUrl() --', fmt, url, new_, i);
  3303. var that = this;
  3304.  
  3305. GM_xmlhttpRequest({
  3306. method: 'GET',
  3307. url: url,
  3308. onload: function(response) {
  3309. var txt = response.responseText,
  3310. params = txt.split('|'),
  3311. start = params[0],
  3312. key = params[3],
  3313. url = [
  3314. start.substr(0, start.length-1), new_,
  3315. '?key=', key,
  3316. ].join('');
  3317.  
  3318. that[fmt].videos[i] = url;
  3319. that.jobs -= 1;
  3320.  
  3321. // Display UI when all processes ended
  3322. if (that.jobs === 0) {
  3323. that.createUI();
  3324. }
  3325. },
  3326. });
  3327. },
  3328.  
  3329. /**
  3330. * Construct UI widgets
  3331. */
  3332. createUI: function() {
  3333. console.log('createUI() --');
  3334. console.log(this);
  3335. var videos = {
  3336. title: this.title,
  3337. links: [],
  3338. formats: [],
  3339. },
  3340. type,
  3341. i;
  3342.  
  3343. for (type in this.formats) {
  3344. console.log('type: ', type);
  3345. if (this[type].videos.length > 0) {
  3346. videos.links.push(this[type].videos);
  3347. videos.formats.push(this.formats[type]);
  3348. }
  3349. }
  3350. if (videos.formats.length > 0) {
  3351. multiFiles.run(videos);
  3352. }
  3353. },
  3354. };
  3355.  
  3356. monkey.extend('tv.sohu.com', [
  3357. 'http://tv.sohu.com/',
  3358. ], monkey_sohu);
  3359.  
  3360.  
  3361. /**
  3362. * tucao.cc
  3363. */
  3364. var monkey_tucao = {
  3365.  
  3366. url: '',
  3367. title: '',
  3368. playerId: '',
  3369. key: '',
  3370. timestamp: '',
  3371. vid: '',
  3372. type: '',
  3373. vids: [],
  3374. pos: 0,
  3375. videos: [],
  3376. format: '标清',
  3377. redirect: false,
  3378. types: {
  3379. sina: 'sina',
  3380. tudou: false, // redirect to original url
  3381. youku: false, // redirect to original url
  3382. },
  3383.  
  3384. run: function() {
  3385. console.log('run()');
  3386. this.getVid();
  3387. },
  3388.  
  3389. /**
  3390. * Get video id
  3391. */
  3392. getVid: function() {
  3393. console.log('getVid() -- ');
  3394. var playerCode = document.querySelectorAll(
  3395. 'ul#player_code li');
  3396.  
  3397. if (playerCode && playerCode.length === 2) {
  3398. this.vids = playerCode[0].firstChild.nodeValue.split('**');
  3399. if (this.vids[this.vids.length - 1] == '') {
  3400. // remove empty vid
  3401. this.vids.pop();
  3402. }
  3403. this.playerId = playerCode[1].innerHTML;
  3404. this.getTitle();
  3405. }
  3406. },
  3407.  
  3408. /**
  3409. * Get video title
  3410. */
  3411. getTitle: function() {
  3412. console.log('getTitle()');
  3413. var params;
  3414.  
  3415. if (this.vids.length === 1 || location.hash === '') {
  3416. this.pos = 0;
  3417. this.url = location.href;
  3418. } else {
  3419. // hash starts with 1, not 0
  3420. this.pos = parseInt(location.hash.replace('#', '')) - 1;
  3421. this.url = location.href.replace(location.hash, '');
  3422. }
  3423. params = getQueryVariable(this.vids[this.pos].split('|')[0]);
  3424. this.vid = params.vid;
  3425. this.type = params.type;
  3426. if (this.vids.length === 1) {
  3427. this.title = document.title.substr(0, document.title.length - 16);
  3428. } else {
  3429. this.title = this.vids[this.pos].split('|')[1];
  3430. }
  3431. this.getUrl();
  3432. },
  3433.  
  3434. /**
  3435. * Get original url
  3436. */
  3437. getUrl: function(type) {
  3438. console.log('getUrl()');
  3439. var url,
  3440. params,
  3441. that = this;
  3442.  
  3443. if (this.types[this.type] === false) {
  3444. this.redirectTo();
  3445. return;
  3446. }
  3447.  
  3448. this.calcKey();
  3449. url = [
  3450. 'http://www.tucao.cc/api/playurl.php',
  3451. '?type=',
  3452. this.type,
  3453. '&vid=',
  3454. this.vid,
  3455. '&key=', this.key,
  3456. '&r=', this.timestamp
  3457. ].join('');
  3458.  
  3459. console.log('url: ', url);
  3460. GM_xmlhttpRequest({
  3461. method: 'GET',
  3462. url: url,
  3463. onload: function(response) {
  3464. console.log(response);
  3465. var xml = parseXML(response.responseText),
  3466. durls = xml.querySelectorAll('durl'),
  3467. durl,
  3468. url,
  3469. i;
  3470.  
  3471. for (i = 0; durl = durls[i]; i += 1) {
  3472. url = durl.querySelector('url');
  3473. that.videos.push(
  3474. url.innerHTML.replace('<![CDATA[', '').replace(']]>', ''));
  3475. }
  3476.  
  3477. that.createUI();
  3478. },
  3479. });
  3480. },
  3481.  
  3482. /**
  3483. * 计算这个请求的授权key.
  3484. * 算法来自于: http://www.cnbeining.com/2014/05/serious-businesstucao-cc-c-video-resolution/
  3485. * @return [key, timestamp]
  3486. */
  3487. calcKey: function() {
  3488. console.log('calcKey () --');
  3489. var time = new Date().getTime();
  3490.  
  3491. this.timestamp = Math.round(time / 1000);
  3492. var local3 = this.timestamp ^ 2774181285;
  3493. var local4 = parseInt(this.vid, 10);
  3494. var local5 = local3 + local4;
  3495. local5 = (local5 < 0) ? (-(local5) >> 0) : (local5 >> 0);
  3496. this.key = 'tucao' + local5.toString(16) + '.cc';
  3497. },
  3498.  
  3499. /**
  3500. * Redirect to original url
  3501. */
  3502. redirectTo: function() {
  3503. console.log('redirectTo() --');
  3504. console.log(this);
  3505. var urls = {
  3506. tudou: function(vid) {
  3507. return 'http://www.tudou.com/programs/view/' + vid + '/';
  3508. },
  3509. youku: function(vid) {
  3510. return 'http://v.youku.com/v_show/id_' + vid + '.html';
  3511. },
  3512. };
  3513.  
  3514. this.redirect = true;
  3515. this.videos.push(urls[this.type](this.vid));
  3516. this.formats.push('原始地址');
  3517. this.createUI();
  3518. },
  3519.  
  3520. /**
  3521. * Construct ui widgets
  3522. */
  3523. createUI: function() {
  3524. console.log('createUI() -- ');
  3525. console.log(this);
  3526. var videos = {
  3527. title: this.title,
  3528. formats: [],
  3529. links: [],
  3530. ok: true,
  3531. msg: '',
  3532. },
  3533. video,
  3534. i;
  3535.  
  3536. videos.links.push(this.videos);
  3537. videos.formats.push(this.format);
  3538. multiFiles.run(videos);
  3539. },
  3540.  
  3541. }
  3542.  
  3543.  
  3544. monkey.extend('www.tucao.cc', [
  3545. 'http://www.tucao.cc/play/',
  3546. ], monkey_tucao);
  3547.  
  3548. /**
  3549. * tudou.com
  3550. */
  3551. var monkey_tudou = {
  3552.  
  3553. url:'', // document.location.href
  3554. title: '',
  3555. iid: '',
  3556. vcode: '',
  3557. segs: {},
  3558. totalJobs: 0,
  3559. formats: {
  3560. 2: '240P', // 流畅
  3561. 3: '360P', // 清晰
  3562. 4: '480P', // 高清
  3563. 5: '720P', // 超清
  3564. 52: '240P(mp4)',
  3565. 53: '360P(mp4)',
  3566. 54: '480P(mp4)',
  3567. 99: '原画质' // 原画质
  3568. },
  3569. links: {
  3570. },
  3571.  
  3572. run: function() {
  3573. console.log('run() --');
  3574. this.router();
  3575. },
  3576.  
  3577. /**
  3578. * Page router control
  3579. */
  3580. router: function() {
  3581. console.log('router() --');
  3582. var scripts = document.querySelectorAll('script'),
  3583. script,
  3584. titleReg = /kw:\s*['"]([^'"]+)['"]/,
  3585. titleMatch,
  3586. iidReg = /iid\s*[:=]\s*(\d+)/,
  3587. iidMatch,
  3588. vcodeReg = /vcode: '([^']+)'/,
  3589. vcodeMatch,
  3590. i;
  3591.  
  3592. for (i = 0; script = scripts[i]; i += 1) {
  3593. if (this.vcode.length === 0) {
  3594. vcodeMatch = vcodeReg.exec(script.innerHTML);
  3595. console.log('vcodeMatch:', vcodeMatch);
  3596. if (vcodeMatch && vcodeMatch.length > 1) {
  3597. this.vcode = vcodeMatch[1];
  3598. this.redirectToYouku();
  3599. return;
  3600. }
  3601. }
  3602.  
  3603. if (this.title.length === 0) {
  3604. titleMatch = titleReg.exec(script.innerHTML);
  3605. console.log('titleMatch:', titleMatch);
  3606. if (titleMatch) {
  3607. this.title = titleMatch[1];
  3608. }
  3609. }
  3610.  
  3611. if (this.iid.length === 0) {
  3612. iidMatch = iidReg.exec(script.innerHTML);
  3613. console.log('iidMatch:', iidMatch);
  3614. if (iidMatch) {
  3615. this.iid = iidMatch[1];
  3616. this.getByIid();
  3617. return;
  3618. }
  3619. }
  3620. }
  3621. //this.getPlayList();
  3622. },
  3623.  
  3624. /**
  3625. * Get video info by vid
  3626. */
  3627. getByIid: function() {
  3628. console.log('getByIid()');
  3629. console.log(this);
  3630.  
  3631. var that = this,
  3632. url = 'http://www.tudou.com/outplay/goto/getItemSegs.action?iid=' +
  3633. this.iid;
  3634.  
  3635. GM_xmlhttpRequest({
  3636. method: 'GET',
  3637. url: url,
  3638. onload: function(response) {
  3639. console.log('respone:', response);
  3640. that.segs = JSON.parse(response.responseText);
  3641. that.getAllVideos();
  3642. },
  3643. });
  3644. },
  3645.  
  3646. // getPlayList: function() {
  3647. // console.log('getPlayList()');
  3648. // console.log(this);
  3649. // },
  3650.  
  3651. /**
  3652. * Get all video links
  3653. */
  3654. getAllVideos: function() {
  3655. console.log('getAllVideos() --');
  3656. console.log(this);
  3657. var key,
  3658. videos,
  3659. video,
  3660. i;
  3661.  
  3662. for (key in this.segs) {
  3663. videos = this.segs[key];
  3664. for (i = 0; video = videos[i]; i += 1) {
  3665. console.log(key, video);
  3666. this.links[key] = [];
  3667. this.totalJobs += 1;
  3668. this.getVideoUrl(key, video['k'], video['no']);
  3669. }
  3670. }
  3671. },
  3672.  
  3673.  
  3674. /**
  3675. * Get video url
  3676. */
  3677. getVideoUrl: function(key, k, num) {
  3678. console.log('getVideoUrl() --');
  3679. var url = 'http://ct.v2.tudou.com/f?id=' + k,
  3680. that = this;
  3681.  
  3682. GM_xmlhttpRequest({
  3683. method: 'GET',
  3684. url: url,
  3685. onload: function(response) {
  3686. console.log('response:', response);
  3687. var reg = /<f[^>]+>([^<]+)</,
  3688. match = reg.exec(response.responseText);
  3689.  
  3690. if (match && match.length > 1) {
  3691. that.links[key][num] = match[1];
  3692. that.totalJobs -= 1;
  3693. if (that.totalJobs === 0) {
  3694. that.createUI();
  3695. }
  3696. }
  3697. },
  3698. });
  3699. },
  3700.  
  3701. /**
  3702. * Redirect url to youku.com.
  3703. * Because tudou.com use youku.com as video source on /albumplay/ page.
  3704. */
  3705. redirectToYouku: function() {
  3706. var url = 'http://v.youku.com/v_show/id_' + this.vcode + '.html';
  3707. console.log('redirectToYouku:', url);
  3708. this.createUI();
  3709. },
  3710.  
  3711. /**
  3712. * Construct UI widgets
  3713. */
  3714. createUI: function() {
  3715. console.log('createUI()');
  3716. console.log(this);
  3717. var videos = {
  3718. title: this.title,
  3719. formats: [],
  3720. links: [],
  3721. ok: true,
  3722. msg: '',
  3723. },
  3724. type,
  3725. i;
  3726.  
  3727. if (this.vcode.length > 0) {
  3728. videos.title = '原始地址';
  3729. videos.links.push('http://v.youku.com/v_show/id_' + this.vcode + '.html');
  3730. videos.formats.push('');
  3731. singleFile.run(videos);
  3732. } else {
  3733. for (type in this.links) {
  3734. videos.links.push(this.links[type]);
  3735. videos.formats.push(this.formats[type]);
  3736. }
  3737. console.log('videos: ', videos);
  3738. multiFiles.run(videos);
  3739. }
  3740. },
  3741.  
  3742. };
  3743.  
  3744. monkey.extend('www.tudou.com', [
  3745. 'http://www.tudou.com/albumplay/',
  3746. 'http://www.tudou.com/listplay/',
  3747. 'http://www.tudou.com/programs/view/',
  3748. ], monkey_tudou);
  3749.  
  3750. /**
  3751. * wasu.cn
  3752. */
  3753. var monkey_wasu = {
  3754.  
  3755. id: '',
  3756. key: '',
  3757. url: '',
  3758. title: '',
  3759. link: '',
  3760. format: '高清',
  3761.  
  3762. run: function() {
  3763. console.log('run() --');
  3764. this.getTitle();
  3765. },
  3766.  
  3767. getTitle: function() {
  3768. console.log('getTitle() --');
  3769. var h3 = document.querySelector('div.play_movie div.play_site div.l h3');
  3770. if (h3) {
  3771. this.title = h3.innerHTML;
  3772. } else {
  3773. this.title = document.title.replace(
  3774. '高清电影全集在线观看-正版高清电影-华数TV', '').replace(
  3775. ' 正版高清电影', '');
  3776. }
  3777. this.getVid();
  3778. },
  3779.  
  3780. /**
  3781. * Get video id
  3782. */
  3783. getVid: function() {
  3784. console.log('getVid()--');
  3785. var reg = /show\/id\/(\d+)/,
  3786. match = reg.exec(location.href),
  3787. url,
  3788. that = this;
  3789.  
  3790. if (!match || match.length !== 2) {
  3791. console.error('Failed to get vid!');
  3792. return
  3793. }
  3794. this.vid = match[1];
  3795. url = 'http://www.wasu.cn/wap/play/show/id/' + this.vid,
  3796.  
  3797. console.log('url: ', url);
  3798. GM_xmlhttpRequest({
  3799. method: 'GET',
  3800. url: url,
  3801. onload: function(response) {
  3802. var txt = response.responseText,
  3803. keyReg = /'key'\s*:\s*'([^']+)'/,
  3804. urlReg = /'url'\s*:\s*'([^']+)'/,
  3805. keyMatch,
  3806. urlMatch;
  3807.  
  3808. keyMatch = keyReg.exec(txt);
  3809. if (! keyMatch || keyMatch.length !== 2) {
  3810. console.error('Failed to get key: ', keyMatch);
  3811. return;
  3812. }
  3813. that.key = keyMatch[1];
  3814. urlMatch = urlReg.exec(txt);
  3815. that.url = urlMatch[1];
  3816. that.getVideoInfo();
  3817. },
  3818. });
  3819. },
  3820.  
  3821. /**
  3822. * Get video information
  3823. */
  3824. getVideoInfo: function() {
  3825. console.log('getVideoInfo() --');
  3826. var url = [
  3827. 'http://www.wasu.cn/wap/Api/getVideoUrl/id/', this.vid,
  3828. '/key/', this.key,
  3829. '/url/', this.url,
  3830. '/type/txt',
  3831. ].join(''),
  3832. that = this;
  3833.  
  3834. console.log('video info link: ', url);
  3835. GM_xmlhttpRequest({
  3836. method: 'GET',
  3837. url: url,
  3838. onload: function(response) {
  3839. that.link = response.responseText;
  3840. that.createUI();
  3841. },
  3842. });
  3843. },
  3844.  
  3845. createUI: function() {
  3846. console.log('createUI() --');
  3847. console.log(this);
  3848. var videos = {
  3849. title: this.title,
  3850. formats: [],
  3851. links: [],
  3852. ok: true,
  3853. msg: '',
  3854. };
  3855.  
  3856. if (this.link.length === 0) {
  3857. videos.ok = false;
  3858. videos.msg = 'Failed to get video link';
  3859. } else {
  3860. videos.formats.push(this.format);
  3861. videos.links.push([this.link]);
  3862. }
  3863. multiFiles.run(videos);
  3864. },
  3865.  
  3866. };
  3867.  
  3868. monkey.extend('www.wasu.cn', [
  3869. 'http://www.wasu.cn/Play/show/id/',
  3870. 'http://www.wasu.cn/play/show/id/',
  3871. 'http://www.wasu.cn/wap/Play/show/id/',
  3872. 'http://www.wasu.cn/wap/play/show/id/',
  3873. ], monkey_wasu);
  3874.  
  3875.  
  3876. /**
  3877. * weiqitv.com
  3878. */
  3879. var monkey_weiqitv = {
  3880. sid: '',
  3881. vid: '',
  3882. title: '',
  3883. videos: {},
  3884. formats: {
  3885. 'default': '标清flv', // 640x360
  3886. '2': '高清flv', // 960x540
  3887. '3': '超清flv', // 1280x720
  3888. '4': '高清mp4', // 850x480
  3889. '5': '超清mp4', // 1280x720
  3890. },
  3891.  
  3892. run: function() {
  3893. console.log('run() -- ');
  3894. this.title = document.title.replace('围棋TV - ', '');
  3895. this.getVid();
  3896. },
  3897.  
  3898. getVid: function() {
  3899. console.log('getVid() --');
  3900. var vidReg = /vid:(\d+),/,
  3901. vidMatch,
  3902. sidReg = /sid:(\d+)\s*/,
  3903. sidMatch,
  3904. scripts = document.querySelectorAll('script'),
  3905. script,
  3906. i;
  3907.  
  3908. for (i = 0; i < scripts.length; i += 1) {
  3909. script = scripts[i];
  3910. vidMatch = vidReg.exec(script.innerHTML);
  3911. if (vidMatch && vidMatch.length === 2) {
  3912. this.vid = vidMatch[1];
  3913. sidMatch = sidReg.exec(script.innerHTML);
  3914. this.sid = sidMatch[1];
  3915. break;
  3916. }
  3917. }
  3918. if (this.vid.length === 0) {
  3919. console.error('Failed to get vid!');
  3920. } else {
  3921. this.getVideoInfo();
  3922. }
  3923. },
  3924.  
  3925. getVideoInfo: function() {
  3926. var that = this,
  3927. url = [
  3928. 'http://www.yunsp.com.cn:8080/dispatch/videoPlay/getInfo?',
  3929. 'vid=', this.vid,
  3930. '&sid=', this.sid,
  3931. '&isList=0&ecode=notexist',
  3932. ].join('');
  3933.  
  3934. console.log('url: ', url);
  3935. GM_xmlhttpRequest({
  3936. method: 'GET',
  3937. url: url,
  3938. onload: function(response) {
  3939. var json = JSON.parse(response.responseText),
  3940. videoInfo = json[0].videoInfo,
  3941. format;
  3942.  
  3943. for (format in that.formats) {
  3944. if (format in videoInfo) {
  3945. that.videos[format] = videoInfo[format].url;
  3946. }
  3947. }
  3948. that.createUI();
  3949. },
  3950. });
  3951. },
  3952.  
  3953. /**
  3954. * construct ui widgets.
  3955. */
  3956. createUI: function() {
  3957. console.log('createUI() --');
  3958. console.log(this);
  3959. var videos = {
  3960. title: this.title,
  3961. formats: [],
  3962. links: [],
  3963. ok: true,
  3964. msg: '',
  3965. },
  3966. types = ['default', '4', '2', '5', '3'],
  3967. type,
  3968. url,
  3969. i;
  3970. for (i = 0; type = types[i]; i += 1) {
  3971. url = this.videos[type];
  3972. if (url && url.length > 0) {
  3973. videos.links.push([url]);
  3974. videos.formats.push(this.formats[type]);
  3975. }
  3976. }
  3977.  
  3978. multiFiles.run(videos);
  3979. },
  3980. };
  3981.  
  3982. monkey.extend('www.weiqitv.com', [
  3983. 'http://www.weiqitv.com/index/live_back?videoId=',
  3984. 'http://www.weiqitv.com/index/video_play?videoId=',
  3985. ], monkey_weiqitv);
  3986.  
  3987. /**
  3988. * youku.com
  3989. */
  3990. var monkey_youku = {
  3991. // store xhr result, with json format
  3992. rs: null,
  3993. brs: null,
  3994.  
  3995. // store video formats and its urls
  3996. data: null,
  3997. title: '',
  3998. videoId: '',
  3999.  
  4000. run: function() {
  4001. console.log('run() --');
  4002. this.getVideoId();
  4003. },
  4004.  
  4005. /**
  4006. * Get video id, and stored in yk.videoId.
  4007. *
  4008. * Page url for playing page almost like this:
  4009. * http://v.youku.com/v_show/id_XMjY1OTk1ODY0.html
  4010. * http://v.youku.com/v_playlist/f17273995o1p0.html
  4011. */
  4012. getVideoId: function() {
  4013. console.log('getVideoId() --');
  4014. var url = location.href,
  4015. idReg = /(?:id_)(.*)(?:.html)/,
  4016. idMatch = idReg.exec(url),
  4017. idReg2 = /(?:v_playlist\/f)(.*)(?:o1p\d.html)/,
  4018. idMatch2 = idReg2.exec(url);
  4019.  
  4020. console.log('idMatch: ', idMatch);
  4021. console.log('idMatch2: ', idMatch2);
  4022. if (idMatch && idMatch.length === 2) {
  4023. this.videoId = idMatch[1];
  4024. this.getPlayListMeta();
  4025. } else if (idMatch2 && idMatch2.length === 2) {
  4026. this.videoId = idMatch2[1];
  4027. this.getPlayListMeta();
  4028. } else {
  4029. error('Failed to get video id!');
  4030. }
  4031. },
  4032.  
  4033. /**
  4034. * Get metadata of video playlist
  4035. */
  4036. getPlayListMeta: function() {
  4037. console.log('getPlaylistMeta() --');
  4038. var url = 'http://v.youku.com/player/getPlayList/VideoIDS/' + this.videoId,
  4039. url2 = url + '/Pf/4/ctype/12/ev/1',
  4040. that = this;
  4041.  
  4042. console.log('url2:', url2);
  4043. GM_xmlhttpRequest({
  4044. method: 'GET',
  4045. url: url2,
  4046. onload: function(response) {
  4047. var json = JSON.parse(response.responseText);
  4048. if (json.data.length === 0) {
  4049. console.error('Error: video not found!');
  4050. return;
  4051. }
  4052. that.rs = json.data[0];
  4053. that.title = that.rs.title;
  4054. that.parseVideo();
  4055. }
  4056. });
  4057.  
  4058. GM_xmlhttpRequest({
  4059. method: 'GET',
  4060. url: url,
  4061. onload: function(response) {
  4062. var json = JSON.parse(response.responseText);
  4063. if (json.data.length === 0) {
  4064. console.error('Error: video not found!');
  4065. return;
  4066. }
  4067. that.brs = json.data[0];
  4068. that.parseVideo();
  4069. }
  4070. });
  4071. },
  4072.  
  4073. parseVideo: function() {
  4074. console.log('parseVideo() --');
  4075. if (! this.rs || ! this.brs) {
  4076. return;
  4077. }
  4078.  
  4079. var streamtypes = this.rs.streamtypes,
  4080. streamfileids = this.rs.streamfileids,
  4081. data = {},
  4082. seed = this.rs.seed,
  4083. segs = this.rs.segs,
  4084. key,
  4085. value,
  4086. k,
  4087. v,
  4088. ip = this.rs.ip,
  4089. bsegs = this.brs.segs,
  4090. sid,
  4091. token,
  4092. i,
  4093. k,
  4094. number,
  4095. fileId0,
  4096. fileId,
  4097. ep,
  4098. pass1 = 'becaf9be',
  4099. pass2 = 'bf7e5f01',
  4100. typeArray = {
  4101. 'flv': 'flv', 'mp4': 'mp4', 'hd2': 'flv', '3gphd': 'mp4',
  4102. '3gp': 'flv', 'hd3': 'flv'
  4103. },
  4104. sharpness = {
  4105. 'flv': 'normal', 'flvhd': 'normal', 'mp4': 'high',
  4106. 'hd2': 'super', '3gphd': 'high', '3g': 'normal',
  4107. 'hd3': 'original'
  4108. },
  4109. filetype;
  4110.  
  4111. [sid, token] = this.yk_e(pass1, this.yk_na(this.rs.ep)).split('_');
  4112. for (key in segs) {
  4113. value = segs[key];
  4114. if (streamtypes.indexOf(key) > -1) {
  4115. for (i in value) {
  4116. v = value[i];
  4117. number = parseInt(v.no, 10).toString(16).toUpperCase();
  4118. if (number.length === 1) {
  4119. number = '0'.concat(number);
  4120. }
  4121. // 构建视频地址K值
  4122. k = v.k;
  4123. if (!k || k === -1) {
  4124. console.log(bsegs, bsegs[key], bsegs[key][i]);
  4125. k = bsegs[key][i]['k'];
  4126. }
  4127. fileId0 = this.getFileId(streamfileids[key], seed);
  4128. fileId = fileId0.substr(0, 8) + number + fileId0.substr(10);
  4129. ep = encodeURIComponent(this.yk_d(
  4130. this.yk_e(pass2, [sid, fileId, token].join('_'))));
  4131.  
  4132. // 判断后缀类型, 获得后缀
  4133. filetype = typeArray[key];
  4134. data[key] = data[key] || [];
  4135. data[key].push([
  4136. 'http://k.youku.com/player/getFlvPath/sid/', sid,
  4137. '_00/st/', filetype,
  4138. '/fileid/', fileId,
  4139. '?K=', k,
  4140. '&ctype=12&ev=1&token=', token,
  4141. '&oip=', ip,
  4142. '&ep=', ep,
  4143. ].join(''));
  4144. }
  4145. }
  4146. }
  4147. this.data = data;
  4148. this.createUI();
  4149. },
  4150.  
  4151. /**
  4152. * Get file id of each video file.
  4153. *
  4154. * @param string seed
  4155. * - the file seed number.
  4156. * @param string fileId
  4157. * - file Id.
  4158. * @return string
  4159. * - return decrypted file id.
  4160. */
  4161. getFileId: function(fileId, seed) {
  4162. console.log('getFileId() --');
  4163. function getFileIdMixed(seed) {
  4164. var mixed = [],
  4165. source = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP' +
  4166. 'QRSTUVWXYZ/\\:._-1234567890',
  4167. len = source.length,
  4168. index,
  4169. i;
  4170. for (i = 0; i < len; i += 1) {
  4171. seed = (seed * 211 + 30031) % 65536;
  4172. index = Math.floor(seed / 65536 * source.length);
  4173. mixed.push(source.charAt(index));
  4174. source = source.replace(source.charAt(index), '');
  4175. }
  4176. return mixed;
  4177. }
  4178.  
  4179. var mixed = getFileIdMixed(seed),
  4180. ids = fileId.split('\*'),
  4181. len = ids.length - 1,
  4182. realId = '',
  4183. idx,
  4184. i;
  4185.  
  4186. for (i = 0; i < len; i += 1) {
  4187. idx = parseInt(ids[i]);
  4188. realId += mixed[idx];
  4189. }
  4190. return realId;
  4191. },
  4192.  
  4193. /**
  4194. * Timestamp
  4195. */
  4196. getSid: function() {
  4197. return String((new Date()).getTime()) + '01';
  4198. },
  4199.  
  4200. /**
  4201. * Decryption
  4202. */
  4203. yk_d: function(s) {
  4204. var len = s.length,
  4205. i = 0,
  4206. result = [],
  4207. e = 0,
  4208. g = 0,
  4209. h,
  4210. chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  4211.  
  4212. if (len === 0) {
  4213. return '';
  4214. }
  4215.  
  4216. while (i < len) {
  4217. e = s.charCodeAt(i) & 255;
  4218. i = i + 1;
  4219. if (i === len) {
  4220. result.push(chars.charAt(e >> 2));
  4221. result.push(chars.charAt((e & 3) << 4));
  4222. result.push('==');
  4223. break
  4224. }
  4225. g = s.charCodeAt(i);
  4226. i = i + 1;
  4227. if (i === len) {
  4228. result.push(chars.charAt(e >> 2));
  4229. result.push(chars.charAt((e & 3) << 4 | (g & 240) >> 4));
  4230. result.push(chars.charAt((g & 15) << 2));
  4231. result.push('=');
  4232. break
  4233. }
  4234. h = s.charCodeAt(i);
  4235. i = i + 1;
  4236. result.push(chars.charAt(e >> 2));
  4237. result.push(chars.charAt((e & 3) << 4 | (g & 240) >> 4));
  4238. result.push(chars.charAt((g & 15) << 2 | (h & 192) >> 6));
  4239. result.push(chars.charAt(h & 63));
  4240. }
  4241. return result.join('');
  4242. },
  4243.  
  4244. yk_na: function(a) {
  4245. if (! a) {
  4246. return '';
  4247. }
  4248.  
  4249. 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],
  4250. i = a.length,
  4251. e = [],
  4252. f = 0,
  4253. b,
  4254. c;
  4255.  
  4256. while (f < i) {
  4257. do {
  4258. c = h[a.charCodeAt(f++) & 255];
  4259. } while (f < i && c === -1);
  4260. if (c === -1) {
  4261. break;
  4262. }
  4263.  
  4264. do {
  4265. b = h[a.charCodeAt(f++) & 255];
  4266. } while (f < i && b === -1);
  4267. if (b === -1) {
  4268. break;
  4269. }
  4270. e.push(String.fromCharCode(c << 2 | (b & 48) >> 4));
  4271.  
  4272. do {
  4273. c = a.charCodeAt(f++) & 255;
  4274. if (c === 61) {
  4275. return e.join('');
  4276. }
  4277. c = h[c];
  4278. } while (f < i && c === -1);
  4279. if (c === -1) {
  4280. break;
  4281. }
  4282. e.push(String.fromCharCode((b & 15) << 4 | (c & 60) >> 2));
  4283.  
  4284. do {
  4285. b = a.charCodeAt(f) & 255;
  4286. f = f + 1;
  4287. if (b === 61) {
  4288. return e.join('');
  4289. }
  4290. b = h[b];
  4291. } while (f < i && b === -1);
  4292. if (b === -1) {
  4293. break;
  4294. }
  4295. e.push(String.fromCharCode((c & 3) << 6 | b));
  4296. }
  4297.  
  4298. return e.join('');
  4299. },
  4300.  
  4301. yk_e: function(a, c) {
  4302. var f = 0,
  4303. i = '',
  4304. e = [],
  4305. q = 0,
  4306. h = 0,
  4307. b = {};
  4308. for (h = 0; h < 256; h = h + 1) {
  4309. b[h] = h;
  4310. }
  4311. for (h = 0; h < 256; h = h + 1) {
  4312. f = ((f + b[h]) + a.charCodeAt(h % a.length)) % 256;
  4313. i = b[h];
  4314. b[h] = b[f];
  4315. b[f] = i;
  4316. }
  4317. for (q = 0, f = 0, h = 0; q < c.length; q = q + 1) {
  4318. h = (h + 1) % 256;
  4319. f = (f + b[h]) % 256;
  4320. i = b[h];
  4321. b[h] = b[f];
  4322. b[f] = i;
  4323. e.push(String.fromCharCode(c.charCodeAt(q) ^ b[(b[h] + b[f]) % 256]));
  4324. }
  4325. return e.join('');
  4326. },
  4327.  
  4328. /**
  4329. * construct video data and create UI widgets.
  4330. */
  4331. createUI: function() {
  4332. console.log('createUI() --');
  4333. console.log(this);
  4334. var videos = {
  4335. title: this.title,
  4336. formats: [],
  4337. links: [],
  4338. },
  4339. types = {
  4340. '3gp': '3G',
  4341. '3gphd': '3G高清',
  4342. flv: '标清',
  4343. flvhd: '高清Flv',
  4344. mp4: '高清',
  4345. hd2: '超清',
  4346. hd3: '1080P',
  4347. },
  4348. type;
  4349.  
  4350. for(type in types) {
  4351. if (type in this.data) {
  4352. videos.formats.push(types[type]);
  4353. videos.links.push(this.data[type]);
  4354. }
  4355. }
  4356.  
  4357. multiFiles.run(videos);
  4358. },
  4359. };
  4360.  
  4361. monkey.extend('v.youku.com', [
  4362. 'http://v.youku.com/v_show/id_',
  4363. 'http://v.youku.com/v_playlist/',
  4364. ], monkey_youku);
  4365.  
  4366.  
  4367. /**
  4368. * youtube.com
  4369. */
  4370. var monkey_youtube = {
  4371. videoId: '',
  4372. videoInfoUrl: null,
  4373. videoTitle: '',
  4374. stream: null,
  4375. adaptive_fmts: null,
  4376. urlInfo: false,
  4377.  
  4378. // format list comes from https://github.com/rg3/youtube-dl
  4379. formats: {
  4380. '5': {ext: 'flv', width: 400, height: 240, resolution: '240p'},
  4381. '6': {ext: 'flv', width: 450, height: 270, resolution: '270p'},
  4382. '13': {ext: '3gp', resolution: 'unknown'},
  4383. '17': {ext: '3gp', width: 176, height: 144, resolution: '144p'},
  4384. '18': {ext: 'mp4', width: 640, height: 360, resolution: '360p'},
  4385. '22': {ext: 'mp4', width: 1280, height: 720, resolution: '720p'},
  4386. '34': {ext: 'flv', width: 640, height: 360, resolution: '360p'},
  4387. '35': {ext: 'flv', width: 854, height: 480, resolution: '720p'},
  4388. '36': {ext: '3gp', width: 320, height: 240, resolution: '240p'},
  4389. '37': {ext: 'mp4', width: 1920, height: 1080, resolution: '1080p'},
  4390. '38': {ext: 'mp4', width: 4096, height: 3072, resolution: '4k'},
  4391. '43': {ext: 'webm', width: 640, height: 360, resolution: '360p'},
  4392. '44': {ext: 'webm', width: 854, height: 480, resolution: '480p'},
  4393. '45': {ext: 'webm', width: 1280, height: 720, resolution: '720p'},
  4394. '46': {ext: 'webm', width: 1920, height: 1080, resolution: '1080p'},
  4395.  
  4396.  
  4397. // 3d videos
  4398. '82': {'ext': 'mp4', 'height': 360, 'resolution': '360p', 'format_note': '3D', 'preference': -20},
  4399. '83': {'ext': 'mp4', 'height': 480, 'resolution': '480p', 'format_note': '3D', 'preference': -20},
  4400. '84': {'ext': 'mp4', 'height': 720, 'resolution': '720p', 'format_note': '3D', 'preference': -20},
  4401. '85': {'ext': 'mp4', 'height': 1080, 'resolution': '1080p', 'format_note': '3D', 'preference': -20},
  4402. '100': {'ext': 'webm', 'height': 360, 'resolution': '360p', 'format_note': '3D', 'preference': -20},
  4403. '101': {'ext': 'webm', 'height': 480, 'resolution': '480p', 'format_note': '3D', 'preference': -20},
  4404. '102': {'ext': 'webm', 'height': 720, 'resolution': '720p', 'format_note': '3D', 'preference': -20},
  4405.  
  4406. // Apple HTTP Live Streaming
  4407. '92': {'ext': 'mp4', 'height': 240, 'resolution': '240p', 'format_note': 'HLS', 'preference': -10},
  4408. '93': {'ext': 'mp4', 'height': 360, 'resolution': '360p', 'format_note': 'HLS', 'preference': -10},
  4409. '94': {'ext': 'mp4', 'height': 480, 'resolution': '480p', 'format_note': 'HLS', 'preference': -10},
  4410. '95': {'ext': 'mp4', 'height': 720, 'resolution': '720p', 'format_note': 'HLS', 'preference': -10},
  4411. '96': {'ext': 'mp4', 'height': 1080, 'resolution': '1080p', 'format_note': 'HLS', 'preference': -10},
  4412. '132': {'ext': 'mp4', 'height': 240, 'resolution': '240p', 'format_note': 'HLS', 'preference': -10},
  4413. '151': {'ext': 'mp4', 'height': 72, 'resolution': '72p', 'format_note': 'HLS', 'preference': -10},
  4414.  
  4415. // DASH mp4 video
  4416. '133': {'ext': 'mp4', 'width': 400, 'height': 240, 'resolution': '240p', 'format_note': 'DASH video', 'preference': -40},
  4417. '134': {'ext': 'mp4', 'width': 640, 'height': 360, 'resolution': '360p', 'format_note': 'DASH video', 'preference': -40},
  4418. '135': {'ext': 'mp4', 'width': 854, 'height': 480, 'resolution': '480p', 'format_note': 'DASH video', 'preference': -40},
  4419. '136': {'ext': 'mp4', 'width': 1280, 'height': 720, 'resolution': '720p', 'format_note': 'DASH video', 'preference': -40},
  4420. '137': {'ext': 'mp4', 'width': 1920, 'height': 1080, 'resolution': '1080p', 'format_note': 'DASH video', 'preference': -40},
  4421. '138': {'ext': 'mp4', 'width': 1921, 'height': 1081, 'resolution': '>1080p', 'format_note': 'DASH video', 'preference': -40},
  4422. '160': {'ext': 'mp4', 'width': 256, 'height': 192, 'resolution': '192p', 'format_note': 'DASH video', 'preference': -40},
  4423. '264': {'ext': 'mp4', 'width': 1920, 'height': 1080, 'resolution': '1080p', 'format_note': 'DASH video', 'preference': -40},
  4424.  
  4425. // Dash mp4 audio
  4426. '139': {'ext': 'm4a', 'format_note': 'DASH audio', 'vcodec': 'none', 'abr': 48, 'preference': -50},
  4427. '140': {'ext': 'm4a', 'format_note': 'DASH audio', 'vcodec': 'none', 'abr': 128, 'preference': -50},
  4428. '141': {'ext': 'm4a', 'format_note': 'DASH audio', 'vcodec': 'none', 'abr': 256, 'preference': -50},
  4429.  
  4430. // Dash webm
  4431. '167': {'ext': 'webm', 'height': 360, 'width': 640, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40, resolution: '360p'},
  4432. '168': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40, resolution: '480p'},
  4433. '169': {'ext': 'webm', 'height': 720, 'width': 1280, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40, resolution: '720p'},
  4434. '170': {'ext': 'webm', 'height': 1080, 'width': 1920, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40, resolution: '1080p'},
  4435. '218': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40, resolution: '480p'},
  4436. '219': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40, resolution: '480p'},
  4437. '242': {'ext': 'webm', 'height': 240, 'resolution': '240p', 'format_note': 'DASH webm', 'preference': -40},
  4438. '243': {'ext': 'webm', 'height': 360, 'resolution': '360p', 'format_note': 'DASH webm', 'preference': -40},
  4439. '244': {'ext': 'webm', 'height': 480, 'resolution': '480p', 'format_note': 'DASH webm', 'preference': -40},
  4440. '245': {'ext': 'webm', 'height': 480, 'resolution': '480p', 'format_note': 'DASH webm', 'preference': -40},
  4441. '246': {'ext': 'webm', 'height': 480, 'resolution': '480p', 'format_note': 'DASH webm', 'preference': -40},
  4442. '247': {'ext': 'webm', 'height': 720, 'resolution': '720p', 'format_note': 'DASH webm', 'preference': -40},
  4443. '248': {'ext': 'webm', 'height': 1080, 'resolution': '1080p', 'format_note': 'DASH webm', 'preference': -40},
  4444.  
  4445. // Dash webm audio
  4446. '171': {'ext': 'webm', 'vcodec': 'none', 'format_note': 'DASH webm audio', 'abr': 48, 'preference': -50},
  4447. '172': {'ext': 'webm', 'vcodec': 'none', 'format_note': 'DASH webm audio', 'abr': 256, 'preference': -50},
  4448.  
  4449. // RTMP (unnamed)
  4450. '_rtmp': {'protocol': 'rtmp'},
  4451. },
  4452.  
  4453. run: function() {
  4454. console.log('run() --');
  4455. this.getURLInfo();
  4456. },
  4457.  
  4458. /**
  4459. * parse location.href
  4460. */
  4461. getURLInfo: function() {
  4462. this.urlInfo = this.parseURI(unsafeWindow.location.href);
  4463. if (document.location.href.contains('/embed/')) {
  4464. window.location.href = this.urlInfo.replace('/embed/', '/watch?v=');
  4465. } else {
  4466. this.getVideo();
  4467. }
  4468. },
  4469.  
  4470. /**
  4471. * Get video url info:
  4472. */
  4473. getVideo: function () {
  4474. console.log('getVideo()--');
  4475. var that = this;
  4476.  
  4477. if (!this.urlInfo.params['v']) {
  4478. return;
  4479. }
  4480.  
  4481. this.videoId = this.urlInfo.params['v'];
  4482. this.videoInfoUrl = [
  4483. '/get_video_info',
  4484. '?video_id=', this.videoId,
  4485. //'&el=player_embeded&hl=en&gl=US',
  4486. '&el=html5&hl=en&gl=US',
  4487. '&eurl=https://youtube.googleapis.com/v/', this.videoId,
  4488. ].join('');
  4489. this.videoTitle = unsafeWindow.document.title.substr(
  4490. 0, unsafeWindow.document.title.length - 10);
  4491.  
  4492. GM_xmlhttpRequest({
  4493. method: 'GET',
  4494. url: this.videoInfoUrl,
  4495. onload: function(response) {
  4496. console.log('xhr response: ', response);
  4497. that.parseStream(response.responseText);
  4498. },
  4499. });
  4500. },
  4501.  
  4502. /**
  4503. * Parse stream info from xhr text:
  4504. */
  4505. parseStream: function(rawVideoInfo) {
  4506. console.log('parseStream() ---');
  4507. var that = this;
  4508.  
  4509. /**
  4510. * Parse the stream text to Object
  4511. */
  4512. function _parseStream(rawStream){
  4513. var a = decodeURIComponent(rawStream).split(',');
  4514. return a.map(that.urlHashToObject);
  4515. }
  4516.  
  4517. this.videoInfo = this.urlHashToObject(rawVideoInfo);
  4518. this.stream = _parseStream(this.videoInfo.url_encoded_fmt_stream_map);
  4519. this.adaptive_fmts = _parseStream(this.videoInfo.adaptive_fmts)
  4520. this.createUI();
  4521. },
  4522.  
  4523. /**
  4524. * Create download list:
  4525. */
  4526. createUI: function() {
  4527. console.log('createUI() -- ');
  4528. console.log('this: ', this);
  4529. var videos = {
  4530. title: this.videoTitle,
  4531. formats: [],
  4532. links: [],
  4533. ok: true,
  4534. msg: '',
  4535. },
  4536. video,
  4537. format,
  4538. formatName,
  4539. url,
  4540. streams = this.stream.concat(this.adaptive_fmts),
  4541. i;
  4542.  
  4543. for (i = 0; video = streams[i]; i += 1) {
  4544. format = this.formats[video['itag']];
  4545. if (! format) {
  4546. console.error('current format not supported: ', video);
  4547. continue;
  4548. }
  4549. formatName = []
  4550. if ('format_note' in format) {
  4551. formatName.push(format.format_note);
  4552. }
  4553. if ('resolution' in format) {
  4554. if (formatName.length > 0) {
  4555. formatName.push('-');
  4556. }
  4557. formatName.push(format.resolution);
  4558. }
  4559. if ('ext' in format) {
  4560. formatName.push('.');
  4561. formatName.push(format.ext);
  4562. }
  4563. formatName = formatName.join('');
  4564. if (videos.formats.indexOf(formatName) >= 0) {
  4565. continue;
  4566. }
  4567. videos.formats.push(formatName);
  4568. url = decodeURIComponent(video.url);
  4569. if ('sig' in video) {
  4570. url = url + '&signature=' + video.sig
  4571. }
  4572. videos.links.push(url);
  4573. }
  4574.  
  4575. if (videos.links.length === 0) {
  4576. videos.ok = false;
  4577. videos.msg = 'This video does not allowed to download';
  4578. }
  4579. singleFile.run(videos);
  4580. },
  4581.  
  4582. /**
  4583. * Parse URL hash and convert to Object.
  4584. */
  4585. urlHashToObject: function(hashText) {
  4586. var list = hashText.split('&'),
  4587. output = {},
  4588. len = list.length,
  4589. i = 0,
  4590. tmp = '';
  4591.  
  4592. for (i = 0; i < len; i += 1) {
  4593. tmp = list[i].split('=')
  4594. output[tmp[0]] = tmp[1];
  4595. }
  4596. return output;
  4597. },
  4598.  
  4599. /**
  4600. * FROM: http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
  4601. * This function creates a new anchor element and uses location
  4602. * properties (inherent) to get the desired URL data. Some String
  4603. * operations are used (to normalize results across browsers).
  4604. */
  4605. parseURI: function(url) {
  4606. var a = unsafeWindow.document.createElement('a');
  4607. a.href = url;
  4608. return {
  4609. source: url,
  4610. protocol: a.protocol.replace(':',''),
  4611. host: a.hostname,
  4612. port: a.port,
  4613. query: a.search,
  4614. params: (function(){
  4615. var ret = {},
  4616. seg = a.search.replace(/^\?/,'').split('&'),
  4617. len = seg.length,
  4618. i = 0,
  4619. s;
  4620.  
  4621. for (i = 0; i< len; i += 1) {
  4622. if (seg[i]) {
  4623. s = seg[i].split('=');
  4624. ret[s[0]] = s[1];
  4625. }
  4626. }
  4627. return ret;
  4628. })(),
  4629. file: (a.pathname.match(/\/([^\/?#]+)$/i) || [,''])[1],
  4630. hash: a.hash.replace('#',''),
  4631. path: a.pathname.replace(/^([^\/])/,'/$1'),
  4632. relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [,''])[1],
  4633. segments: a.pathname.replace(/^\//,'').split('/')
  4634. };
  4635. },
  4636. };
  4637.  
  4638.  
  4639. monkey.extend('www.youtube.com', [
  4640. 'http://www.youtube.com/watch?v=',
  4641. 'https://www.youtube.com/watch?v=',
  4642. 'http://www.youtube.com/embed/',
  4643. 'https://www.youtube.com/embed/',
  4644. ], monkey_youtube);
  4645.  
  4646. // In the end, get video handler and call it
  4647. monkey.run();
  4648.  
  4649. }).call(this);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址