Play-With-MPV

使用 MPV 播放网页上的视频

当前为 2022-09-20 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Play-With-MPV
  3. // @name:zh 使用 MPV 播放
  4. // @description 使用 MPV 播放网页上的视频
  5. // @namespace https://github.com/LuckyPuppy514
  6. // @version 2.1.0
  7. // @commit v1.2.1 新增 powershell 脚本升级提醒功能
  8. // @commit v1.2.2 修复 youtube 标题带 | 导致错误脚本升级提醒
  9. // @commit v1.2.3 修改 imomoe 域名
  10. // @commit v1.3.0 新增域名:www.6dm.cc, www.dmla.cc(第一线路:大部分支持,其他线路:小部分支持)
  11. // @commit v1.3.0 新增域名:www.dm233.me(线路III:大部分支持,其他线路:大部分不支持)
  12. // @commit v1.3.0 代码重构,使用继承方便后续添加网站支持
  13. // @commit v1.4.0 b站bug修复:标题带数字,解析出错,修复并优化了获取视频链接的速度
  14. // @commit v1.4.0 新增对plex支持(本地:*://*/web/index.html*,远程:https://app.plex.tv/desktop/*)
  15. // @commit v1.4.1 修复b站番剧播放目录为列表时,无法获取正确集数的bug
  16. // @commit v1.4.2 修复b站番剧播放的bug
  17. // @commit v1.4.3 修改cdn为unpkg,某些网络无法访问cdn,导致js加载失败(有问题,请自行修改:unpkg.com => cdn.jsdelivr.net/npm)
  18. // @commit v1.4.4 www.dmla.cc 域名变更为:www.dmlaa.com
  19. // @commit v1.4.5 ddrk.me 域名变更为:ddys.tv
  20. // @commit v1.5.0 代码优化,去除 powershell 脚本,只需添加注册表信息即可
  21. // @commit v1.5.1 B站添加 cid 参数,配合 https://github.com/itKelis/MPV-Play-BiliBili-Comments 可实现弹幕功能
  22. // @commit v1.5.2 注册表代码升级,支持中文标题
  23. // @commit v1.5.3 添加低端影视备用域名
  24. // @commit v2.0.0 代码重构:1. 新增对B站av号视频支持;2. B站,油管,低端影视同步网页播放时间;3. 新增MPV路径设置,方便生成注册表;4. 新增Youtube代理设置;5. 减少暂停失败情况
  25. // @commit v2.0.1 更新 mpv.net_CM 安装教程链接
  26. // @commit v2.0.2 更新 www.6dm.cc 域名为 www.996dm.com
  27. // @commit v2.0.3 B站接口变更,画质上限:4K => 8K HDR,音质上限:192K => Dolby Hi-Res
  28. // @commit v2.0.4 修复B站 Hi-Res 音频链接抓取错误的问题
  29. // @commit v2.0.5 新增巴哈姆特(https://ani.gamer.com.tw)支持
  30. // @commit v2.0.6 代码优化;设置代理时,对巴哈姆特也生效
  31. // @commit v2.0.8 修复油管全屏图标仍然显示的问题
  32. // @commit v2.0.9 界面细节优化
  33. // @commit v2.1.0 修复低端影视出现10s广告提醒时,无法抓取链接的问题
  34. // @homepage https://github.com/LuckyPuppy514/Play-With-MPV
  35. // @author LuckyPuppy514
  36. // @copyright 2022, Grant LuckyPuppy514 (https://github.com/LuckyPuppy514)
  37. // @license MIT
  38. // @icon https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/mpv.png
  39. // @match *://www.youtube.com/*
  40. // @include https://www.youtube.com/watch/*
  41. // @include https://www.bilibili.com/bangumi/play/*
  42. // @include https://www.bilibili.com/video/*
  43. // @connect api.bilibili.com
  44. // @include https://ddys.tv/*
  45. // @include https://ddys2.me/*
  46. // @include https://www.996dm.com/play/*
  47. // @include http://www.996dm.com/play/*
  48. // @include http://www.dmlaa.com/play/*
  49. // @include https://danmu.yhdmjx.com/*
  50. // @include https://www.dm233.me/play/*
  51. // @include http://www.dmh8.com/player/*
  52. // @include https://www.yhdmp.net/vp/*
  53. // @include https://ani.gamer.com.tw/animeVideo.php?*
  54. // @run-at document-end
  55. // @require https://unpkg.com/js-base64@3.6.1/base64.js
  56. // @require https://unpkg.com/jquery@3.2.1/dist/jquery.min.js
  57. // @grant GM_setValue
  58. // @grant GM_getValue
  59. // ==/UserScript==
  60.  
  61. 'use strict';
  62. // 注册表版本
  63. const REG_VERSION = "20220907";
  64. // 不输出控制台信息
  65. const NO_TERMINAL = true;
  66.  
  67. // const IS_DEBUG = true;
  68. // function debug(data) {
  69. // if (IS_DEBUG) {
  70. // console.log(data);
  71. // }
  72. // }
  73.  
  74. const DIV =
  75. `
  76. <div id="pwmpv-button-div">
  77. <button id="pwmpv-about-button"></button>
  78. <button id="pwmpv-play-button"></button>
  79. <button id="pwmpv-setting-button"></button>
  80. </div>
  81.  
  82. <div id="pwmpv-about-div">
  83. <span class="pwmpv-title-span">✨ 关于 Play-With-MPV <button class="pwmpv-close-button">❌</button></span>
  84. <table id="pwmpv-about-table">
  85. <tr>
  86. <td colspan="6" class="pwmpv-title-td">使用 MPV 播放网页中的视频(解码 ⬆️ 补帧 着色器 更多💡)</td>
  87. </tr>
  88. <tr>
  89. <td colspan="2"><a href="https://github.com/LuckyPuppy514/Play-With-MPV#-%E7%AE%80%E4%BB%8B" target="_blank">🔗 支持网站 🔗</a></td>
  90. <td colspan="4">
  91. <a href="https://www.bilibili.com/" target="_blank"><img class="pwmpv-support-url-icon" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/bilibili.ico"/></a>
  92. <a href="https://ddys2.me/" target="_blank"><img class="pwmpv-support-url-icon-small" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/ddrk.webp"/></a>
  93. <a href="https://www.youtube.com/" target="_blank"><img class="pwmpv-support-url-icon" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/youtube.png"/></a>
  94. <a href="https://ani.gamer.com.tw/" target="_blank"><img class="pwmpv-support-url-icon" style="width: 57px;" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/bahaLOGO_1200x630.jpg"\></a>
  95. <a href="https://www.996dm.com/" target="_blank"><img class="pwmpv-support-url-icon" style="width: 95px;" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/logo_f.png"/></a>
  96. <a href="http://www.dmlaa.com/" target="_blank"><img class="pwmpv-support-url-icon-small" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/fengchedongman.jpg"/></a>
  97. <a href="https://www.dm233.me/" target="_blank"><img class="pwmpv-support-url-icon-small" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/dm233.ico"/></a>
  98. </td>
  99. </tr>
  100. <tr>
  101. <td colspan="2"><a href="https://www.lckp.top/archives/mpvnetcm">🤖 支持软件 🤖</a></td>
  102. <td colspan="2">
  103. <a href="https://www.lckp.top/archives/mpvnetcm" target="_blank"><img class="pwmpv-support-url-icon" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/mpvnet.png" /></a>
  104. </td>
  105. <td colspan="2">
  106. <a href="https://www.lckp.top/archives/mpv-lazy" target="_blank"><img class="pwmpv-support-url-icon-large" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/mpv.png" /></a>
  107. </td>
  108. </tr>
  109. <tr>
  110. <td colspan="2"><a href="https://github.com/LuckyPuppy514/Play-With-MPV" target="_blank">👆 项目源码 👆</a></td>
  111. <td colspan="2"><a href="https://greasyfork.org/zh-CN/scripts/444056-play-with-mpv" target="_blank">🆕 版本更新 🆕</a></td>
  112. <td colspan="2"><a href="https://github.com/LuckyPuppy514/Play-With-MPV/issues/new" target="_blank">👻 问题反馈 👻</a></td>
  113. </tr>
  114. </table>
  115. <span class="pwmpv-footer-span">
  116. <a href="https://greasyfork.org/zh-CN/scripts/444056-play-with-mpv" target="_blank"><img class="pwmpv-footer-icon" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/tampermonkey.png"/></a>
  117. <a href="https://www.lckp.top" target="_blank">2022 © LuckyPuppy514</a>
  118. <a href="https://github.com/LuckyPuppy514/Play-With-MPV" target="_blank"><img class="pwmpv-footer-icon" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/github.png"/></a>
  119. </span>
  120. </div>
  121.  
  122. <div id="pwmpv-setting-div">
  123. <span class="pwmpv-title-span">🌟 Play-With-MPV 设置 🌟 <button class="pwmpv-close-button">❌</button></span>
  124. <table id="pwmpv-setting-table">
  125. <tr>
  126. <td class="pwmpv-title-td">🔥 MPV路径 🔥</td>
  127. <td><input id="pwmpv-mpv-path-input" type=text placeholder="请输入你的 mpv.com 路径,例如:D:\\daily\\mpv\\mpv.com"></td>
  128. </tr>
  129. <tr>
  130. <td colspan="2" class="pwmpv-tips-td">🩸 如果使用 v2rayN Clash 客户端科学上网,要看油管需要手动添加代理设置 🩸</td>
  131. </tr>
  132. <tr>
  133. <td class="pwmpv-title-td">🌐 代理设置 🌐</td>
  134. <td><input id="pwmpv-proxy-input" type=text placeholder="请输入你的 http 或 socks 代理,例如:http://127.0.0.1:10809"></td>
  135. </tr>
  136. <tr>
  137. <td class="pwmpv-title-td">📺 B站编码 📺</td>
  138. <td>
  139. <select id="pwmpv-bilibili-codecs-select">
  140. <option value="12" selected>HEVC 体积中,硬件要求中,网络要求中</option>
  141. <option value="13">AV1 体积小,硬件要求高,网络要求低</option>
  142. <option value="7">AVC 体积大,硬件要求低,网络要求高</option>
  143. </select>
  144. </td>
  145. </tr>
  146. <tr>
  147. <td colspan="2">
  148. <button id="pwmpv-save-button">保存设置</button>
  149. <button id="pwmpv-download-button" data-tip="请先输入 MPV 路径,并保存设置">下载注册表</button>
  150. </td>
  151. </tr>
  152. </table>
  153. <span class="pwmpv-footer-span">
  154. <a href="https://greasyfork.org/zh-CN/scripts/444056-play-with-mpv" target="_blank"><img class="pwmpv-footer-icon" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/tampermonkey.png"/></a>
  155. <a href="https://www.lckp.top" target="_blank">2022 © LuckyPuppy514</a>
  156. <a href="https://github.com/LuckyPuppy514/Play-With-MPV" target="_blank"><img class="pwmpv-footer-icon" src="https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/github.png"/></a>
  157. </span>
  158. </div>
  159.  
  160. <iframe id="firefox-iframe" src="about:blank" style="display:none;"></iframe>
  161. `
  162. const CSS =
  163. `
  164. .pwmpv-close-button {
  165. position: absolute;
  166. top: 3px;
  167. right: 3px;
  168. height: 25px;
  169. width: 40px;
  170. border: none;
  171. font-size: 18px;
  172. background-color: rgba(0, 0, 0, 0);
  173. }
  174. .pwmpv-close-button:hover {
  175. background-color: rgba(0, 0, 0, 0.3);
  176. cursor: pointer;
  177. }
  178. #pwmpv-button-div {
  179. display: none;
  180. }
  181. .pwmpv-title-span {
  182. padding-top: 10px;
  183. font-size: 15px;
  184. }
  185. #pwmpv-about-button {
  186. position: fixed;
  187. bottom: 58px;
  188. left: 8px;
  189. z-index: 999998;
  190.  
  191. width: 25px;
  192. height: 25px;
  193. border: none;
  194. border-radius: 50%;
  195. background-size: cover;
  196. background-color: rgba(255, 255, 255, 0);
  197. background-image: url(https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/about-pink.png);
  198. }
  199. #pwmpv-about-button:hover {
  200. bottom: 56px;
  201. left: 6px;
  202. z-index: 999999;
  203.  
  204. width: 27px;
  205. height: 27px;
  206. cursor: pointer;
  207. }
  208. #pwmpv-about-div {
  209. position: fixed;
  210. top: 40%;
  211. left: 50%;
  212. transform: translate(-50%, -50%);
  213. z-index: 999999;
  214.  
  215. width: 600px;
  216. height: 300px;
  217. border: 6px solid rgba(255, 255, 255, 0.5);
  218. background-color: rgba(234, 122, 153, 0.9);
  219. display: none;
  220. flex-direction: column;
  221. border-radius: 6px;
  222. align-items: center;
  223. color: rgba(255, 255, 255, 1);
  224. }
  225. #pwmpv-about-table {
  226. margin-top: 10px;
  227. width: 570px;
  228. height: 240px;
  229. border-radius: 5px !important;
  230. border: 3px solid rgba(255, 255, 255, 1) !important;
  231. text-align: center;
  232. }
  233. #pwmpv-about-table td {
  234. border: 2px solid rgba(255, 255, 255, 0.5);
  235. }
  236. #pwmpv-about-div a {
  237. color: rgba(255, 255, 255, 1);
  238. text-decoration: none;
  239. font-size: 14px;
  240. }
  241.  
  242. #pwmpv-play-button {
  243. position: fixed;
  244. bottom: 16px;
  245. left: 20px;
  246. z-index: 999999;
  247.  
  248. width: 50px;
  249. height: 50px;
  250. border: none;
  251. border-radius: 50%;
  252. background-size: cover;
  253. background-image: url(https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/mpvnet.png);
  254. cursor: pointer;
  255. }
  256. #pwmpv-play-button:hover {
  257. bottom: 14px;
  258. left: 18px;
  259.  
  260. width: 54px;
  261. height: 54px;
  262. cursor: pointer;
  263. }
  264.  
  265. #pwmpv-setting-button {
  266. position: fixed;
  267. bottom: 56px;
  268. left: 58px;
  269. z-index: 999998;
  270.  
  271. width: 28px;
  272. height: 28px;
  273. border: none;
  274. border-radius: 50%;
  275. background-size: cover;
  276. background-color: rgba(255, 255, 255, 0);
  277. background-image: url(https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/lx-setting.png);
  278. }
  279. #pwmpv-setting-button:hover {
  280. bottom: 54px;
  281. left: 56px;
  282. z-index: 999999;
  283.  
  284. width: 32px;
  285. height: 32px;
  286. cursor: pointer;
  287. }
  288. #pwmpv-setting-div {
  289. position: fixed;
  290. top: 40%;
  291. left: 50%;
  292. transform: translate(-50%, -50%);
  293. z-index: 999999;
  294.  
  295. width: 600px;
  296. height: 300px;
  297. border: 6px solid rgba(255, 255, 255, 0.5);
  298. background-color: rgba(65, 146, 247, 0.9);
  299. display: none;
  300. flex-direction: column;
  301. border-radius: 6px;
  302. align-items: center;
  303. color: rgba(255, 255, 255, 1);
  304. }
  305. #pwmpv-setting-table {
  306. margin-top: 10px;
  307. width: 570px;
  308. height: 240px;
  309. border-radius: 5px !important;
  310. border: 3px solid rgba(255, 255, 255, 1) !important;
  311. text-align: center;
  312. padding: 10px;
  313. }
  314. #pwmpv-setting-table td {
  315. border: 0px solid rgba(255, 255, 255, 0.5);
  316. padding-top: 10px;
  317. }
  318. .pwmpv-title-td{
  319. width: 120px;
  320. height: 30px;
  321. border: none;
  322. font-size: 14px;
  323. }
  324. #pwmpv-setting-table input {
  325. width: 400px;
  326. height: 30px;
  327. border: none;
  328. outline: none;
  329. padding-left: 6px;
  330. border-radius: 3px;
  331. color: rgba(0, 0, 0, 1);
  332. background-color: rgba(255, 255, 255, 0.9);
  333. }
  334. #pwmpv-bilibili-codecs-select {
  335. width: 406px;
  336. height: 30px;
  337. border: none;
  338. outline: none;
  339. padding-left: 6px;
  340. border-radius: 3px;
  341. color: rgba(0, 0, 0, 1);
  342. background-color: rgba(255, 255, 255, 0.9);
  343. }
  344. #pwmpv-save-button {
  345. margin-left: 80px;
  346. width: 300px;
  347. height: 30px;
  348. border: none;
  349. border-radius: 3px;
  350. color: rgba(255, 255, 255, 1);
  351. background-color: rgba(0, 255, 50, 0.6);
  352. }
  353. #pwmpv-save-button:hover {
  354. background-color: rgba(0, 255, 0, 0.8);
  355. cursor: pointer;
  356. }
  357. .pwmpv-download-enable:hover {
  358. background-color: rgba(0, 255, 0, 0.8);
  359. cursor: pointer;
  360. }
  361. .pwmpv-download-disable:hover {
  362. cursor: pointer;
  363. }
  364. .pwmpv-download-enable {
  365. margin-left: 10px;
  366. width: 80px;
  367. height: 30px;
  368. border: none;
  369. border-radius: 3px;
  370. color: rgba(255, 255, 255, 1);
  371. background-color: rgba(0, 255, 50, 0.6);
  372. }
  373. .pwmpv-download-disable {
  374. margin-left: 10px;
  375. width: 80px;
  376. height: 30px;
  377. border: none;
  378. border-radius: 3px;
  379. color: rgba(255, 255, 255, 1);
  380. background-color: rgba(0, 0, 0, 0.5);
  381. }
  382. .pwmpv-tips-td {
  383. color: rgba(255, 0, 0, 1);
  384. font-size: 14px;
  385. font-weight: bold;
  386. }
  387. .pwmpv-footer-span {
  388. margin-top: 10px;
  389. margin-bottom: 10px;
  390. color: rgba(255, 255, 255, 1);
  391. }
  392. .pwmpv-footer-span a {
  393. color: rgba(255, 255, 255, 1);
  394. text-decoration: none;
  395. font-size: 14px;
  396. margin-bottom: 1px;
  397. }
  398. .pwmpv-footer-icon {
  399. width: 18px;
  400. height: 18px;
  401. margin-left: 5px;
  402. margin-right: 5px;
  403. margin-bottom: -2px;
  404. }
  405. .pwmpv-support-url-icon {
  406. width: 30px;
  407. height: 30px;
  408. margin-left: 8px;
  409. margin-right: 8px;
  410. }
  411. .pwmpv-support-url-icon-small {
  412. width: 25px;
  413. height: 25px;
  414. margin-left: 8px;
  415. margin-right: 8px;
  416. margin-bottom: 2px;
  417. }
  418. .pwmpv-support-url-icon-large {
  419. width: 37px;
  420. height: 37px;
  421. margin-left: 8px;
  422. margin-right: 8px;
  423. margin-bottom: -4px;
  424. }
  425. `
  426.  
  427. const REG =
  428. `Windows Registry Editor Version 5.00
  429. [HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Google\\Chrome]
  430. "ExternalProtocolDialogShowAlwaysOpenCheckbox"=dword:00000001
  431.  
  432. [HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\Edge]
  433. "ExternalProtocolDialogShowAlwaysOpenCheckbox"=dword:00000001
  434.  
  435. [HKEY_CLASSES_ROOT\\mpv]
  436. @="mpv Protocol"
  437. "URL Protocol"=""
  438.  
  439. [HKEY_CLASSES_ROOT\\mpv\\DefaultIcon]
  440. @=""
  441.  
  442. [HKEY_CLASSES_ROOT\\mpv\\shell]
  443. @=""
  444.  
  445. [HKEY_CLASSES_ROOT\\mpv\\shell\\open]
  446. @=""
  447.  
  448. [HKEY_CLASSES_ROOT\\mpv\\shell\\open\\command]
  449. @="cmd /V:ON /C \\"FOR /F \\"tokens=* USEBACKQ\\" %%F IN (\`powershell -command \\"Add-Type -AssemblyName System.Web;[System.Web.HTTPUtility]::UrlDecode('%1')\\"\`) DO (SET param=%%F) & SET param=!param:mpv://=! & start /min MPV_PATH !param!\\""
  450. `
  451.  
  452. // element id
  453. const BUTTON_DIV = "pwmpv-button-div";
  454. const ABOUT_BUTTON_ID = "pwmpv-about-button";
  455. const ABOUT_DIV_ID = "pwmpv-about-div";
  456. const PLAY_BUTTON_ID = "pwmpv-play-button";
  457. const SETTING_BUTTON_ID = "pwmpv-setting-button";
  458. const SETTING_DIV_ID = "pwmpv-setting-div";
  459. const MPV_PATH_INPUT_ID = "pwmpv-mpv-path-input";
  460. const PROXY_INPUT_ID = "pwmpv-proxy-input";
  461. const BILIBILI_CODECS_SELECT_ID = "pwmpv-bilibili-codecs-select";
  462. const SAVE_BUTTON_ID = "pwmpv-save-button";
  463. const DOWNLOAD_BUTTON_ID = "pwmpv-download-button";
  464. // display
  465. const DISPLAY_NONE = "none";
  466. const DISPLAY_FLEX = "flex";
  467. // GM value key
  468. const KEY_MPV_PATH = "MPV_PATH";
  469. const KEY_PROXY = "PROXY";
  470. const KEY_REG_VERSION = "REG_VERSION";
  471. const KEY_BILIBILI_CODECS = "BILIBILI_CODECS";
  472. const BILIBILI_CODECS_HEVC = "12";
  473.  
  474. function appendHTML() {
  475. var div = document.createElement("div");
  476. div.innerHTML = DIV.trim();
  477. document.body.appendChild(div);
  478. }
  479. function appendCSS() {
  480. var css = document.createElement("style");
  481. css.innerHTML = CSS.trim();
  482. document.head.appendChild(css);
  483. }
  484. var bilibiliCodecs;
  485. var isFullScreen = false;
  486. function addListener() {
  487. // 关于
  488. var aboutButton = document.getElementById(ABOUT_BUTTON_ID);
  489. var aboutDiv = document.getElementById(ABOUT_DIV_ID);
  490. aboutButton.onclick = function () {
  491. if (aboutDiv.style.display != DISPLAY_FLEX) {
  492. aboutDiv.style.display = DISPLAY_FLEX;
  493. settingDiv.style.display = DISPLAY_NONE;
  494. } else {
  495. aboutDiv.style.display = DISPLAY_NONE;
  496. }
  497. };
  498.  
  499. // 播放
  500. var playButton = document.getElementById(PLAY_BUTTON_ID);
  501. playButton.onclick = function () {
  502. let regVersion = GM_getValue(KEY_REG_VERSION);
  503. if (!regVersion || regVersion != REG_VERSION) {
  504. showSettingDiv();
  505. Toast("🆕 注册表配置有更新,请重新下载并添加注册表信息 🆕");
  506. return;
  507. }
  508. handler.playCurrentVideoWithMPV();
  509. }
  510.  
  511. // 设置
  512. var settingButton = document.getElementById(SETTING_BUTTON_ID);
  513. var bilibiliCodecsSelect = document.getElementById(BILIBILI_CODECS_SELECT_ID);
  514. var saveButton = document.getElementById(SAVE_BUTTON_ID);
  515. var downloadButton = document.getElementById(DOWNLOAD_BUTTON_ID);
  516. var settingDiv = document.getElementById(SETTING_DIV_ID);
  517. var mpvPathInput = document.getElementById(MPV_PATH_INPUT_ID);
  518. var proxyInput = document.getElementById(PROXY_INPUT_ID);
  519. settingButton.onclick = function () {
  520. if (settingDiv.style.display != DISPLAY_FLEX) {
  521. showSettingDiv();
  522. aboutDiv.style.display = DISPLAY_NONE;
  523. } else {
  524. settingDiv.style.display = DISPLAY_NONE;
  525. }
  526. };
  527. bilibiliCodecs = GM_getValue(KEY_BILIBILI_CODECS);
  528. if (!bilibiliCodecs) {
  529. bilibiliCodecs = BILIBILI_CODECS_HEVC;
  530. GM_setValue(KEY_BILIBILI_CODECS, bilibiliCodecs);
  531. }
  532. bilibiliCodecsSelect.onchange = function () {
  533. bilibiliCodecs = this.value;
  534. Toast("💡 保存设置后,请刷新当前页面以更新编码 💡", 1500);
  535. };
  536. saveButton.onclick = function () {
  537. let oldMpvPath = GM_getValue(KEY_MPV_PATH);
  538. let mpvPath = mpvPathInput.value;
  539. let proxy = proxyInput.value;
  540. if (!mpvPath) {
  541. downloadButton.className = "pwmpv-download-disable";
  542. Toast("⚠️ MPV路径不能为空 ⚠️", 1500);
  543. return;
  544. }
  545. if (/.*[\u4e00-\u9fa5]+.*$/.test(mpvPath)) {
  546. downloadButton.className = "pwmpv-download-disable";
  547. Toast("⚠️ MPV路径不能包含中文 ⚠️", 1500)
  548. return;
  549. }
  550. mpvPath = mpvPath.replaceAll("/", "\\");
  551. mpvPath = mpvPath.replaceAll("\\\\", "\\");
  552. mpvPath = mpvPath.replaceAll("\\", "\\\\");
  553. GM_setValue(KEY_MPV_PATH, mpvPath);
  554. GM_setValue(KEY_PROXY, proxy);
  555. GM_setValue(KEY_BILIBILI_CODECS, bilibiliCodecs);
  556. // debug(proxy);
  557. downloadButton.className = "pwmpv-download-enable";
  558. if (oldMpvPath != mpvPath) {
  559. Toast("🔥 请重新添加注册表信息 🔥", 2500);
  560. downloadButton.click();
  561. } else {
  562. Toast("✅ 保存成功 ✅", 1500);
  563. }
  564. };
  565. downloadButton.onclick = function () {
  566. // 生成注册表信息
  567. var a = document.createElement('a');
  568. var blob = new Blob([REG.replace(KEY_MPV_PATH, GM_getValue(KEY_MPV_PATH))], { 'type': 'application/octet-stream' });
  569. a.href = window.URL.createObjectURL(blob);
  570. a.download = "mpv.reg";
  571. a.click();
  572. GM_setValue(KEY_REG_VERSION, REG_VERSION);
  573. }
  574. var closeButtons = document.getElementsByClassName("pwmpv-close-button");
  575. for (let closeButton of closeButtons) {
  576. closeButton.onclick = function () {
  577. aboutDiv.style.display = DISPLAY_NONE;
  578. settingDiv.style.display = DISPLAY_NONE;
  579. }
  580. }
  581. // 全屏
  582. document.addEventListener("fullscreenchange", () => {
  583. if (document.fullscreenElement) {
  584. isFullScreen = true;
  585. document.getElementById(BUTTON_DIV).style.display = DISPLAY_NONE;
  586. } else {
  587. isFullScreen = false;
  588. handler.checkCurrentVideoUrl();
  589. }
  590. });
  591. }
  592. // 显示设置窗口
  593. function showSettingDiv() {
  594. var downloadButton = document.getElementById(DOWNLOAD_BUTTON_ID);
  595. var settingDiv = document.getElementById(SETTING_DIV_ID);
  596. var bilibiliCodecsSelect = document.getElementById(BILIBILI_CODECS_SELECT_ID);
  597. var mpvPathInput = document.getElementById(MPV_PATH_INPUT_ID);
  598. var proxyInput = document.getElementById(PROXY_INPUT_ID);
  599. let mpvPath = GM_getValue(KEY_MPV_PATH);
  600. let proxy = GM_getValue(KEY_PROXY);
  601. bilibiliCodecs = GM_getValue(KEY_BILIBILI_CODECS);
  602. if (mpvPath) {
  603. mpvPathInput.value = mpvPath;
  604. downloadButton.className = "pwmpv-download-enable";
  605. } else {
  606. downloadButton.className = "pwmpv-download-disable";
  607. }
  608. if (proxy) {
  609. proxyInput.value = proxy;
  610. }
  611. bilibiliCodecsSelect.value = bilibiliCodecs;
  612. settingDiv.style.display = DISPLAY_FLEX;
  613. }
  614. // 显示消息
  615. function Toast(msg, duration) {
  616. duration = isNaN(duration) ? 3000 : duration;
  617. var m = document.createElement('div');
  618. m.innerHTML = msg;
  619. m.style.cssText = "max-width:60%;min-width: 150px;padding:0 14px;height: 40px;color: rgb(255, 255, 255);line-height: 40px;text-align: center;border-radius: 4px;position: fixed;top: 15%;left: 50%;transform: translate(-50%, -50%);z-index: 999999;background: rgba(0, 0, 0, 0.6);font-size: 14px;";
  620. document.body.appendChild(m);
  621. setTimeout(function () {
  622. var d = 0.5;
  623. m.style.opacity = '0';
  624. setTimeout(function () { document.body.removeChild(m) }, d * 1000);
  625. }, duration);
  626. }
  627.  
  628. // mpv urlprotocol
  629. const MPV_URLPROTOCOL = "mpv://";
  630. // mpv urlprotocol link
  631. class UrlProtocol {
  632. constructor() {
  633. this.link = MPV_URLPROTOCOL + '"' + currentVideoUrl + '"';
  634. this.appendNoTerminal();
  635. this.needAppendTitle = false;
  636. }
  637. // 添加参数
  638. append(param) {
  639. this.link = this.link + ' ' + param;
  640. }
  641. // 禁止命令行输出及控制
  642. appendNoTerminal() {
  643. if (NO_TERMINAL) {
  644. this.append('--no-terminal');
  645. }
  646. }
  647. // 开始时间(如果 mpv 开启了退出时记住播放状态,则记住状态优先级更高)
  648. appendStartTime() {
  649. let startTime = handler.getStartTime();
  650. if (startTime) {
  651. this.append('--ss="' + startTime + '"');
  652. }
  653. }
  654. // 标题
  655. appendTitle() {
  656. this.needAppendTitle = true;
  657. }
  658. // 代理
  659. appendProxy() {
  660. let proxy = GM_getValue(KEY_PROXY);
  661. if (proxy) {
  662. this.append('--http-proxy=' + proxy + ' --ytdl-raw-options=proxy=[' + proxy + ']');
  663. }
  664. }
  665. // 最终链接
  666. getLink() {
  667. if (this.needAppendTitle) {
  668. // 限制标题长度(url 有长度限制)
  669. let maxLength = 1900 - this.link.length;
  670. let title = encodeURIComponent(document.title);
  671. if (title.length > maxLength) {
  672. title = title.substring(0, maxLength) + '...';
  673. }
  674. this.append('--force-media-title="' + title + '"');
  675. }
  676. return this.link;
  677. }
  678. }
  679.  
  680. // 网页处理器
  681. var handler;
  682. class Handler {
  683. // 获取当前视频链接
  684. getCurrentVideoUrl() { }
  685. // 获取开始时间
  686. getStartTime() { return null; }
  687. // 暂停网页视频
  688. pauseCurrentVideo() { document.getElementsByTagName("video")[0].pause(); }
  689. // 获取调用 mpv 链接
  690. getUrlProtocolLink() {
  691. let urlProtocol = new UrlProtocol;
  692. urlProtocol.appendStartTime();
  693. urlProtocol.appendTitle();
  694. return urlProtocol.getLink();
  695. }
  696. // 校验视频链接是否有效
  697. checkCurrentVideoUrl() {
  698. if (this.baseCheckCurrentVideoUrl()){
  699. if(!isFullScreen){
  700. document.getElementById(BUTTON_DIV).style.display = DISPLAY_FLEX;
  701. }
  702. return true;
  703. }
  704. return false;
  705. }
  706.  
  707. // 调用 mpv 播放
  708. playCurrentVideoWithMPV() {
  709. window.open(this.getUrlProtocolLink(), "_self");
  710. let i = 0;
  711. while (i < 3) {
  712. i++;
  713. setTimeout(function () {
  714. handler.pauseCurrentVideo();
  715. }, 2000 * i);
  716. }
  717. }
  718. // 根据 class name 获取播放时间
  719. getStartTimeByClassName(className) {
  720. let startTimeElements = document.getElementsByClassName(className);
  721. let length = startTimeElements.length;
  722. if (length > 0) {
  723. return startTimeElements[length - 1].innerHTML;
  724. }
  725. return null;
  726. }
  727. // 视频链接基础校验
  728. baseCheckCurrentVideoUrl(){
  729. // debug("current video url: " + currentVideoUrl);
  730. if (!currentVideoUrl || !currentVideoUrl.startsWith("http")
  731. || currentVideoUrl.indexOf("yun.66dm.net") != -1
  732. || currentVideoUrl.indexOf("www.xmfans.me") != -1
  733. || currentVideoUrl.indexOf("sod.bunediy.com") != -1) {
  734. return false;
  735. }
  736. return true;
  737. }
  738. }
  739.  
  740. // 油管
  741. const YOUTUBE = "www.youtube.com";
  742.  
  743. class YoutubeHandler extends Handler {
  744. // 获取当前视频链接
  745. getCurrentVideoUrl() {
  746. currentVideoUrl = currentUrl;
  747. this.checkCurrentVideoUrl();
  748. }
  749. // 获取开始时间
  750. getStartTime() {
  751. return this.getStartTimeByClassName("ytp-time-current");
  752. }
  753. // 获取调用 mpv 链接
  754. getUrlProtocolLink() {
  755. let urlProtocol = new UrlProtocol;
  756. urlProtocol.appendStartTime();
  757. urlProtocol.appendProxy();
  758. return urlProtocol.getLink();
  759. }
  760. // 校验视频链接是否有效
  761. checkCurrentVideoUrl() {
  762. if (currentUrl.indexOf("/watch") == -1 && currentUrl.indexOf("/playlist") == -1) {
  763. return false;
  764. }
  765. return super.checkCurrentVideoUrl();
  766. }
  767. }
  768.  
  769. // B站
  770. const BILIBILI = "www.bilibili.com";
  771. // B站 API
  772. const BILIBILI_API = 'https://api.bilibili.com';
  773. // cid 用于传递给 mpv 获取弹幕
  774. var bilibiliCid;
  775.  
  776. class BilibiliHandler extends Handler {
  777. // 获取当前视频链接
  778. getCurrentVideoUrl() {
  779. let index = currentUrl.indexOf('/video/');
  780. if (index != -1) {
  781. // 投稿视频
  782. let param = "";
  783. let videoId = currentUrl.substring(index + 7);
  784. if (videoId.startsWith("BV")) {
  785. param = "bvid=" + videoId.substring(2, 12);
  786. } else if (videoId.startsWith("av")) {
  787. param = "aid=" + videoId.substring(2, 10);
  788. } else {
  789. // debug("bilibili video id invalid: " + videoId);
  790. return;
  791. }
  792. // debug("bilibili video id: " + param);
  793. getBilibiliVideoUrl(param);
  794.  
  795. } else {
  796. // 番剧
  797. let aElement = document.getElementsByClassName('ep-item cursor visited')[0];
  798. if (!aElement) {
  799. aElement = document.getElementsByClassName('ep-item cursor')[0];
  800. }
  801. let epid = aElement.getElementsByTagName('a')[0].href;
  802. epid = epid.substring(epid.indexOf('/ep') + 3);
  803. epid = epid.substring(0, epid.indexOf('/'));
  804. // debug('epid: ' + epid);
  805.  
  806. let eno = document.getElementsByClassName("ep-list-progress")[0];
  807. if (!eno) {
  808. return;
  809. }
  810. eno = eno.innerHTML;
  811. eno = eno.substring(0, eno.indexOf('/'));
  812. // debug('eno: ' + eno);
  813. getBilibiliBangumiUrl(epid, eno);
  814. }
  815. }
  816. // 获取开始时间
  817. getStartTime() {
  818. let startTime = this.getStartTimeByClassName("bpx-player-ctrl-time-current");
  819. if (!startTime) {
  820. startTime = this.getStartTimeByClassName("squirtle-video-time-now");
  821. }
  822. return startTime;
  823. }
  824. // 获取调用 mpv 链接
  825. getUrlProtocolLink() {
  826. let urlProtocol = new UrlProtocol;
  827. urlProtocol.appendStartTime();
  828. urlProtocol.appendTitle();
  829. urlProtocol.append('--audio-file="' + currentAudioUrl + '"');
  830. urlProtocol.append('--http-header-fields="referer: ' + currentUrl + ',user-agent: ' + navigator.userAgent + '"');
  831. urlProtocol.append('--script-opts="cid=' + bilibiliCid + '"');
  832. return urlProtocol.getLink();
  833. }
  834. }
  835. // 获取B站投稿视频链接
  836. function getBilibiliVideoUrl(param) {
  837. $.ajax({
  838. type: "GET",
  839. url: BILIBILI_API + "/x/web-interface/view?" + param,
  840. xhrFields: {
  841. withCredentials: true
  842. },
  843. success: function (res) {
  844. // debug("get acid and cid by avid/bvid result: ");
  845. // debug(res);
  846. let avid = res.data.aid;
  847. let cid = res.data.cid;
  848. let index = currentUrl.indexOf("?p=");
  849. if (index != -1 && res.data.pages.length > 1) {
  850. let p = currentUrl.substring(index + 3);
  851. let endIndex = p.indexOf("&");
  852. if (endIndex != -1) {
  853. p = p.substring(0, endIndex);
  854. }
  855. cid = res.data.pages[p - 1].cid;
  856. }
  857. getBilibiliPlayUrl(avid, cid);
  858. }
  859. })
  860. }
  861. // 获取B站番剧视频链接
  862. function getBilibiliBangumiUrl(epid, eno) {
  863. if (!epid || !eno) {
  864. return;
  865. }
  866. $.ajax({
  867. type: "GET",
  868. url: BILIBILI_API + "/pgc/view/web/season?ep_id=" + epid,
  869. xhrFields: {
  870. withCredentials: true
  871. },
  872. success: function (res) {
  873. // debug("get acid and cid by epid result: ");
  874. // debug(res);
  875. let episodes = res.result.episodes;
  876. if (eno.indexOf('PV') != -1 || eno.indexOf('OP') != -1 || eno.indexOf('ED') != -1) {
  877. return;
  878. }
  879. let episode = episodes[eno - 1];
  880. getBilibiliPlayUrl(episode.aid, episode.cid);
  881. }
  882. })
  883. }
  884. // 获取B站视频播放链接
  885. function getBilibiliPlayUrl(avid, cid) {
  886. // debug("avid: " + avid);
  887. // debug("cid: " + cid);
  888. bilibiliCid = cid;
  889.  
  890. let queryBilibiliVideoUrl = "/x/player/playurl?"
  891. + "qn=&otype=json&fourk=1&fnver=0&fnval=4048"
  892. + "&avid=" + avid
  893. + "&cid=" + cid;
  894. $.ajax({
  895. type: "GET",
  896. url: BILIBILI_API + queryBilibiliVideoUrl,
  897. xhrFields: {
  898. withCredentials: true
  899. },
  900. success: function (res) {
  901. // debug(res);
  902. let dash = res.data.dash;
  903. let hiRes = dash.flac;
  904. let dolby = dash.dolby;
  905. if (hiRes && hiRes.audio) {
  906. // debug("hi-res: on");
  907. currentAudioUrl = hiRes.audio.baseUrl;
  908. } else if (dolby && dolby.audio) {
  909. // debug("dolby: on");
  910. currentAudioUrl = dolby.audio[0].base_url;
  911. } else {
  912. // debug(dash.audio[0].id);
  913. currentAudioUrl = dash.audio[0].baseUrl;
  914. }
  915. let baseUrl = dash.video[0].baseUrl;
  916. let id = dash.video[0].id;
  917. let i = 1;
  918. while (i < dash.video.length) {
  919. if (dash.video[i].id != id) {
  920. break;
  921. }
  922. if (dash.video[i].codecid == bilibiliCodecs) {
  923. baseUrl = dash.video[i].baseUrl;
  924. break;
  925. }
  926. i++;
  927. }
  928. currentVideoUrl = baseUrl;
  929. handler.checkCurrentVideoUrl();
  930. }
  931. });
  932. }
  933.  
  934. // 低端影视
  935. const DDRK = "ddys.tv, ddys2.me";
  936. // 低端影视播放状态
  937. var ddrkPlayStatus;
  938.  
  939. class DdrkHandler extends Handler {
  940. // 获取当前视频链接
  941. getCurrentVideoUrl() {
  942. // 点击播放按钮加载 video 元素
  943. if (!ddrkPlayStatus) {
  944. let ddrkPlayButton = document.getElementsByClassName('vjs-big-play-button')[0];
  945. if (!ddrkPlayButton) {
  946. // debug("ddrk get play button fail");
  947. return;
  948. }
  949. ddrkPlayButton.click();
  950. ddrkPlayStatus = true;
  951. }
  952. currentVideoUrl = document.getElementById('vjsp_html5_api').src;
  953. this.checkCurrentVideoUrl();
  954. }
  955. // 获取开始时间
  956. getStartTime() {
  957. return this.getStartTimeByClassName("vjs-time-tooltip");
  958. }
  959. }
  960.  
  961. // 樱花动漫网
  962. const DM6CC = "www.6dm.cc, www.996dm.com";
  963.  
  964. class Dm6ccHandler extends Handler {
  965. // 获取当前视频链接
  966. constructor() {
  967. super();
  968. window.addEventListener('message', function (event) {
  969. currentVideoUrl = event.data;
  970. this.checkCurrentVideoUrl();
  971. window.removeEventListener("message", () => { });
  972. }, false);
  973. }
  974. // 暂停网页视频
  975. pauseCurrentVideo() {
  976. document.getElementsByTagName("iframe")[2].contentWindow.postMessage("pause", "https://" + YHDMJX);
  977. }
  978. }
  979.  
  980. // 风车动漫
  981. const DMLACC = "www.dmlaa.com";
  982.  
  983. class DmlaccHandler extends Handler {
  984. // 获取当前视频链接
  985. constructor() {
  986. super();
  987. window.addEventListener('message', function (event) {
  988. currentVideoUrl = event.data;
  989. this.checkCurrentVideoUrl();
  990. window.removeEventListener("message", () => { });
  991. }, false);
  992. }
  993. // 暂停网页视频
  994. pauseCurrentVideo() {
  995. document.getElementsByTagName("iframe")[2].contentWindow.postMessage("pause", "https://" + YHDMJX);
  996. }
  997. }
  998.  
  999. // 樱花动漫网和风车动漫实际播放地址
  1000. const YHDMJX = "danmu.yhdmjx.com";
  1001.  
  1002. class YhdmjxHandler extends Handler {
  1003. constructor() {
  1004. super();
  1005. // 监听父页面暂停指令
  1006. window.addEventListener("message", function (event) {
  1007. if (event.data == "pause") {
  1008. document.getElementsByTagName('video')[0].pause();
  1009. }
  1010. }, false);
  1011. }
  1012. // 获取当前视频链接
  1013. getCurrentVideoUrl() {
  1014. // 发送视频链接到父页面
  1015. currentVideoUrl = document.getElementsByTagName('video')[0].src;
  1016. if (this.checkCurrentVideoUrl()) {
  1017. window.parent.postMessage(currentVideoUrl, "*");
  1018. }
  1019. }
  1020. // 校验视频链接是否有效
  1021. checkCurrentVideoUrl() {
  1022. return this.baseCheckCurrentVideoUrl();
  1023. }
  1024. }
  1025.  
  1026. // 233动漫网
  1027. const DM233 = "www.dm233.me";
  1028.  
  1029. class Dm233Handler extends Handler {
  1030. constructor() {
  1031. super();
  1032. this.videoElement = null;
  1033. }
  1034. // 获取当前视频链接
  1035. getCurrentVideoUrl() {
  1036. let iframe = document.getElementById('id_main_playiframe');
  1037. this.videoElement = iframe.contentWindow.document.getElementsByTagName("video")[0];
  1038. let videoUrl = this.videoElement.src;
  1039. if (videoUrl.startsWith("blob:")) {
  1040. videoUrl = iframe.src;
  1041. let startIndex = videoUrl.indexOf('url=http') + 4;
  1042. let endIndex = videoUrl.indexOf('&getplay_url=');
  1043. videoUrl = decodeURIComponent(videoUrl.substring(startIndex, endIndex));
  1044. }
  1045. currentVideoUrl = videoUrl;
  1046. this.checkCurrentVideoUrl();
  1047. }
  1048. // 获取开始时间
  1049. getStartTime() {
  1050. return this.getStartTimeByClassName("dplayer-ptime");
  1051. }
  1052. // 暂停网页视频
  1053. pauseCurrentVideo() {
  1054. this.videoElement.pause();
  1055. }
  1056. }
  1057.  
  1058. // 樱花动漫
  1059. const DMH8 = "www.dmh8.com";
  1060.  
  1061. class Dmh8Handler extends Handler {
  1062. // 获取当前视频链接
  1063. getCurrentVideoUrl() {
  1064. let iframe = document.getElementsByTagName('iframe')[2];
  1065. let videoUrl = iframe.src;
  1066. let startIndex = videoUrl.indexOf('url=http') + 4;
  1067. let endIndex = videoUrl.indexOf('m3u8') + 4;
  1068. currentVideoUrl = decodeURIComponent(videoUrl.substring(startIndex, endIndex));
  1069. this.checkCurrentVideoUrl();
  1070. }
  1071. // 获取开始时间
  1072. getStartTime() {
  1073. return this.getStartTimeByClassName("dplayer-ptime");
  1074. }
  1075. }
  1076.  
  1077. // 樱花动漫
  1078. const YHDMP = "www.yhdmp.net";
  1079.  
  1080. class YhdmpHandler extends Handler {
  1081. constructor() {
  1082. super();
  1083. this.videoElement = null;
  1084. }
  1085. // 获取当前视频链接
  1086. getCurrentVideoUrl() {
  1087. let iframe = document.getElementById('yh_playfram');
  1088. this.videoElement = iframe.contentWindow.document.getElementsByTagName("video")[0];
  1089.  
  1090. let videoUrl = iframe.src;
  1091. let startIndex = videoUrl.indexOf('url=http') + 4;
  1092. let endIndex = videoUrl.indexOf('&getplay_url=');
  1093. currentVideoUrl = decodeURIComponent(videoUrl.substring(startIndex, endIndex));
  1094. this.checkCurrentVideoUrl();
  1095. }
  1096. // 获取开始时间
  1097. getStartTime() {
  1098. return this.getStartTimeByClassName("dplayer-ptime");
  1099. }
  1100. // 暂停网页视频
  1101. pauseCurrentVideo() {
  1102. this.videoElement.pause();
  1103. }
  1104. }
  1105. // 巴哈姆特
  1106. const GAMER = "ani.gamer.com.tw";
  1107. // 巴哈姆特 API
  1108. const GAMER_API = "https://ani.gamer.com.tw/ajax/m3u8.php";
  1109.  
  1110. class GamerHandler extends Handler {
  1111. // 获取当前视频链接
  1112. getCurrentVideoUrl() {
  1113. let index = currentUrl.indexOf("sn=") + 3;
  1114. if (index == -1) {
  1115. return;
  1116. }
  1117. let sn = currentUrl.substring(index);
  1118. index = sn.indexOf("&");
  1119. if (index != -1) {
  1120. sn = sn.substring(0, index);
  1121. }
  1122. let device = localStorage.ANIME_deviceid;
  1123. // debug("sn: " + sn + ", device: " + device);
  1124. $.ajax({
  1125. type: "GET",
  1126. url: GAMER_API + "?sn=" + sn + "&device=" + device,
  1127. xhrFields: {
  1128. withCredentials: true
  1129. },
  1130. success: function (res) {
  1131. // debug(res);
  1132. currentVideoUrl = JSON.parse(res).src;
  1133. handler.checkCurrentVideoUrl();
  1134. }
  1135. })
  1136. }
  1137. // 获取开始时间
  1138. getStartTime() {
  1139. return this.getStartTimeByClassName("vjs-current-time-display");
  1140. }
  1141. // 获取调用 mpv 链接
  1142. getUrlProtocolLink() {
  1143. let urlProtocol = new UrlProtocol;
  1144. urlProtocol.appendStartTime();
  1145. urlProtocol.appendTitle();
  1146. urlProtocol.appendProxy();
  1147. urlProtocol.append('--http-header-fields="origin: https://ani.gamer.com.tw"');
  1148. return urlProtocol.getLink();
  1149. }
  1150. }
  1151.  
  1152. // 最大尝试次数
  1153. const MAX_TRY_TIME = 8;
  1154. // 定时器
  1155. var timers;
  1156. // 当前页面链接
  1157. var currentUrl;
  1158. // 当前页面域名
  1159. var currentDomain;
  1160. // 当前页面视频链接
  1161. var currentVideoUrl;
  1162. // 当前页面音频链接
  1163. var currentAudioUrl;
  1164. // 巴哈姆特视频时长
  1165. var gamerDurationTime;
  1166.  
  1167. // 初始化当前页信息
  1168. function initCurrentPageInfo() {
  1169. // debug("init current page info ......");
  1170. document.getElementById(BUTTON_DIV).style.display = DISPLAY_NONE;
  1171. if (timers) {
  1172. for (let timer of timers) {
  1173. // debug("clear timer");
  1174. clearTimeout(timer);
  1175. }
  1176. }
  1177. currentUrl = window.location.href;
  1178. currentDomain = window.location.host;
  1179. currentVideoUrl = "";
  1180. ddrkPlayStatus = false;
  1181. }
  1182. // 创建处理器
  1183. function createHandler() {
  1184. // debug("start create handler: " + currentDomain);
  1185. if (BILIBILI.indexOf(currentDomain) != -1) {
  1186. handler = new BilibiliHandler();
  1187. } else if (DDRK.indexOf(currentDomain) != -1) {
  1188. handler = new DdrkHandler();
  1189. } else if (YOUTUBE.indexOf(currentDomain) != -1) {
  1190. handler = new YoutubeHandler();
  1191. } else if (DM6CC.indexOf(currentDomain) != -1) {
  1192. handler = new Dm6ccHandler();
  1193. } else if (DMLACC.indexOf(currentDomain) != -1) {
  1194. handler = new DmlaccHandler();
  1195. } else if (YHDMJX.indexOf(currentDomain) != -1) {
  1196. handler = new YhdmjxHandler();
  1197. } else if (DM233.indexOf(currentDomain) != -1) {
  1198. handler = new Dm233Handler();
  1199. } else if (DMH8.indexOf(currentDomain) != -1) {
  1200. handler = new Dmh8Handler();
  1201. } else if (YHDMP.indexOf(currentDomain) != -1) {
  1202. handler = new YhdmpHandler();
  1203. } else if (GAMER.indexOf(currentDomain) != -1) {
  1204. handler = new GamerHandler();
  1205. }
  1206. }
  1207. // 刷新视频链接
  1208. function refreshCurrentVideoUrl() {
  1209. // debug("refresh current video url: " + currentVideoUrl);
  1210. // debug("current url: " + currentUrl);
  1211. timers = new Array();
  1212. let tryTime = 0;
  1213. while (tryTime < MAX_TRY_TIME) {
  1214. timers[tryTime] = setTimeout(function () {
  1215. if (!handler.checkCurrentVideoUrl()) {
  1216. handler.getCurrentVideoUrl();
  1217. }
  1218. // debug("timer done");
  1219. }, tryTime * 2000 + 700);
  1220. tryTime = tryTime + 1;
  1221. }
  1222. }
  1223. // 页面变更监听器
  1224. function pageChangeListener() {
  1225. // debug("page change listener");
  1226. let needRefresh = false;
  1227. let newCurrentUrl = window.location.href;
  1228. if (currentUrl != newCurrentUrl) {
  1229. needRefresh = true;
  1230. }
  1231. // 巴哈姆特
  1232. if (!needRefresh && GAMER.indexOf(currentDomain) != -1) {
  1233. let oldGamerDurationTime = gamerDurationTime;
  1234. let durationDiv = document.getElementsByClassName("vjs-duration-display")[0];
  1235. if (durationDiv) {
  1236. gamerDurationTime = durationDiv.innerHTML;
  1237. if (oldGamerDurationTime && oldGamerDurationTime != gamerDurationTime) {
  1238. needRefresh = true;
  1239. }
  1240. }
  1241. }
  1242. if (needRefresh) {
  1243. // debug("page change");
  1244. initCurrentPageInfo();
  1245. refreshCurrentVideoUrl();
  1246. }
  1247. }
  1248.  
  1249. // 添加组件和监听器
  1250. appendHTML();
  1251. appendCSS();
  1252. addListener();
  1253.  
  1254. // 初始化页面信息
  1255. initCurrentPageInfo();
  1256. // 创建处理器
  1257. createHandler();
  1258. // 刷新视频链接
  1259. refreshCurrentVideoUrl();
  1260. // 定时监听页面变化
  1261. setInterval(pageChangeListener, 700);