├── .babelrc ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── dist └── iqiyi-player-switch.user.js ├── package-lock.json ├── package.json ├── scripts └── clean.js ├── src ├── cookies.js ├── detector.js ├── faker.js ├── fullscreen.js ├── hooker.js ├── index.js ├── logger.js ├── meta.js ├── outsite.js ├── parsed-data.js ├── patch.js ├── utils.js └── web-fullscreen.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "targets": { 5 | "browsers": ["chrome >= 43", "firefox >= 45", "edge >= 15"] 6 | } 7 | }] 8 | ], 9 | "plugins": [ 10 | [ 11 | "transform-runtime", 12 | { 13 | "helpers": false, 14 | "polyfill": false, 15 | "regenerator": true, 16 | "moduleName": "babel-runtime" 17 | } 18 | ] 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | root: true, 4 | parser: 'babel-eslint', 5 | 'env': { 6 | 'browser': true, 7 | 'node': true, 8 | 'es6': true 9 | }, 10 | 'extends': 'eslint:recommended', 11 | 'parserOptions': { 12 | 'ecmaVersion': 6, 13 | 'sourceType': 'module' 14 | }, 15 | 'rules': { 16 | 'indent': [ 17 | 'error', 18 | 4 19 | ], 20 | 'quotes': [ 21 | 'error', 22 | 'single', 23 | {"allowTemplateLiterals": true} 24 | ], 25 | 'semi': [ 26 | 'error', 27 | 'always' 28 | ], 29 | }, 30 | globals: { 31 | GM_registerMenuCommand: false, 32 | GM_xmlhttpRequest: false, 33 | unsafeWindow: false, 34 | GM_addStyle: false, 35 | GM_getValue: false, 36 | GM_setValue: false, 37 | GM_info: false, 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 gooyie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iqiyi-player-switch 2 | 3 | 爱奇艺flash播放器与html5播放器随意切换,改善html5播放器播放体验。 4 | 5 | ## 脚本管理器兼容 6 | `Greasemonkey`用不了(沙箱限制),`Tampermonkey`和`Violentmonkey`可正常使用。 7 | 8 | ## 安装 9 | * [GitHub](https://raw.githubusercontent.com/gooyie/userscript-iqiyi-player-switch/master/dist/iqiyi-player-switch.user.js) 10 | * [GreasyFork](https://greasyfork.org/zh-CN/scripts/28356-iqiyi-player-switch) 11 | 12 | ## 脚本实现的功能 13 | * 在脚本管理器上添加菜单命令用于播放器切换(默认启用html5播放器) 14 | * firefox 播放`f4v` 15 | * 和谐广告 16 | * 和谐非内嵌水印 17 | * 解除会员清晰度限制 18 | * [外链html5播放](https://github.com/gooyie/userscript-iqiyi-player-switch/issues/7)(不完善) 19 | * 快捷键 20 | * 可选的使用 WebSocket 加载视频 21 | 22 | ## 键盘快捷键 23 | 快捷键仿照`PotPlayer`和`youtube` 24 | 25 | | 按键 | 功能 | 26 | | ---- | ---- | 27 | | 空格 | 播放 / 暂停 | 28 | | enter | 全屏 / 退出全屏 | 29 | | ctrl + enter | 网页全屏 / 退出网页全屏 | 30 | | esc | 退出网页全屏 | 31 | | ↑ | 音量增加 5% | 32 | | ↓ | 音量减少 5% | 33 | | m | 静音 / 取消静音 | 34 | | d | 上一帧 | 35 | | f | 下一帧 | 36 | | ← | 步退5秒 | 37 | | → | 步进5秒 | 38 | | ctrl + ← | 步退30秒 | 39 | | ctrl + → | 步进30秒 | 40 | | shift + ← | 步退1分钟 | 41 | | shift + → | 步进1分钟 | 42 | | ctrl + alt + ← | 步退5分钟 | 43 | | ctrl + alt + → | 步进5分钟 | 44 | | 0 ~ 9 | 定位到视频的 x0%| 45 | | c | 播放速率提高 0.1 | 46 | | x | 播放速率降低 0.1 | 47 | | z | 正常/之前的播放速率 | 48 | | shift + p | 播放上一集 | 49 | | shift + n | 播放下一集 | 50 | 51 | ## 鼠标快捷键 52 | 53 | | 操作 | 条件 | 功能 | 54 | | ---- | ---- | ---- | 55 | | 单击左键 | 在播放区域 | 播放 / 暂停 | 56 | | 双击左键 | 在播放区域 | 全屏切换 | 57 | | ctrl + 双击左键 | 在播放区域 | 网页全屏切换 | 58 | | 滚动滚轮 | 全屏或网页全屏 | 音量调节 | 59 | 60 | ## WebSocket 61 | 在播放器的设置里添加了一个开关,默认为关闭状态。 62 | 63 | ![WebSocket setting](https://user-images.githubusercontent.com/25021141/37519377-d305b5e2-2953-11e8-96e6-b2d63210c479.png) 64 | 65 | 开启就使用 WebSocket 加载视频,可规避ISP的缓存避免被重定向出现 CORS 错误。 66 | 67 | ![blocked by CORS policy](https://user-images.githubusercontent.com/25021141/37519374-d05a6446-2953-11e8-931e-59e31f3e7af3.png) 68 | 69 | 不开启就是按照 iqiyi 的策略,即默认使用 Fetch 或 XHR(firefox),如果一直出错就尝试用 WebSocket。 70 | 71 | **注意:[有些地区的CDN还不支持 WebSocket,启用后会无法播放。](https://github.com/gooyie/userscript-iqiyi-player-switch/issues/21)** 72 | 73 | ## 切换播放器 74 | 菜单命令是要切换过去的播放器 75 | 76 | ![tm-switch](https://user-images.githubusercontent.com/25021141/27002463-abce11aa-4e15-11e7-96d3-00ba314dbfbe.png) 77 | ![vm-switch](https://user-images.githubusercontent.com/25021141/27002466-b3b9407e-4e15-11e7-8c43-c1c7129bd899.png) 78 | -------------------------------------------------------------------------------- /dist/iqiyi-player-switch.user.js: -------------------------------------------------------------------------------- 1 | 2 | // ==UserScript== 3 | // @name iqiyi-player-switch 4 | // @namespace https://github.com/gooyie/userscript-iqiyi-player-switch 5 | // @homepageURL https://github.com/gooyie/userscript-iqiyi-player-switch 6 | // @supportURL https://github.com/gooyie/userscript-iqiyi-player-switch/issues 7 | // @updateURL https://raw.githubusercontent.com/gooyie/userscript-iqiyi-player-switch/master/dist/iqiyi-player-switch.user.js 8 | // @description 爱奇艺flash播放器与html5播放器随意切换,改善html5播放器播放体验。 9 | // @version 1.14.0 10 | // @compatible chrome >= 43 11 | // @compatible firefox >= 45 12 | // @compatible edge >= 15 13 | // @author gooyie 14 | // @license MIT License 15 | // 16 | // @include *://*.iqiyi.com/* 17 | // @include *://v.baidu.com/* 18 | // @include *://music.baidu.com/mv/* 19 | // @include *://www.zybus.com/* 20 | // @grant GM_registerMenuCommand 21 | // @grant GM_xmlhttpRequest 22 | // @grant GM_addStyle 23 | // @grant GM_getValue 24 | // @grant GM_setValue 25 | // @grant GM_info 26 | // @grant unsafeWindow 27 | // @connect qiyi.com 28 | // @run-at document-start 29 | // ==/UserScript== 30 | 31 | /******/ (function(modules) { // webpackBootstrap 32 | /******/ // The module cache 33 | /******/ var installedModules = {}; 34 | /******/ 35 | /******/ // The require function 36 | /******/ function __webpack_require__(moduleId) { 37 | /******/ 38 | /******/ // Check if module is in cache 39 | /******/ if(installedModules[moduleId]) { 40 | /******/ return installedModules[moduleId].exports; 41 | /******/ } 42 | /******/ // Create a new module (and put it into the cache) 43 | /******/ var module = installedModules[moduleId] = { 44 | /******/ i: moduleId, 45 | /******/ l: false, 46 | /******/ exports: {} 47 | /******/ }; 48 | /******/ 49 | /******/ // Execute the module function 50 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 51 | /******/ 52 | /******/ // Flag the module as loaded 53 | /******/ module.l = true; 54 | /******/ 55 | /******/ // Return the exports of the module 56 | /******/ return module.exports; 57 | /******/ } 58 | /******/ 59 | /******/ 60 | /******/ // expose the modules object (__webpack_modules__) 61 | /******/ __webpack_require__.m = modules; 62 | /******/ 63 | /******/ // expose the module cache 64 | /******/ __webpack_require__.c = installedModules; 65 | /******/ 66 | /******/ // define getter function for harmony exports 67 | /******/ __webpack_require__.d = function(exports, name, getter) { 68 | /******/ if(!__webpack_require__.o(exports, name)) { 69 | /******/ Object.defineProperty(exports, name, { 70 | /******/ configurable: false, 71 | /******/ enumerable: true, 72 | /******/ get: getter 73 | /******/ }); 74 | /******/ } 75 | /******/ }; 76 | /******/ 77 | /******/ // getDefaultExport function for compatibility with non-harmony modules 78 | /******/ __webpack_require__.n = function(module) { 79 | /******/ var getter = module && module.__esModule ? 80 | /******/ function getDefault() { return module['default']; } : 81 | /******/ function getModuleExports() { return module; }; 82 | /******/ __webpack_require__.d(getter, 'a', getter); 83 | /******/ return getter; 84 | /******/ }; 85 | /******/ 86 | /******/ // Object.prototype.hasOwnProperty.call 87 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 88 | /******/ 89 | /******/ // __webpack_public_path__ 90 | /******/ __webpack_require__.p = ""; 91 | /******/ 92 | /******/ // Load entry module and return exports 93 | /******/ return __webpack_require__(__webpack_require__.s = 5); 94 | /******/ }) 95 | /************************************************************************/ 96 | /******/ ([ 97 | /* 0 */ 98 | /***/ (function(module, exports, __webpack_require__) { 99 | 100 | "use strict"; 101 | 102 | 103 | Object.defineProperty(exports, "__esModule", { 104 | value: true 105 | }); 106 | 107 | 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"); } }; }(); 108 | 109 | 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; }; }(); 110 | 111 | var _logger = __webpack_require__(1); 112 | 113 | var _logger2 = _interopRequireDefault(_logger); 114 | 115 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 116 | 117 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 118 | 119 | var Hooker = function () { 120 | function Hooker() { 121 | _classCallCheck(this, Hooker); 122 | } 123 | 124 | _createClass(Hooker, null, [{ 125 | key: '_hookCall', 126 | value: function _hookCall(cb) { 127 | var call = Function.prototype.call; 128 | Function.prototype.call = function () { 129 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 130 | args[_key] = arguments[_key]; 131 | } 132 | 133 | var ret = call.apply(this, args); 134 | try { 135 | if (args && cb(args)) { 136 | Function.prototype.call = call; 137 | cb = function cb() {}; 138 | _logger2.default.info(`The native function call has been restored`); 139 | } 140 | } catch (err) { 141 | _logger2.default.error(err.stack); 142 | } 143 | return ret; 144 | }; 145 | this._hookCall = null; 146 | } 147 | }, { 148 | key: '_isModuleCall', 149 | value: function _isModuleCall(args) { 150 | // module.exports, module, module.exports, require 151 | return args.length === 4 && args[1] && Object.getPrototypeOf(args[1]) === Object.prototype && args[1].hasOwnProperty('exports'); 152 | } 153 | }, { 154 | key: '_hookModuleCall', 155 | value: function _hookModuleCall(cb, pred) { 156 | var _this = this; 157 | 158 | var callbacksMap = new Map([[pred, [cb]]]); 159 | this._hookCall(function (args) { 160 | if (!_this._isModuleCall(args)) return; 161 | 162 | var exports = args[1].exports; 163 | var _iteratorNormalCompletion = true; 164 | var _didIteratorError = false; 165 | var _iteratorError = undefined; 166 | 167 | try { 168 | for (var _iterator = callbacksMap[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 169 | var _ref = _step.value; 170 | 171 | var _ref2 = _slicedToArray(_ref, 2); 172 | 173 | var _pred = _ref2[0]; 174 | var callbacks = _ref2[1]; 175 | 176 | if (!_pred.apply(_this, [exports])) continue; 177 | callbacks.forEach(function (cb) { 178 | return cb(exports, args); 179 | }); 180 | _this.keepalive || callbacksMap.delete(_pred); 181 | !callbacksMap.size && (_this._hookModuleCall = null); 182 | break; 183 | } 184 | } catch (err) { 185 | _didIteratorError = true; 186 | _iteratorError = err; 187 | } finally { 188 | try { 189 | if (!_iteratorNormalCompletion && _iterator.return) { 190 | _iterator.return(); 191 | } 192 | } finally { 193 | if (_didIteratorError) { 194 | throw _iteratorError; 195 | } 196 | } 197 | } 198 | 199 | return !callbacksMap.size; 200 | }); 201 | 202 | this._hookModuleCall = function (cb, pred) { 203 | if (callbacksMap.has(pred)) { 204 | callbacksMap.get(pred).push(cb); 205 | } else { 206 | callbacksMap.set(pred, [cb]); 207 | } 208 | }; 209 | } 210 | }, { 211 | key: '_isJqueryModuleCall', 212 | value: function _isJqueryModuleCall(exports) { 213 | return exports.hasOwnProperty('fn') && exports.fn.hasOwnProperty('jquery'); 214 | } 215 | }, { 216 | key: 'hookJquery', 217 | value: function hookJquery() { 218 | var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {}; 219 | 220 | this._hookModuleCall(cb, this._isJqueryModuleCall); 221 | } 222 | }, { 223 | key: 'hookJqueryAjax', 224 | value: function hookJqueryAjax(cb) { 225 | this.hookJquery(function (exports) { 226 | var ajax = exports.ajax.bind(exports); 227 | exports.ajax = function (url) { 228 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 229 | 230 | if (typeof url === 'object') { 231 | var _ref3 = [url.url, url]; 232 | url = _ref3[0]; 233 | options = _ref3[1]; 234 | } 235 | var isHijacked = cb(url, options); 236 | if (isHijacked) return; 237 | return ajax(url, options); 238 | }; 239 | }); 240 | } 241 | }, { 242 | key: '_isHttpModuleCall', 243 | value: function _isHttpModuleCall(exports) { 244 | return exports.hasOwnProperty('jsonp') && exports.hasOwnProperty('ajax'); 245 | } 246 | }, { 247 | key: 'hookHttp', 248 | value: function hookHttp(cb) { 249 | this._hookModuleCall(cb, this._isHttpModuleCall); 250 | } 251 | }, { 252 | key: 'hookHttpJsonp', 253 | value: function hookHttpJsonp(cb) { 254 | this.hookHttp(function (exports) { 255 | var jsonp = exports.jsonp.bind(exports); 256 | exports.jsonp = function (options) { 257 | var isHijacked = cb(options); 258 | if (isHijacked) return; 259 | return jsonp(options); 260 | }; 261 | }); 262 | } 263 | }, { 264 | key: '_isLogoModuleCall', 265 | value: function _isLogoModuleCall(exports) { 266 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('showLogo'); 267 | } 268 | }, { 269 | key: 'hookLogo', 270 | value: function hookLogo(cb) { 271 | this._hookModuleCall(cb, this._isLogoModuleCall); 272 | } 273 | }, { 274 | key: '_isFullScreenModuleCall', 275 | value: function _isFullScreenModuleCall(exports) { 276 | return exports.__proto__ && exports.__proto__.hasOwnProperty('isFullScreen'); 277 | } 278 | }, { 279 | key: 'hookFullScreen', 280 | value: function hookFullScreen(cb) { 281 | this._hookModuleCall(cb, this._isFullScreenModuleCall); 282 | } 283 | }, { 284 | key: '_isWebFullScreenModuleCall', 285 | value: function _isWebFullScreenModuleCall(exports) { 286 | return exports.__proto__ && exports.__proto__.hasOwnProperty('isWebFullScreen'); 287 | } 288 | }, { 289 | key: 'hookWebFullScreen', 290 | value: function hookWebFullScreen(cb) { 291 | this._hookModuleCall(cb, this._isWebFullScreenModuleCall); 292 | } 293 | }, { 294 | key: 'hookWebFullScreenInit', 295 | value: function hookWebFullScreenInit(cb) { 296 | this.hookWebFullScreen(function (exports) { 297 | var init = exports.__proto__.init; 298 | exports.__proto__.init = function (wrapper, btn) { 299 | cb(this, wrapper, btn); 300 | init.apply(this, [wrapper, btn]); 301 | }; 302 | }); 303 | } 304 | }, { 305 | key: '_isPluginControlsModuleCall', 306 | value: function _isPluginControlsModuleCall(exports) { 307 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('initFullScreen'); 308 | } 309 | }, { 310 | key: 'hookPluginControls', 311 | value: function hookPluginControls(cb) { 312 | this._hookModuleCall(cb, this._isPluginControlsModuleCall); 313 | } 314 | }, { 315 | key: 'hookPluginControlsInit', 316 | value: function hookPluginControlsInit(cb) { 317 | this.hookPluginControls(function (exports) { 318 | var init = exports.prototype.init; 319 | exports.prototype.init = function () { 320 | cb(this); 321 | init.apply(this); 322 | }; 323 | }); 324 | } 325 | }, { 326 | key: 'hookInitFullScreen', 327 | value: function hookInitFullScreen(cb) { 328 | this.hookPluginControls(function (exports) { 329 | var initFullScreen = exports.prototype.initFullScreen; 330 | exports.prototype.initFullScreen = function () { 331 | cb(this); 332 | initFullScreen.apply(this); 333 | }; 334 | }); 335 | } 336 | }, { 337 | key: '_isCoreModuleCall', 338 | value: function _isCoreModuleCall(exports) { 339 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('getdefaultvds') && exports.prototype.hasOwnProperty('getMovieInfo'); 340 | } 341 | }, { 342 | key: 'hookCore', 343 | value: function hookCore(cb) { 344 | this._hookModuleCall(cb, this._isCoreModuleCall); 345 | } 346 | }, { 347 | key: '_isSkinBaseModuleCall', 348 | value: function _isSkinBaseModuleCall(exports) { 349 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('_checkPlugin'); 350 | } 351 | }, { 352 | key: 'hookSkinBase', 353 | value: function hookSkinBase(cb) { 354 | this._hookModuleCall(cb, this._isSkinBaseModuleCall); 355 | } 356 | }, { 357 | key: '_isPluginHotKeysModuleCall', 358 | value: function _isPluginHotKeysModuleCall(exports) { 359 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('_keydown'); 360 | } 361 | }, { 362 | key: 'hookPluginHotKeys', 363 | value: function hookPluginHotKeys(cb) { 364 | this._hookModuleCall(cb, this._isPluginHotKeysModuleCall); 365 | } 366 | }, { 367 | key: '_isFragmentModuleCall', 368 | value: function _isFragmentModuleCall(exports) { 369 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('parseData'); 370 | } 371 | }, { 372 | key: 'hookFragment', 373 | value: function hookFragment(cb) { 374 | this._hookModuleCall(cb, this._isFragmentModuleCall); 375 | } 376 | }, { 377 | key: 'hookParseData', 378 | value: function hookParseData(cb) { 379 | this.hookFragment(function (exports) { 380 | var parseData = exports.prototype.parseData; 381 | exports.prototype.parseData = function () { 382 | for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 383 | args[_key2] = arguments[_key2]; 384 | } 385 | 386 | parseData.apply(this, args); 387 | cb(this); 388 | }; 389 | }); 390 | } 391 | }, { 392 | key: '_isUserModuleCall', 393 | value: function _isUserModuleCall(exports) { 394 | return exports.__proto__ && exports.__proto__.hasOwnProperty('isVip'); 395 | } 396 | }, { 397 | key: 'hookUser', 398 | value: function hookUser(cb) { 399 | this._hookModuleCall(cb, this._isUserModuleCall); 400 | } 401 | }, { 402 | key: '_isShowRequestModuleCall', 403 | value: function _isShowRequestModuleCall(exports) { 404 | return 'function' === typeof exports && exports.compressRequestKey && exports.prototype.hasOwnProperty('request'); 405 | } 406 | }, { 407 | key: 'hookShowRequest', 408 | value: function hookShowRequest(cb) { 409 | this._hookModuleCall(cb, this._isShowRequestModuleCall); 410 | } 411 | }, { 412 | key: '_isDefaultSkinModuleCall', 413 | value: function _isDefaultSkinModuleCall(exports) { 414 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('_initDBClicks'); 415 | } 416 | }, { 417 | key: 'hookDefaultSkin', 418 | value: function hookDefaultSkin(cb) { 419 | this._hookModuleCall(cb, this._isDefaultSkinModuleCall); 420 | } 421 | }, { 422 | key: '_isConfigModuleCall', 423 | value: function _isConfigModuleCall(exports) { 424 | return exports.loadType && exports.dispatchCfg; 425 | } 426 | }, { 427 | key: 'hookConfig', 428 | value: function hookConfig(cb) { 429 | this._hookModuleCall(cb, this._isConfigModuleCall); 430 | } 431 | }]); 432 | 433 | return Hooker; 434 | }(); 435 | 436 | Hooker.keepalive = false; 437 | 438 | exports.default = Hooker; 439 | 440 | /***/ }), 441 | /* 1 */ 442 | /***/ (function(module, exports, __webpack_require__) { 443 | 444 | "use strict"; 445 | 446 | 447 | Object.defineProperty(exports, "__esModule", { 448 | value: true 449 | }); 450 | 451 | 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; }; }(); 452 | 453 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 454 | 455 | /* eslint-disable no-console */ 456 | var Logger = function () { 457 | function Logger(tag) { 458 | _classCallCheck(this, Logger); 459 | 460 | this._tag = tag; 461 | } 462 | 463 | _createClass(Logger, [{ 464 | key: 'log', 465 | value: function log() { 466 | var _console; 467 | 468 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 469 | args[_key] = arguments[_key]; 470 | } 471 | 472 | (_console = console).log.apply(_console, [this.tag + args.shift()].concat(args)); 473 | } 474 | }, { 475 | key: 'info', 476 | value: function info() { 477 | var _console2; 478 | 479 | for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 480 | args[_key2] = arguments[_key2]; 481 | } 482 | 483 | (_console2 = console).log.apply(_console2, ['%c' + this.tag + '%c' + args.shift(), 'color: green; font-weight: bolder', 'color: blue'].concat(args)); 484 | } 485 | }, { 486 | key: 'debug', 487 | value: function debug() { 488 | var _console3; 489 | 490 | for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { 491 | args[_key3] = arguments[_key3]; 492 | } 493 | 494 | (_console3 = console).debug.apply(_console3, [this.tag + args.shift()].concat(args)); 495 | } 496 | }, { 497 | key: 'warn', 498 | value: function warn() { 499 | var _console4; 500 | 501 | for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { 502 | args[_key4] = arguments[_key4]; 503 | } 504 | 505 | (_console4 = console).warn.apply(_console4, [this.tag + args.shift()].concat(args)); 506 | } 507 | }, { 508 | key: 'error', 509 | value: function error() { 510 | var _console5; 511 | 512 | for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { 513 | args[_key5] = arguments[_key5]; 514 | } 515 | 516 | (_console5 = console).error.apply(_console5, [this.tag + args.shift()].concat(args)); 517 | } 518 | }, { 519 | key: 'tag', 520 | get: function get() { 521 | return this._tag; 522 | } 523 | }]); 524 | 525 | return Logger; 526 | }(); 527 | 528 | exports.default = new Logger(`[${GM_info.script.name}]`); 529 | 530 | /***/ }), 531 | /* 2 */ 532 | /***/ (function(module, exports, __webpack_require__) { 533 | 534 | "use strict"; 535 | 536 | 537 | Object.defineProperty(exports, "__esModule", { 538 | value: true 539 | }); 540 | 541 | 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; }; }(); 542 | 543 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 544 | 545 | var Detector = function () { 546 | function Detector() { 547 | _classCallCheck(this, Detector); 548 | } 549 | 550 | _createClass(Detector, null, [{ 551 | key: 'isSupportHtml5', 552 | value: function isSupportHtml5() { 553 | var v = document.createElement('video'); 554 | 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"')); 555 | } 556 | }, { 557 | key: 'isSupportVms', 558 | value: function isSupportVms() { 559 | return !!(window.MediaSource && window.URL && window.WebSocket && window.ReadableStream && (window.RTCSessionDescription || window.webkitRTCSessionDescription) && (window.RTCPeerConnection || window.webkitRTCPeerConnection) && (window.RTCIceCandidate || window.webkitRTCIceCandidate)); 560 | } 561 | }, { 562 | key: 'isSupportM3u8', 563 | value: function isSupportM3u8() { 564 | var v = document.createElement('video'); 565 | return !!(v.canPlayType('application/x-mpegurl') && v.canPlayType('application/vnd.apple.mpegurl')); 566 | } 567 | }, { 568 | key: 'isChrome', 569 | value: function isChrome() { 570 | return (/chrome/i.test(navigator.userAgent) 571 | ); 572 | } 573 | }, { 574 | key: 'isFirefox', 575 | value: function isFirefox() { 576 | return (/firefox/i.test(navigator.userAgent) 577 | ); 578 | } 579 | }, { 580 | key: 'isEdge', 581 | value: function isEdge() { 582 | return (/edge/i.test(navigator.userAgent) 583 | ); 584 | } 585 | }, { 586 | key: 'isInIFrame', 587 | value: function isInIFrame() { 588 | return window.top !== window.self; 589 | } 590 | }, { 591 | key: 'isOutsite', 592 | value: function isOutsite() { 593 | return !/\.iqiyi\.com$/.test(location.host); 594 | } 595 | }, { 596 | key: 'isOutsideLink', 597 | value: function isOutsideLink() { 598 | return location.hash === '#outsidelink'; 599 | } 600 | }, { 601 | key: 'hasFlashPlugin', 602 | value: function hasFlashPlugin() { 603 | var plugins = unsafeWindow.navigator.plugins; 604 | return !!(plugins['Shockwave Flash'] && plugins['Shockwave Flash'].description); 605 | } 606 | }]); 607 | 608 | return Detector; 609 | }(); 610 | 611 | exports.default = Detector; 612 | 613 | /***/ }), 614 | /* 3 */ 615 | /***/ (function(module, exports, __webpack_require__) { 616 | 617 | "use strict"; 618 | 619 | 620 | Object.defineProperty(exports, "__esModule", { 621 | value: true 622 | }); 623 | 624 | 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; }; }(); 625 | 626 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 627 | 628 | var Faker = function () { 629 | function Faker() { 630 | _classCallCheck(this, Faker); 631 | } 632 | 633 | _createClass(Faker, null, [{ 634 | key: 'fakeMacPlatform', 635 | value: function fakeMacPlatform() { 636 | var PLAFORM_MAC = 'mac'; 637 | Object.defineProperty(unsafeWindow.navigator, 'platform', { get: function get() { 638 | return PLAFORM_MAC; 639 | } }); 640 | } 641 | }, { 642 | key: 'fakeSafari', 643 | value: function fakeSafari() { 644 | var UA_SAFARY = 'safari'; 645 | Object.defineProperty(unsafeWindow.navigator, 'userAgent', { get: function get() { 646 | return UA_SAFARY; 647 | } }); 648 | } 649 | }, { 650 | key: 'fakeChrome', 651 | value: function fakeChrome() { 652 | var ver = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; 653 | 654 | var UA_CHROME = `Chrome/${ver}`; 655 | Object.defineProperty(unsafeWindow.navigator, 'userAgent', { get: function get() { 656 | return UA_CHROME; 657 | } }); 658 | } 659 | }, { 660 | key: 'fakeFlashPlugin', 661 | value: function fakeFlashPlugin() { 662 | var plugin = { 663 | description: 'Shockwave Flash 26.0 r0', 664 | filename: 'pepflashplayer64_26_0_0_131.dll', 665 | length: 0, 666 | name: 'Shockwave Flash' 667 | }; 668 | 669 | Reflect.setPrototypeOf(plugin, Plugin.prototype); 670 | unsafeWindow.navigator.plugins['Shockwave Flash'] = plugin; 671 | } 672 | }]); 673 | 674 | return Faker; 675 | }(); 676 | 677 | exports.default = Faker; 678 | 679 | /***/ }), 680 | /* 4 */ 681 | /***/ (function(module, exports) { 682 | 683 | var g; 684 | 685 | // This works in non-strict mode 686 | g = (function() { 687 | return this; 688 | })(); 689 | 690 | try { 691 | // This works if eval is allowed (see CSP) 692 | g = g || Function("return this")() || (1,eval)("this"); 693 | } catch(e) { 694 | // This works if the window reference is available 695 | if(typeof window === "object") 696 | g = window; 697 | } 698 | 699 | // g can still be undefined, but nothing to do about it... 700 | // We return undefined, instead of nothing here, so it's 701 | // easier to handle this case. if(!global) { ...} 702 | 703 | module.exports = g; 704 | 705 | 706 | /***/ }), 707 | /* 5 */ 708 | /***/ (function(module, exports, __webpack_require__) { 709 | 710 | "use strict"; 711 | 712 | 713 | 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"); } }; }(); 714 | 715 | var _logger = __webpack_require__(1); 716 | 717 | var _logger2 = _interopRequireDefault(_logger); 718 | 719 | var _cookies = __webpack_require__(6); 720 | 721 | var _cookies2 = _interopRequireDefault(_cookies); 722 | 723 | var _detector = __webpack_require__(2); 724 | 725 | var _detector2 = _interopRequireDefault(_detector); 726 | 727 | var _faker = __webpack_require__(3); 728 | 729 | var _faker2 = _interopRequireDefault(_faker); 730 | 731 | var _outsite = __webpack_require__(7); 732 | 733 | var _patch = __webpack_require__(12); 734 | 735 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 736 | 737 | var PLAYER_TYPE = { 738 | Html5VOD: 'h5_VOD', 739 | FlashVOD: 'flash_VOD' 740 | }; 741 | 742 | function forceHtml5() { 743 | _cookies2.default.set('player_forcedType', PLAYER_TYPE.Html5VOD, { domain: '.iqiyi.com' }); 744 | _logger2.default.info(`The 'player_forcedType' cookie has been set as '${PLAYER_TYPE.Html5VOD}'`); 745 | } 746 | 747 | function forceFlash() { 748 | _cookies2.default.set('player_forcedType', PLAYER_TYPE.FlashVOD, { domain: '.iqiyi.com' }); 749 | _logger2.default.info(`The 'player_forcedType' cookie has been set as '${PLAYER_TYPE.FlashVOD}'`); 750 | } 751 | 752 | function clean() { 753 | _cookies2.default.remove('player_forcedType', { domain: '.iqiyi.com' }); 754 | _logger2.default.info(`Removed the 'player_forcedType' cookie`); 755 | } 756 | 757 | function switchTo(type) { 758 | _logger2.default.info(`Switching to ${type} ...`); 759 | GM_setValue('player_forcedType', type); 760 | document.location.reload(); 761 | } 762 | 763 | function registerMenu() { 764 | var MENU_NAME = { 765 | HTML5: 'HTML5播放器', 766 | FLASH: 'Flash播放器' 767 | }; 768 | 769 | var currType = GM_getValue('player_forcedType', PLAYER_TYPE.Html5VOD); // 默认为Html5播放器,免去切换。 770 | 771 | var _ref = currType === PLAYER_TYPE.Html5VOD ? [PLAYER_TYPE.FlashVOD, MENU_NAME.FLASH] : [PLAYER_TYPE.Html5VOD, MENU_NAME.HTML5], 772 | _ref2 = _slicedToArray(_ref, 2), 773 | type = _ref2[0], 774 | name = _ref2[1]; 775 | 776 | GM_registerMenuCommand(name, function () { 777 | return switchTo(type); 778 | }, null); 779 | _logger2.default.info(`Registered the menu.`); 780 | } 781 | 782 | function mustKeepHooking() { 783 | return location.search.includes('list'); // https://github.com/gooyie/userscript-iqiyi-player-switch/issues/15 784 | } 785 | 786 | //============================================================================= 787 | 788 | registerMenu(); 789 | 790 | var currType = GM_getValue('player_forcedType', PLAYER_TYPE.Html5VOD); 791 | if (currType === PLAYER_TYPE.Html5VOD) { 792 | if (_detector2.default.isSupportHtml5()) { 793 | if (_detector2.default.isOutsite()) { 794 | (0, _outsite.replaceFlash)(); 795 | } else { 796 | forceHtml5(); 797 | 798 | if (_detector2.default.isFirefox()) { 799 | // Fake Chrome with a version number less than 43 800 | // to use the data engine to play videos better than HD and to use the XHR loader 801 | // because Firefox has not yet implemented ReadableStream to support the Fetch loader. 802 | _faker2.default.fakeChrome(42); 803 | } 804 | 805 | if (mustKeepHooking()) { 806 | _patch.keepHookingPatch.install(); 807 | } 808 | _patch.adsPatch.install(); 809 | _patch.controlsPatch.install(); 810 | _patch.watermarksPatch.install(); 811 | _patch.vipPatch.install(); 812 | _patch.keyShortcutsPatch.install(); 813 | _patch.mouseShortcutsPatch.install(); 814 | _patch.useWebSocketLoaderPatch.install(); 815 | 816 | if (_detector2.default.isInIFrame() && _detector2.default.isOutsideLink()) { 817 | (0, _outsite.adaptIframe)(); 818 | } 819 | } 820 | } else { 821 | alert('╮(╯▽╰)╭ 你的浏览器播放不了html5视频~~~~'); 822 | } 823 | } else { 824 | forceFlash(); 825 | } 826 | 827 | window.addEventListener('unload', function () { 828 | return clean(); 829 | }); 830 | 831 | /***/ }), 832 | /* 6 */ 833 | /***/ (function(module, exports, __webpack_require__) { 834 | 835 | "use strict"; 836 | 837 | 838 | Object.defineProperty(exports, "__esModule", { 839 | value: true 840 | }); 841 | 842 | 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; }; }(); 843 | 844 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 845 | 846 | var Cookies = function () { 847 | function Cookies() { 848 | _classCallCheck(this, Cookies); 849 | } 850 | 851 | _createClass(Cookies, null, [{ 852 | key: 'get', 853 | value: function get(key) { 854 | var value = void 0; 855 | if (new RegExp('^[^\\x00-\\x20\\x7f\\(\\)<>@,;:\\\\\\"\\[\\]\\?=\\{\\}\\/\\u0080-\\uffff]+$').test(key)) { 856 | // eslint-disable-line no-control-regex 857 | var re = new RegExp('(^| )' + key + '=([^;]*)(;|$)'); 858 | var rs = re.exec(document.cookie); 859 | value = rs ? rs[2] : ''; 860 | } 861 | return value ? decodeURIComponent(value) : ''; 862 | } 863 | }, { 864 | key: 'set', 865 | value: function set(k, v) { 866 | var o = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 867 | 868 | var n = o.expires; 869 | if ('number' == typeof o.expires) { 870 | n = new Date(); 871 | n.setTime(n.getTime() + o.expires); 872 | } 873 | var key = k; 874 | var value = encodeURIComponent(v); 875 | var path = o.path ? '; path=' + o.path : ''; 876 | var expires = n ? '; expires=' + n.toGMTString() : ''; 877 | var domain = o.domain ? '; domain=' + o.domain : ''; 878 | document.cookie = `${key}=${value}${path}${expires}${domain}`; 879 | } 880 | }, { 881 | key: 'remove', 882 | value: function remove(k) { 883 | var o = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 884 | 885 | o.expires = new Date(0); 886 | this.set(k, '', o); 887 | } 888 | }]); 889 | 890 | return Cookies; 891 | }(); 892 | 893 | exports.default = Cookies; 894 | 895 | /***/ }), 896 | /* 7 */ 897 | /***/ (function(module, exports, __webpack_require__) { 898 | 899 | "use strict"; 900 | 901 | 902 | Object.defineProperty(exports, "__esModule", { 903 | value: true 904 | }); 905 | exports.adaptIframe = exports.replaceFlash = undefined; 906 | 907 | var _regenerator = __webpack_require__(8); 908 | 909 | var _regenerator2 = _interopRequireDefault(_regenerator); 910 | 911 | var embedSrc = function () { 912 | var _ref2 = _asyncToGenerator( /*#__PURE__*/_regenerator2.default.mark(function _callee(targetNode, _ref) { 913 | var tvid = _ref.tvid, 914 | vid = _ref.vid; 915 | var url; 916 | return _regenerator2.default.wrap(function _callee$(_context) { 917 | while (1) { 918 | switch (_context.prev = _context.next) { 919 | case 0: 920 | targetNode.innerHTML = `
正在获取视频源...
`; 921 | 922 | _context.prev = 1; 923 | _context.next = 4; 924 | return (0, _utils.getVideoUrl)(tvid, vid); 925 | 926 | case 4: 927 | url = _context.sent; 928 | 929 | _logger2.default.info('source url: %s', url); 930 | targetNode.innerHTML = ``; 931 | _context.next = 12; 932 | break; 933 | 934 | case 9: 935 | _context.prev = 9; 936 | _context.t0 = _context['catch'](1); 937 | 938 | targetNode.innerHTML = `

获取视频源出错!

${_context.t0.message}

`; 939 | 940 | case 12: 941 | case 'end': 942 | return _context.stop(); 943 | } 944 | } 945 | }, _callee, this, [[1, 9]]); 946 | })); 947 | 948 | return function embedSrc(_x, _x2) { 949 | return _ref2.apply(this, arguments); 950 | }; 951 | }(); 952 | 953 | var _logger = __webpack_require__(1); 954 | 955 | var _logger2 = _interopRequireDefault(_logger); 956 | 957 | var _hooker = __webpack_require__(0); 958 | 959 | var _hooker2 = _interopRequireDefault(_hooker); 960 | 961 | var _faker = __webpack_require__(3); 962 | 963 | var _faker2 = _interopRequireDefault(_faker); 964 | 965 | var _detector = __webpack_require__(2); 966 | 967 | var _detector2 = _interopRequireDefault(_detector); 968 | 969 | var _utils = __webpack_require__(11); 970 | 971 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 972 | 973 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } 974 | 975 | function replaceFlash() { 976 | if (!_detector2.default.hasFlashPlugin()) _faker2.default.fakeFlashPlugin(); 977 | 978 | var observer = new MutationObserver(function (records, self) { 979 | var _iteratorNormalCompletion = true; 980 | var _didIteratorError = false; 981 | var _iteratorError = undefined; 982 | 983 | try { 984 | for (var _iterator = records[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 985 | var record = _step.value; 986 | 987 | if (record.type !== 'childList' || !record.addedNodes) continue; 988 | 989 | var _iteratorNormalCompletion2 = true; 990 | var _didIteratorError2 = false; 991 | var _iteratorError2 = undefined; 992 | 993 | try { 994 | for (var _iterator2 = record.addedNodes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { 995 | var node = _step2.value; 996 | 997 | if (node.nodeName !== 'OBJECT' && node.nodeName !== 'EMBED') continue; 998 | _logger2.default.info('found node', node); 999 | 1000 | var text = node.outerHTML; 1001 | var vid = (0, _utils.findVid)(text); 1002 | var tvid = (0, _utils.findTvid)(text); 1003 | 1004 | if (tvid && vid) { 1005 | _logger2.default.info('found tvid: %s, vid: %s', tvid, vid); 1006 | embedSrc(node.parentNode, { tvid, vid }); 1007 | self.disconnect(); 1008 | _logger2.default.info('stoped observation'); 1009 | } 1010 | } 1011 | } catch (err) { 1012 | _didIteratorError2 = true; 1013 | _iteratorError2 = err; 1014 | } finally { 1015 | try { 1016 | if (!_iteratorNormalCompletion2 && _iterator2.return) { 1017 | _iterator2.return(); 1018 | } 1019 | } finally { 1020 | if (_didIteratorError2) { 1021 | throw _iteratorError2; 1022 | } 1023 | } 1024 | } 1025 | } 1026 | } catch (err) { 1027 | _didIteratorError = true; 1028 | _iteratorError = err; 1029 | } finally { 1030 | try { 1031 | if (!_iteratorNormalCompletion && _iterator.return) { 1032 | _iterator.return(); 1033 | } 1034 | } finally { 1035 | if (_didIteratorError) { 1036 | throw _iteratorError; 1037 | } 1038 | } 1039 | } 1040 | }); 1041 | 1042 | observer.observe(document.body || document.documentElement, { subtree: true, childList: true }); 1043 | _logger2.default.info('started observation'); 1044 | } 1045 | 1046 | function adaptIframe() { 1047 | var style = ` 1048 | body[class|="qypage"] { 1049 | overflow: hidden !important; 1050 | background: #000 !important; 1051 | visibility: hidden; 1052 | } 1053 | 1054 | .mod-func { 1055 | display: none !important; 1056 | } 1057 | 1058 | .${GM_info.script.name}.info { 1059 | width: 20em; 1060 | height: 5em; 1061 | position: absolute; 1062 | top: 0; 1063 | bottom: 0; 1064 | left: 0; 1065 | right: 0; 1066 | margin: auto; 1067 | text-align: center; 1068 | line-height: 5em; 1069 | font-size: 1em; 1070 | color: #ccc; 1071 | } 1072 | 1073 | .${GM_info.script.name}.error { 1074 | height: 3em; 1075 | position: absolute; 1076 | top: 0; 1077 | bottom: 0; 1078 | left: 0; 1079 | right: 0; 1080 | margin: auto; 1081 | text-align: center; 1082 | font-size: 1em; 1083 | color: #c00; 1084 | } 1085 | `; 1086 | 1087 | GM_addStyle(style); 1088 | 1089 | _hooker2.default.hookWebFullScreen(function (exports) { 1090 | var init = exports.__proto__.init; 1091 | exports.__proto__.init = function (wrapper, btn) { 1092 | init.apply(this, [wrapper, btn]); 1093 | this.enter(); 1094 | 1095 | btn[0].style.display = 'none'; 1096 | document.body.style.visibility = 'visible'; 1097 | }; 1098 | 1099 | exports.__proto__.exit = function () {}; 1100 | }); 1101 | 1102 | _hooker2.default.hookCore(function (exports) { 1103 | exports.prototype.hasNextVideo = function () { 1104 | return null; 1105 | }; 1106 | }); 1107 | } 1108 | 1109 | exports.replaceFlash = replaceFlash; 1110 | exports.adaptIframe = adaptIframe; 1111 | 1112 | /***/ }), 1113 | /* 8 */ 1114 | /***/ (function(module, exports, __webpack_require__) { 1115 | 1116 | module.exports = __webpack_require__(9); 1117 | 1118 | 1119 | /***/ }), 1120 | /* 9 */ 1121 | /***/ (function(module, exports, __webpack_require__) { 1122 | 1123 | /* WEBPACK VAR INJECTION */(function(global) {// This method of obtaining a reference to the global object needs to be 1124 | // kept identical to the way it is obtained in runtime.js 1125 | var g = 1126 | typeof global === "object" ? global : 1127 | typeof window === "object" ? window : 1128 | typeof self === "object" ? self : this; 1129 | 1130 | // Use `getOwnPropertyNames` because not all browsers support calling 1131 | // `hasOwnProperty` on the global `self` object in a worker. See #183. 1132 | var hadRuntime = g.regeneratorRuntime && 1133 | Object.getOwnPropertyNames(g).indexOf("regeneratorRuntime") >= 0; 1134 | 1135 | // Save the old regeneratorRuntime in case it needs to be restored later. 1136 | var oldRuntime = hadRuntime && g.regeneratorRuntime; 1137 | 1138 | // Force reevalutation of runtime.js. 1139 | g.regeneratorRuntime = undefined; 1140 | 1141 | module.exports = __webpack_require__(10); 1142 | 1143 | if (hadRuntime) { 1144 | // Restore the original runtime. 1145 | g.regeneratorRuntime = oldRuntime; 1146 | } else { 1147 | // Remove the global property added by runtime.js. 1148 | try { 1149 | delete g.regeneratorRuntime; 1150 | } catch(e) { 1151 | g.regeneratorRuntime = undefined; 1152 | } 1153 | } 1154 | 1155 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4))) 1156 | 1157 | /***/ }), 1158 | /* 10 */ 1159 | /***/ (function(module, exports, __webpack_require__) { 1160 | 1161 | /* WEBPACK VAR INJECTION */(function(global) {/** 1162 | * Copyright (c) 2014, Facebook, Inc. 1163 | * All rights reserved. 1164 | * 1165 | * This source code is licensed under the BSD-style license found in the 1166 | * https://raw.github.com/facebook/regenerator/master/LICENSE file. An 1167 | * additional grant of patent rights can be found in the PATENTS file in 1168 | * the same directory. 1169 | */ 1170 | 1171 | !(function(global) { 1172 | "use strict"; 1173 | 1174 | var Op = Object.prototype; 1175 | var hasOwn = Op.hasOwnProperty; 1176 | var undefined; // More compressible than void 0. 1177 | var $Symbol = typeof Symbol === "function" ? Symbol : {}; 1178 | var iteratorSymbol = $Symbol.iterator || "@@iterator"; 1179 | var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator"; 1180 | var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; 1181 | 1182 | var inModule = typeof module === "object"; 1183 | var runtime = global.regeneratorRuntime; 1184 | if (runtime) { 1185 | if (inModule) { 1186 | // If regeneratorRuntime is defined globally and we're in a module, 1187 | // make the exports object identical to regeneratorRuntime. 1188 | module.exports = runtime; 1189 | } 1190 | // Don't bother evaluating the rest of this file if the runtime was 1191 | // already defined globally. 1192 | return; 1193 | } 1194 | 1195 | // Define the runtime globally (as expected by generated code) as either 1196 | // module.exports (if we're in a module) or a new, empty object. 1197 | runtime = global.regeneratorRuntime = inModule ? module.exports : {}; 1198 | 1199 | function wrap(innerFn, outerFn, self, tryLocsList) { 1200 | // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator. 1201 | var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator; 1202 | var generator = Object.create(protoGenerator.prototype); 1203 | var context = new Context(tryLocsList || []); 1204 | 1205 | // The ._invoke method unifies the implementations of the .next, 1206 | // .throw, and .return methods. 1207 | generator._invoke = makeInvokeMethod(innerFn, self, context); 1208 | 1209 | return generator; 1210 | } 1211 | runtime.wrap = wrap; 1212 | 1213 | // Try/catch helper to minimize deoptimizations. Returns a completion 1214 | // record like context.tryEntries[i].completion. This interface could 1215 | // have been (and was previously) designed to take a closure to be 1216 | // invoked without arguments, but in all the cases we care about we 1217 | // already have an existing method we want to call, so there's no need 1218 | // to create a new function object. We can even get away with assuming 1219 | // the method takes exactly one argument, since that happens to be true 1220 | // in every case, so we don't have to touch the arguments object. The 1221 | // only additional allocation required is the completion record, which 1222 | // has a stable shape and so hopefully should be cheap to allocate. 1223 | function tryCatch(fn, obj, arg) { 1224 | try { 1225 | return { type: "normal", arg: fn.call(obj, arg) }; 1226 | } catch (err) { 1227 | return { type: "throw", arg: err }; 1228 | } 1229 | } 1230 | 1231 | var GenStateSuspendedStart = "suspendedStart"; 1232 | var GenStateSuspendedYield = "suspendedYield"; 1233 | var GenStateExecuting = "executing"; 1234 | var GenStateCompleted = "completed"; 1235 | 1236 | // Returning this object from the innerFn has the same effect as 1237 | // breaking out of the dispatch switch statement. 1238 | var ContinueSentinel = {}; 1239 | 1240 | // Dummy constructor functions that we use as the .constructor and 1241 | // .constructor.prototype properties for functions that return Generator 1242 | // objects. For full spec compliance, you may wish to configure your 1243 | // minifier not to mangle the names of these two functions. 1244 | function Generator() {} 1245 | function GeneratorFunction() {} 1246 | function GeneratorFunctionPrototype() {} 1247 | 1248 | // This is a polyfill for %IteratorPrototype% for environments that 1249 | // don't natively support it. 1250 | var IteratorPrototype = {}; 1251 | IteratorPrototype[iteratorSymbol] = function () { 1252 | return this; 1253 | }; 1254 | 1255 | var getProto = Object.getPrototypeOf; 1256 | var NativeIteratorPrototype = getProto && getProto(getProto(values([]))); 1257 | if (NativeIteratorPrototype && 1258 | NativeIteratorPrototype !== Op && 1259 | hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) { 1260 | // This environment has a native %IteratorPrototype%; use it instead 1261 | // of the polyfill. 1262 | IteratorPrototype = NativeIteratorPrototype; 1263 | } 1264 | 1265 | var Gp = GeneratorFunctionPrototype.prototype = 1266 | Generator.prototype = Object.create(IteratorPrototype); 1267 | GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype; 1268 | GeneratorFunctionPrototype.constructor = GeneratorFunction; 1269 | GeneratorFunctionPrototype[toStringTagSymbol] = 1270 | GeneratorFunction.displayName = "GeneratorFunction"; 1271 | 1272 | // Helper for defining the .next, .throw, and .return methods of the 1273 | // Iterator interface in terms of a single ._invoke method. 1274 | function defineIteratorMethods(prototype) { 1275 | ["next", "throw", "return"].forEach(function(method) { 1276 | prototype[method] = function(arg) { 1277 | return this._invoke(method, arg); 1278 | }; 1279 | }); 1280 | } 1281 | 1282 | runtime.isGeneratorFunction = function(genFun) { 1283 | var ctor = typeof genFun === "function" && genFun.constructor; 1284 | return ctor 1285 | ? ctor === GeneratorFunction || 1286 | // For the native GeneratorFunction constructor, the best we can 1287 | // do is to check its .name property. 1288 | (ctor.displayName || ctor.name) === "GeneratorFunction" 1289 | : false; 1290 | }; 1291 | 1292 | runtime.mark = function(genFun) { 1293 | if (Object.setPrototypeOf) { 1294 | Object.setPrototypeOf(genFun, GeneratorFunctionPrototype); 1295 | } else { 1296 | genFun.__proto__ = GeneratorFunctionPrototype; 1297 | if (!(toStringTagSymbol in genFun)) { 1298 | genFun[toStringTagSymbol] = "GeneratorFunction"; 1299 | } 1300 | } 1301 | genFun.prototype = Object.create(Gp); 1302 | return genFun; 1303 | }; 1304 | 1305 | // Within the body of any async function, `await x` is transformed to 1306 | // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test 1307 | // `hasOwn.call(value, "__await")` to determine if the yielded value is 1308 | // meant to be awaited. 1309 | runtime.awrap = function(arg) { 1310 | return { __await: arg }; 1311 | }; 1312 | 1313 | function AsyncIterator(generator) { 1314 | function invoke(method, arg, resolve, reject) { 1315 | var record = tryCatch(generator[method], generator, arg); 1316 | if (record.type === "throw") { 1317 | reject(record.arg); 1318 | } else { 1319 | var result = record.arg; 1320 | var value = result.value; 1321 | if (value && 1322 | typeof value === "object" && 1323 | hasOwn.call(value, "__await")) { 1324 | return Promise.resolve(value.__await).then(function(value) { 1325 | invoke("next", value, resolve, reject); 1326 | }, function(err) { 1327 | invoke("throw", err, resolve, reject); 1328 | }); 1329 | } 1330 | 1331 | return Promise.resolve(value).then(function(unwrapped) { 1332 | // When a yielded Promise is resolved, its final value becomes 1333 | // the .value of the Promise<{value,done}> result for the 1334 | // current iteration. If the Promise is rejected, however, the 1335 | // result for this iteration will be rejected with the same 1336 | // reason. Note that rejections of yielded Promises are not 1337 | // thrown back into the generator function, as is the case 1338 | // when an awaited Promise is rejected. This difference in 1339 | // behavior between yield and await is important, because it 1340 | // allows the consumer to decide what to do with the yielded 1341 | // rejection (swallow it and continue, manually .throw it back 1342 | // into the generator, abandon iteration, whatever). With 1343 | // await, by contrast, there is no opportunity to examine the 1344 | // rejection reason outside the generator function, so the 1345 | // only option is to throw it from the await expression, and 1346 | // let the generator function handle the exception. 1347 | result.value = unwrapped; 1348 | resolve(result); 1349 | }, reject); 1350 | } 1351 | } 1352 | 1353 | if (typeof global.process === "object" && global.process.domain) { 1354 | invoke = global.process.domain.bind(invoke); 1355 | } 1356 | 1357 | var previousPromise; 1358 | 1359 | function enqueue(method, arg) { 1360 | function callInvokeWithMethodAndArg() { 1361 | return new Promise(function(resolve, reject) { 1362 | invoke(method, arg, resolve, reject); 1363 | }); 1364 | } 1365 | 1366 | return previousPromise = 1367 | // If enqueue has been called before, then we want to wait until 1368 | // all previous Promises have been resolved before calling invoke, 1369 | // so that results are always delivered in the correct order. If 1370 | // enqueue has not been called before, then it is important to 1371 | // call invoke immediately, without waiting on a callback to fire, 1372 | // so that the async generator function has the opportunity to do 1373 | // any necessary setup in a predictable way. This predictability 1374 | // is why the Promise constructor synchronously invokes its 1375 | // executor callback, and why async functions synchronously 1376 | // execute code before the first await. Since we implement simple 1377 | // async functions in terms of async generators, it is especially 1378 | // important to get this right, even though it requires care. 1379 | previousPromise ? previousPromise.then( 1380 | callInvokeWithMethodAndArg, 1381 | // Avoid propagating failures to Promises returned by later 1382 | // invocations of the iterator. 1383 | callInvokeWithMethodAndArg 1384 | ) : callInvokeWithMethodAndArg(); 1385 | } 1386 | 1387 | // Define the unified helper method that is used to implement .next, 1388 | // .throw, and .return (see defineIteratorMethods). 1389 | this._invoke = enqueue; 1390 | } 1391 | 1392 | defineIteratorMethods(AsyncIterator.prototype); 1393 | AsyncIterator.prototype[asyncIteratorSymbol] = function () { 1394 | return this; 1395 | }; 1396 | runtime.AsyncIterator = AsyncIterator; 1397 | 1398 | // Note that simple async functions are implemented on top of 1399 | // AsyncIterator objects; they just return a Promise for the value of 1400 | // the final result produced by the iterator. 1401 | runtime.async = function(innerFn, outerFn, self, tryLocsList) { 1402 | var iter = new AsyncIterator( 1403 | wrap(innerFn, outerFn, self, tryLocsList) 1404 | ); 1405 | 1406 | return runtime.isGeneratorFunction(outerFn) 1407 | ? iter // If outerFn is a generator, return the full iterator. 1408 | : iter.next().then(function(result) { 1409 | return result.done ? result.value : iter.next(); 1410 | }); 1411 | }; 1412 | 1413 | function makeInvokeMethod(innerFn, self, context) { 1414 | var state = GenStateSuspendedStart; 1415 | 1416 | return function invoke(method, arg) { 1417 | if (state === GenStateExecuting) { 1418 | throw new Error("Generator is already running"); 1419 | } 1420 | 1421 | if (state === GenStateCompleted) { 1422 | if (method === "throw") { 1423 | throw arg; 1424 | } 1425 | 1426 | // Be forgiving, per 25.3.3.3.3 of the spec: 1427 | // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume 1428 | return doneResult(); 1429 | } 1430 | 1431 | context.method = method; 1432 | context.arg = arg; 1433 | 1434 | while (true) { 1435 | var delegate = context.delegate; 1436 | if (delegate) { 1437 | var delegateResult = maybeInvokeDelegate(delegate, context); 1438 | if (delegateResult) { 1439 | if (delegateResult === ContinueSentinel) continue; 1440 | return delegateResult; 1441 | } 1442 | } 1443 | 1444 | if (context.method === "next") { 1445 | // Setting context._sent for legacy support of Babel's 1446 | // function.sent implementation. 1447 | context.sent = context._sent = context.arg; 1448 | 1449 | } else if (context.method === "throw") { 1450 | if (state === GenStateSuspendedStart) { 1451 | state = GenStateCompleted; 1452 | throw context.arg; 1453 | } 1454 | 1455 | context.dispatchException(context.arg); 1456 | 1457 | } else if (context.method === "return") { 1458 | context.abrupt("return", context.arg); 1459 | } 1460 | 1461 | state = GenStateExecuting; 1462 | 1463 | var record = tryCatch(innerFn, self, context); 1464 | if (record.type === "normal") { 1465 | // If an exception is thrown from innerFn, we leave state === 1466 | // GenStateExecuting and loop back for another invocation. 1467 | state = context.done 1468 | ? GenStateCompleted 1469 | : GenStateSuspendedYield; 1470 | 1471 | if (record.arg === ContinueSentinel) { 1472 | continue; 1473 | } 1474 | 1475 | return { 1476 | value: record.arg, 1477 | done: context.done 1478 | }; 1479 | 1480 | } else if (record.type === "throw") { 1481 | state = GenStateCompleted; 1482 | // Dispatch the exception by looping back around to the 1483 | // context.dispatchException(context.arg) call above. 1484 | context.method = "throw"; 1485 | context.arg = record.arg; 1486 | } 1487 | } 1488 | }; 1489 | } 1490 | 1491 | // Call delegate.iterator[context.method](context.arg) and handle the 1492 | // result, either by returning a { value, done } result from the 1493 | // delegate iterator, or by modifying context.method and context.arg, 1494 | // setting context.delegate to null, and returning the ContinueSentinel. 1495 | function maybeInvokeDelegate(delegate, context) { 1496 | var method = delegate.iterator[context.method]; 1497 | if (method === undefined) { 1498 | // A .throw or .return when the delegate iterator has no .throw 1499 | // method always terminates the yield* loop. 1500 | context.delegate = null; 1501 | 1502 | if (context.method === "throw") { 1503 | if (delegate.iterator.return) { 1504 | // If the delegate iterator has a return method, give it a 1505 | // chance to clean up. 1506 | context.method = "return"; 1507 | context.arg = undefined; 1508 | maybeInvokeDelegate(delegate, context); 1509 | 1510 | if (context.method === "throw") { 1511 | // If maybeInvokeDelegate(context) changed context.method from 1512 | // "return" to "throw", let that override the TypeError below. 1513 | return ContinueSentinel; 1514 | } 1515 | } 1516 | 1517 | context.method = "throw"; 1518 | context.arg = new TypeError( 1519 | "The iterator does not provide a 'throw' method"); 1520 | } 1521 | 1522 | return ContinueSentinel; 1523 | } 1524 | 1525 | var record = tryCatch(method, delegate.iterator, context.arg); 1526 | 1527 | if (record.type === "throw") { 1528 | context.method = "throw"; 1529 | context.arg = record.arg; 1530 | context.delegate = null; 1531 | return ContinueSentinel; 1532 | } 1533 | 1534 | var info = record.arg; 1535 | 1536 | if (! info) { 1537 | context.method = "throw"; 1538 | context.arg = new TypeError("iterator result is not an object"); 1539 | context.delegate = null; 1540 | return ContinueSentinel; 1541 | } 1542 | 1543 | if (info.done) { 1544 | // Assign the result of the finished delegate to the temporary 1545 | // variable specified by delegate.resultName (see delegateYield). 1546 | context[delegate.resultName] = info.value; 1547 | 1548 | // Resume execution at the desired location (see delegateYield). 1549 | context.next = delegate.nextLoc; 1550 | 1551 | // If context.method was "throw" but the delegate handled the 1552 | // exception, let the outer generator proceed normally. If 1553 | // context.method was "next", forget context.arg since it has been 1554 | // "consumed" by the delegate iterator. If context.method was 1555 | // "return", allow the original .return call to continue in the 1556 | // outer generator. 1557 | if (context.method !== "return") { 1558 | context.method = "next"; 1559 | context.arg = undefined; 1560 | } 1561 | 1562 | } else { 1563 | // Re-yield the result returned by the delegate method. 1564 | return info; 1565 | } 1566 | 1567 | // The delegate iterator is finished, so forget it and continue with 1568 | // the outer generator. 1569 | context.delegate = null; 1570 | return ContinueSentinel; 1571 | } 1572 | 1573 | // Define Generator.prototype.{next,throw,return} in terms of the 1574 | // unified ._invoke helper method. 1575 | defineIteratorMethods(Gp); 1576 | 1577 | Gp[toStringTagSymbol] = "Generator"; 1578 | 1579 | // A Generator should always return itself as the iterator object when the 1580 | // @@iterator function is called on it. Some browsers' implementations of the 1581 | // iterator prototype chain incorrectly implement this, causing the Generator 1582 | // object to not be returned from this call. This ensures that doesn't happen. 1583 | // See https://github.com/facebook/regenerator/issues/274 for more details. 1584 | Gp[iteratorSymbol] = function() { 1585 | return this; 1586 | }; 1587 | 1588 | Gp.toString = function() { 1589 | return "[object Generator]"; 1590 | }; 1591 | 1592 | function pushTryEntry(locs) { 1593 | var entry = { tryLoc: locs[0] }; 1594 | 1595 | if (1 in locs) { 1596 | entry.catchLoc = locs[1]; 1597 | } 1598 | 1599 | if (2 in locs) { 1600 | entry.finallyLoc = locs[2]; 1601 | entry.afterLoc = locs[3]; 1602 | } 1603 | 1604 | this.tryEntries.push(entry); 1605 | } 1606 | 1607 | function resetTryEntry(entry) { 1608 | var record = entry.completion || {}; 1609 | record.type = "normal"; 1610 | delete record.arg; 1611 | entry.completion = record; 1612 | } 1613 | 1614 | function Context(tryLocsList) { 1615 | // The root entry object (effectively a try statement without a catch 1616 | // or a finally block) gives us a place to store values thrown from 1617 | // locations where there is no enclosing try statement. 1618 | this.tryEntries = [{ tryLoc: "root" }]; 1619 | tryLocsList.forEach(pushTryEntry, this); 1620 | this.reset(true); 1621 | } 1622 | 1623 | runtime.keys = function(object) { 1624 | var keys = []; 1625 | for (var key in object) { 1626 | keys.push(key); 1627 | } 1628 | keys.reverse(); 1629 | 1630 | // Rather than returning an object with a next method, we keep 1631 | // things simple and return the next function itself. 1632 | return function next() { 1633 | while (keys.length) { 1634 | var key = keys.pop(); 1635 | if (key in object) { 1636 | next.value = key; 1637 | next.done = false; 1638 | return next; 1639 | } 1640 | } 1641 | 1642 | // To avoid creating an additional object, we just hang the .value 1643 | // and .done properties off the next function object itself. This 1644 | // also ensures that the minifier will not anonymize the function. 1645 | next.done = true; 1646 | return next; 1647 | }; 1648 | }; 1649 | 1650 | function values(iterable) { 1651 | if (iterable) { 1652 | var iteratorMethod = iterable[iteratorSymbol]; 1653 | if (iteratorMethod) { 1654 | return iteratorMethod.call(iterable); 1655 | } 1656 | 1657 | if (typeof iterable.next === "function") { 1658 | return iterable; 1659 | } 1660 | 1661 | if (!isNaN(iterable.length)) { 1662 | var i = -1, next = function next() { 1663 | while (++i < iterable.length) { 1664 | if (hasOwn.call(iterable, i)) { 1665 | next.value = iterable[i]; 1666 | next.done = false; 1667 | return next; 1668 | } 1669 | } 1670 | 1671 | next.value = undefined; 1672 | next.done = true; 1673 | 1674 | return next; 1675 | }; 1676 | 1677 | return next.next = next; 1678 | } 1679 | } 1680 | 1681 | // Return an iterator with no values. 1682 | return { next: doneResult }; 1683 | } 1684 | runtime.values = values; 1685 | 1686 | function doneResult() { 1687 | return { value: undefined, done: true }; 1688 | } 1689 | 1690 | Context.prototype = { 1691 | constructor: Context, 1692 | 1693 | reset: function(skipTempReset) { 1694 | this.prev = 0; 1695 | this.next = 0; 1696 | // Resetting context._sent for legacy support of Babel's 1697 | // function.sent implementation. 1698 | this.sent = this._sent = undefined; 1699 | this.done = false; 1700 | this.delegate = null; 1701 | 1702 | this.method = "next"; 1703 | this.arg = undefined; 1704 | 1705 | this.tryEntries.forEach(resetTryEntry); 1706 | 1707 | if (!skipTempReset) { 1708 | for (var name in this) { 1709 | // Not sure about the optimal order of these conditions: 1710 | if (name.charAt(0) === "t" && 1711 | hasOwn.call(this, name) && 1712 | !isNaN(+name.slice(1))) { 1713 | this[name] = undefined; 1714 | } 1715 | } 1716 | } 1717 | }, 1718 | 1719 | stop: function() { 1720 | this.done = true; 1721 | 1722 | var rootEntry = this.tryEntries[0]; 1723 | var rootRecord = rootEntry.completion; 1724 | if (rootRecord.type === "throw") { 1725 | throw rootRecord.arg; 1726 | } 1727 | 1728 | return this.rval; 1729 | }, 1730 | 1731 | dispatchException: function(exception) { 1732 | if (this.done) { 1733 | throw exception; 1734 | } 1735 | 1736 | var context = this; 1737 | function handle(loc, caught) { 1738 | record.type = "throw"; 1739 | record.arg = exception; 1740 | context.next = loc; 1741 | 1742 | if (caught) { 1743 | // If the dispatched exception was caught by a catch block, 1744 | // then let that catch block handle the exception normally. 1745 | context.method = "next"; 1746 | context.arg = undefined; 1747 | } 1748 | 1749 | return !! caught; 1750 | } 1751 | 1752 | for (var i = this.tryEntries.length - 1; i >= 0; --i) { 1753 | var entry = this.tryEntries[i]; 1754 | var record = entry.completion; 1755 | 1756 | if (entry.tryLoc === "root") { 1757 | // Exception thrown outside of any try block that could handle 1758 | // it, so set the completion value of the entire function to 1759 | // throw the exception. 1760 | return handle("end"); 1761 | } 1762 | 1763 | if (entry.tryLoc <= this.prev) { 1764 | var hasCatch = hasOwn.call(entry, "catchLoc"); 1765 | var hasFinally = hasOwn.call(entry, "finallyLoc"); 1766 | 1767 | if (hasCatch && hasFinally) { 1768 | if (this.prev < entry.catchLoc) { 1769 | return handle(entry.catchLoc, true); 1770 | } else if (this.prev < entry.finallyLoc) { 1771 | return handle(entry.finallyLoc); 1772 | } 1773 | 1774 | } else if (hasCatch) { 1775 | if (this.prev < entry.catchLoc) { 1776 | return handle(entry.catchLoc, true); 1777 | } 1778 | 1779 | } else if (hasFinally) { 1780 | if (this.prev < entry.finallyLoc) { 1781 | return handle(entry.finallyLoc); 1782 | } 1783 | 1784 | } else { 1785 | throw new Error("try statement without catch or finally"); 1786 | } 1787 | } 1788 | } 1789 | }, 1790 | 1791 | abrupt: function(type, arg) { 1792 | for (var i = this.tryEntries.length - 1; i >= 0; --i) { 1793 | var entry = this.tryEntries[i]; 1794 | if (entry.tryLoc <= this.prev && 1795 | hasOwn.call(entry, "finallyLoc") && 1796 | this.prev < entry.finallyLoc) { 1797 | var finallyEntry = entry; 1798 | break; 1799 | } 1800 | } 1801 | 1802 | if (finallyEntry && 1803 | (type === "break" || 1804 | type === "continue") && 1805 | finallyEntry.tryLoc <= arg && 1806 | arg <= finallyEntry.finallyLoc) { 1807 | // Ignore the finally entry if control is not jumping to a 1808 | // location outside the try/catch block. 1809 | finallyEntry = null; 1810 | } 1811 | 1812 | var record = finallyEntry ? finallyEntry.completion : {}; 1813 | record.type = type; 1814 | record.arg = arg; 1815 | 1816 | if (finallyEntry) { 1817 | this.method = "next"; 1818 | this.next = finallyEntry.finallyLoc; 1819 | return ContinueSentinel; 1820 | } 1821 | 1822 | return this.complete(record); 1823 | }, 1824 | 1825 | complete: function(record, afterLoc) { 1826 | if (record.type === "throw") { 1827 | throw record.arg; 1828 | } 1829 | 1830 | if (record.type === "break" || 1831 | record.type === "continue") { 1832 | this.next = record.arg; 1833 | } else if (record.type === "return") { 1834 | this.rval = this.arg = record.arg; 1835 | this.method = "return"; 1836 | this.next = "end"; 1837 | } else if (record.type === "normal" && afterLoc) { 1838 | this.next = afterLoc; 1839 | } 1840 | 1841 | return ContinueSentinel; 1842 | }, 1843 | 1844 | finish: function(finallyLoc) { 1845 | for (var i = this.tryEntries.length - 1; i >= 0; --i) { 1846 | var entry = this.tryEntries[i]; 1847 | if (entry.finallyLoc === finallyLoc) { 1848 | this.complete(entry.completion, entry.afterLoc); 1849 | resetTryEntry(entry); 1850 | return ContinueSentinel; 1851 | } 1852 | } 1853 | }, 1854 | 1855 | "catch": function(tryLoc) { 1856 | for (var i = this.tryEntries.length - 1; i >= 0; --i) { 1857 | var entry = this.tryEntries[i]; 1858 | if (entry.tryLoc === tryLoc) { 1859 | var record = entry.completion; 1860 | if (record.type === "throw") { 1861 | var thrown = record.arg; 1862 | resetTryEntry(entry); 1863 | } 1864 | return thrown; 1865 | } 1866 | } 1867 | 1868 | // The context.catch method must only be called with a location 1869 | // argument that corresponds to a known catch block. 1870 | throw new Error("illegal catch attempt"); 1871 | }, 1872 | 1873 | delegateYield: function(iterable, resultName, nextLoc) { 1874 | this.delegate = { 1875 | iterator: values(iterable), 1876 | resultName: resultName, 1877 | nextLoc: nextLoc 1878 | }; 1879 | 1880 | if (this.method === "next") { 1881 | // Deliberately forget the last sent value so that we don't 1882 | // accidentally pass it on to the delegate. 1883 | this.arg = undefined; 1884 | } 1885 | 1886 | return ContinueSentinel; 1887 | } 1888 | }; 1889 | })( 1890 | // Among the various tricks for obtaining a reference to the global 1891 | // object, this seems to be the most reliable technique that does not 1892 | // use indirect eval (which violates Content Security Policy). 1893 | typeof global === "object" ? global : 1894 | typeof window === "object" ? window : 1895 | typeof self === "object" ? self : this 1896 | ); 1897 | 1898 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4))) 1899 | 1900 | /***/ }), 1901 | /* 11 */ 1902 | /***/ (function(module, exports, __webpack_require__) { 1903 | 1904 | "use strict"; 1905 | 1906 | 1907 | Object.defineProperty(exports, "__esModule", { 1908 | value: true 1909 | }); 1910 | 1911 | function getVideoUrl(tvid, vid) { 1912 | return new Promise(function (resolve, reject) { 1913 | GM_xmlhttpRequest({ 1914 | url: `http://cache.video.qiyi.com/jp/vi/${tvid}/${vid}/?callback=callback`, 1915 | method: 'GET', 1916 | timeout: 8e3, 1917 | onload: function onload(details) { 1918 | try { 1919 | var json = JSON.parse(/callback\s*\(\s*(\{.*\})\s*\)/.exec(details.responseText)[1]); 1920 | resolve(json.vu); 1921 | } catch (err) { 1922 | reject(err); 1923 | } 1924 | }, 1925 | onerror: reject, 1926 | onabort: reject, 1927 | ontimeout: reject 1928 | }); 1929 | }); 1930 | } 1931 | 1932 | function findVid(text) { 1933 | var result = /vid=([\da-z]+)/i.exec(text); 1934 | return result ? result[1] : null; 1935 | } 1936 | 1937 | function findTvid(text) { 1938 | var result = /tvid=(\d+)/i.exec(text); 1939 | return result ? result[1] : null; 1940 | } 1941 | 1942 | exports.getVideoUrl = getVideoUrl; 1943 | exports.findVid = findVid; 1944 | exports.findTvid = findTvid; 1945 | 1946 | /***/ }), 1947 | /* 12 */ 1948 | /***/ (function(module, exports, __webpack_require__) { 1949 | 1950 | "use strict"; 1951 | 1952 | 1953 | Object.defineProperty(exports, "__esModule", { 1954 | value: true 1955 | }); 1956 | exports.useWebSocketLoaderPatch = exports.mouseShortcutsPatch = exports.keyShortcutsPatch = exports.keepHookingPatch = exports.watermarksPatch = exports.controlsPatch = exports.adsPatch = exports.vipPatch = undefined; 1957 | 1958 | 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; }; }(); 1959 | 1960 | var _logger = __webpack_require__(1); 1961 | 1962 | var _logger2 = _interopRequireDefault(_logger); 1963 | 1964 | var _hooker = __webpack_require__(0); 1965 | 1966 | var _hooker2 = _interopRequireDefault(_hooker); 1967 | 1968 | var _fullscreen = __webpack_require__(13); 1969 | 1970 | var _webFullscreen = __webpack_require__(14); 1971 | 1972 | var _parsedData = __webpack_require__(15); 1973 | 1974 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 1975 | 1976 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 1977 | 1978 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 1979 | 1980 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 1981 | 1982 | var Patch = function () { 1983 | function Patch() { 1984 | _classCallCheck(this, Patch); 1985 | 1986 | this._installed = false; 1987 | } 1988 | 1989 | _createClass(Patch, [{ 1990 | key: 'install', 1991 | value: function install() { 1992 | if (!this._installed) { 1993 | this._installed = true; 1994 | this._prepare(); 1995 | this._apply(); 1996 | } 1997 | } 1998 | }, { 1999 | key: '_prepare', 2000 | value: function _prepare() {} 2001 | }, { 2002 | key: '_apply', 2003 | value: function _apply() {} 2004 | }]); 2005 | 2006 | return Patch; 2007 | }(); 2008 | 2009 | var VipPatch = function (_Patch) { 2010 | _inherits(VipPatch, _Patch); 2011 | 2012 | function VipPatch() { 2013 | _classCallCheck(this, VipPatch); 2014 | 2015 | return _possibleConstructorReturn(this, (VipPatch.__proto__ || Object.getPrototypeOf(VipPatch)).call(this)); 2016 | } 2017 | 2018 | _createClass(VipPatch, [{ 2019 | key: '_apply', 2020 | value: function _apply() { 2021 | _hooker2.default.hookUser(function (exports) { 2022 | var proto = exports.__proto__; 2023 | proto.isVipSync = function () { 2024 | return true; 2025 | }; 2026 | proto.isVip = function (cb) { 2027 | return setTimeout(cb, 0, true); 2028 | }; 2029 | _logger2.default.info('The vip patch has been installed'); 2030 | }); 2031 | } 2032 | }]); 2033 | 2034 | return VipPatch; 2035 | }(Patch); 2036 | 2037 | var AdsPatch = function (_Patch2) { 2038 | _inherits(AdsPatch, _Patch2); 2039 | 2040 | function AdsPatch() { 2041 | _classCallCheck(this, AdsPatch); 2042 | 2043 | return _possibleConstructorReturn(this, (AdsPatch.__proto__ || Object.getPrototypeOf(AdsPatch)).call(this)); 2044 | } 2045 | 2046 | _createClass(AdsPatch, [{ 2047 | key: '_fakeAdsData', 2048 | value: function _fakeAdsData() { 2049 | return {}; 2050 | } 2051 | }, { 2052 | key: '_apply', 2053 | value: function _apply() { 2054 | var _this3 = this; 2055 | 2056 | _hooker2.default.hookShowRequest(function (exports) { 2057 | var proto = exports.prototype; 2058 | proto.request = function (cb) { 2059 | return setTimeout(cb, 0, _this3._fakeAdsData()); 2060 | }; 2061 | _logger2.default.info('The ads patch has been installed'); 2062 | }); 2063 | } 2064 | }]); 2065 | 2066 | return AdsPatch; 2067 | }(Patch); 2068 | 2069 | var WatermarksPatch = function (_Patch3) { 2070 | _inherits(WatermarksPatch, _Patch3); 2071 | 2072 | function WatermarksPatch() { 2073 | _classCallCheck(this, WatermarksPatch); 2074 | 2075 | return _possibleConstructorReturn(this, (WatermarksPatch.__proto__ || Object.getPrototypeOf(WatermarksPatch)).call(this)); 2076 | } 2077 | 2078 | _createClass(WatermarksPatch, [{ 2079 | key: '_apply', 2080 | value: function _apply() { 2081 | _hooker2.default.hookLogo(function (exports) { 2082 | exports.prototype.showLogo = function () {}; 2083 | _logger2.default.info('The watermarks patch has been installed'); 2084 | }); 2085 | } 2086 | }]); 2087 | 2088 | return WatermarksPatch; 2089 | }(Patch); 2090 | 2091 | var ControlsPatch = function (_Patch4) { 2092 | _inherits(ControlsPatch, _Patch4); 2093 | 2094 | // Prevent the player controls were disabled. 2095 | function ControlsPatch() { 2096 | _classCallCheck(this, ControlsPatch); 2097 | 2098 | return _possibleConstructorReturn(this, (ControlsPatch.__proto__ || Object.getPrototypeOf(ControlsPatch)).call(this)); 2099 | } 2100 | 2101 | _createClass(ControlsPatch, [{ 2102 | key: '_apply', 2103 | value: function _apply() { 2104 | _hooker2.default.hookSkinBase(function (exports) { 2105 | exports.prototype._checkPlugin = function () {}; // This function disables the player controls when playing ads and enables when done. 2106 | _logger2.default.info('The controls patch has been installed'); 2107 | }); 2108 | } 2109 | }]); 2110 | 2111 | return ControlsPatch; 2112 | }(Patch); 2113 | 2114 | var CorePatch = function (_Patch5) { 2115 | _inherits(CorePatch, _Patch5); 2116 | 2117 | function CorePatch() { 2118 | _classCallCheck(this, CorePatch); 2119 | 2120 | return _possibleConstructorReturn(this, (CorePatch.__proto__ || Object.getPrototypeOf(CorePatch)).call(this)); 2121 | } 2122 | 2123 | _createClass(CorePatch, [{ 2124 | key: '_prepare', 2125 | value: function _prepare() { 2126 | this._initShowTip(); 2127 | this._initPlaybackRate(); 2128 | } 2129 | }, { 2130 | key: '_initShowTip', 2131 | value: function _initShowTip() { 2132 | _hooker2.default.hookPluginControlsInit(function (that) { 2133 | that.core.on('showtip', function (event) { 2134 | that.setcontroltip.apply(that, [{ str: event.data, x: that._process.offset().left, y: 3, cut: true, timeout: true }]); 2135 | if (that.$plugin.hasClass('process_hidden')) { 2136 | that._controltips.css('top', '-25px'); 2137 | } else if (that.$plugin.hasClass('bottom-hide')) { 2138 | that._controltips.css('top', '-38px'); 2139 | } 2140 | }); 2141 | }); 2142 | } 2143 | }, { 2144 | key: '_initPlaybackRate', 2145 | value: function _initPlaybackRate() { 2146 | _hooker2.default.hookPluginControls(function (exports) { 2147 | exports.prototype.initPlaybackRate = function () { 2148 | var core = this.core; 2149 | 2150 | var rate = parseFloat(localStorage.getItem('QiyiPlayerPlaybackRate')); 2151 | rate = isNaN(rate) ? 1 : rate; 2152 | 2153 | if (core.getCurrStatus() === 'playing') { 2154 | core.setPlaybackRate(rate); 2155 | } else { 2156 | var onstatuschanged = function onstatuschanged(evt) { 2157 | if (evt.data.state === 'playing') { 2158 | core.setPlaybackRate(rate); 2159 | core.un('statusChanged', onstatuschanged); 2160 | } 2161 | }; 2162 | core.on('statusChanged', onstatuschanged); 2163 | } 2164 | 2165 | var $ul = this.$playbackrateUl; 2166 | $ul.find(`[data-pbrate="${rate}"]`).addClass('selected'); 2167 | 2168 | var $items = $ul.find('li'); 2169 | $items.on('click', function () { 2170 | var rate = parseFloat(this.getAttribute('data-pbrate')); 2171 | if (!this.classList.contains('selected')) { 2172 | $items.removeClass('selected'); 2173 | this.classList.add('selected'); 2174 | } 2175 | localStorage.setItem('QiyiPlayerPlaybackRate', rate); 2176 | core.setPlaybackRate(rate); 2177 | }); 2178 | 2179 | this.$playsettingicon.on('click', function () { 2180 | var rate = core.getPlaybackRate(); 2181 | var $item = $ul.find(`[data-pbrate="${rate}"]`); 2182 | if ($item.length === 1) { 2183 | if (!$item.hasClass('selected')) { 2184 | $items.removeClass('selected'); 2185 | $item.addClass('selected'); 2186 | } 2187 | } else { 2188 | $items.removeClass('selected'); 2189 | } 2190 | }); 2191 | }; 2192 | }); 2193 | } 2194 | }, { 2195 | key: '_apply', 2196 | value: function _apply() { 2197 | _hooker2.default.hookCore(function (exports) { 2198 | var proto = exports.prototype; 2199 | 2200 | proto._showTip = function (msg) { 2201 | this.fire({ type: 'showtip', data: msg }); 2202 | }; 2203 | 2204 | proto.getFPS = function () { 2205 | if (_parsedData.flvInfo) { 2206 | return _parsedData.flvInfo.videoConfigTag.sps.frame_rate.fps; 2207 | } else { 2208 | return 25; // f4v极速以上,动画23.976、电影24、电视剧25。 2209 | } 2210 | }; 2211 | 2212 | proto.prevFrame = function () { 2213 | var video = this.video(); 2214 | var seekTime = Math.max(0, Math.min(this.getDuration(), video.currentTime - 1 / this.getFPS())); 2215 | video.currentTime = seekTime; 2216 | this._showTip('上一帧'); 2217 | }; 2218 | 2219 | proto.nextFrame = function () { 2220 | var video = this.video(); 2221 | var seekTime = Math.max(0, Math.min(this.getDuration(), video.currentTime + 1 / this.getFPS())); 2222 | video.currentTime = seekTime; 2223 | this._showTip('下一帧'); 2224 | }; 2225 | 2226 | proto.seek = function () { 2227 | var _engine; 2228 | 2229 | var video = this.video(); 2230 | var playbackRate = video.playbackRate; 2231 | (_engine = this._engine).seek.apply(_engine, arguments); 2232 | video.playbackRate = playbackRate; 2233 | }; 2234 | 2235 | proto.stepSeek = function (stepTime) { 2236 | var seekTime = Math.max(0, Math.min(this.getDuration(), this.getCurrenttime() + stepTime)); 2237 | var msg = void 0; 2238 | 2239 | if (Math.abs(stepTime) < 60) { 2240 | msg = stepTime > 0 ? `步进:${stepTime}秒` : `步退:${Math.abs(stepTime)}秒`; 2241 | } else { 2242 | msg = stepTime > 0 ? `步进:${stepTime / 60}分钟` : `步退:${Math.abs(stepTime) / 60}分钟`; 2243 | } 2244 | this._showTip(msg); 2245 | 2246 | this.seek(seekTime, true); 2247 | }; 2248 | 2249 | proto.rangeSeek = function (range) { 2250 | var duration = this.getDuration(); 2251 | var seekTime = Math.max(0, Math.min(duration, duration * range)); 2252 | this.seek(seekTime, true); 2253 | this._showTip('定位:' + (range * 100).toFixed(0) + '%'); 2254 | }; 2255 | 2256 | proto.toggleMute = function () { 2257 | if (this.getMuted()) { 2258 | this.setMuted(false); 2259 | this._showTip('取消静音'); 2260 | } else { 2261 | this.setMuted(true); 2262 | this._showTip('静音'); 2263 | } 2264 | }; 2265 | 2266 | proto.adjustVolume = function (value) { 2267 | var volume = this.getVolume() + value; 2268 | volume = Math.max(0, Math.min(1, volume.toFixed(2))); 2269 | this.setVolume(volume); 2270 | this.fire({ type: 'keyvolumechange' }); 2271 | }; 2272 | 2273 | proto.getPlaybackRate = function () { 2274 | // iqiyi 的这个方法有bug,没把值返回! 2275 | return this._engine.getPlaybackRate(); 2276 | }; 2277 | 2278 | proto.adjustPlaybackRate = function (value) { 2279 | var currRate = this.getPlaybackRate(); 2280 | var rate = Math.max(0.2, Math.min(5, parseFloat((currRate + value).toFixed(1)))); 2281 | 2282 | localStorage.setItem('QiyiPlayerPlaybackRate', rate); 2283 | this.setPlaybackRate(rate); 2284 | this._showTip(`播放速率:${rate}`); 2285 | }; 2286 | 2287 | proto.turnPlaybackRate = function () { 2288 | var currRate = this.getPlaybackRate(); 2289 | var rate = void 0; 2290 | if (currRate !== 1) { 2291 | this._backRate = currRate; 2292 | rate = 1; 2293 | } else { 2294 | rate = this._backRate || 1; 2295 | } 2296 | 2297 | this.setPlaybackRate(rate); 2298 | this._showTip(`播放速率:${rate}`); 2299 | }; 2300 | 2301 | proto.hasPrevVideo = function () { 2302 | return this._getVideoIndexInList(this._movieinfo.tvid) > 0 || this._getVideoIndexInList(this._movieinfo.oldTvid) > 0; 2303 | }; 2304 | 2305 | proto.playNext = function () { 2306 | if (this.hasNextVideo()) { 2307 | this._showTip('播放下一集'); 2308 | this.switchNextVideo(); 2309 | } else { 2310 | this._showTip('没有下一集哦'); 2311 | } 2312 | }; 2313 | 2314 | proto.playPrev = function () { 2315 | if (this.hasPrevVideo()) { 2316 | this._showTip('播放上一集'); 2317 | this.switchPreVideo(); 2318 | } else { 2319 | this._showTip('没有上一集哦'); 2320 | } 2321 | }; 2322 | 2323 | _logger2.default.info('The core patch has been installed'); 2324 | }); 2325 | } 2326 | }]); 2327 | 2328 | return CorePatch; 2329 | }(Patch); 2330 | 2331 | var corePatch = new CorePatch(); 2332 | 2333 | var KeyShortcutsPatch = function (_Patch6) { 2334 | _inherits(KeyShortcutsPatch, _Patch6); 2335 | 2336 | function KeyShortcutsPatch() { 2337 | _classCallCheck(this, KeyShortcutsPatch); 2338 | 2339 | return _possibleConstructorReturn(this, (KeyShortcutsPatch.__proto__ || Object.getPrototypeOf(KeyShortcutsPatch)).call(this)); 2340 | } 2341 | 2342 | _createClass(KeyShortcutsPatch, [{ 2343 | key: '_prepare', 2344 | value: function _prepare() { 2345 | corePatch.install(); 2346 | } 2347 | }, { 2348 | key: '_apply', 2349 | value: function _apply() { 2350 | _hooker2.default.hookPluginHotKeys(function (exports) { 2351 | var proto = exports.prototype; 2352 | 2353 | proto.init = function () { 2354 | document.addEventListener('keydown', this._keydown.bind(this)); 2355 | }; 2356 | 2357 | proto._isValidTarget = function (target) { 2358 | return target.nodeName === 'BODY' || target.nodeName == 'VIDEO' || target.classList.contains('pw-video'); // 全局 2359 | // return target.nodeName === 'VIDEO' || target.classList.contains('pw-video'); // 非全局 2360 | }; 2361 | 2362 | proto._keydown = function (event) { 2363 | if (!this._isValidTarget(event.target)) return; 2364 | 2365 | var keyCode = event.keyCode, 2366 | ctrlKey = event.ctrlKey, 2367 | shiftKey = event.shiftKey, 2368 | altKey = event.altKey; 2369 | 2370 | var core = this.core; 2371 | 2372 | switch (keyCode) { 2373 | case 32: 2374 | // Spacebar 2375 | if (!ctrlKey && !shiftKey && !altKey) { 2376 | if (core.isPaused()) { 2377 | core.play(true); 2378 | core._showTip('播放'); 2379 | } else { 2380 | core.pause(true); 2381 | core._showTip('暂停'); 2382 | } 2383 | } else { 2384 | return; 2385 | } 2386 | break; 2387 | case 39: // → Arrow Right 2388 | case 37: 2389 | { 2390 | // ← Arrow Left 2391 | var stepTime = void 0; 2392 | if (!ctrlKey && !shiftKey && !altKey) { 2393 | stepTime = 39 === keyCode ? 5 : -5; 2394 | } else if (ctrlKey && !shiftKey && !altKey) { 2395 | stepTime = 39 === keyCode ? 30 : -30; 2396 | } else if (!ctrlKey && shiftKey && !altKey) { 2397 | stepTime = 39 === keyCode ? 60 : -60; 2398 | } else if (ctrlKey && !shiftKey && altKey) { 2399 | stepTime = 39 === keyCode ? 3e2 : -3e2; // 5分钟 2400 | } else { 2401 | return; 2402 | } 2403 | 2404 | core.stepSeek(stepTime); 2405 | break; 2406 | } 2407 | case 38: // ↑ Arrow Up 2408 | case 40: 2409 | // ↓ Arrow Down 2410 | if (!ctrlKey && !shiftKey && !altKey) { 2411 | core.adjustVolume(38 === keyCode ? 0.05 : -0.05); 2412 | } else { 2413 | return; 2414 | } 2415 | break; 2416 | case 77: 2417 | // M 2418 | if (!ctrlKey && !shiftKey && !altKey) { 2419 | core.toggleMute(); 2420 | } else { 2421 | return; 2422 | } 2423 | break; 2424 | case 13: 2425 | // Enter 2426 | if (!ctrlKey && !shiftKey && !altKey) { 2427 | _fullscreen.fullscreen.toggle(); 2428 | } else if (ctrlKey && !shiftKey && !altKey) { 2429 | _webFullscreen.webFullscreen.toggle(); 2430 | } else { 2431 | return; 2432 | } 2433 | break; 2434 | case 67: // C 2435 | case 88: 2436 | // X 2437 | if (!ctrlKey && !shiftKey && !altKey) { 2438 | core.adjustPlaybackRate(67 === keyCode ? 0.1 : -0.1); 2439 | } else { 2440 | return; 2441 | } 2442 | break; 2443 | case 90: 2444 | // Z 2445 | if (!ctrlKey && !shiftKey && !altKey) { 2446 | core.turnPlaybackRate(); 2447 | } else { 2448 | return; 2449 | } 2450 | break; 2451 | case 68: // D 2452 | case 70: 2453 | // F 2454 | if (!ctrlKey && !shiftKey && !altKey) { 2455 | core.pause(true); 2456 | if (keyCode === 68) { 2457 | core.prevFrame(); 2458 | } else { 2459 | core.nextFrame(); 2460 | } 2461 | } else { 2462 | return; 2463 | } 2464 | break; 2465 | case 80: // P 2466 | case 78: 2467 | // N 2468 | if (!ctrlKey && shiftKey && !altKey) { 2469 | if (keyCode === 78) { 2470 | core.playNext(); 2471 | } else { 2472 | core.playPrev(); 2473 | } 2474 | } else { 2475 | return; 2476 | } 2477 | break; 2478 | case 27: 2479 | // ESC 2480 | if (!event.ctrlKey && !event.shiftKey && !event.altKey) _webFullscreen.webFullscreen.isWebFullScreen() && _webFullscreen.webFullscreen.exit(); 2481 | return; 2482 | default: 2483 | if (keyCode >= 48 && keyCode <= 57) { 2484 | // 0 ~ 9 2485 | if (!ctrlKey && !shiftKey && !altKey) { 2486 | core.rangeSeek((keyCode - 48) * 0.1); 2487 | } else { 2488 | return; 2489 | } 2490 | } else { 2491 | return; 2492 | } 2493 | } 2494 | 2495 | event.preventDefault(); 2496 | event.stopPropagation(); 2497 | }; 2498 | 2499 | _logger2.default.info('The keyboard shortcuts patch has been installed'); 2500 | }); 2501 | } 2502 | }]); 2503 | 2504 | return KeyShortcutsPatch; 2505 | }(Patch); 2506 | 2507 | var MouseShortcutsPatch = function (_Patch7) { 2508 | _inherits(MouseShortcutsPatch, _Patch7); 2509 | 2510 | function MouseShortcutsPatch() { 2511 | _classCallCheck(this, MouseShortcutsPatch); 2512 | 2513 | return _possibleConstructorReturn(this, (MouseShortcutsPatch.__proto__ || Object.getPrototypeOf(MouseShortcutsPatch)).call(this)); 2514 | } 2515 | 2516 | _createClass(MouseShortcutsPatch, [{ 2517 | key: '_prepare', 2518 | value: function _prepare() { 2519 | corePatch.install(); 2520 | } 2521 | }, { 2522 | key: '_apply', 2523 | value: function _apply() { 2524 | _hooker2.default.hookDefaultSkin(function (exports) { 2525 | exports.prototype._initDBClicks = function () { 2526 | var timer = void 0, 2527 | core = this.core; 2528 | this.videoWrapper.find('video').on('click', function () { 2529 | if (timer) { 2530 | clearTimeout(timer); 2531 | timer = null; 2532 | return; 2533 | } 2534 | timer = setTimeout(function () { 2535 | if (core.isPaused()) { 2536 | core.play(true); 2537 | } else { 2538 | core.pause(true); 2539 | } 2540 | timer = null; 2541 | }, 200); 2542 | }).on('dblclick', function (event) { 2543 | event.preventDefault(); 2544 | event.stopPropagation(); 2545 | if (event.ctrlKey) { 2546 | _webFullscreen.webFullscreen.toggle(); 2547 | } else { 2548 | _fullscreen.fullscreen.toggle(); 2549 | } 2550 | }).on('wheel', function (event) { 2551 | if (_fullscreen.fullscreen.isFullScreen() || _webFullscreen.webFullscreen.isWebFullScreen()) { 2552 | var delta = event.wheelDelta || event.detail || event.deltaY && -event.deltaY; 2553 | core.adjustVolume(delta > 0 ? 0.05 : -0.05); 2554 | } 2555 | }); 2556 | }; 2557 | 2558 | _logger2.default.info('The mouse shortcuts patch has been installed'); 2559 | }); 2560 | } 2561 | }]); 2562 | 2563 | return MouseShortcutsPatch; 2564 | }(Patch); 2565 | 2566 | var UseWebSocketLoaderPatch = function (_Patch8) { 2567 | _inherits(UseWebSocketLoaderPatch, _Patch8); 2568 | 2569 | function UseWebSocketLoaderPatch() { 2570 | _classCallCheck(this, UseWebSocketLoaderPatch); 2571 | 2572 | var _this9 = _possibleConstructorReturn(this, (UseWebSocketLoaderPatch.__proto__ || Object.getPrototypeOf(UseWebSocketLoaderPatch)).call(this)); 2573 | 2574 | _this9.tryWs = GM_getValue('tryWs', false); 2575 | return _this9; 2576 | } 2577 | 2578 | _createClass(UseWebSocketLoaderPatch, [{ 2579 | key: '_prepare', 2580 | value: function _prepare() { 2581 | this._addSetting(); 2582 | } 2583 | }, { 2584 | key: '_apply', 2585 | value: function _apply() { 2586 | var _this10 = this; 2587 | 2588 | var that = this; 2589 | _hooker2.default.hookFragment(function (exports) { 2590 | Reflect.defineProperty(exports.prototype, 'tryWS', { 2591 | get: function get() { 2592 | return _this10._tryWs || that.tryWs; 2593 | }, // Will use the WebSocket loader if the value of tryWs is true. 2594 | set: function set(value) { 2595 | return _this10._tryWs = value; 2596 | } // The value of tryWs will be true if the Fetch loader fails. 2597 | }); 2598 | _logger2.default.info('The WebSocket loader patch has been installed'); 2599 | }); 2600 | } 2601 | }, { 2602 | key: '_addSetting', 2603 | value: function _addSetting() { 2604 | var that = this; 2605 | _hooker2.default.hookPluginControls(function (exports) { 2606 | var initSetting = exports.prototype.initSetting; 2607 | exports.prototype.initSetting = function () { 2608 | var _this11 = this; 2609 | 2610 | var div = document.createElement('div'); 2611 | div.innerHTML = ` 2612 |
2613 | WebSocket 2614 |
2615 |
`; 2616 | var item = div.querySelector('.setPop_item'); 2617 | this.$playsettingbox.find('.video_setPop_top').append(item); 2618 | this.$usewebsocketBtn = this.$playsettingbox.find('[data-player-hook="usewebsocketloader"]'); 2619 | 2620 | if (that.tryWs) { 2621 | this.$usewebsocketBtn.removeClass('setPop_switch_close'); 2622 | } 2623 | this.$usewebsocketBtn.on('click', function () { 2624 | _this11.$usewebsocketBtn.toggleClass('setPop_switch_close'); 2625 | that.tryWs = !that.tryWs; 2626 | GM_setValue('tryWs', that.tryWs); 2627 | }); 2628 | 2629 | initSetting.apply(this); 2630 | }; 2631 | }); 2632 | } 2633 | }]); 2634 | 2635 | return UseWebSocketLoaderPatch; 2636 | }(Patch); 2637 | 2638 | var KeepHookingPatch = function (_Patch9) { 2639 | _inherits(KeepHookingPatch, _Patch9); 2640 | 2641 | function KeepHookingPatch() { 2642 | _classCallCheck(this, KeepHookingPatch); 2643 | 2644 | return _possibleConstructorReturn(this, (KeepHookingPatch.__proto__ || Object.getPrototypeOf(KeepHookingPatch)).call(this)); 2645 | } 2646 | 2647 | _createClass(KeepHookingPatch, [{ 2648 | key: '_apply', 2649 | value: function _apply() { 2650 | _hooker2.default.keepalive = true; 2651 | _logger2.default.info('The keep hooking patch has been installed'); 2652 | } 2653 | }]); 2654 | 2655 | return KeepHookingPatch; 2656 | }(Patch); 2657 | 2658 | var vipPatch = exports.vipPatch = new VipPatch(); 2659 | var adsPatch = exports.adsPatch = new AdsPatch(); 2660 | var controlsPatch = exports.controlsPatch = new ControlsPatch(); 2661 | var watermarksPatch = exports.watermarksPatch = new WatermarksPatch(); 2662 | var keepHookingPatch = exports.keepHookingPatch = new KeepHookingPatch(); 2663 | var keyShortcutsPatch = exports.keyShortcutsPatch = new KeyShortcutsPatch(); 2664 | var mouseShortcutsPatch = exports.mouseShortcutsPatch = new MouseShortcutsPatch(); 2665 | var useWebSocketLoaderPatch = exports.useWebSocketLoaderPatch = new UseWebSocketLoaderPatch(); 2666 | 2667 | /***/ }), 2668 | /* 13 */ 2669 | /***/ (function(module, exports, __webpack_require__) { 2670 | 2671 | "use strict"; 2672 | 2673 | 2674 | Object.defineProperty(exports, "__esModule", { 2675 | value: true 2676 | }); 2677 | exports.fullscreen = undefined; 2678 | 2679 | var _hooker = __webpack_require__(0); 2680 | 2681 | var _hooker2 = _interopRequireDefault(_hooker); 2682 | 2683 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 2684 | 2685 | var fullscreen = void 0; 2686 | _hooker2.default.hookFullScreen(function (_exports) { 2687 | return exports.fullscreen = fullscreen = _exports; 2688 | }); 2689 | 2690 | exports.fullscreen = fullscreen; 2691 | 2692 | /***/ }), 2693 | /* 14 */ 2694 | /***/ (function(module, exports, __webpack_require__) { 2695 | 2696 | "use strict"; 2697 | 2698 | 2699 | Object.defineProperty(exports, "__esModule", { 2700 | value: true 2701 | }); 2702 | exports.webFullscreen = undefined; 2703 | 2704 | var _hooker = __webpack_require__(0); 2705 | 2706 | var _hooker2 = _interopRequireDefault(_hooker); 2707 | 2708 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 2709 | 2710 | var webFullscreen = void 0; 2711 | _hooker2.default.hookWebFullScreen(function (_exports) { 2712 | return exports.webFullscreen = webFullscreen = _exports; 2713 | }); 2714 | 2715 | exports.webFullscreen = webFullscreen; 2716 | 2717 | /***/ }), 2718 | /* 15 */ 2719 | /***/ (function(module, exports, __webpack_require__) { 2720 | 2721 | "use strict"; 2722 | 2723 | 2724 | Object.defineProperty(exports, "__esModule", { 2725 | value: true 2726 | }); 2727 | exports.flvInfo = undefined; 2728 | 2729 | var _hooker = __webpack_require__(0); 2730 | 2731 | var _hooker2 = _interopRequireDefault(_hooker); 2732 | 2733 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 2734 | 2735 | var flvInfo = void 0; 2736 | _hooker2.default.hookParseData(function (that) { 2737 | return exports.flvInfo = flvInfo = that.flvInfo; 2738 | }); 2739 | 2740 | exports.flvInfo = flvInfo; 2741 | 2742 | /***/ }) 2743 | /******/ ]); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iqiyi-player-switch", 3 | "version": "1.14.0", 4 | "description": "爱奇艺flash播放器与html5播放器随意切换,改善html5播放器播放体验。", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "lint": "eslint src", 8 | "fix": "eslint src --fix", 9 | "clean": "node scripts/clean", 10 | "prewatch": "npm run clean", 11 | "watch": "webpack --watch -d", 12 | "dev": "npm run watch", 13 | "prebuild": "npm run clean", 14 | "build": "webpack --define process.env.NODE_ENV='production'", 15 | "precommit": "npm run lint" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/gooyie/userscript-iqiyi-player-switch.git" 20 | }, 21 | "keywords": [ 22 | "html5", 23 | "qiyi", 24 | "iqiyi", 25 | "player", 26 | "userscript" 27 | ], 28 | "author": "gooyie", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/gooyie/userscript-iqiyi-player-switch/issues" 32 | }, 33 | "homepage": "https://github.com/gooyie/userscript-iqiyi-player-switch", 34 | "devDependencies": { 35 | "babel-cli": "^6.24.1", 36 | "babel-eslint": "^7.2.3", 37 | "babel-loader": "^7.1.4", 38 | "babel-plugin-transform-runtime": "^6.23.0", 39 | "babel-preset-env": "^1.6.1", 40 | "eslint": "^3.19.0", 41 | "eslint-loader": "^1.9.0", 42 | "fs-extra": "^3.0.1", 43 | "husky": "^0.13.4", 44 | "webpack": "^3.11.0" 45 | }, 46 | "dependencies": {} 47 | } 48 | -------------------------------------------------------------------------------- /scripts/clean.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | 3 | fs.emptyDirSync('dist'); 4 | -------------------------------------------------------------------------------- /src/cookies.js: -------------------------------------------------------------------------------- 1 | 2 | class Cookies { 3 | static get(key) { 4 | let value; 5 | if (new RegExp('^[^\\x00-\\x20\\x7f\\(\\)<>@,;:\\\\\\"\\[\\]\\?=\\{\\}\\/\\u0080-\\uffff]+$').test(key)) { // eslint-disable-line no-control-regex 6 | let re = new RegExp('(^| )' + key + '=([^;]*)(;|$)'); 7 | let rs = re.exec(document.cookie); 8 | value = rs ? rs[2] : ''; 9 | } 10 | return value ? decodeURIComponent(value) : ''; 11 | } 12 | 13 | static set(k, v, o={}) { 14 | let n = o.expires; 15 | if ('number' == typeof o.expires) { 16 | n = new Date(); 17 | n.setTime(n.getTime() + o.expires); 18 | } 19 | let key = k; 20 | let value = encodeURIComponent(v); 21 | let path = o.path ? '; path=' + o.path : ''; 22 | let expires = n ? '; expires=' + n.toGMTString() : ''; 23 | let domain = o.domain ? '; domain=' + o.domain : ''; 24 | document.cookie = `${key}=${value}${path}${expires}${domain}`; 25 | } 26 | 27 | static remove(k, o={}) { 28 | o.expires = new Date(0); 29 | this.set(k, '', o); 30 | } 31 | } 32 | 33 | export default Cookies; 34 | -------------------------------------------------------------------------------- /src/detector.js: -------------------------------------------------------------------------------- 1 | 2 | class Detector { 3 | static isSupportHtml5() { 4 | let v = document.createElement('video'); 5 | return !!( 6 | v.canPlayType('audio/mp4; codecs="mp4a.40.2"') && 7 | v.canPlayType('video/mp4; codecs="avc1.640029"') && 8 | v.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"') 9 | ); 10 | } 11 | 12 | static isSupportVms() { 13 | return !!( 14 | window.MediaSource && window.URL && window.WebSocket && window.ReadableStream && 15 | (window.RTCSessionDescription || window.webkitRTCSessionDescription) && 16 | (window.RTCPeerConnection || window.webkitRTCPeerConnection) && 17 | (window.RTCIceCandidate || window.webkitRTCIceCandidate) 18 | ); 19 | } 20 | 21 | static isSupportM3u8() { 22 | let v = document.createElement('video'); 23 | return !!( 24 | v.canPlayType('application/x-mpegurl') && 25 | v.canPlayType('application/vnd.apple.mpegurl') 26 | ); 27 | } 28 | 29 | static isChrome() { 30 | return /chrome/i.test(navigator.userAgent); 31 | } 32 | 33 | static isFirefox() { 34 | return /firefox/i.test(navigator.userAgent); 35 | } 36 | 37 | static isEdge() { 38 | return /edge/i.test(navigator.userAgent); 39 | } 40 | 41 | static isInIFrame() { 42 | return window.top !== window.self; 43 | } 44 | 45 | static isOutsite() { 46 | return !/\.iqiyi\.com$/.test(location.host); 47 | } 48 | 49 | static isOutsideLink() { 50 | return location.hash === '#outsidelink'; 51 | } 52 | 53 | static hasFlashPlugin() { 54 | const plugins = unsafeWindow.navigator.plugins; 55 | return !!(plugins['Shockwave Flash'] && plugins['Shockwave Flash'].description); 56 | } 57 | } 58 | 59 | export default Detector; 60 | -------------------------------------------------------------------------------- /src/faker.js: -------------------------------------------------------------------------------- 1 | 2 | class Faker { 3 | static fakeMacPlatform() { 4 | const PLAFORM_MAC = 'mac'; 5 | Object.defineProperty(unsafeWindow.navigator, 'platform', {get: () => PLAFORM_MAC}); 6 | } 7 | 8 | static fakeSafari() { 9 | const UA_SAFARY = 'safari'; 10 | Object.defineProperty(unsafeWindow.navigator, 'userAgent', {get: () => UA_SAFARY}); 11 | } 12 | 13 | static fakeChrome(ver = '') { 14 | const UA_CHROME = `Chrome/${ver}`; 15 | Object.defineProperty(unsafeWindow.navigator, 'userAgent', {get: () => UA_CHROME}); 16 | } 17 | 18 | static fakeFlashPlugin() { 19 | let plugin = { 20 | description: 'Shockwave Flash 26.0 r0', 21 | filename: 'pepflashplayer64_26_0_0_131.dll', 22 | length: 0, 23 | name: 'Shockwave Flash', 24 | }; 25 | 26 | Reflect.setPrototypeOf(plugin, Plugin.prototype); 27 | unsafeWindow.navigator.plugins['Shockwave Flash'] = plugin; 28 | } 29 | } 30 | 31 | export default Faker; 32 | -------------------------------------------------------------------------------- /src/fullscreen.js: -------------------------------------------------------------------------------- 1 | import Hooker from './hooker'; 2 | 3 | let fullscreen; 4 | Hooker.hookFullScreen(_exports => fullscreen = _exports); 5 | 6 | export { fullscreen }; 7 | -------------------------------------------------------------------------------- /src/hooker.js: -------------------------------------------------------------------------------- 1 | import Logger from './logger'; 2 | 3 | class Hooker { 4 | static _hookCall(cb) { 5 | const call = Function.prototype.call; 6 | Function.prototype.call = function(...args) { 7 | let ret = call.apply(this, args); 8 | try { 9 | if (args && cb(args)) { 10 | Function.prototype.call = call; 11 | cb = () => {}; 12 | Logger.info(`The native function call has been restored`); 13 | } 14 | } catch (err) { 15 | Logger.error(err.stack); 16 | } 17 | return ret; 18 | }; 19 | this._hookCall = null; 20 | } 21 | 22 | static _isModuleCall(args) { // module.exports, module, module.exports, require 23 | return args.length === 4 && args[1] && Object.getPrototypeOf(args[1]) === Object.prototype && args[1].hasOwnProperty('exports'); 24 | } 25 | 26 | static _hookModuleCall(cb, pred) { 27 | const callbacksMap = new Map([[pred, [cb]]]); 28 | this._hookCall((args) => { 29 | if (!this._isModuleCall(args)) return; 30 | 31 | const exports = args[1].exports; 32 | for (const [pred, callbacks] of callbacksMap) { 33 | if (!pred.apply(this, [exports])) continue; 34 | callbacks.forEach(cb => cb(exports, args)); 35 | this.keepalive || callbacksMap.delete(pred); 36 | !callbacksMap.size && (this._hookModuleCall = null); 37 | break; 38 | } 39 | 40 | return !callbacksMap.size; 41 | }); 42 | 43 | this._hookModuleCall = (cb, pred) => { 44 | if (callbacksMap.has(pred)) { 45 | callbacksMap.get(pred).push(cb); 46 | } else { 47 | callbacksMap.set(pred, [cb]); 48 | } 49 | }; 50 | } 51 | 52 | static _isJqueryModuleCall(exports) { 53 | return exports.hasOwnProperty('fn') && exports.fn.hasOwnProperty('jquery'); 54 | } 55 | 56 | static hookJquery(cb = ()=>{}) { 57 | this._hookModuleCall(cb, this._isJqueryModuleCall); 58 | } 59 | 60 | static hookJqueryAjax(cb) { 61 | this.hookJquery((exports) => { 62 | const ajax = exports.ajax.bind(exports); 63 | exports.ajax = function(url, options = {}) { 64 | if (typeof url === 'object') { 65 | [url, options] = [url.url, url]; 66 | } 67 | let isHijacked = cb(url, options); 68 | if (isHijacked) return; 69 | return ajax(url, options); 70 | }; 71 | }); 72 | } 73 | 74 | static _isHttpModuleCall(exports) { 75 | return exports.hasOwnProperty('jsonp') && exports.hasOwnProperty('ajax'); 76 | } 77 | 78 | static hookHttp(cb) { 79 | this._hookModuleCall(cb, this._isHttpModuleCall); 80 | } 81 | 82 | static hookHttpJsonp(cb) { 83 | this.hookHttp((exports) => { 84 | const jsonp = exports.jsonp.bind(exports); 85 | exports.jsonp = function(options) { 86 | let isHijacked = cb(options); 87 | if (isHijacked) return; 88 | return jsonp(options); 89 | }; 90 | }); 91 | } 92 | 93 | static _isLogoModuleCall(exports) { 94 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('showLogo'); 95 | } 96 | 97 | static hookLogo(cb) { 98 | this._hookModuleCall(cb, this._isLogoModuleCall); 99 | } 100 | 101 | static _isFullScreenModuleCall(exports) { 102 | return exports.__proto__ && exports.__proto__.hasOwnProperty('isFullScreen'); 103 | } 104 | 105 | static hookFullScreen(cb) { 106 | this._hookModuleCall(cb, this._isFullScreenModuleCall); 107 | } 108 | 109 | static _isWebFullScreenModuleCall(exports) { 110 | return exports.__proto__ && exports.__proto__.hasOwnProperty('isWebFullScreen'); 111 | } 112 | 113 | static hookWebFullScreen(cb) { 114 | this._hookModuleCall(cb, this._isWebFullScreenModuleCall); 115 | } 116 | 117 | static hookWebFullScreenInit(cb) { 118 | this.hookWebFullScreen((exports) => { 119 | const init = exports.__proto__.init; 120 | exports.__proto__.init = function(wrapper, btn) { 121 | cb(this, wrapper, btn); 122 | init.apply(this, [wrapper, btn]); 123 | }; 124 | }); 125 | } 126 | 127 | static _isPluginControlsModuleCall(exports) { 128 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('initFullScreen'); 129 | } 130 | 131 | static hookPluginControls(cb) { 132 | this._hookModuleCall(cb, this._isPluginControlsModuleCall); 133 | } 134 | 135 | static hookPluginControlsInit(cb) { 136 | this.hookPluginControls((exports) => { 137 | const init = exports.prototype.init; 138 | exports.prototype.init = function() { 139 | cb(this); 140 | init.apply(this); 141 | }; 142 | }); 143 | } 144 | 145 | static hookInitFullScreen(cb) { 146 | this.hookPluginControls((exports) => { 147 | const initFullScreen = exports.prototype.initFullScreen; 148 | exports.prototype.initFullScreen = function() { 149 | cb(this); 150 | initFullScreen.apply(this); 151 | }; 152 | }); 153 | } 154 | 155 | static _isCoreModuleCall(exports) { 156 | return 'function' === typeof exports && 157 | exports.prototype.hasOwnProperty('getdefaultvds') && 158 | exports.prototype.hasOwnProperty('getMovieInfo'); 159 | } 160 | 161 | static hookCore(cb) { 162 | this._hookModuleCall(cb, this._isCoreModuleCall); 163 | } 164 | 165 | static _isSkinBaseModuleCall(exports) { 166 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('_checkPlugin'); 167 | } 168 | 169 | static hookSkinBase(cb) { 170 | this._hookModuleCall(cb, this._isSkinBaseModuleCall); 171 | } 172 | 173 | static _isPluginHotKeysModuleCall(exports) { 174 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('_keydown'); 175 | } 176 | 177 | static hookPluginHotKeys(cb) { 178 | this._hookModuleCall(cb, this._isPluginHotKeysModuleCall); 179 | } 180 | 181 | static _isFragmentModuleCall(exports) { 182 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('parseData'); 183 | } 184 | 185 | static hookFragment(cb) { 186 | this._hookModuleCall(cb, this._isFragmentModuleCall); 187 | } 188 | 189 | static hookParseData(cb) { 190 | this.hookFragment((exports) => { 191 | const parseData = exports.prototype.parseData; 192 | exports.prototype.parseData = function(...args) { 193 | parseData.apply(this, args); 194 | cb(this); 195 | }; 196 | }); 197 | } 198 | 199 | static _isUserModuleCall(exports) { 200 | return exports.__proto__ && exports.__proto__.hasOwnProperty('isVip'); 201 | } 202 | 203 | static hookUser(cb) { 204 | this._hookModuleCall(cb, this._isUserModuleCall); 205 | } 206 | 207 | static _isShowRequestModuleCall(exports) { 208 | return 'function' === typeof exports && exports.compressRequestKey && exports.prototype.hasOwnProperty('request'); 209 | } 210 | 211 | static hookShowRequest(cb) { 212 | this._hookModuleCall(cb, this._isShowRequestModuleCall); 213 | } 214 | 215 | static _isDefaultSkinModuleCall(exports) { 216 | return 'function' === typeof exports && exports.prototype.hasOwnProperty('_initDBClicks'); 217 | } 218 | 219 | static hookDefaultSkin(cb) { 220 | this._hookModuleCall(cb, this._isDefaultSkinModuleCall); 221 | } 222 | 223 | static _isConfigModuleCall(exports) { 224 | return exports.loadType && exports.dispatchCfg; 225 | } 226 | 227 | static hookConfig(cb) { 228 | this._hookModuleCall(cb, this._isConfigModuleCall); 229 | } 230 | } 231 | 232 | Hooker.keepalive = false; 233 | 234 | export default Hooker; 235 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Logger from './logger'; 2 | import Cookies from './cookies'; 3 | import Detector from './detector'; 4 | import Faker from './faker'; 5 | import { replaceFlash, adaptIframe } from './outsite'; 6 | import { 7 | vipPatch, 8 | adsPatch, 9 | controlsPatch, 10 | watermarksPatch, 11 | keepHookingPatch, 12 | keyShortcutsPatch, 13 | mouseShortcutsPatch, 14 | useWebSocketLoaderPatch, 15 | } from './patch'; 16 | 17 | const PLAYER_TYPE = { 18 | Html5VOD: 'h5_VOD', 19 | FlashVOD: 'flash_VOD' 20 | }; 21 | 22 | function forceHtml5() { 23 | Cookies.set('player_forcedType', PLAYER_TYPE.Html5VOD, {domain: '.iqiyi.com'}); 24 | Logger.info(`The 'player_forcedType' cookie has been set as '${PLAYER_TYPE.Html5VOD}'`); 25 | } 26 | 27 | function forceFlash() { 28 | Cookies.set('player_forcedType', PLAYER_TYPE.FlashVOD, {domain: '.iqiyi.com'}); 29 | Logger.info(`The 'player_forcedType' cookie has been set as '${PLAYER_TYPE.FlashVOD}'`); 30 | } 31 | 32 | function clean() { 33 | Cookies.remove('player_forcedType', {domain: '.iqiyi.com'}); 34 | Logger.info(`Removed the 'player_forcedType' cookie`); 35 | } 36 | 37 | function switchTo(type) { 38 | Logger.info(`Switching to ${type} ...`); 39 | GM_setValue('player_forcedType', type); 40 | document.location.reload(); 41 | } 42 | 43 | function registerMenu() { 44 | const MENU_NAME = { 45 | HTML5: 'HTML5播放器', 46 | FLASH: 'Flash播放器' 47 | }; 48 | 49 | let currType = GM_getValue('player_forcedType', PLAYER_TYPE.Html5VOD); // 默认为Html5播放器,免去切换。 50 | let [type, name] = currType === PLAYER_TYPE.Html5VOD ? [PLAYER_TYPE.FlashVOD, MENU_NAME.FLASH] : [PLAYER_TYPE.Html5VOD, MENU_NAME.HTML5]; 51 | GM_registerMenuCommand(name, () => switchTo(type), null); 52 | Logger.info(`Registered the menu.`); 53 | } 54 | 55 | function mustKeepHooking() { 56 | return location.search.includes('list'); // https://github.com/gooyie/userscript-iqiyi-player-switch/issues/15 57 | } 58 | 59 | //============================================================================= 60 | 61 | registerMenu(); 62 | 63 | let currType = GM_getValue('player_forcedType', PLAYER_TYPE.Html5VOD); 64 | if (currType === PLAYER_TYPE.Html5VOD) { 65 | if (Detector.isSupportHtml5()) { 66 | if (Detector.isOutsite()) { 67 | replaceFlash(); 68 | } else { 69 | forceHtml5(); 70 | 71 | if (Detector.isFirefox()) { 72 | // Fake Chrome with a version number less than 43 73 | // to use the data engine to play videos better than HD and to use the XHR loader 74 | // because Firefox has not yet implemented ReadableStream to support the Fetch loader. 75 | Faker.fakeChrome(42); 76 | } 77 | 78 | if (mustKeepHooking()) { 79 | keepHookingPatch.install(); 80 | } 81 | adsPatch.install(); 82 | controlsPatch.install(); 83 | watermarksPatch.install(); 84 | vipPatch.install(); 85 | keyShortcutsPatch.install(); 86 | mouseShortcutsPatch.install(); 87 | useWebSocketLoaderPatch.install(); 88 | 89 | if (Detector.isInIFrame() && Detector.isOutsideLink()) { 90 | adaptIframe(); 91 | } 92 | } 93 | } else { 94 | alert('╮(╯▽╰)╭ 你的浏览器播放不了html5视频~~~~'); 95 | } 96 | } else { 97 | forceFlash(); 98 | } 99 | 100 | window.addEventListener('unload', () => clean()); 101 | -------------------------------------------------------------------------------- /src/logger.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | class Logger { 3 | constructor(tag) { 4 | this._tag = tag; 5 | } 6 | 7 | get tag() { 8 | return this._tag; 9 | } 10 | 11 | log(...args) { 12 | console.log(this.tag + args.shift(), ...args); 13 | } 14 | 15 | info(...args) { 16 | console.log('%c' + this.tag + '%c' + args.shift(), 17 | 'color: green; font-weight: bolder', 'color: blue', ...args); 18 | } 19 | 20 | debug(...args) { 21 | console.debug(this.tag + args.shift(), ...args); 22 | } 23 | 24 | warn(...args) { 25 | console.warn(this.tag + args.shift(), ...args); 26 | } 27 | 28 | error(...args) { 29 | console.error(this.tag + args.shift(), ...args); 30 | } 31 | } 32 | 33 | export default new Logger(`[${GM_info.script.name}]`); 34 | -------------------------------------------------------------------------------- /src/meta.js: -------------------------------------------------------------------------------- 1 | const pkg = require('../package'); 2 | 3 | module.exports = ` 4 | // ==UserScript== 5 | // @name ${pkg.name} 6 | // @namespace ${pkg.homepage} 7 | // @homepageURL ${pkg.homepage} 8 | // @supportURL ${pkg.bugs.url} 9 | // @updateURL https://raw.githubusercontent.com/${pkg.author}/userscript-iqiyi-player-switch/master/dist/${pkg.name}.user.js 10 | // @description ${pkg.description} 11 | // @version ${pkg.version} 12 | // @compatible chrome >= 43 13 | // @compatible firefox >= 45 14 | // @compatible edge >= 15 15 | // @author ${pkg.author} 16 | // @license ${pkg.license} License 17 | // 18 | // @include *://*.iqiyi.com/* 19 | // @include *://v.baidu.com/* 20 | // @include *://music.baidu.com/mv/* 21 | // @include *://www.zybus.com/* 22 | // @grant GM_registerMenuCommand 23 | // @grant GM_xmlhttpRequest 24 | // @grant GM_addStyle 25 | // @grant GM_getValue 26 | // @grant GM_setValue 27 | // @grant GM_info 28 | // @grant unsafeWindow 29 | // @connect qiyi.com 30 | // @run-at document-start 31 | // ==/UserScript== 32 | `; 33 | -------------------------------------------------------------------------------- /src/outsite.js: -------------------------------------------------------------------------------- 1 | import Logger from './logger'; 2 | import Hooker from './hooker'; 3 | import Faker from './faker'; 4 | import Detector from './detector'; 5 | import { getVideoUrl, findVid, findTvid } from './utils'; 6 | 7 | async function embedSrc(targetNode, {tvid, vid}) { 8 | targetNode.innerHTML = `
正在获取视频源...
`; 9 | 10 | try { 11 | let url = await getVideoUrl(tvid, vid); 12 | Logger.info('source url: %s', url); 13 | targetNode.innerHTML = ``; 14 | } catch (err) { 15 | targetNode.innerHTML = `

获取视频源出错!

${err.message}

`; 16 | } 17 | } 18 | 19 | function replaceFlash() { 20 | if (!Detector.hasFlashPlugin()) Faker.fakeFlashPlugin(); 21 | 22 | const observer = new MutationObserver((records, self) => { 23 | for (let record of records) { 24 | if (record.type !== 'childList' || !record.addedNodes) continue; 25 | 26 | for (let node of record.addedNodes) { 27 | if (node.nodeName !== 'OBJECT' && node.nodeName !== 'EMBED') continue; 28 | Logger.info('found node', node); 29 | 30 | let text = node.outerHTML; 31 | let vid = findVid(text); 32 | let tvid = findTvid(text); 33 | 34 | if (tvid && vid) { 35 | Logger.info('found tvid: %s, vid: %s', tvid, vid); 36 | embedSrc(node.parentNode, {tvid, vid}); 37 | self.disconnect(); 38 | Logger.info('stoped observation'); 39 | } 40 | } 41 | } 42 | }); 43 | 44 | observer.observe(document.body || document.documentElement, {subtree: true, childList: true}); 45 | Logger.info('started observation'); 46 | } 47 | 48 | function adaptIframe() { 49 | let style = ` 50 | body[class|="qypage"] { 51 | overflow: hidden !important; 52 | background: #000 !important; 53 | visibility: hidden; 54 | } 55 | 56 | .mod-func { 57 | display: none !important; 58 | } 59 | 60 | .${GM_info.script.name}.info { 61 | width: 20em; 62 | height: 5em; 63 | position: absolute; 64 | top: 0; 65 | bottom: 0; 66 | left: 0; 67 | right: 0; 68 | margin: auto; 69 | text-align: center; 70 | line-height: 5em; 71 | font-size: 1em; 72 | color: #ccc; 73 | } 74 | 75 | .${GM_info.script.name}.error { 76 | height: 3em; 77 | position: absolute; 78 | top: 0; 79 | bottom: 0; 80 | left: 0; 81 | right: 0; 82 | margin: auto; 83 | text-align: center; 84 | font-size: 1em; 85 | color: #c00; 86 | } 87 | `; 88 | 89 | GM_addStyle(style); 90 | 91 | Hooker.hookWebFullScreen((exports) => { 92 | const init = exports.__proto__.init; 93 | exports.__proto__.init = function(wrapper, btn) { 94 | init.apply(this, [wrapper, btn]); 95 | this.enter(); 96 | 97 | btn[0].style.display = 'none'; 98 | document.body.style.visibility = 'visible'; 99 | }; 100 | 101 | exports.__proto__.exit = () => {}; 102 | }); 103 | 104 | Hooker.hookCore((exports) => { 105 | exports.prototype.hasNextVideo = () => null; 106 | }); 107 | } 108 | 109 | export { replaceFlash, adaptIframe }; 110 | -------------------------------------------------------------------------------- /src/parsed-data.js: -------------------------------------------------------------------------------- 1 | import Hooker from './hooker'; 2 | 3 | let flvInfo; 4 | Hooker.hookParseData(that => flvInfo = that.flvInfo); 5 | 6 | export { flvInfo }; 7 | -------------------------------------------------------------------------------- /src/patch.js: -------------------------------------------------------------------------------- 1 | import Logger from './logger'; 2 | import Hooker from './hooker'; 3 | import { fullscreen } from './fullscreen'; 4 | import { webFullscreen } from './web-fullscreen'; 5 | import { flvInfo } from './parsed-data'; 6 | 7 | class Patch { 8 | constructor() { 9 | this._installed = false; 10 | } 11 | 12 | install() { 13 | if (!this._installed) { 14 | this._installed = true; 15 | this._prepare(); 16 | this._apply(); 17 | } 18 | } 19 | 20 | _prepare() {} 21 | 22 | _apply() {} 23 | } 24 | 25 | class VipPatch extends Patch { 26 | constructor() { 27 | super(); 28 | } 29 | 30 | _apply() { 31 | Hooker.hookUser((exports) => { 32 | const proto = exports.__proto__; 33 | proto.isVipSync = () => true; 34 | proto.isVip = (cb) => setTimeout(cb, 0, true); 35 | Logger.info('The vip patch has been installed'); 36 | }); 37 | } 38 | } 39 | 40 | class AdsPatch extends Patch { 41 | constructor() { 42 | super(); 43 | } 44 | 45 | _fakeAdsData() { 46 | return {}; 47 | } 48 | 49 | _apply() { 50 | Hooker.hookShowRequest((exports) => { 51 | const proto = exports.prototype; 52 | proto.request = (cb) => setTimeout(cb, 0, this._fakeAdsData()); 53 | Logger.info('The ads patch has been installed'); 54 | }); 55 | } 56 | } 57 | 58 | class WatermarksPatch extends Patch { 59 | constructor() { 60 | super(); 61 | } 62 | 63 | _apply() { 64 | Hooker.hookLogo((exports) => { 65 | exports.prototype.showLogo = () => {}; 66 | Logger.info('The watermarks patch has been installed'); 67 | }); 68 | } 69 | } 70 | 71 | class ControlsPatch extends Patch { // Prevent the player controls were disabled. 72 | constructor() { 73 | super(); 74 | } 75 | 76 | _apply() { 77 | Hooker.hookSkinBase((exports) => { 78 | exports.prototype._checkPlugin = () => {}; // This function disables the player controls when playing ads and enables when done. 79 | Logger.info('The controls patch has been installed'); 80 | }); 81 | } 82 | } 83 | 84 | class CorePatch extends Patch { 85 | constructor() { 86 | super(); 87 | } 88 | 89 | _prepare() { 90 | this._initShowTip(); 91 | this._initPlaybackRate(); 92 | } 93 | 94 | _initShowTip() { 95 | Hooker.hookPluginControlsInit((that) => { 96 | that.core.on('showtip', (event) => { 97 | that.setcontroltip.apply(that, [{str: event.data, x: that._process.offset().left, y: 3, cut: true, timeout: true}]); 98 | if (that.$plugin.hasClass('process_hidden')) { 99 | that._controltips.css('top', '-25px'); 100 | } else if (that.$plugin.hasClass('bottom-hide')) { 101 | that._controltips.css('top', '-38px'); 102 | } 103 | }); 104 | }); 105 | } 106 | 107 | _initPlaybackRate() { 108 | Hooker.hookPluginControls((exports) => { 109 | exports.prototype.initPlaybackRate = function() { 110 | const core = this.core; 111 | 112 | let rate = parseFloat(localStorage.getItem('QiyiPlayerPlaybackRate')); 113 | rate = isNaN(rate) ? 1 : rate; 114 | 115 | if (core.getCurrStatus() === 'playing') { 116 | core.setPlaybackRate(rate); 117 | } else { 118 | const onstatuschanged = (evt) => { 119 | if (evt.data.state === 'playing') { 120 | core.setPlaybackRate(rate); 121 | core.un('statusChanged', onstatuschanged); 122 | } 123 | }; 124 | core.on('statusChanged', onstatuschanged); 125 | } 126 | 127 | const $ul = this.$playbackrateUl; 128 | $ul.find(`[data-pbrate="${rate}"]`).addClass('selected'); 129 | 130 | const $items = $ul.find('li'); 131 | $items.on('click', function() { 132 | const rate = parseFloat(this.getAttribute('data-pbrate')); 133 | if (!this.classList.contains('selected')) { 134 | $items.removeClass('selected'); 135 | this.classList.add('selected'); 136 | } 137 | localStorage.setItem('QiyiPlayerPlaybackRate', rate); 138 | core.setPlaybackRate(rate); 139 | }); 140 | 141 | this.$playsettingicon.on('click', function() { 142 | const rate = core.getPlaybackRate(); 143 | const $item = $ul.find(`[data-pbrate="${rate}"]`); 144 | if ($item.length === 1) { 145 | if (!$item.hasClass('selected')) { 146 | $items.removeClass('selected'); 147 | $item.addClass('selected'); 148 | } 149 | } else { 150 | $items.removeClass('selected'); 151 | } 152 | }); 153 | }; 154 | }); 155 | } 156 | 157 | _apply() { 158 | Hooker.hookCore((exports) => { 159 | const proto = exports.prototype; 160 | 161 | proto._showTip = function(msg) { 162 | this.fire({type: 'showtip', data: msg}); 163 | }; 164 | 165 | proto.getFPS = function() { 166 | if (flvInfo) { 167 | return flvInfo.videoConfigTag.sps.frame_rate.fps; 168 | } else { 169 | return 25; // f4v极速以上,动画23.976、电影24、电视剧25。 170 | } 171 | }; 172 | 173 | proto.prevFrame = function() { 174 | const video = this.video(); 175 | const seekTime = Math.max(0, Math.min(this.getDuration(), video.currentTime - 1 / this.getFPS())); 176 | video.currentTime = seekTime; 177 | this._showTip('上一帧'); 178 | }; 179 | 180 | proto.nextFrame = function() { 181 | const video = this.video(); 182 | const seekTime = Math.max(0, Math.min(this.getDuration(), video.currentTime + 1 / this.getFPS())); 183 | video.currentTime = seekTime; 184 | this._showTip('下一帧'); 185 | }; 186 | 187 | proto.seek = function(...args) { 188 | const video = this.video(); 189 | const playbackRate = video.playbackRate; 190 | this._engine.seek(...args); 191 | video.playbackRate = playbackRate; 192 | }; 193 | 194 | proto.stepSeek = function(stepTime) { 195 | const seekTime = Math.max(0, Math.min(this.getDuration(), this.getCurrenttime() + stepTime)); 196 | let msg; 197 | 198 | if (Math.abs(stepTime) < 60) { 199 | msg = stepTime > 0 ? `步进:${stepTime}秒` : `步退:${Math.abs(stepTime)}秒`; 200 | } else { 201 | msg = stepTime > 0 ? `步进:${stepTime/60}分钟` : `步退:${Math.abs(stepTime)/60}分钟`; 202 | } 203 | this._showTip(msg); 204 | 205 | this.seek(seekTime, true); 206 | }; 207 | 208 | proto.rangeSeek = function(range) { 209 | const duration = this.getDuration(); 210 | const seekTime = Math.max(0, Math.min(duration, duration * range)); 211 | this.seek(seekTime, true); 212 | this._showTip('定位:' + (range * 100).toFixed(0) + '%'); 213 | }; 214 | 215 | proto.toggleMute = function() { 216 | if (this.getMuted()) { 217 | this.setMuted(false); 218 | this._showTip('取消静音'); 219 | } else { 220 | this.setMuted(true); 221 | this._showTip('静音'); 222 | } 223 | }; 224 | 225 | proto.adjustVolume = function(value) { 226 | let volume = this.getVolume() + value; 227 | volume = Math.max(0, Math.min(1, volume.toFixed(2))); 228 | this.setVolume(volume); 229 | this.fire({type: 'keyvolumechange'}); 230 | }; 231 | 232 | proto.getPlaybackRate = function() { // iqiyi 的这个方法有bug,没把值返回! 233 | return this._engine.getPlaybackRate(); 234 | }; 235 | 236 | proto.adjustPlaybackRate = function(value) { 237 | const currRate = this.getPlaybackRate(); 238 | const rate = Math.max(0.2, Math.min(5, parseFloat((currRate + value).toFixed(1)))); 239 | 240 | localStorage.setItem('QiyiPlayerPlaybackRate', rate); 241 | this.setPlaybackRate(rate); 242 | this._showTip(`播放速率:${rate}`); 243 | }; 244 | 245 | proto.turnPlaybackRate = function() { 246 | const currRate = this.getPlaybackRate(); 247 | let rate; 248 | if (currRate !== 1) { 249 | this._backRate = currRate; 250 | rate = 1; 251 | } else { 252 | rate = this._backRate || 1; 253 | } 254 | 255 | this.setPlaybackRate(rate); 256 | this._showTip(`播放速率:${rate}`); 257 | }; 258 | 259 | proto.hasPrevVideo = function() { 260 | return this._getVideoIndexInList(this._movieinfo.tvid) > 0 || this._getVideoIndexInList(this._movieinfo.oldTvid) > 0; 261 | }; 262 | 263 | proto.playNext = function() { 264 | if (this.hasNextVideo()) { 265 | this._showTip('播放下一集'); 266 | this.switchNextVideo(); 267 | } else { 268 | this._showTip('没有下一集哦'); 269 | } 270 | }; 271 | 272 | proto.playPrev = function() { 273 | if (this.hasPrevVideo()) { 274 | this._showTip('播放上一集'); 275 | this.switchPreVideo(); 276 | } else { 277 | this._showTip('没有上一集哦'); 278 | } 279 | }; 280 | 281 | Logger.info('The core patch has been installed'); 282 | }); 283 | } 284 | } 285 | 286 | const corePatch = new CorePatch(); 287 | 288 | class KeyShortcutsPatch extends Patch { 289 | constructor() { 290 | super(); 291 | } 292 | 293 | _prepare() { 294 | corePatch.install(); 295 | } 296 | 297 | _apply() { 298 | Hooker.hookPluginHotKeys((exports) => { 299 | const proto = exports.prototype; 300 | 301 | proto.init = function() { 302 | document.addEventListener('keydown', this._keydown.bind(this)); 303 | }; 304 | 305 | proto._isValidTarget = function(target) { 306 | return target.nodeName === 'BODY' || target.nodeName == 'VIDEO' || target.classList.contains('pw-video'); // 全局 307 | // return target.nodeName === 'VIDEO' || target.classList.contains('pw-video'); // 非全局 308 | }; 309 | 310 | proto._keydown = function(event) { 311 | if (!this._isValidTarget(event.target)) return; 312 | 313 | const { keyCode, ctrlKey, shiftKey, altKey } = event; 314 | const core = this.core; 315 | 316 | switch (keyCode) { 317 | case 32: // Spacebar 318 | if (!ctrlKey && !shiftKey && !altKey) { 319 | if (core.isPaused()) { 320 | core.play(true); 321 | core._showTip('播放'); 322 | } else { 323 | core.pause(true); 324 | core._showTip('暂停'); 325 | } 326 | } else { 327 | return; 328 | } 329 | break; 330 | case 39: // → Arrow Right 331 | case 37: { // ← Arrow Left 332 | let stepTime; 333 | if (!ctrlKey && !shiftKey && !altKey) { 334 | stepTime = 39 === keyCode ? 5 : -5; 335 | } else if (ctrlKey && !shiftKey && !altKey) { 336 | stepTime = 39 === keyCode ? 30 : -30; 337 | } else if (!ctrlKey && shiftKey && !altKey) { 338 | stepTime = 39 === keyCode ? 60 : -60; 339 | } else if (ctrlKey && !shiftKey && altKey) { 340 | stepTime = 39 === keyCode ? 3e2 : -3e2; // 5分钟 341 | } else { 342 | return; 343 | } 344 | 345 | core.stepSeek(stepTime); 346 | break; 347 | } 348 | case 38: // ↑ Arrow Up 349 | case 40: // ↓ Arrow Down 350 | if (!ctrlKey && !shiftKey && !altKey) { 351 | core.adjustVolume(38 === keyCode ? 0.05 : -0.05); 352 | } else { 353 | return; 354 | } 355 | break; 356 | case 77: // M 357 | if (!ctrlKey && !shiftKey && !altKey) { 358 | core.toggleMute(); 359 | } else { 360 | return; 361 | } 362 | break; 363 | case 13: // Enter 364 | if (!ctrlKey && !shiftKey && !altKey) { 365 | fullscreen.toggle(); 366 | } else if (ctrlKey && !shiftKey && !altKey) { 367 | webFullscreen.toggle(); 368 | } else { 369 | return; 370 | } 371 | break; 372 | case 67: // C 373 | case 88: // X 374 | if (!ctrlKey && !shiftKey && !altKey) { 375 | core.adjustPlaybackRate(67 === keyCode ? 0.1 : -0.1); 376 | } else { 377 | return; 378 | } 379 | break; 380 | case 90: // Z 381 | if (!ctrlKey && !shiftKey && !altKey) { 382 | core.turnPlaybackRate(); 383 | } else { 384 | return; 385 | } 386 | break; 387 | case 68: // D 388 | case 70: // F 389 | if (!ctrlKey && !shiftKey && !altKey) { 390 | core.pause(true); 391 | if (keyCode === 68) { 392 | core.prevFrame(); 393 | } else { 394 | core.nextFrame(); 395 | } 396 | } else { 397 | return; 398 | } 399 | break; 400 | case 80: // P 401 | case 78: // N 402 | if (!ctrlKey && shiftKey && !altKey) { 403 | if (keyCode === 78) { 404 | core.playNext(); 405 | } else { 406 | core.playPrev(); 407 | } 408 | } else { 409 | return; 410 | } 411 | break; 412 | case 27: // ESC 413 | if (!event.ctrlKey && !event.shiftKey && !event.altKey) 414 | webFullscreen.isWebFullScreen() && webFullscreen.exit(); 415 | return; 416 | default: 417 | if (keyCode >= 48 && keyCode <= 57) { // 0 ~ 9 418 | if (!ctrlKey && !shiftKey && !altKey) { 419 | core.rangeSeek((keyCode - 48) * 0.1); 420 | } else { 421 | return; 422 | } 423 | } else { 424 | return; 425 | } 426 | } 427 | 428 | event.preventDefault(); 429 | event.stopPropagation(); 430 | }; 431 | 432 | Logger.info('The keyboard shortcuts patch has been installed'); 433 | }); 434 | } 435 | } 436 | 437 | class MouseShortcutsPatch extends Patch { 438 | constructor() { 439 | super(); 440 | } 441 | 442 | _prepare() { 443 | corePatch.install(); 444 | } 445 | 446 | _apply() { 447 | Hooker.hookDefaultSkin((exports) => { 448 | exports.prototype._initDBClicks = function() { 449 | let timer, core = this.core; 450 | this.videoWrapper.find('video').on('click', () => { 451 | if (timer) { 452 | clearTimeout(timer); 453 | timer = null; 454 | return; 455 | } 456 | timer = setTimeout(() => { 457 | if (core.isPaused()) { 458 | core.play(true); 459 | } else { 460 | core.pause(true); 461 | } 462 | timer = null; 463 | }, 200); 464 | }).on('dblclick', (event) => { 465 | event.preventDefault(); 466 | event.stopPropagation(); 467 | if (event.ctrlKey) { 468 | webFullscreen.toggle(); 469 | } else { 470 | fullscreen.toggle(); 471 | } 472 | }).on('wheel', (event) => { 473 | if (fullscreen.isFullScreen() || webFullscreen.isWebFullScreen()) { 474 | const delta = event.wheelDelta || event.detail || (event.deltaY && -event.deltaY); 475 | core.adjustVolume(delta > 0 ? 0.05 : -0.05); 476 | } 477 | }); 478 | }; 479 | 480 | Logger.info('The mouse shortcuts patch has been installed'); 481 | }); 482 | } 483 | } 484 | 485 | class UseWebSocketLoaderPatch extends Patch { 486 | constructor() { 487 | super(); 488 | this.tryWs = GM_getValue('tryWs', false); 489 | } 490 | 491 | _prepare() { 492 | this._addSetting(); 493 | } 494 | 495 | _apply() { 496 | const that = this; 497 | Hooker.hookFragment((exports) => { 498 | Reflect.defineProperty(exports.prototype, 'tryWS', { 499 | get: () => this._tryWs || that.tryWs, // Will use the WebSocket loader if the value of tryWs is true. 500 | set: (value) => this._tryWs = value, // The value of tryWs will be true if the Fetch loader fails. 501 | }); 502 | Logger.info('The WebSocket loader patch has been installed'); 503 | }); 504 | } 505 | 506 | _addSetting() { 507 | const that = this; 508 | Hooker.hookPluginControls((exports) => { 509 | const initSetting = exports.prototype.initSetting; 510 | exports.prototype.initSetting = function() { 511 | const div = document.createElement('div'); 512 | div.innerHTML = ` 513 |
514 | WebSocket 515 |
516 |
`; 517 | const item = div.querySelector('.setPop_item'); 518 | this.$playsettingbox.find('.video_setPop_top').append(item); 519 | this.$usewebsocketBtn = this.$playsettingbox.find('[data-player-hook="usewebsocketloader"]'); 520 | 521 | if (that.tryWs) { 522 | this.$usewebsocketBtn.removeClass('setPop_switch_close'); 523 | } 524 | this.$usewebsocketBtn.on('click', () => { 525 | this.$usewebsocketBtn.toggleClass('setPop_switch_close'); 526 | that.tryWs = !that.tryWs; 527 | GM_setValue('tryWs', that.tryWs); 528 | }); 529 | 530 | initSetting.apply(this); 531 | }; 532 | }); 533 | } 534 | } 535 | 536 | class KeepHookingPatch extends Patch { 537 | constructor() { 538 | super(); 539 | } 540 | 541 | _apply() { 542 | Hooker.keepalive = true; 543 | Logger.info('The keep hooking patch has been installed'); 544 | } 545 | } 546 | 547 | export const vipPatch = new VipPatch(); 548 | export const adsPatch = new AdsPatch(); 549 | export const controlsPatch = new ControlsPatch(); 550 | export const watermarksPatch = new WatermarksPatch(); 551 | export const keepHookingPatch = new KeepHookingPatch(); 552 | export const keyShortcutsPatch = new KeyShortcutsPatch(); 553 | export const mouseShortcutsPatch = new MouseShortcutsPatch(); 554 | export const useWebSocketLoaderPatch = new UseWebSocketLoaderPatch(); 555 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | 2 | function getVideoUrl(tvid, vid) { 3 | return new Promise((resolve, reject) => { 4 | GM_xmlhttpRequest({ 5 | url: `http://cache.video.qiyi.com/jp/vi/${tvid}/${vid}/?callback=callback`, 6 | method: 'GET', 7 | timeout: 8e3, 8 | onload: (details) => { 9 | try { 10 | let json = JSON.parse(/callback\s*\(\s*(\{.*\})\s*\)/.exec(details.responseText)[1]); 11 | resolve(json.vu); 12 | } catch (err) { 13 | reject(err); 14 | } 15 | }, 16 | onerror: reject, 17 | onabort: reject, 18 | ontimeout: reject 19 | }); 20 | }); 21 | } 22 | 23 | function findVid(text) { 24 | let result = /vid=([\da-z]+)/i.exec(text); 25 | return result ? result[1] : null; 26 | } 27 | 28 | function findTvid(text) { 29 | let result = /tvid=(\d+)/i.exec(text); 30 | return result ? result[1] : null; 31 | } 32 | 33 | export { getVideoUrl, findVid, findTvid }; 34 | -------------------------------------------------------------------------------- /src/web-fullscreen.js: -------------------------------------------------------------------------------- 1 | import Hooker from './hooker'; 2 | 3 | let webFullscreen; 4 | Hooker.hookWebFullScreen(_exports => webFullscreen = _exports); 5 | 6 | export { webFullscreen }; 7 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | 4 | const pkg = require('./package'); 5 | const meta = require('./src/meta'); 6 | const srcPath = path.resolve(__dirname, 'src'); 7 | const distPath = path.resolve(__dirname, 'dist'); 8 | 9 | module.exports = [ 10 | { 11 | context: srcPath, 12 | entry: { 13 | 'index': './index.js' 14 | }, 15 | output: { 16 | path: distPath, 17 | filename: `./${pkg.name}.user.js` 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | enforce: 'pre', 23 | test: /\.js$/, 24 | include: [ 25 | srcPath 26 | ], 27 | loader: 'eslint-loader', 28 | }, 29 | { 30 | test: /\.js$/, 31 | include: [ 32 | srcPath 33 | ], 34 | loader: 'babel-loader' 35 | } 36 | ] 37 | }, 38 | plugins: [ 39 | new webpack.optimize.ModuleConcatenationPlugin(), 40 | new webpack.BannerPlugin({ 41 | banner: meta, 42 | raw: true, 43 | entryOnly: true, 44 | }), 45 | ] 46 | } 47 | ]; 48 | --------------------------------------------------------------------------------