ykh5p

youku html5 player +

当前为 2017-06-10 提交的版本,查看 最新版本

  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 _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; }; }();
  6.  
  7. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  8.  
  9. // ==UserScript==
  10. // @name ykh5p
  11. // @namespace https://github.com/gooyie/ykh5p
  12. // @homepageURL https://github.com/gooyie/ykh5p
  13. // @supportURL https://github.com/gooyie/ykh5p/issues
  14. // @version 0.3.0
  15. // @description youku html5 player +
  16. // @author gooyie
  17. // @license MIT License
  18. //
  19. // @include *://v.youku.com/*
  20. // @grant GM_info
  21. // @grant GM_addStyle
  22. // @run-at document-start
  23. // ==/UserScript==
  24.  
  25. (function () {
  26. 'use strict';
  27.  
  28. /* eslint-disable no-console */
  29.  
  30. var Logger = function () {
  31. function Logger() {
  32. _classCallCheck(this, Logger);
  33. }
  34.  
  35. _createClass(Logger, null, [{
  36. key: 'log',
  37. value: function log() {
  38. var _console;
  39.  
  40. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  41. args[_key] = arguments[_key];
  42. }
  43.  
  44. (_console = console).log.apply(_console, ['%c' + this.tag + '%c' + args.shift(), 'color: #fff; background: #2FB3FF', ''].concat(args));
  45. }
  46. }, {
  47. key: 'info',
  48. value: function info() {
  49. var _console2;
  50.  
  51. for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  52. args[_key2] = arguments[_key2];
  53. }
  54.  
  55. (_console2 = console).info.apply(_console2, [this.tag + args.shift()].concat(args));
  56. }
  57. }, {
  58. key: 'debug',
  59. value: function debug() {
  60. var _console3;
  61.  
  62. for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
  63. args[_key3] = arguments[_key3];
  64. }
  65.  
  66. (_console3 = console).debug.apply(_console3, [this.tag + args.shift()].concat(args));
  67. }
  68. }, {
  69. key: 'warn',
  70. value: function warn() {
  71. var _console4;
  72.  
  73. for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
  74. args[_key4] = arguments[_key4];
  75. }
  76.  
  77. (_console4 = console).warn.apply(_console4, [this.tag + args.shift()].concat(args));
  78. }
  79. }, {
  80. key: 'error',
  81. value: function error() {
  82. var _console5;
  83.  
  84. for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
  85. args[_key5] = arguments[_key5];
  86. }
  87.  
  88. (_console5 = console).error.apply(_console5, [this.tag + args.shift()].concat(args));
  89. }
  90. }, {
  91. key: 'tag',
  92. get: function get() {
  93. return '[' + GM_info.script.name + ']';
  94. }
  95. }]);
  96.  
  97. return Logger;
  98. }();
  99. /* eslint-enable no-console */
  100.  
  101. var Hooker = function () {
  102. function Hooker() {
  103. _classCallCheck(this, Hooker);
  104. }
  105.  
  106. _createClass(Hooker, null, [{
  107. key: 'hookCall',
  108. value: function hookCall() {
  109. var after = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  110. var before = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
  111.  
  112. var call = Function.prototype.call;
  113. Function.prototype.call = function () {
  114. for (var _len6 = arguments.length, args = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
  115. args[_key6] = arguments[_key6];
  116. }
  117.  
  118. try {
  119. if (args) before.apply(undefined, args);
  120. } catch (err) {
  121. Logger.error(err.stack);
  122. }
  123.  
  124. var ret = call.apply(this, args);
  125.  
  126. try {
  127. if (args) after.apply(undefined, args);
  128. } catch (err) {
  129. Logger.error(err.stack);
  130. }
  131.  
  132. return ret;
  133. };
  134.  
  135. Function.prototype.call.toString = Function.prototype.call.toLocaleString = function () {
  136. return 'function call() { [native code] }';
  137. };
  138. }
  139. }, {
  140. key: '_isFactoryCall',
  141. value: function _isFactoryCall(args) {
  142. // m.exports, _dereq_, m, m.exports, outer, modules, cache, entry
  143. return args.length === 8 && args[2] instanceof Object && args[2].hasOwnProperty('exports');
  144. }
  145. }, {
  146. key: 'hookFactoryCall',
  147. value: function hookFactoryCall() {
  148. var _this = this;
  149.  
  150. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  151.  
  152. this.hookCall(function () {
  153. for (var _len7 = arguments.length, args = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
  154. args[_key7] = arguments[_key7];
  155. }
  156.  
  157. if (_this._isFactoryCall(args)) cb.apply(undefined, args);
  158. });
  159. }
  160. }, {
  161. key: '_isManagerFactoryCall',
  162. value: function _isManagerFactoryCall() {
  163. var exports = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  164.  
  165. return 'function' === typeof exports && exports.prototype.hasOwnProperty('upsDataSuccess');
  166. }
  167. }, {
  168. key: 'hookManager',
  169. value: function hookManager() {
  170. var _this2 = this;
  171.  
  172. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  173.  
  174. this.hookFactoryCall(function () {
  175. for (var _len8 = arguments.length, args = Array(_len8), _key8 = 0; _key8 < _len8; _key8++) {
  176. args[_key8] = arguments[_key8];
  177. }
  178.  
  179. if (_this2._isManagerFactoryCall(args[2].exports)) cb(args[2].exports);
  180. });
  181. }
  182. }, {
  183. key: 'hookUpsDataSuccess',
  184. value: function hookUpsDataSuccess() {
  185. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  186.  
  187. this.hookManager(function (exports) {
  188. var upsDataSuccess = exports.prototype.upsDataSuccess;
  189. exports.prototype.upsDataSuccess = function (data) {
  190. cb(data);
  191. upsDataSuccess.apply(this, [data]);
  192. };
  193. });
  194. }
  195. }, {
  196. key: '_isSkinsViewRenderCall',
  197. value: function _isSkinsViewRenderCall(args) {
  198. return args.length === 3 && args[1] === 'spvdiv' && args[2].className === 'spv_player';
  199. }
  200. }, {
  201. key: 'hookSkinsViewRender',
  202. value: function hookSkinsViewRender() {
  203. var _this3 = this;
  204.  
  205. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  206.  
  207. this.hookCall(undefined, function () {
  208. for (var _len9 = arguments.length, args = Array(_len9), _key9 = 0; _key9 < _len9; _key9++) {
  209. args[_key9] = arguments[_key9];
  210. }
  211.  
  212. if (_this3._isSkinsViewRenderCall(args)) cb(args[2]);
  213. });
  214. }
  215. }, {
  216. key: '_isUtilFactoryCall',
  217. value: function _isUtilFactoryCall() {
  218. var exports = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  219.  
  220. return exports.hasOwnProperty('getJsonp') && exports.TAG === 'util';
  221. }
  222. }, {
  223. key: 'hookUtil',
  224. value: function hookUtil() {
  225. var _this4 = this;
  226.  
  227. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  228.  
  229. this.hookFactoryCall(function () {
  230. for (var _len10 = arguments.length, args = Array(_len10), _key10 = 0; _key10 < _len10; _key10++) {
  231. args[_key10] = arguments[_key10];
  232. }
  233.  
  234. if (_this4._isUtilFactoryCall(args[2].exports)) cb(args[2].exports);
  235. });
  236. }
  237. }, {
  238. key: 'hookGetJsonp',
  239. value: function hookGetJsonp() {
  240. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  241.  
  242. this.hookUtil(function (exports) {
  243. var getJsonp = exports.getJsonp.bind(exports);
  244. exports.getJsonp = function () {
  245. for (var _len11 = arguments.length, args = Array(_len11), _key11 = 0; _key11 < _len11; _key11++) {
  246. args[_key11] = arguments[_key11];
  247. }
  248.  
  249. // url, onload, onerror, ontimeout, timeout
  250. if (cb(args)) return; // hijack
  251. getJsonp.apply(undefined, args);
  252. };
  253. });
  254. }
  255. }, {
  256. key: '_isH5PlayerCoreFactoryCall',
  257. value: function _isH5PlayerCoreFactoryCall() {
  258. var exports = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  259.  
  260. return exports instanceof Object && exports.hasOwnProperty('YoukuH5PlayerCore');
  261. }
  262. }, {
  263. key: 'hookH5PlayerCore',
  264. value: function hookH5PlayerCore() {
  265. var _this5 = this;
  266.  
  267. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  268.  
  269. this.hookFactoryCall(function () {
  270. for (var _len12 = arguments.length, args = Array(_len12), _key12 = 0; _key12 < _len12; _key12++) {
  271. args[_key12] = arguments[_key12];
  272. }
  273.  
  274. if (_this5._isH5PlayerCoreFactoryCall(args[2].exports)) cb(args[2].exports);
  275. });
  276. }
  277. }, {
  278. key: 'hookRealStartPlay',
  279. value: function hookRealStartPlay() {
  280. var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
  281.  
  282. this.hookH5PlayerCore(function (exports) {
  283. var _realStartPlay = exports.YoukuH5PlayerCore.prototype._realStartPlay;
  284. exports.YoukuH5PlayerCore.prototype._realStartPlay = function () {
  285. for (var _len13 = arguments.length, args = Array(_len13), _key13 = 0; _key13 < _len13; _key13++) {
  286. args[_key13] = arguments[_key13];
  287. }
  288.  
  289. cb(this, args);
  290. _realStartPlay.apply(this, args);
  291. };
  292. });
  293. }
  294. }]);
  295.  
  296. return Hooker;
  297. }();
  298.  
  299. var Mocker = function () {
  300. function Mocker() {
  301. _classCallCheck(this, Mocker);
  302. }
  303.  
  304. _createClass(Mocker, null, [{
  305. key: 'mockVip',
  306. value: function mockVip() {
  307. Hooker.hookUpsDataSuccess(function (data) {
  308. if (data.user) {
  309. data.user.vip = true;
  310. } else {
  311. data.user = { vip: true };
  312. }
  313. Logger.log('解除会员画质限制');
  314. });
  315. }
  316. }]);
  317.  
  318. return Mocker;
  319. }();
  320.  
  321. var Blocker = function () {
  322. function Blocker() {
  323. _classCallCheck(this, Blocker);
  324. }
  325.  
  326. _createClass(Blocker, null, [{
  327. key: '_isAdReq',
  328. value: function _isAdReq(url) {
  329. return (/atm\.youku\.com/.test(url)
  330. );
  331. }
  332. }, {
  333. key: 'blockAd',
  334. value: function blockAd() {
  335. var _this6 = this;
  336.  
  337. Hooker.hookGetJsonp(function (args) {
  338. var _args = _slicedToArray(args, 3),
  339. url = _args[0],
  340. /* onload */onerror = _args[2];
  341.  
  342. if (_this6._isAdReq(url)) {
  343. setTimeout(onerror, 0); // async invoke
  344. Logger.log('blocked ad request', url);
  345. return true;
  346. }
  347. });
  348. }
  349. }]);
  350.  
  351. return Blocker;
  352. }();
  353.  
  354. var Patcher = function () {
  355. function Patcher() {
  356. _classCallCheck(this, Patcher);
  357. }
  358.  
  359. _createClass(Patcher, null, [{
  360. key: 'patchQualitySetting',
  361. value: function patchQualitySetting() {
  362. Hooker.hookSkinsViewRender(function (elem) {
  363. var autoRe = /<spvdiv\s+customer="auto"[^<>]*>自动<\/spvdiv>/;
  364. var mp4Re = /<spvdiv\s+customer="mp4"[^<>]*>标清<\/spvdiv>/;
  365. var autoDiv = autoRe.exec(elem.innerHTML)[0];
  366. var hd3Div = autoDiv.replace('auto', 'mp4hd3').replace('自动', '1080P');
  367. elem.innerHTML = elem.innerHTML.replace(autoRe, hd3Div).replace(mp4Re, '$&' + autoDiv);
  368. Logger.log('设置里优先画质增加1080P选项并对齐到当前画质');
  369. });
  370.  
  371. GM_addStyle('\n spvdiv.spv_setting_1080, spvdiv.spv_setting_panel {\n width: 300px !important;\n }\n ');
  372. }
  373. }, {
  374. key: 'patchQualityFallback',
  375. value: function patchQualityFallback() {
  376. Hooker.hookH5PlayerCore(function (exports) {
  377. var SHOWHD = new Map([['flvhd', '标清'], ['3gphd', '标清'], ['mp4hd', '高清'], ['mp4hd2', '超清'], ['mp4hd3', '1080p']]);
  378.  
  379. exports.YoukuH5PlayerCore.prototype._initControlInfo = function () {
  380. if (!this._videoInfo.langcodes) return;
  381.  
  382. var control = this.control;
  383. if (!control.lang || !this._videoInfo.langcodes.includes(control.lang)) {
  384. control.lang = this._videoInfo.langcodes[0];
  385. }
  386.  
  387. var hdcodes = this._videoInfo.hdList[control.lang].hdcodes;
  388. if (!hdcodes.includes(control.hd)) {
  389. // 如果设置的优先画质在当前播放的视频里没有
  390. var hd = control.hd;
  391. control.hd = hdcodes[hdcodes.length - 1]; // 向下选择最高画质(原逻辑是给最渣画质!)
  392. Logger.log('\u4F18\u5148\u753B\u8D28\uFF08' + SHOWHD.get(hd) + '\uFF09\u5728\u5F53\u524D\u64AD\u653E\u7684\u89C6\u9891\u91CC\u6CA1\u6709\uFF0C\u5411\u4E0B\u9009\u62E9\u6700\u9AD8\u753B\u8D28\uFF08' + SHOWHD.get(control.hd) + '\uFF09\u3002');
  393. }
  394.  
  395. control.autoplay = control.autoplay || false;
  396. control.fullscreen = control.fullscreen || false;
  397. };
  398. });
  399. }
  400. }, {
  401. key: 'patchVolumeMemory',
  402. value: function patchVolumeMemory() {
  403. Hooker.hookRealStartPlay(function (that) {
  404. if (0 === parseFloat(localStorage.getItem('spv_volume'))) {
  405. that.UIControl.__proto__.mute.apply(that.UIControl);
  406. } else {
  407. that.UIControl.__proto__.nomute.apply(that.UIControl);
  408. }
  409.  
  410. that.EventManager.on('VolumeChange', function (data) {
  411. localStorage.setItem('spv_volume', data.value);
  412. });
  413.  
  414. Logger.log('开启音量记忆');
  415. });
  416. }
  417. }]);
  418.  
  419. return Patcher;
  420. }();
  421.  
  422. function enableH5Player() {
  423. sessionStorage.setItem('P_l_h5', 1);
  424. Logger.log('启用html5播放器');
  425. }
  426.  
  427. function recoverPlayer() {
  428. sessionStorage.removeItem('P_l_h5');
  429. Logger.log('恢复原播放器');
  430. }
  431.  
  432. //=============================================================================
  433.  
  434. enableH5Player();
  435. window.addEventListener('unload', function () {
  436. return recoverPlayer();
  437. }); // 禁用脚本刷新页面可恢复播放器
  438.  
  439. Blocker.blockAd();
  440. Mocker.mockVip();
  441. Patcher.patchQualitySetting();
  442. Patcher.patchQualityFallback();
  443. Patcher.patchVolumeMemory();
  444. })();