iqiyi player switch

iqiyi player switch between flash and html5

当前为 2017-05-18 提交的版本,查看 最新版本

  1. 'use strict';
  2.  
  3. var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
  4.  
  5. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  6.  
  7. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  8.  
  9. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  10.  
  11. // ==UserScript==
  12. // @name iqiyi player switch
  13. // @namespace https://github.com/gooyie/userscript-iqiyi-player-switch
  14. // @homepageURL https://github.com/gooyie/userscript-iqiyi-player-switch
  15. // @supportURL https://github.com/gooyie/userscript-iqiyi-player-switch/issues
  16. // @version 1.7.0
  17. // @description iqiyi player switch between flash and html5
  18. // @author gooyie
  19. // @license MIT License
  20. //
  21. // @include *://*.iqiyi.com/*
  22. // @grant GM_registerMenuCommand
  23. // @grant GM_getValue
  24. // @grant GM_setValue
  25. // @grant GM_info
  26. // @grant GM_log
  27. // @grant unsafeWindow
  28. // @require https://greasyfork.org/scripts/29319-web-streams-polyfill/code/web-streams-polyfill.js?version=191261
  29. // @require https://greasyfork.org/scripts/29306-fetch-readablestream/code/fetch-readablestream.js?version=191832
  30. // @require https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/3.3.4/adapter.min.js
  31. // @require https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.7.0/js/md5.min.js
  32. // @run-at document-start
  33. // ==/UserScript==
  34.  
  35. (function () {
  36. 'use strict';
  37.  
  38. var PLAYER_TYPE = {
  39. Html5VOD: 'h5_VOD',
  40. FlashVOD: 'flash_VOD'
  41. };
  42.  
  43. var Logger = function () {
  44. function Logger() {
  45. _classCallCheck(this, Logger);
  46. }
  47.  
  48. _createClass(Logger, null, [{
  49. key: 'log',
  50. value: function log(msg) {
  51. GM_log(this.tag + msg);
  52. }
  53. }, {
  54. key: 'tag',
  55. get: function get() {
  56. return '[' + GM_info.script.name + ']: ';
  57. }
  58. }]);
  59.  
  60. return Logger;
  61. }();
  62.  
  63. var Cookies = function () {
  64. function Cookies() {
  65. _classCallCheck(this, Cookies);
  66. }
  67.  
  68. _createClass(Cookies, null, [{
  69. key: 'get',
  70. value: function get(key) {
  71. var value = void 0;
  72. if (new RegExp('^[^\\x00-\\x20\\x7f\\(\\)<>@,;:\\\\\\"\\[\\]\\?=\\{\\}\\/\\u0080-\\uffff]+$').test(key)) {
  73. var re = new RegExp('(^| )' + key + '=([^;]*)(;|$)');
  74. var rs = re.exec(document.cookie);
  75. value = rs ? rs[2] : '';
  76. }
  77. return value ? decodeURIComponent(value) : '';
  78. }
  79. }, {
  80. key: 'set',
  81. value: function set(k, v) {
  82. var o = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  83.  
  84. var n = o.expires;
  85. if ('number' == typeof o.expires) {
  86. n = new Date();
  87. n.setTime(n.getTime() + o.expires);
  88. }
  89. var key = k;
  90. var value = encodeURIComponent(v);
  91. var path = o.path ? '; path=' + o.path : '';
  92. var expires = n ? '; expires=' + n.toGMTString() : '';
  93. var domain = o.domain ? '; domain=' + o.domain : '';
  94. document.cookie = key + '=' + value + path + expires + domain;
  95. }
  96. }, {
  97. key: 'remove',
  98. value: function remove(k) {
  99. var o = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  100.  
  101. o.expires = new Date(0);
  102. this.set(k, '', o);
  103. }
  104. }]);
  105.  
  106. return Cookies;
  107. }();
  108.  
  109. var Detector = function () {
  110. function Detector() {
  111. _classCallCheck(this, Detector);
  112. }
  113.  
  114. _createClass(Detector, null, [{
  115. key: 'isSupportHtml5',
  116. value: function isSupportHtml5() {
  117. var v = document.createElement('video');
  118. return !!(v.canPlayType('audio/mp4; codecs="mp4a.40.2"') && v.canPlayType('video/mp4; codecs="avc1.640029"') && v.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"'));
  119. }
  120. }, {
  121. key: 'isSupportVms',
  122. value: function isSupportVms() {
  123. return !!(window.MediaSource && window.URL && window.WebSocket && window.ReadableStream && (window.RTCSessionDescription || window.webkitRTCSessionDescription) && (window.RTCPeerConnection || window.webkitRTCPeerConnection) && (window.RTCIceCandidate || window.webkitRTCIceCandidate));
  124. }
  125. }, {
  126. key: 'isSupportM3u8',
  127. value: function isSupportM3u8() {
  128. var v = document.createElement('video');
  129. return !!(v.canPlayType('application/x-mpegurl') && v.canPlayType('application/vnd.apple.mpegurl'));
  130. }
  131. }, {
  132. key: 'isChrome',
  133. value: function isChrome() {
  134. return (/chrome/i.test(navigator.userAgent)
  135. );
  136. }
  137. }, {
  138. key: 'isFirefox',
  139. value: function isFirefox() {
  140. return (/firefox/i.test(navigator.userAgent)
  141. );
  142. }
  143. }, {
  144. key: 'isEdge',
  145. value: function isEdge() {
  146. return (/edge/i.test(navigator.userAgent)
  147. );
  148. }
  149. }]);
  150.  
  151. return Detector;
  152. }();
  153.  
  154. var Hooker = function () {
  155. function Hooker() {
  156. _classCallCheck(this, Hooker);
  157. }
  158.  
  159. _createClass(Hooker, null, [{
  160. key: 'hookCall',
  161. value: function hookCall() {
  162. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  163.  
  164.  
  165. var call = Function.prototype.call;
  166. Function.prototype.call = function () {
  167. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  168. args[_key] = arguments[_key];
  169. }
  170.  
  171. var ret = call.bind(this).apply(undefined, args);
  172. if (args) cb.apply(undefined, args);
  173. return ret;
  174. };
  175.  
  176. Function.prototype.call.toString = Function.prototype.call.toLocaleString = function () {
  177. return 'function call() { [native code] }';
  178. };
  179. }
  180. }, {
  181. key: '_isFactoryCall',
  182. value: function _isFactoryCall(args) {
  183. // module.exports, module, module.exports, require
  184. return args.length === 4 && 'object' === _typeof(args[1]) && args[1].hasOwnProperty('exports');
  185. }
  186. }, {
  187. key: 'hookFactoryCall',
  188. value: function hookFactoryCall() {
  189. var _this = this;
  190.  
  191. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  192.  
  193. this.hookCall(function () {
  194. for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  195. args[_key2] = arguments[_key2];
  196. }
  197.  
  198. if (_this._isFactoryCall(args)) cb.apply(undefined, args);
  199. });
  200. }
  201. }, {
  202. key: '_isJqueryFactoryCall',
  203. value: function _isJqueryFactoryCall(exports) {
  204. return exports.hasOwnProperty('fn') && exports.fn.hasOwnProperty('jquery');
  205. }
  206. }, {
  207. key: 'hookJquery',
  208. value: function hookJquery() {
  209. var _this2 = this;
  210.  
  211. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  212.  
  213. this.hookFactoryCall(function () {
  214. for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
  215. args[_key3] = arguments[_key3];
  216. }
  217.  
  218. if (_this2._isJqueryFactoryCall(args[1].exports)) cb.apply(undefined, args);
  219. });
  220. }
  221. }, {
  222. key: 'hookJqueryAjax',
  223. value: function hookJqueryAjax() {
  224. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  225.  
  226. this.hookJquery(function () {
  227. for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
  228. args[_key4] = arguments[_key4];
  229. }
  230.  
  231. var exports = args[1].exports;
  232.  
  233. var ajax = exports.ajax.bind(exports);
  234.  
  235. exports.ajax = function (url) {
  236. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  237.  
  238. if ((typeof url === 'undefined' ? 'undefined' : _typeof(url)) === 'object') {
  239. var _ref = [url.url, url];
  240. url = _ref[0];
  241. options = _ref[1];
  242. }
  243.  
  244. var isHijacked = cb(url, options);
  245. if (isHijacked) return;
  246.  
  247. return ajax(url, options);
  248. };
  249. });
  250. }
  251. }, {
  252. key: '_isHttpFactoryCall',
  253. value: function _isHttpFactoryCall() {
  254. var exports = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  255.  
  256. return exports.hasOwnProperty('jsonp') && exports.hasOwnProperty('ajax');
  257. }
  258. }, {
  259. key: 'hookHttp',
  260. value: function hookHttp() {
  261. var _this3 = this;
  262.  
  263. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  264.  
  265. this.hookFactoryCall(function () {
  266. for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
  267. args[_key5] = arguments[_key5];
  268. }
  269.  
  270. if (_this3._isHttpFactoryCall(args[1].exports)) cb.apply(undefined, args);
  271. });
  272. }
  273. }, {
  274. key: 'hookHttpJsonp',
  275. value: function hookHttpJsonp() {
  276. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  277.  
  278. this.hookHttp(function () {
  279. for (var _len6 = arguments.length, args = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
  280. args[_key6] = arguments[_key6];
  281. }
  282.  
  283. var exports = args[1].exports;
  284.  
  285. var jsonp = exports.jsonp.bind(exports);
  286.  
  287. exports.jsonp = function (options) {
  288. var isHijacked = cb(options);
  289. if (isHijacked) return;
  290. return jsonp(options);
  291. };
  292. });
  293. }
  294. }, {
  295. key: '_isLogoFactoryCall',
  296. value: function _isLogoFactoryCall() {
  297. var exports = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  298.  
  299. return 'function' === typeof exports && exports.prototype.hasOwnProperty('showLogo');
  300. }
  301. }, {
  302. key: 'hookLogo',
  303. value: function hookLogo() {
  304. var _this4 = this;
  305.  
  306. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  307.  
  308. this.hookFactoryCall(function () {
  309. for (var _len7 = arguments.length, args = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
  310. args[_key7] = arguments[_key7];
  311. }
  312.  
  313. if (_this4._isLogoFactoryCall(args[1].exports)) cb(args[1].exports);
  314. });
  315. }
  316. }]);
  317.  
  318. return Hooker;
  319. }();
  320.  
  321. var Faker = function () {
  322. function Faker() {
  323. _classCallCheck(this, Faker);
  324. }
  325.  
  326. _createClass(Faker, null, [{
  327. key: 'fakeMacPlatform',
  328. value: function fakeMacPlatform() {
  329. var PLAFORM_MAC = 'mac';
  330. Object.defineProperty(unsafeWindow.navigator, 'platform', { get: function get() {
  331. return PLAFORM_MAC;
  332. } });
  333. }
  334. }, {
  335. key: 'fakeSafari',
  336. value: function fakeSafari() {
  337. var UA_SAFARY = 'safari';
  338. Object.defineProperty(unsafeWindow.navigator, 'userAgent', { get: function get() {
  339. return UA_SAFARY;
  340. } });
  341. }
  342. }, {
  343. key: 'fakeChrome',
  344. value: function fakeChrome() {
  345. var UA_CHROME = 'chrome';
  346. Object.defineProperty(unsafeWindow.navigator, 'userAgent', { get: function get() {
  347. return UA_CHROME;
  348. } });
  349. }
  350. }, {
  351. key: '_calcSign',
  352. value: function _calcSign(authcookie) {
  353. var RESPONSE_KEY = '-0J1d9d^ESd)9jSsja';
  354. return md5(authcookie.substring(5, 39).split('').reverse().join('') + '<1<' + RESPONSE_KEY);
  355. }
  356. }, {
  357. key: 'fakeVipRes',
  358. value: function fakeVipRes(authcookie) {
  359. var json = {
  360. code: 'A00000',
  361. data: {
  362. sign: this._calcSign(authcookie)
  363. }
  364. };
  365. return json;
  366. }
  367. }, {
  368. key: 'fakeAdRes',
  369. value: function fakeAdRes() {
  370. var json = {};
  371. return json;
  372. }
  373. }, {
  374. key: 'fakePassportCookie',
  375. value: function fakePassportCookie() {
  376. Cookies.set('P00001', 'faked_passport', { domain: '.iqiyi.com' });
  377. Logger.log('faked passport cookie');
  378. }
  379. }]);
  380.  
  381. return Faker;
  382. }();
  383.  
  384. var Mocker = function () {
  385. function Mocker() {
  386. _classCallCheck(this, Mocker);
  387. }
  388.  
  389. _createClass(Mocker, null, [{
  390. key: 'mock',
  391. value: function mock() {
  392. var _this5 = this;
  393.  
  394. var currType = GM_getValue('player_forcedType', PLAYER_TYPE.Html5VOD);
  395.  
  396. if (currType === PLAYER_TYPE.Html5VOD) {
  397. if (!Detector.isSupportHtml5()) {
  398. alert('╮(╯▽╰)╭ 你的浏览器播放不了html5视频~~~~');
  399. return;
  400. }
  401.  
  402. this.forceHtml5();
  403. this.mockForBestDefintion();
  404. this.mockAd();
  405. this.mockVip();
  406. this.mockLogo();
  407. } else {
  408. this.forceFlash();
  409. }
  410.  
  411. window.addEventListener('unload', function (event) {
  412. return _this5.destroy();
  413. });
  414. }
  415. }, {
  416. key: 'forceHtml5',
  417. value: function forceHtml5() {
  418. Logger.log('setting player_forcedType cookie as ' + PLAYER_TYPE.Html5VOD);
  419. Cookies.set('player_forcedType', PLAYER_TYPE.Html5VOD, { domain: '.iqiyi.com' });
  420. }
  421. }, {
  422. key: 'forceFlash',
  423. value: function forceFlash() {
  424. Logger.log('setting player_forcedType cookie as ' + PLAYER_TYPE.FlashVOD);
  425. Cookies.set('player_forcedType', PLAYER_TYPE.FlashVOD, { domain: '.iqiyi.com' });
  426. }
  427. }, {
  428. key: 'mockToUseVms',
  429. value: function mockToUseVms() {
  430. Faker.fakeChrome();
  431. }
  432. }, {
  433. key: 'mockToUseM3u8',
  434. value: function mockToUseM3u8() {
  435. Faker.fakeMacPlatform();
  436. Faker.fakeSafari();
  437. }
  438. }, {
  439. key: '_isVideoReq',
  440. value: function _isVideoReq(url) {
  441. return (/^https?:\/\/(?:\d+.?){4}\/videos\/v.*$/.test(url)
  442. );
  443. }
  444. }, {
  445. key: 'mockForBestDefintion',
  446. value: function mockForBestDefintion() {
  447. var _this6 = this;
  448.  
  449. // apply shims
  450. if (Detector.isFirefox()) {
  451. var fetch = unsafeWindow.fetch.bind(unsafeWindow);
  452.  
  453. unsafeWindow.fetch = function (url, opts) {
  454. if (_this6._isVideoReq(url)) {
  455. Logger.log('fetching stream ' + url);
  456. return fetchStream(url, opts); // xhr with moz-chunked-arraybuffer
  457. } else {
  458. return fetch(url, opts);
  459. }
  460. };
  461. } else if (Detector.isEdge()) {
  462. // export to the global window object
  463. unsafeWindow.RTCIceCandidate = window.RTCIceCandidate;
  464. unsafeWindow.RTCPeerConnection = window.RTCPeerConnection;
  465. unsafeWindow.RTCSessionDescription = window.RTCSessionDescription;
  466. }
  467. // auto fall-back
  468. if (Detector.isSupportVms()) {
  469. if (!Detector.isChrome()) this.mockToUseVms(); // vms, 1080p or higher
  470. } else if (Detector.isSupportM3u8()) {
  471. this.mockToUseM3u8(); // tmts m3u8
  472. } else {
  473. // by default, tmts mp4 ...
  474. }
  475. }
  476. }, {
  477. key: '_isAdReq',
  478. value: function _isAdReq(url) {
  479. var AD_URL = 'http://t7z.cupid.iqiyi.com/show2';
  480. return url.indexOf(AD_URL) === 0;
  481. }
  482. }, {
  483. key: 'mockAd',
  484. value: function mockAd() {
  485. var _this7 = this;
  486.  
  487. Hooker.hookJqueryAjax(function (url, options) {
  488. if (_this7._isAdReq(url)) {
  489. var res = Faker.fakeAdRes();
  490. (options.complete || options.success)({ responseJSON: res }, 'success');
  491. Logger.log('mocked ad request ' + url);
  492. return true;
  493. }
  494. });
  495. }
  496. }, {
  497. key: '_isCheckVipReq',
  498. value: function _isCheckVipReq(url) {
  499. var CHECK_VIP_URL = 'https://cmonitor.iqiyi.com/apis/user/check_vip.action';
  500. return url === CHECK_VIP_URL;
  501. }
  502. }, {
  503. key: '_isLogin',
  504. value: function _isLogin() {
  505. return !!Cookies.get('P00001');
  506. }
  507. }, {
  508. key: 'mockVip',
  509. value: function mockVip() {
  510. var _this8 = this;
  511.  
  512. if (!this._isLogin()) Faker.fakePassportCookie();
  513.  
  514. Hooker.hookHttpJsonp(function (options) {
  515. var url = options.url;
  516.  
  517. if (_this8._isCheckVipReq(url)) {
  518. var res = Faker.fakeVipRes(options.params.authcookie);
  519. options.success(res);
  520. Logger.log('mocked check vip request ' + url);
  521. return true;
  522. }
  523. });
  524. }
  525. }, {
  526. key: 'mockLogo',
  527. value: function mockLogo() {
  528. Hooker.hookLogo(function (exports) {
  529. return exports.prototype.showLogo = function () {};
  530. });
  531. }
  532. }, {
  533. key: 'destroy',
  534. value: function destroy() {
  535. Cookies.remove('player_forcedType', { domain: '.iqiyi.com' });
  536. if (Cookies.get('P00001') === 'faked_passport') Cookies.remove('P00001', { domain: '.iqiyi.com' });
  537. Logger.log('removed cookies.');
  538. }
  539. }]);
  540.  
  541. return Mocker;
  542. }();
  543.  
  544. var Switcher = function () {
  545. function Switcher() {
  546. _classCallCheck(this, Switcher);
  547. }
  548.  
  549. _createClass(Switcher, null, [{
  550. key: 'switchTo',
  551. value: function switchTo(toType) {
  552. Logger.log('switching to ' + toType + ' ...');
  553.  
  554. GM_setValue('player_forcedType', toType);
  555. document.location.reload();
  556. }
  557. }]);
  558.  
  559. return Switcher;
  560. }();
  561.  
  562. function registerMenu() {
  563. var MENU_NAME = {
  564. HTML5: 'HTML5播放器',
  565. FLASH: 'Flash播放器'
  566. };
  567.  
  568. var currType = GM_getValue('player_forcedType', PLAYER_TYPE.Html5VOD); // 默认为Html5播放器,免去切换。
  569.  
  570. var _ref2 = currType === PLAYER_TYPE.Html5VOD ? [PLAYER_TYPE.FlashVOD, MENU_NAME.FLASH] : [PLAYER_TYPE.Html5VOD, MENU_NAME.HTML5],
  571. _ref3 = _slicedToArray(_ref2, 2),
  572. toType = _ref3[0],
  573. name = _ref3[1];
  574.  
  575. GM_registerMenuCommand(name, function () {
  576. return Switcher.switchTo(toType);
  577. }, null);
  578. Logger.log('registered menu.');
  579. }
  580.  
  581. registerMenu();
  582. Mocker.mock();
  583. })();