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