Play-With-MPV

使用 MPV 播放网页上的视频

当前为 2022-10-18 提交的版本,查看 最新版本

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