Play-With-MPV

使用 MPV 播放网页上的视频

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

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