Play-With-MPV

使用 mpv 播放网页中的视频,并支持 potplayer 及自定义播放器

当前为 2023-08-19 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Play-With-MPV
  3. // @name:zh 使用 MPV 播放
  4. // @namespace https://github.com/LuckyPuppy514
  5. // @version 3.8.4
  6. // @author LuckyPuppy514
  7. // @copyright 2023, Grant LuckyPuppy514 (https://github.com/LuckyPuppy514)
  8. // @license MIT
  9. // @description 使用 mpv 播放网页中的视频,并支持 potplayer 及自定义播放器
  10. // @homepage https://github.com/LuckyPuppy514/Play-With-MPV
  11. // @icon https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/mpv.png
  12. // @match https://www.bilibili.com/bangumi/play/*
  13. // @match https://www.bilibili.com/video/*
  14. // @match https://www.bilibili.com/festival/*
  15. // @match https://www.bilibili.com/list/*
  16. // @match https://live.bilibili.com/*
  17. // @match https://www.ixigua.com/*
  18. // @match https://yun.nxflv.com/?url=*
  19. // @match https://ddys.art/*
  20. // @match https://ddys.pro/*
  21. // @include *://*.libvio.*
  22. // @match https://*.chinaeast2.cloudapp.chinacloudapi.cn/*?url=*
  23. // @match https://*.cfnode1.xyz/*?url=*
  24. // @match https://www.nivod.tv/*
  25. // @match https://www.pkmkv.com/py/*
  26. // @match https://www.pkmkv.com/addons/dplayer/?url=*
  27. // @match https://www.btnull.org/py/*
  28. // @match https://www.btnull.to/py/*
  29. // @match https://www.btnull.nu/py/*
  30. // @match https://www.btnull.in/py/*
  31. // @match *://www.926dm.com/play/*
  32. // @match *://www.dmlaa.com/play/*
  33. // @match *://www.qdmsh.com/play/*
  34. // @match *://www.ntdm8.com/play/*
  35. // @match https://danmu.yhdmjx.com/*?url=*
  36. // @match https://dick.xfani.com/watch/*
  37. // @match https://dick.xfani.com/addons/dp/player/*
  38. // @match https://player.moedot.net/player/*
  39. // @match https://m3.moedot.net/muiplayer/?url=*
  40. // @match https://www.mgnacg.com/bangumi/*
  41. // @match https://play.mknacg.top:8585/*
  42. // @match https://www.omofun.top/index.php/vod/play/id/*
  43. // @match https://*.omofun.top/?url=*
  44. // @match https://spdcat.net/vodplay/*
  45. // @match https://spdcat.net/addons/dp/player/*
  46. // @match http://www.dm88.me/player/*
  47. // @match https://jianghu.live2008.com/*?url=*
  48. // @match https://www.kk151.com/play/*
  49. // @match https://jx.m3u8.tv/jiexi/?url=*
  50. // @match https://jx.wolongzywcdn.com:65/m3u8.php?url=*
  51. // @match https://www.m3u8.tv.cdn.8old.cn/jx.php?url=*
  52. // @match https://jx.wujinkk.com/dplayer/?url=*
  53. // @match https://www.ikdmjx.com/?url=*
  54. // @match https://hls.kuaibofang.com/?url=*
  55. // @match https://jx.jxbdzyw.com/m3u8/?url=*
  56. // @match https://hdzyk.com/?m=*
  57. // @match https://1080zyk1.com/?m=*
  58. // @match https://1080zyk2.com/?m=*
  59. // @match https://1080zyk3.com/?m=*
  60. // @match https://1080zyk4.com/?m=*
  61. // @match https://1080zyk5.com/?m=*
  62. // @match https://vip.zykbf.com/?url=*
  63. // @match https://*.yzzy-tv1.com/*
  64. // @match https://*.yzzy-tv-cdn.com/*
  65. // @match https://www.bdys10.com/*
  66. // @match https://www.haitu.tv/*
  67. // @include *://*alist*
  68. // @include *://*:5244*
  69. // @match *://*/*.mp4
  70. // @match *://*/*.mkv
  71. // @match https://www.dora-family.com/Resource:TV
  72. // @match https://www.olehdtv.com/*
  73. // @match *://tkznp.com/vodplay/*
  74. // @match *://www.tkznp.com/vodplay/*
  75. // @match *://www.tkznp1.com/vodplay/*
  76. // @match *://www.tkznp2.com/vodplay/*
  77. // @match *://www.tkznp3.com/vodplay/*
  78. // @match *://www.tkznp4.com/vodplay/*
  79. // @match *://www.tkznp5.com/vodplay/*
  80. // @match *://www.tkznp6.com/vodplay/*
  81. // @match https://vip.ckllk.com/?url=*
  82. // @match https://www.hdmoli.com/*
  83. // @match https://play.qwertwe.top/xplay/?url=*
  84. // @match https://www.anfuns.cc/play/*
  85. // @match https://www.anfuns.cc/vapi/*
  86. // @match https://www.youtube.com/*
  87. // @match https://odysee.com/*
  88. // @match https://rumble.com/*
  89. // @match https://www.bitchute.com/*
  90. // @match https://ani.gamer.com.tw/animeVideo.php?sn=*
  91. // @match https://hanime1.me/watch?v=*
  92. // @match https://jable.tv/videos/*
  93. // @match https://ok.ru/*
  94. // @match https://tver.jp/*
  95. // @match https://www.lckp.top/play-with-mpv/index.html
  96. // @match https://www.douyin.com/
  97. // @match https://www.douyin.com/video/*
  98. // @match https://www.douyin.com/discover?modal_id=*
  99. // @match https://www.mengfan.tv/play/*
  100. // @match https://video1.beijcloud.com/player/?url=*
  101. // @match https://www.tucao.cam/play/*
  102. // @match https://mypikpak.com/drive/*
  103. // @match https://www.icourse163.org/learn/*
  104. // @match https://www.iole.tv/*
  105. // @match https://www.zhihu.com/zvideo/*
  106. // @match *://www.susudm8.com/*
  107. // @match *://susudyy.com/*
  108. // @match *://buding3.com/*
  109. // @match *://buding6.com/*
  110. // @match *://v2.shenjw.com:*/wap.php?url=*
  111. // @match *://u88.xigua88ok.com:*/wap.php?url=*
  112. // @match *://test3.gqyy8.com:*/f/aliplayer.php?url=*
  113. // @match *://v.mksec.cn/*
  114. // @include *://*dsh*.com/*
  115. // @match https://www.twitch.tv/*
  116. // @match https://jojo.bdys.top/watch/*
  117. // @match https://www.agemys.org/play/*
  118. // @include https://vip.sp-flv.com:*?url=*
  119. // @connect api.bilibili.com
  120. // @connect api.live.bilibili.com
  121. // @require https://unpkg.com/jquery@3.2.1/dist/jquery.min.js
  122. // @require https://unpkg.com/md5@2.3.0/dist/md5.min.js
  123. // @grant GM_setValue
  124. // @grant GM_getValue
  125. // @run-at document-end
  126. // ==/UserScript==
  127.  
  128. 'use strict';
  129.  
  130. const INFO = `
  131.  
  132. ▶️🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽◀️
  133. ▶️ ◀️
  134. ▶️ Play-With-MPV ◀️
  135. ▶️ ◀️
  136. ▶️ https://github.com/LuckyPuppy514/Play-With-MPV ◀️
  137. ▶️ ◀️
  138. ▶️ © 2023 LuckyPuppy514 ◀️
  139. ▶️ ◀️
  140. ▶️🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼◀️
  141.  
  142. `;
  143. // gm key
  144. const KEY = {
  145. config: "config"
  146. }
  147. // 默认配置
  148. const DEFAULT_CONFIG = {
  149. player: "mpv",
  150. mpv: {
  151. path: "",
  152. regVersion: "20230514",
  153. },
  154. potplayer: {
  155. path: "",
  156. regVersion: "20230514",
  157. },
  158. proxy: "",
  159. bestQuality: "2160p",
  160. bilibiliCodecs: 12,
  161. playAuto: 0,
  162. closeAuto: 0,
  163. syncStartTime: 0,
  164. subtitlePrefer: "zh-Hans",
  165. customPlayer: {
  166. name: "custom",
  167. params: {
  168. videoUrl: 'iina://weblink?url=${EvideoUrl}',
  169. audioUrl: '',
  170. subtitleUrl: '',
  171. title: '',
  172. startTime: '',
  173. referer: '',
  174. origin: '',
  175. proxy: '',
  176. other: ''
  177. }
  178. },
  179. version: "20230702"
  180. };
  181. var currentConfig;
  182. // 视频链接匹配正则
  183. const VIDEO_URL_REGEX = /https?:\/\/(?![^"^']*http)[^"^']+(\.|%2e)(m3u8|m3u|mp4|mkv|flv|avi)(\?[\w&=-]+|)/g;
  184. // 父子页面方法名
  185. const METHOD = {
  186. pause: "PAUSE",
  187. report: "REPORT"
  188. };
  189. // 时间 ms
  190. const TIME = {
  191. out: 3000,
  192. toast: 3500,
  193. refresh: 600,
  194. reportInterval: 600,
  195. pauseInterval: 2000,
  196. showButton: 5000,
  197. }
  198. // 尝试次数
  199. var tryTime = 0;
  200. const TRY_TIME = {
  201. maxPause: 5,
  202. maxParse: 8
  203. };
  204. // 播放器配置
  205. const PLAYER = {
  206. mpv: {
  207. name: "mpv",
  208. params: {
  209. videoUrl: 'mpv://"${videoUrl}"',
  210. audioUrl: ' --audio-file="${audioUrl}"',
  211. subtitleUrl: ' --sub-file="${subtitleUrl}"',
  212. title: ' --force-media-title="${title}"',
  213. startTime: ' --start=${startTime}',
  214. referer: ' --http-header-fields="referer: ${referer}"',
  215. origin: ' --http-header-fields="origin: ${origin}"',
  216. proxy: ' --http-proxy=${proxy} --ytdl-raw-options=proxy=[${proxy}]',
  217. other: ' ${other}'
  218. }
  219. },
  220. potplayer: {
  221. name: "potplayer",
  222. params: {
  223. videoUrl: 'potplayer://${videoUrl} /current',
  224. subtitleUrl: ' /sub="${subtitleUrl}"',
  225. title: ' /title="${title}"',
  226. startTime: ' /seek=${startTime}',
  227. referer: ' /referer="${referer}"',
  228. origin: ' /headers="origin: ${origin}"',
  229. proxy: ' /user_agent="${proxy}"'
  230. }
  231. },
  232. custom: {
  233. name: "custom",
  234. params: undefined
  235. }
  236. }
  237. // 页面信息
  238. var page = {
  239. host: undefined,
  240. url: undefined,
  241. isFullScreen: false,
  242. };
  243. // 处理器
  244. var handler;
  245. // 前缀
  246. const PREFIX = "pwm";
  247. // 组件 id
  248. const ID = {
  249. loadingDiv: `${PREFIX}-loading-div`,
  250. toastDiv: `${PREFIX}-toast-div`,
  251. buttonDiv: `${PREFIX}-button-div`,
  252. infoButton: `${PREFIX}-info-button`,
  253. infoDiv: `${PREFIX}-info-div`,
  254. infoTable: `${PREFIX}-info-table`,
  255. playButton: `${PREFIX}-play-button`,
  256. settingButton: `${PREFIX}-setting-button`,
  257. settingDiv: `${PREFIX}-setting-div`,
  258. settingTable: `${PREFIX}-setting-table`,
  259. playerRadio: `${PREFIX}-player-radio`,
  260. softwarePathInput: `${PREFIX}-software-path-input`,
  261. proxyInput: `${PREFIX}-proxy-input`,
  262. bestQualityRadio: `${PREFIX}-best-quality-radio`,
  263. bilibiliCodecsRadio: `${PREFIX}-bilibili-codecs-radio`,
  264. saveButton: `${PREFIX}-save-button`,
  265. downloadButton: `${PREFIX}-download-button`,
  266. deleteButton: `${PREFIX}-delete-button`,
  267. playAutoInput: `${PREFIX}-play-auto-input`,
  268. closeAutoInput: `${PREFIX}-close-auto-input`,
  269. syncStartTimeInput: `${PREFIX}-sync-start-time-input`,
  270. syncStartTimeSpan: `${PREFIX}-sync-start-time-span`,
  271. infoDiv: `${PREFIX}-info-div`,
  272. infoTable: `${PREFIX}-info-table`,
  273. subtitlePreferRadio: `${PREFIX}-subtitle-prefer-radio`,
  274. customPlayerButton: `${PREFIX}-custom-player-button`,
  275. customPlayerTable: `${PREFIX}-custom-player-table`,
  276. videoUrlParamInput: `${PREFIX}-video-url-param-input`,
  277. audioUrlParamInput: `${PREFIX}-audio-url-param-input`,
  278. subtitleUrlParamInput: `${PREFIX}-subtitle-url-param-input`,
  279. titleParamInput: `${PREFIX}-title-param-input`,
  280. startTimeParamInput: `${PREFIX}-start-time-param-input`,
  281. proxyParamInput: `${PREFIX}-proxy-param-input`,
  282. refererParamInput: `${PREFIX}-referer-param-input`,
  283. originParamInput: `${PREFIX}-origin-param-input`,
  284. nxParserIframe: `${PREFIX}-nx-parser-iframe`
  285. }
  286. // 组件 class
  287. const CLASS = {
  288. button: `${PREFIX}-button`,
  289. titleSpan: `${PREFIX}-title-span-class`,
  290. titleTd: `${PREFIX}-title-td-class`,
  291. closeButton: `${PREFIX}-cloase-button-class`,
  292. tipSpan: `${PREFIX}-tip-span-class`,
  293. footerSpan: `${PREFIX}-footer-span-class`,
  294. switchLabel: `${PREFIX}-switch-label-class`,
  295. sliderSpan: `${PREFIX}-slider-span-class`,
  296. roundSpan: `${PREFIX}-round-span-class`,
  297. readOnly: `${PREFIX}-read-only-class`,
  298. footerA: `${PREFIX}-footer-a-class`,
  299. infoInput: `${PREFIX}-info-input-class`,
  300. }
  301. // 消息类型
  302. const TOAST_TYPE = {
  303. info: "info",
  304. warn: "warn",
  305. error: "error"
  306. }
  307. // 图标
  308. const ICON_BASE64 = {
  309. custom: "url('data:image/png;base64,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')",
  310. back: "url('data:image/png;base64,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')",
  311. }
  312. const CSS = `
  313. #${ID.loadingDiv} {
  314. display: none;
  315. position: fixed;
  316. bottom: 50%;
  317. left: 50%;
  318. z-index: 999999;
  319. transform: translate(-50%, -50%);
  320. background-color: rgba(255, 255, 255, 0);
  321. }
  322. #${ID.loadingDiv} .spinner {
  323. width: 40px;
  324. height: 40px;
  325. background-color: rgba(255, 255, 255, 1);
  326. -webkit-animation: sk-rotateplane 1.2s infinite ease-in-out;
  327. animation: sk-rotateplane 1.2s infinite ease-in-out;
  328. }
  329. @-webkit-keyframes sk-rotateplane {
  330. 0% {
  331. -webkit-transform: perspective(120px)
  332. }
  333.  
  334. 50% {
  335. -webkit-transform: perspective(120px) rotateY(180deg)
  336. }
  337.  
  338. 100% {
  339. -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg)
  340. }
  341. }
  342.  
  343. @keyframes sk-rotateplane {
  344. 0% {
  345. transform: perspective(120px) rotateX(0deg) rotateY(0deg);
  346. -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg)
  347. }
  348.  
  349. 50% {
  350. transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
  351. -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg)
  352. }
  353.  
  354. 100% {
  355. transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
  356. -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
  357. }
  358. }
  359. #${ID.toastDiv} {
  360. display: none;
  361. position: fixed;
  362. bottom: 80%;
  363. left: 50%;
  364. max-width: 60%;
  365. min-width: 150px;
  366. padding: 0 14px;
  367. height: 40px;
  368. color: rgb(255, 255, 255);
  369. line-height: 40px;
  370. text-align: center;
  371. border-radius: 4px;
  372. transform: translate(-50%, -50%);
  373. z-index: 999999;
  374. background: rgba(0, 255, 0, .9);
  375. font-size: 14px;
  376. font-weight: blod;
  377. }
  378. /* 按钮 */
  379. #${ID.buttonDiv} {
  380. display: none;
  381. position: fixed;
  382. bottom: 0;
  383. left: 0;
  384. cursor: pointer;
  385. z-index: 99999;
  386. width: 90px;
  387. height: 90px;
  388. }
  389. #${ID.buttonDiv}:hover .${CLASS.button} {
  390. visibility: visible !important;
  391. }
  392. .${CLASS.button} {
  393. position: fixed;
  394. cursor: pointer;
  395. z-index: 99999;
  396. border: none;
  397. border-radius: 50%;
  398. background-size: cover;
  399. background-color: rgba(255, 255, 255, 0);
  400. }
  401. #${ID.playButton} {
  402. bottom: 10px;
  403. left: 16px;
  404. width: 50px;
  405. height: 50px;
  406. background-image: url(data:image/png;base64,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);
  407. }
  408. #${ID.playButton}:hover {
  409. bottom: 8px;
  410. left: 14px;
  411. width: 54px;
  412. height: 54px;
  413. }
  414. /* 设置 */
  415. #${ID.settingButton} {
  416. bottom: 50px;
  417. left: 56px;
  418. width: 28px;
  419. height: 28px;
  420. background-image: url(data:image/png;base64,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);
  421. }
  422. #${ID.settingButton}:hover {
  423. bottom: 48px;
  424. left: 52px;
  425. width: 32px;
  426. height: 32px;
  427. }
  428. #${ID.settingDiv},
  429. #${ID.infoDiv} {
  430. position: fixed;
  431. top: 40%;
  432. left: 50%;
  433. transform: translate(-50%, -50%);
  434. z-index: 999999999;
  435. width: 900px;
  436. height: 520px;
  437. background-color: rgb(65, 146, 247);
  438. display: none;
  439. flex-direction: column;
  440. border-radius: 6px;
  441. align-items: center;
  442. color: rgba(0, 0, 0, .7);
  443. font-family: "微软雅黑";
  444. }
  445. #${ID.infoDiv} {
  446. background-color: rgb(234, 122, 153) !important;
  447. }
  448. #${ID.infoTable},
  449. #${ID.settingTable},
  450. #${ID.customPlayerTable} {
  451. width: 800px;
  452. height: 460px;
  453. border-radius: 5px !important;
  454. border: 3px solid rgba(255, 255, 255, 1) !important;
  455. text-align: left;
  456. border-collapse: unset !important;
  457. display: flex;
  458. justify-content: center;
  459. padding-top: 3px;
  460. padding-bottom: 12px;
  461. line-height: 20px !important;
  462. font-weight: normal !important;
  463. }
  464. #${ID.customPlayerTable} {
  465. display: none;
  466. }
  467. #${ID.infoTable} td,
  468. #${ID.settingTable} td,
  469. #${ID.customPlayerTable} td {
  470. font-size: 14px;
  471. border: 0px solid rgba(255, 255, 255, 0.5);
  472. padding: 19px 0px 0px 0px !important;
  473. }
  474. #${ID.infoTable} td {
  475. padding-top: 16.5px !important;
  476. }
  477. .${CLASS.titleSpan} {
  478. padding-top: 12.5px !important;;
  479. padding-bottom: 12.5px !important;;
  480. font-size: 16px;
  481. font-weight: bold;
  482. color: rgba(255, 255, 255, 1) !important;
  483. }
  484. .${CLASS.closeButton} {
  485. position: absolute;
  486. top: 4px;
  487. right: 8px;
  488. height: 25px;
  489. width: 25px;
  490. border: none;
  491. font-size: 17px !important;
  492. font-weight: normal !important;
  493. background-color: rgba(0, 0, 0, 0);
  494. line-height: 0px;
  495. border-radius: 3px;
  496. transform: scale(1.32, 1);
  497. color: rgba(255, 255, 255, 1);
  498. }
  499. .${CLASS.closeButton}:hover {
  500. font-size: 20px;
  501. background-color: rgba(255, 255, 255, .5);
  502. cursor: pointer;
  503. }
  504. .${CLASS.tipSpan} {
  505. font-size: 12px;
  506. color: yellow;
  507. position: fixed;
  508. }
  509. .${CLASS.titleTd} {
  510. position: relative;
  511. width: 80px;
  512. height: 30px;
  513. border: none;
  514. font-size: 14px;
  515. text-align: center;
  516. color: rgba(255, 255, 255, 1) !important;
  517. cursor: default;
  518. }
  519. #${ID.infoTable} input,
  520. #${ID.settingTable} input,
  521. #${ID.customPlayerTable} input {
  522. font-size: 12px !important;
  523. width: 500px;
  524. height: 26px;
  525. border: none;
  526. outline: none;
  527. text-indent: 5px;
  528. padding: 0px !important;
  529. border-radius: 0px !important;
  530. color: rgba(0, 0, 0, 1);
  531. background-color: rgba(255, 255, 255, 1);
  532. cursor: auto;
  533. display: flex !important;
  534. margin-top: 1px !important;
  535. margin-bottom: 1px !important;
  536. border-collapse: unset !important;
  537. }
  538. #${ID.infoTable} input:hover,
  539. #${ID.settingTable} input:hover,
  540. #${ID.customPlayerTable} input:hover,
  541. #${ID.infoTable} input:focus-visible,
  542. #${ID.settingTable} input:focus-visible,
  543. #${ID.customPlayerTable} input:focus-visible {
  544. box-shadow: none;
  545. }
  546. #${ID.settingTable} input::placeholder,
  547. #${ID.customPlayerTable} input::placeholder {
  548. font-size: 12px;
  549. color: rgba(0, 0, 0, .3);
  550. }
  551. #${ID.saveButton} {
  552. font-size: 14px;
  553. margin-left: 83px;
  554. width: 300px;
  555. height: 30px;
  556. border: none;
  557. border-radius: 3px;
  558. color: rgba(255, 255, 255, 1);
  559. background-color: rgba(0, 255, 0, .7);
  560. }
  561. #${ID.downloadButton} {
  562. font-size: x-small;
  563. margin-left: 10px;
  564. width: 100px;
  565. height: 30px;
  566. border: none;
  567. border-radius: 3px;
  568. color: rgba(255, 255, 255, 1);
  569. background-color: rgba(0, 255, 0, .7);
  570. }
  571. #${ID.deleteButton} {
  572. text-decoration: none;
  573. font-size: x-small;
  574. width: 80px;
  575. height: 30px;
  576. border: none;
  577. border-radius: 3px;
  578. color: rgba(255, 255, 255, 1);
  579. background-color: rgba(0,0,0,0);
  580. }
  581. #${ID.saveButton}:hover,
  582. #${ID.downloadButton}:hover {
  583. opacity: .8;
  584. background-color: rgba(0, 255, 0, .8);
  585. cursor: pointer;
  586. }
  587. #${ID.deleteButton}:hover {
  588. opacity: .8;
  589. background-color: rgba(0,0,0,0);
  590. cursor: pointer;
  591. }
  592. .${CLASS.footerSpan} {
  593. margin-top: 8px !important;
  594. margin-bottom: 8px !important;
  595. color: rgba(255, 255, 255, 1);
  596. }
  597. #${ID.infoDiv} a,
  598. .${CLASS.footerSpan} a {
  599. color: rgba(255, 255, 255, 1);
  600. text-decoration: none;
  601. font-size: 14px !important;
  602. font-weight: normal !important;
  603. margin-bottom: 1px;
  604. display: inline-block;
  605. }
  606. /* switch */
  607. .${CLASS.switchLabel} {
  608. position: relative;
  609. display: inline-block;
  610. width: 50px;
  611. height: 21px;
  612. margin-top: 3px;
  613. }
  614. .${CLASS.switchLabel} input {
  615. opacity: 0;
  616. width: 0 !important;
  617. height: 0 !important;
  618. }
  619. .${CLASS.sliderSpan} {
  620. position: absolute;
  621. cursor: pointer;
  622. top: 0;
  623. left: 0;
  624. right: 0;
  625. bottom: 0;
  626. background-color: rgba(255, 255, 255, .6);
  627. -webkit-transition: .4s;
  628. transition: .4s;
  629. }
  630. .${CLASS.sliderSpan}:before {
  631. position: absolute;
  632. content: "";
  633. height: 13px;
  634. width: 13px;
  635. left: 4px;
  636. bottom: 4px;
  637. background-color: rgba(255, 255, 255, 1);
  638. -webkit-transition: .4s;
  639. transition: .4s;
  640. }
  641. #${ID.settingDiv} input:checked + .${CLASS.sliderSpan} {
  642. background-color: rgba(0, 255, 0, .7);
  643. }
  644. #${ID.settingDiv} input:focus + .${CLASS.sliderSpan} {
  645. box-shadow: 0 0 1px rgba(0, 255, 0, .7);
  646. }
  647. #${ID.settingDiv} input:checked + .${CLASS.sliderSpan}:before {
  648. -webkit-transform: translateX(29px);
  649. -ms-transform: translateX(29px);
  650. transform: translateX(29px);
  651. }
  652. .${CLASS.sliderSpan}.${CLASS.roundSpan} {
  653. border-radius: 34px;
  654. }
  655. .${CLASS.sliderSpan}.${CLASS.roundSpan}:before {
  656. border-radius: 50%;
  657. }
  658. .${CLASS.readOnly} {
  659. color: rgba(255, 255, 255, .3) !important;
  660. background-color: rgba(0, 0, 0, .3) !important;
  661. cursor: default !important;
  662. opacity: 1 !important;
  663. }
  664. .${CLASS.readOnly}::placeholder {
  665. color: rgba(255, 255, 255, .3) !important;
  666. }
  667. #${ID.infoButton} {
  668. bottom: 52px;
  669. left: 3px;
  670. width: 25px;
  671. height: 25px;
  672. background-image: url(data:image/png;base64,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);
  673. }
  674. #${ID.infoButton}:hover {
  675. bottom: 50px;
  676. left: 1px;
  677. width: 29px;
  678. height: 29px;
  679. }
  680. #${ID.settingDiv} .tabs {
  681. display: flex;
  682. width: 501px;
  683. height: 28px;
  684. }
  685. #${ID.settingDiv} .tabs>.tab {
  686. flex: 1;
  687. display: flex;
  688. border: 1px solid rgb(65,146,247);
  689. margin: 0;
  690. }
  691. #${ID.settingDiv} .tabs>.tab:after {
  692. background: rgba(0, 0, 0, 0);
  693. color: rgba(0, 0, 0, 0) !important;
  694. }
  695. #${ID.settingDiv} .tab>.tab-input {
  696. width: 0 !important;
  697. height: 0 !important;
  698. margin: 0 !important;
  699. display: none !important;
  700. }
  701. #${ID.settingDiv} .tab>.tab-box {
  702. padding: 4px 0px 0.5px 0px;
  703. width: 100%;
  704. height: 22px;
  705. text-align: center;
  706. transition: 0.3s;
  707. background: rgba(255, 255, 255, 1);
  708. font-size: 12px;
  709. font-weight: normal !important;
  710. display: table !important;
  711. color: #000000B3;
  712. }
  713. #${ID.settingDiv} .tab>.tab-box:hover {
  714. opacity: .8;
  715. cursor: pointer;
  716. }
  717. #${ID.settingDiv} .tab>.tab-input:checked+.tab-box {
  718. color: rgba(255, 255, 255, 1);
  719. background: rgba(0, 255, 0, .7);
  720. }
  721. #${ID.customPlayerButton}:hover:after,
  722. #${ID.settingDiv} .${CLASS.titleTd}:hover:after,
  723. .${CLASS.footerA}:hover:after {
  724. position: absolute;
  725. font-size: 12px;
  726. left: 0px;
  727. top: -3px;
  728. padding: 5px 5px 5px 5px !important;
  729. background-color: rgba(0, 0, 0, 0.6);
  730. color: rgba(255, 255, 255, 1);
  731. content: attr(data-tip);
  732. text-align: center;
  733. z-index: 999999;
  734. width: auto !important;
  735. height: auto !important;
  736. white-space: nowrap;
  737. }
  738. #${ID.customPlayerButton}:hover:after {
  739. left: -10px !important;
  740. top: -28px !important;
  741. }
  742. .${CLASS.footerA} {
  743. position: relative;
  744. }
  745. .${CLASS.footerA}:hover:after {
  746. left: 0px !important;
  747. top: -27px !important;
  748. }
  749. #${ID.customPlayerButton} {
  750. position: absolute;
  751. right: 122px;
  752. top: 76px;
  753. width: 22px;
  754. height: 22px;
  755. border: none;
  756. cursor: pointer;
  757. background-image: ${ICON_BASE64.custom};
  758. z-index: 999999;
  759. }
  760. `;
  761. const HTML = `
  762. <div id="${ID.loadingDiv}">
  763. <div class="spinner"></div>
  764. </div>
  765. <div id="${ID.toastDiv}"></div>
  766.  
  767. <div id="${ID.buttonDiv}">
  768. <button id="${ID.infoButton}" class="${CLASS.button}"></button>
  769. <button id="${ID.playButton}" class="${CLASS.button}"></button>
  770. <button id="${ID.settingButton}" class="${CLASS.button}"></button>
  771. </div>
  772.  
  773. <div id="${ID.infoDiv}">
  774. <span class="${CLASS.titleSpan}">Play-With-MPV<button class="${CLASS.closeButton}">X</button></span>
  775. <table id="${ID.infoTable}">
  776. <tr>
  777. <td colspan="4" style="text-align: center; color: white; font-size: 16px;">本 息</td>
  778. </tr>
  779. <tr>
  780. <td class="${CLASS.titleTd}">视频标题</td>
  781. <td colspan="8">
  782. <input type="text" readonly class="${CLASS.infoInput}">
  783. </td>
  784. </tr>
  785. <tr>
  786. <td class="${CLASS.titleTd}">视频链接</td>
  787. <td colspan="8">
  788. <input type="text" readonly class="${CLASS.infoInput}">
  789. </td>
  790. </tr>
  791. <tr>
  792. <td class="${CLASS.titleTd}">音频链接</td>
  793. <td colspan="8">
  794. <input type="text" readonly class="${CLASS.infoInput}">
  795. </td>
  796. </tr>
  797. <tr>
  798. <td class="${CLASS.titleTd}">字幕链接</td>
  799. <td colspan="8">
  800. <input type="text" readonly class="${CLASS.infoInput}">
  801. </td>
  802. </tr>
  803. <tr>
  804. <td class="${CLASS.titleTd}">referer</td>
  805. <td colspan="8">
  806. <input type="text" readonly class="${CLASS.infoInput}">
  807. </td>
  808. </tr>
  809. <tr>
  810. <td class="${CLASS.titleTd}">origin</td>
  811. <td colspan="8">
  812. <input type="text" readonly class="${CLASS.infoInput}">
  813. </td>
  814. </tr>
  815. <tr><td></td></tr>
  816. <tr>
  817. <td style="text-align: right;">
  818. <a href="https://greasyfork.org/zh-CN/scripts/444056-play-with-mpv" target="_blank">🆕 升级 🆕</a>
  819. </td>
  820. <td style="text-align: right;">
  821. <a href="https://www.lckp.top/play-with-mpv/index.html" target="_blank">🧭 网站导航 🧭</a>
  822. </td>
  823. <td style="text-align: right;">
  824. <a href="https://github.com/LuckyPuppy514/Play-With-MPV" target="_blank">🌟 项目源码 🌟</a>
  825. </td>
  826. <td style="text-align: right;">
  827. <a href="https://github.com/LuckyPuppy514/Play-With-MPV/issues/new" target="_blank">👻 反馈 👻</a>
  828. </td>
  829. </tr>
  830. </table>
  831. <span class="${CLASS.footerSpan}">
  832. <a href="https://greasyfork.org/zh-CN/scripts/444056-play-with-mpv" target="_blank" class="${CLASS.footerA}" data-tip="版本升级"> 🆕 </a>
  833. <a href="https://github.com/LuckyPuppy514/Play-With-MPV" target="_blank" class="${CLASS.footerA}" data-tip="项目源码"> © 2023 LuckyPuppy514 </a>
  834. <a href="https://github.com/LuckyPuppy514/Play-With-MPV/issues/new" target="_blank" class="${CLASS.footerA}" data-tip="问题反馈"> 👻 </a>
  835. </span>
  836. </div>
  837.  
  838. <div id="${ID.settingDiv}">
  839. <span class="${CLASS.titleSpan}">Play-With-MPV<button class="${CLASS.closeButton}">X</button></span>
  840. <button id="${ID.customPlayerButton}" class="${CLASS.button}" data-tip="设置自定义播放器"></button>
  841. <table id="${ID.settingTable}">
  842. <tr>
  843. <td class="${CLASS.titleTd}" data-tip="选择 mpv 以外播放器时,部分功能无效">播放软件</td>
  844. <td colspan="8">
  845. <div class="tabs">
  846. <label class="tab">
  847. <input type="radio" name="${ID.playerRadio}" value="${PLAYER.mpv.name}" class="tab-input">
  848. <div class="tab-box">mpv</div>
  849. </label>
  850. <label class="tab">
  851. <input type="radio" name="${ID.playerRadio}" value="${PLAYER.potplayer.name}" class="tab-input">
  852. <div class="tab-box">potplayer</div>
  853. </label>
  854. <label class="tab">
  855. <input type="radio" name="${ID.playerRadio}" value="${PLAYER.custom.name}" class="tab-input">
  856. <div class="tab-box">自定义</div>
  857. </label>
  858. </div>
  859. </td>
  860. </tr>
  861. <tr>
  862. <td class="${CLASS.titleTd}" data-tip="mpv.exe 或 PotPlayerMini64.exe 的完整路径">软件路径</td>
  863. <td colspan="8">
  864. <div>
  865. <input id="${ID.softwarePathInput}" type=text placeholder="请输入软件路径,例如:D://mpvnet//mpvnet.exe">
  866. </div>
  867. </td>
  868. </tr>
  869. <tr>
  870. <td class="${CLASS.titleTd}" data-tip="旁路由网关实现代理一般不需要设置">代理设置</td>
  871. <td colspan="8">
  872. <div>
  873. <input id="${ID.proxyInput}" type=text placeholder="请输入代理地址,例如:http://127.0.0.1:10809">
  874. </div>
  875. </td>
  876. </tr>
  877. <tr>
  878. <td class="${CLASS.titleTd}" data-tip="仅适用于B站或使用 yt-dlp 解析的网站,例如:油管,OK,TVer 等">最高画质</td>
  879. <td colspan="8">
  880. <div class="tabs">
  881. <label class="tab">
  882. <input type="radio" name="${ID.bestQualityRadio}" value="unlimited" class="tab-input">
  883. <div class="tab-box" name="${ID.bestQualityRadio}">无限制</div>
  884. </label>
  885. <label class="tab">
  886. <input type="radio" name="${ID.bestQualityRadio}" value="2160p" class="tab-input">
  887. <div class="tab-box" name="${ID.bestQualityRadio}">2160p</div>
  888. </label>
  889. <label class="tab">
  890. <input type="radio" name="${ID.bestQualityRadio}" value="1440p" class="tab-input">
  891. <div class="tab-box" name="${ID.bestQualityRadio}">1440p</div>
  892. </label>
  893. <label class="tab">
  894. <input type="radio" name="${ID.bestQualityRadio}" value="1080p" class="tab-input">
  895. <div class="tab-box" name="${ID.bestQualityRadio}">1080p</div>
  896. </label>
  897. <label class="tab">
  898. <input type="radio" name="${ID.bestQualityRadio}" value="720p" class="tab-input">
  899. <div class="tab-box" name="${ID.bestQualityRadio}">720p</div>
  900. </label>
  901. </div>
  902. </td>
  903. </tr>
  904. <tr>
  905. <td class="${CLASS.titleTd}" data-tip="仅适用于B站">视频编码</td>
  906. <td colspan="8">
  907. <div class="tabs">
  908. <label class="tab">
  909. <input type="radio" name="${ID.bilibiliCodecsRadio}" value="12" class="tab-input">
  910. <div class="tab-box" name="${ID.bilibiliCodecsRadio}">HEVC</div>
  911. </label>
  912. <label class="tab">
  913. <input type="radio" name="${ID.bilibiliCodecsRadio}" value="13" class="tab-input">
  914. <div class="tab-box" name="${ID.bilibiliCodecsRadio}">AV1</div>
  915. </label>
  916. <label class="tab">
  917. <input type="radio" name="${ID.bilibiliCodecsRadio}" value="7" class="tab-input">
  918. <div class="tab-box" name="${ID.bilibiliCodecsRadio}">AVC</div>
  919. </label>
  920. </div>
  921. </td>
  922. </tr>
  923. <tr>
  924. <td class="${CLASS.titleTd}" data-tip="仅适用于B站">首选字幕</td>
  925. <td colspan="8">
  926. <div class="tabs">
  927. <label class="tab">
  928. <input type="radio" name="${ID.subtitlePreferRadio}" value="zh-Hans" class="tab-input">
  929. <div class="tab-box" name="${ID.subtitlePreferRadio}">简体</div>
  930. </label>
  931. <label class="tab">
  932. <input type="radio" name="${ID.subtitlePreferRadio}" value="zh-Hant" class="tab-input">
  933. <div class="tab-box" name="${ID.subtitlePreferRadio}">繁体</div>
  934. </label>
  935. <label class="tab">
  936. <input type="radio" name="${ID.subtitlePreferRadio}" value="en-US" class="tab-input"=>
  937. <div class="tab-box" name="${ID.subtitlePreferRadio}">英语</div>
  938. </label>
  939. <label class="tab">
  940. <input type="radio" name="${ID.subtitlePreferRadio}" value="off" class="tab-input"=>
  941. <div class="tab-box" name="${ID.subtitlePreferRadio}">关闭</div>
  942. </label>
  943. </div>
  944. </td>
  945. </tr>
  946. <tr>
  947. <td class="${CLASS.titleTd}" data-tip="解析成功自动播放">自动播放</td>
  948. <td colspan="2">
  949. <div>
  950. <label class="${CLASS.switchLabel}">
  951. <input type="checkbox" id="${ID.playAutoInput}">
  952. <span class="${CLASS.sliderSpan} ${CLASS.roundSpan}"></span>
  953. </label>
  954. </div>
  955. </td>
  956. <td class="${CLASS.titleTd}" data-tip="播放时自动关闭页面(和自动播放一起开启时修改配置请前往导航页)">自动关闭</td>
  957. <td colspan="2">
  958. <div>
  959. <label class="${CLASS.switchLabel}">
  960. <input type="checkbox" id="${ID.closeAutoInput}">
  961. <span class="${CLASS.sliderSpan} ${CLASS.roundSpan}"></span>
  962. </label>
  963. </div>
  964. </td>
  965. <td class="${CLASS.titleTd}" data-tip="同步网页播放时间">同步时间</td>
  966. <td colspan="2">
  967. <div>
  968. <label class="${CLASS.switchLabel}">
  969. <input type="checkbox" id="${ID.syncStartTimeInput}">
  970. <span class="${CLASS.sliderSpan} ${CLASS.roundSpan}" id="${ID.syncStartTimeSpan}"></span>
  971. </label>
  972. </div>
  973. </td>
  974. </tr>
  975. <tr>
  976. <td colspan="9">
  977. <button id="${ID.saveButton}">保存设置</button>
  978. <button id="${ID.downloadButton}">下载注册表</button>
  979. <button id="${ID.deleteButton}">删除注册表</button>
  980. </td>
  981. </tr>
  982. </table>
  983. <table id="${ID.customPlayerTable}">
  984. <tr>
  985. <td class="${CLASS.titleTd}" data-tip="必填(视频格式:yt-dlp / m3u8 / flv / m4s / mp4 / mkv ... 播放器不支持则无法播放对应格式视频)">视频参数</td>
  986. <td colspan="8">
  987. <div>
  988. <input id="${ID.videoUrlParamInput}" type=text placeholder='请输入视频参数,例如:mpv://"$\{videoUrl\}"'>
  989. </div>
  990. </td>
  991. </tr>
  992. <tr>
  993. <td class="${CLASS.titleTd}" data-tip="选填(为空则不支持最高画质和视频编码)">音频参数</td>
  994. <td colspan="8">
  995. <div>
  996. <input id="${ID.audioUrlParamInput}" type=text placeholder='请输入音频参数,例如: --audio-file="$\{audioUrl\}"'>
  997. </div>
  998. </td>
  999. </tr>
  1000. <tr>
  1001. <td class="${CLASS.titleTd}" data-tip="选填(为空则无法加载B站外挂字幕)">字幕参数</td>
  1002. <td colspan="8">
  1003. <div>
  1004. <input id="${ID.subtitleUrlParamInput}" type=text placeholder='请输入字幕参数,例如: --sub-file="$\{subtitleUrl\}"'>
  1005. </div>
  1006. </td>
  1007. </tr>
  1008. <tr>
  1009. <td class="${CLASS.titleTd}" data-tip="选填(为空则无法传递标题)">标题参数</td>
  1010. <td colspan="8">
  1011. <div>
  1012. <input id="${ID.titleParamInput}" type=text placeholder='请输入标题参数,例如: --force-media-title="$\{title\}"'>
  1013. </div>
  1014. </td>
  1015. </tr>
  1016. <tr>
  1017. <td class="${CLASS.titleTd}" data-tip="选填(为空则不支持同步时间)">时间参数</td>
  1018. <td colspan="8">
  1019. <div>
  1020. <input id="${ID.startTimeParamInput}" type=text placeholder='请输入时间参数,例如: --start=$\{startTime\}'>
  1021. </div>
  1022. </td>
  1023. </tr>
  1024. <tr>
  1025. <td class="${CLASS.titleTd}" data-tip="选填(为空则不支持代理设置)">代理参数</td>
  1026. <td colspan="8">
  1027. <div>
  1028. <input id="${ID.proxyParamInput}" type=text placeholder='请输入代理参数,例如: --http-proxy=$\{proxy\} --ytdl-raw-options=proxy=[$\{proxy\}]'>
  1029. </div>
  1030. </td>
  1031. </tr>
  1032. <tr>
  1033. <td class="${CLASS.titleTd}" data-tip="选填(为空则无法观看B站和橘子动漫)">referer</td>
  1034. <td colspan="8">
  1035. <div>
  1036. <input id="${ID.refererParamInput}" type=text placeholder='请输入 referer,例如: --http-header-fields="referer: $\{referer\}"'>
  1037. </div>
  1038. </td>
  1039. </tr>
  1040. <tr>
  1041. <td class="${CLASS.titleTd}" data-tip="选填(为空则无法观看巴哈姆特)">origin</td>
  1042. <td colspan="8">
  1043. <div>
  1044. <input id="${ID.originParamInput}" type=text placeholder='请输入 origin,例如: --http-header-fields="origin: $\{origin\}" '>
  1045. </div>
  1046. </td>
  1047. </tr>
  1048. </table>
  1049. <span class="${CLASS.footerSpan}">
  1050. <a href="https://greasyfork.org/zh-CN/scripts/444056-play-with-mpv" target="_blank" class="${CLASS.footerA}" data-tip="版本升级"> 🆕 </a>
  1051. <a href="https://github.com/LuckyPuppy514/Play-With-MPV" target="_blank" class="${CLASS.footerA}" data-tip="项目源码"> © 2023 LuckyPuppy514 </a>
  1052. <a href="https://github.com/LuckyPuppy514/Play-With-MPV/issues/new" target="_blank" class="${CLASS.footerA}" data-tip="问题反馈"> 👻 </a>
  1053. </span>
  1054. </div>
  1055. `;
  1056. const REG =
  1057. `Windows Registry Editor Version 5.00
  1058. [HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Google\\Chrome]
  1059. "ExternalProtocolDialogShowAlwaysOpenCheckbox"=dword:00000001
  1060.  
  1061. [HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\Edge]
  1062. "ExternalProtocolDialogShowAlwaysOpenCheckbox"=dword:00000001
  1063.  
  1064. [HKEY_CLASSES_ROOT\\\${PLAYER_NAME}]
  1065. @="\${PLAYER_NAME} Protocol"
  1066. "URL Protocol"=""
  1067.  
  1068. [HKEY_CLASSES_ROOT\\\${PLAYER_NAME}\\DefaultIcon]
  1069. @=""
  1070.  
  1071. [HKEY_CLASSES_ROOT\\\${PLAYER_NAME}\\shell]
  1072. @=""
  1073.  
  1074. [HKEY_CLASSES_ROOT\\\${PLAYER_NAME}\\shell\\open]
  1075. @=""
  1076.  
  1077. [HKEY_CLASSES_ROOT\\\${PLAYER_NAME}\\shell\\open\\command]
  1078. @="C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe -WindowStyle Hidden -Command \\"& {Add-Type -AssemblyName System.Web;$PARAMS=([System.Web.HTTPUtility]::UrlDecode('%1') -replace '^\${PLAYER_NAME}://'); Start-Process -FilePath \\\\\\\"\${SOFTWARE_PATH}\\\\\\\" -ArgumentList $PARAMS}\\""
  1079. `
  1080. const REG_DELETE =
  1081. `Windows Registry Editor Version 5.00
  1082. [-HKEY_CLASSES_ROOT\\\${PLAYER_NAME}]
  1083. `
  1084. function appendCSS() {
  1085. let css = document.createElement("style");
  1086. css.innerHTML = CSS.trim();
  1087. document.head.appendChild(css);
  1088. }
  1089. function appendHTML() {
  1090. let div = document.createElement("div");
  1091. div.innerHTML = HTML.trim();
  1092. document.body.appendChild(div);
  1093. }
  1094. function loading(visiable) {
  1095. let loadingDiv = document.getElementById(ID.loadingDiv);
  1096. if (visiable) {
  1097. loadingDiv.style.display = "flex";
  1098. setTimeout(() => {
  1099. if (loadingDiv.style.display == "flex") {
  1100. document.getElementById(ID.loadingDiv).style.display = "none";
  1101. toast("超时辣 ...... 😓", TOAST_TYPE.error);
  1102. }
  1103. }, TIME.out);
  1104. } else {
  1105. loadingDiv.style.display = "none";
  1106. }
  1107. }
  1108. function toast(message, type, duration) {
  1109. type = type ? type : TOAST_TYPE.info;
  1110. duration = isNaN(duration) ? TIME.toast : duration;
  1111. let toastDiv = document.getElementById(ID.toastDiv);
  1112. toastDiv.innerHTML = message;
  1113. toastDiv.style.display = "block";
  1114. if (type == TOAST_TYPE.info) {
  1115. toastDiv.style.backgroundColor = "rgba(75, 180 ,54, 1)";
  1116. } else if (type == TOAST_TYPE.warn) {
  1117. toastDiv.style.backgroundColor = "rgba(190, 190, 70, 1)";
  1118. } else if (type == TOAST_TYPE.error) {
  1119. toastDiv.style.backgroundColor = "rgba(210, 51, 35, 1)";
  1120. }
  1121. setTimeout(() => {
  1122. toastDiv.style.display = "none";
  1123. }, duration);
  1124. }
  1125. function addListener() {
  1126. let buttonDiv = document.getElementById(ID.buttonDiv);
  1127. let playButton = document.getElementById(ID.playButton);
  1128. let settingButton = document.getElementById(ID.settingButton);
  1129. let settingDiv = document.getElementById(ID.settingDiv);
  1130. let settingTable = document.getElementById(ID.settingTable);
  1131. let softwarePathInput = document.getElementById(ID.softwarePathInput);
  1132. let proxyInput = document.getElementById(ID.proxyInput);
  1133. let playAutoInput = document.getElementById(ID.playAutoInput);
  1134. let closeAutoInput = document.getElementById(ID.closeAutoInput);
  1135. let syncStartTimeInput = document.getElementById(ID.syncStartTimeInput);
  1136. let syncStartTimeSpan = document.getElementById(ID.syncStartTimeSpan);
  1137. let downloadButton = document.getElementById(ID.downloadButton);
  1138. let deleteButton = document.getElementById(ID.deleteButton);
  1139. let saveButton = document.getElementById(ID.saveButton);
  1140. let closeButtons = document.getElementsByClassName(CLASS.closeButton);
  1141. let infoButton = document.getElementById(ID.infoButton);
  1142. let infoDiv = document.getElementById(ID.infoDiv);
  1143. let customPlayerTable = document.getElementById(ID.customPlayerTable);
  1144. let customPlayerButton = document.getElementById(ID.customPlayerButton);
  1145. let videoUrlParamInput = document.getElementById(ID.videoUrlParamInput);
  1146. let audioUrlParamInput = document.getElementById(ID.audioUrlParamInput);
  1147. let subtitleUrlParamInput = document.getElementById(ID.subtitleUrlParamInput);
  1148. let titleParamInput = document.getElementById(ID.titleParamInput);
  1149. let startTimeParamInput = document.getElementById(ID.startTimeParamInput);
  1150. let proxyParamInput = document.getElementById(ID.proxyParamInput);
  1151. let refererParamInput = document.getElementById(ID.refererParamInput);
  1152. let originParamInput = document.getElementById(ID.originParamInput);
  1153. let infoInputs = document.getElementsByClassName(CLASS.infoInput);
  1154. switchStatus(downloadButton, false);
  1155. // 播放按钮
  1156. playButton.onclick = function () {
  1157. if (currentConfig.player == PLAYER.mpv.name || currentConfig.player == PLAYER.potplayer.name) {
  1158. let message = undefined;
  1159. if (!currentConfig[currentConfig.player].path) {
  1160. message = "请先进行设置";
  1161. } else if (!currentConfig[currentConfig.player].regVersion) {
  1162. message = "请先下载注册表";
  1163. } else if (currentConfig[currentConfig.player].regVersion != DEFAULT_CONFIG[currentConfig.player].regVersion) {
  1164. message = "注册表有更新,请重新下载注册表";
  1165. }
  1166. if (message) {
  1167. toast(message, TOAST_TYPE.warn);
  1168. settingDiv.style.display = "none";
  1169. settingButton.click();
  1170. return;
  1171. }
  1172. }
  1173. loading(true);
  1174. try {
  1175. playButtonClickLimit();
  1176. handler.play();
  1177. if (currentConfig.closeAuto == 1 && page.url !== "https://www.lckp.top/play-with-mpv/index.html") {
  1178. setTimeout(() => {
  1179. if (history.length === 1) {
  1180. window.location.href = "about:blank";
  1181. window.top.close();
  1182. } else {
  1183. history.back();
  1184. }
  1185. }, 1000);
  1186. } else {
  1187. handler.pause();
  1188. }
  1189. } catch (error) {
  1190. toast("出错辣 ...... 😓", TOAST_TYPE.error);
  1191. console.log(error);
  1192. }
  1193. loading(false);
  1194. }
  1195. // 播放快捷键 CTRL + P
  1196. window.onkeydown = async function () {
  1197. if (event.ctrlKey && event.keyCode === 80 && !event.shiftKey) {
  1198. event.preventDefault();
  1199. playButton.click();
  1200. }
  1201. }
  1202. // 设置按钮
  1203. settingButton.onclick = function () {
  1204. let display = settingDiv.style.display;
  1205. if (display == "flex") {
  1206. settingDiv.style.display = "none";
  1207. } else {
  1208. infoDiv.style.display = "none";
  1209. settingDiv.style.display = "flex";
  1210. // 加载配置
  1211. softwarePathInput.value = currentConfig[currentConfig.player].path;
  1212. proxyInput.value = currentConfig.proxy;
  1213. $(`input:radio[name="${ID.bestQualityRadio}"][value="${currentConfig.bestQuality}"]`).prop('checked', true);
  1214. $(`input:radio[name="${ID.bilibiliCodecsRadio}"][value="${currentConfig.bilibiliCodecs}"]`).prop('checked', true);
  1215. $(`input:radio[name="${ID.playerRadio}"][value="${currentConfig.player}"]`).prop('checked', true);
  1216. playAutoInput.checked = currentConfig.playAuto == 1 ? true : false;
  1217. closeAutoInput.checked = currentConfig.closeAuto == 1 ? true : false;
  1218. syncStartTimeInput.checked = currentConfig.syncStartTime == 1 ? true : false;
  1219. $(`input:radio[name="${ID.subtitlePreferRadio}"][value="${currentConfig.subtitlePrefer}"]`).prop('checked', true);
  1220. switchPlayer($(`input:radio[name="${ID.playerRadio}"]:checked`).val());
  1221. }
  1222. }
  1223. // 播放器选择框
  1224. $(`input:radio[name="${ID.playerRadio}"]`).change(function () {
  1225. switchPlayer(this.value);
  1226. });
  1227. // 保存按钮
  1228. saveButton.onclick = function () {
  1229. let playerChecked = $(`input:radio[name="${ID.playerRadio}"]:checked`).val();
  1230. if (playerChecked == PLAYER.mpv.name || playerChecked == PLAYER.potplayer.name) {
  1231. let oldSoftwarePath = currentConfig[playerChecked].path;
  1232. let newSoftwarePath = softwarePathInput.value;
  1233. if (!newSoftwarePath) {
  1234. toast("软件路径不能为空", TOAST_TYPE.error);
  1235. return;
  1236. }
  1237. if (/.*[\u4e00-\u9fa5]+.*/g.test(newSoftwarePath)) {
  1238. toast("软件路径不能包含中文", TOAST_TYPE.error);
  1239. return;
  1240. }
  1241. newSoftwarePath = newSoftwarePath.replace(/[\\|/]+/g, "//");
  1242. if (!newSoftwarePath.endsWith(".com") && !newSoftwarePath.endsWith(".exe")) {
  1243. if (!newSoftwarePath.endsWith("//")) {
  1244. newSoftwarePath = newSoftwarePath + "//";
  1245. }
  1246. if (playerChecked == PLAYER.mpv.name) {
  1247. if (newSoftwarePath.toLowerCase().indexOf("mpvnet") != -1 || newSoftwarePath.toLowerCase().indexOf("mpv.net") != -1) {
  1248. newSoftwarePath = newSoftwarePath + "mpvnet.exe";
  1249. } else {
  1250. newSoftwarePath = newSoftwarePath + "mpv.exe";
  1251. }
  1252. } else if (playerChecked == PLAYER.potplayer.name) {
  1253. newSoftwarePath = newSoftwarePath + "PotPlayerMini64.exe";
  1254. }
  1255. }
  1256. softwarePathInput.value = newSoftwarePath;
  1257. currentConfig[playerChecked].path = newSoftwarePath;
  1258. switchStatus(downloadButton, softwarePathInput.value ? true : false);
  1259. if (oldSoftwarePath != newSoftwarePath) {
  1260. currentConfig[playerChecked].regVersion = "00000000";
  1261. }
  1262. }
  1263. currentConfig.proxy = proxyInput.value;
  1264. currentConfig.bestQuality = $(`input:radio[name="${ID.bestQualityRadio}"]:checked`).val();
  1265. currentConfig.bilibiliCodecs = $(`input:radio[name="${ID.bilibiliCodecsRadio}"]:checked`).val();
  1266. currentConfig.player = playerChecked;
  1267. currentConfig.subtitlePrefer = $(`input:radio[name="${ID.subtitlePreferRadio}"]:checked`).val();
  1268. currentConfig.playAuto = playAutoInput.checked ? 1 : 0;
  1269. currentConfig.closeAuto = closeAutoInput.checked ? 1 : 0;
  1270. currentConfig.syncStartTime = syncStartTimeInput.checked ? 1 : 0;
  1271. GM_setValue(KEY.config, currentConfig);
  1272. if (playAutoInput.checked && closeAutoInput.checked) {
  1273. toast("保存成功,如需修改配置请前往导航页");
  1274. } else {
  1275. toast("保存成功");
  1276. }
  1277. if (currentConfig.playAuto == 1) {
  1278. playButtonClickLimit();
  1279. }
  1280. if (document.getElementById("iptv") && localStorage.category == "iptv") {
  1281. localStorage.player = JSON.stringify(PLAYER[currentConfig.player]);
  1282. document.getElementById("iptv").click();
  1283. }
  1284. init();
  1285. }
  1286. // 下载按钮
  1287. downloadButton.onclick = function () {
  1288. let playerChecked = $(`input:radio[name="${ID.playerRadio}"]:checked`).val();
  1289. currentConfig[playerChecked].regVersion = DEFAULT_CONFIG[playerChecked].regVersion;
  1290. GM_setValue(KEY.config, currentConfig);
  1291. let reg = REG.replace("${SOFTWARE_PATH}", currentConfig[playerChecked].path);
  1292. reg = reg.replace(/\$\{PLAYER_NAME\}/g, playerChecked);
  1293. let a = document.createElement('a');
  1294. let blob = new Blob([reg], { 'type': 'application/octet-stream' });
  1295. a.href = window.URL.createObjectURL(blob);
  1296. a.download = `${playerChecked}-install.reg`;
  1297. a.click();
  1298. }
  1299. deleteButton.onclick = function () {
  1300. let playerChecked = $(`input:radio[name="${ID.playerRadio}"]:checked`).val();
  1301. currentConfig[playerChecked].regVersion = "00000000";
  1302. GM_setValue(KEY.config, currentConfig);
  1303. let reg = REG_DELETE.replace(/\$\{PLAYER_NAME\}/g, playerChecked);
  1304. let a = document.createElement('a');
  1305. let blob = new Blob([reg], { 'type': 'application/octet-stream' });
  1306. a.href = window.URL.createObjectURL(blob);
  1307. a.download = `${playerChecked}-delete.reg`;
  1308. a.click();
  1309. }
  1310. // 关闭按钮
  1311. for (let closeButton of closeButtons) {
  1312. closeButton.onclick = function () {
  1313. settingDiv.style.display = "none";
  1314. infoDiv.style.display = "none";
  1315. }
  1316. }
  1317. // 信息按钮
  1318. infoButton.onclick = function () {
  1319. let display = infoDiv.style.display;
  1320. if (display == "flex") {
  1321. infoDiv.style.display = "none";
  1322. } else {
  1323. settingDiv.style.display = "none";
  1324. infoDiv.style.display = "flex";
  1325. let title = handler.media.title;
  1326. infoInputs[0].value = title ? title : document.title;
  1327. infoInputs[1].value = handler.media.videoUrl;
  1328. infoInputs[2].value = handler.media.audioUrl;
  1329. infoInputs[3].value = handler.media.subtitleUrl;
  1330. infoInputs[4].value = handler.media.referer;
  1331. infoInputs[5].value = handler.media.origin;
  1332. for (const infoInput of infoInputs) {
  1333. if (infoInput.value) {
  1334. infoInput.style.cursor = "pointer";
  1335. infoInput.onclick = function () {
  1336. this.select();
  1337. navigator.clipboard.writeText(this.value);
  1338. toast("已复制到剪贴板");
  1339. }
  1340. }
  1341. }
  1342. }
  1343. }
  1344. let bestQualityRadios = document.getElementsByName(ID.bestQualityRadio);
  1345. let bilibiliCodecsRadios = document.getElementsByName(ID.bilibiliCodecsRadio);
  1346. let subtitlePreferRadios = document.getElementsByName(ID.subtitlePreferRadio);
  1347. // 切换播放器
  1348. function switchPlayer(player) {
  1349. player = PLAYER[player];
  1350. // mpv 和 potplayer 专属
  1351. if (player.name == PLAYER.mpv.name || player.name == PLAYER.potplayer.name) {
  1352. switchStatus(softwarePathInput, true);
  1353. softwarePathInput.value = currentConfig[player.name].path;
  1354. if (softwarePathInput.value) {
  1355. switchStatus(downloadButton, true);
  1356. } else {
  1357. switchStatus(downloadButton, false);
  1358. }
  1359. } else {
  1360. switchStatus(softwarePathInput, false);
  1361. switchStatus(downloadButton, false);
  1362. }
  1363. // 代理
  1364. let flag = player.params.proxy ? true : false;
  1365. switchStatus(proxyInput, flag);
  1366. // 音频
  1367. flag = player.params.audioUrl ? true : false;
  1368. for (const radio of bestQualityRadios) {
  1369. switchStatus(radio, flag);
  1370. }
  1371. for (const radio of bilibiliCodecsRadios) {
  1372. switchStatus(radio, flag);
  1373. }
  1374. // 字幕
  1375. flag = player.params.subtitleUrl ? true : false;
  1376. for (const radio of subtitlePreferRadios) {
  1377. switchStatus(radio, flag);
  1378. }
  1379. // 时间
  1380. flag = player.params.startTime ? true : false;
  1381. switchStatus(syncStartTimeSpan, flag);
  1382. switchStatus(syncStartTimeInput, flag);
  1383. }
  1384. // 全屏
  1385. document.addEventListener("fullscreenchange", () => {
  1386. if (document.fullscreenElement) {
  1387. page.isFullScreen = true;
  1388. buttonDiv.style.display = "none";
  1389. } else {
  1390. page.isFullScreen = false;
  1391. if (handler.media.videoUrl) {
  1392. buttonDiv.style.display = "flex";
  1393. }
  1394. }
  1395. });
  1396. // 限制播放按钮点击频率
  1397. function playButtonClickLimit() {
  1398. playButton.disabled = true;
  1399. setTimeout(() => {
  1400. playButton.disabled = false;
  1401. }, TIME.pauseInterval);
  1402. }
  1403. // 自定义播放器按钮
  1404. customPlayerButton.onclick = function () {
  1405. if (customPlayerTable.style.display == "flex") {
  1406. if (!videoUrlParamInput.value) {
  1407. toast("视频参数不能为空", TOAST_TYPE.error);
  1408. return;
  1409. }
  1410. currentConfig.customPlayer.params.videoUrl = videoUrlParamInput.value;
  1411. currentConfig.customPlayer.params.audioUrl = audioUrlParamInput.value;
  1412. currentConfig.customPlayer.params.subtitleUrl = subtitleUrlParamInput.value;
  1413. currentConfig.customPlayer.params.title = titleParamInput.value;
  1414. currentConfig.customPlayer.params.startTime = startTimeParamInput.value;
  1415. currentConfig.customPlayer.params.proxy = proxyParamInput.value;
  1416. currentConfig.customPlayer.params.referer = refererParamInput.value;
  1417. currentConfig.customPlayer.params.origin = originParamInput.value;
  1418. PLAYER.custom = currentConfig.customPlayer;
  1419. GM_setValue(KEY.config, currentConfig);
  1420. switchPlayer($(`input:radio[name="${ID.playerRadio}"]:checked`).val());
  1421. settingTable.style.display = "flex";
  1422. customPlayerTable.style.display = "none";
  1423. customPlayerButton.style.backgroundImage = ICON_BASE64.custom;
  1424. customPlayerButton.dataset.tip = "设置自定义播放器";
  1425. } else {
  1426. videoUrlParamInput.value = currentConfig.customPlayer.params.videoUrl;
  1427. audioUrlParamInput.value = currentConfig.customPlayer.params.audioUrl;
  1428. subtitleUrlParamInput.value = currentConfig.customPlayer.params.subtitleUrl;
  1429. titleParamInput.value = currentConfig.customPlayer.params.title;
  1430. startTimeParamInput.value = currentConfig.customPlayer.params.startTime;
  1431. proxyParamInput.value = currentConfig.customPlayer.params.proxy;
  1432. refererParamInput.value = currentConfig.customPlayer.params.referer;
  1433. originParamInput.value = currentConfig.customPlayer.params.origin;
  1434. settingTable.style.display = "none";
  1435. customPlayerTable.style.display = "flex";
  1436. customPlayerButton.style.backgroundImage = ICON_BASE64.back;
  1437. customPlayerButton.dataset.tip = "保存并返回";
  1438. }
  1439. }
  1440. }
  1441. // 切换元素状态
  1442. function switchStatus(element, flag) {
  1443. if (flag) {
  1444. element.readOnly = false;
  1445. element.disabled = false;
  1446. element.classList.remove(CLASS.readOnly);
  1447. } else {
  1448. element.readOnly = true;
  1449. element.disabled = true;
  1450. element.classList.add(CLASS.readOnly);
  1451. }
  1452. }
  1453. function sleep(ms) {
  1454. return new Promise(resolve => setTimeout(resolve, ms));
  1455. }
  1456. // 加载配置
  1457. function loadConfig() {
  1458. let oldConifg = GM_getValue(KEY.config);
  1459. currentConfig = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
  1460. currentConfig.mpv.regVersion = "";
  1461. currentConfig.potplayer.regVersion = "";
  1462. if (!oldConifg) {
  1463. GM_setValue(KEY.config, currentConfig);
  1464. } else {
  1465. if (oldConifg.version != DEFAULT_CONFIG.version) {
  1466. for (const key in oldConifg) {
  1467. currentConfig[key] = oldConifg[key];
  1468. }
  1469. if (!currentConfig.mpv.path && currentConfig.mpvPath) {
  1470. currentConfig.mpv.path = currentConfig.mpvPath;
  1471. delete currentConfig['mpvPath'];
  1472. }
  1473. currentConfig.version = DEFAULT_CONFIG.version;
  1474. GM_setValue(KEY.config, currentConfig);
  1475. } else {
  1476. currentConfig = oldConifg;
  1477. GM_setValue(KEY.config, currentConfig);
  1478. }
  1479. }
  1480. PLAYER.custom = currentConfig.customPlayer;
  1481. }
  1482. class Media {
  1483. constructor() {
  1484. this.title = "";
  1485. this.videoUrl = "";
  1486. this.audioUrl = "";
  1487. this.subtitleUrl = "";
  1488. this.startTime = "";
  1489. this.referer = "";
  1490. this.origin = "";
  1491. this.proxy = "";
  1492. this.other = "";
  1493. }
  1494. setTitle(title) {
  1495. this.title = title;
  1496. }
  1497. setVideoUrl(videoUrl) {
  1498. if (this.check(videoUrl)) {
  1499. this.videoUrl = videoUrl;
  1500. if (!this.title) {
  1501. this.setTitle(document.title);
  1502. }
  1503. let nxParserIframe = document.getElementById(ID.nxParserIframe);
  1504. if (nxParserIframe) {
  1505. document.body.removeChild(nxParserIframe);
  1506. }
  1507. if (document.getElementById(ID.buttonDiv)) {
  1508. document.getElementById(ID.buttonDiv).style.display = "flex";
  1509. if (currentConfig.playAuto == 1) {
  1510. document.getElementById(ID.playButton).click();
  1511. }
  1512. document.getElementById(ID.infoButton).style.visibility = "visible";
  1513. document.getElementById(ID.settingButton).style.visibility = "visible";
  1514. document.getElementById(ID.playButton).style.visibility = "visible";
  1515. setTimeout(() => {
  1516. document.getElementById(ID.infoButton).style.visibility = "hidden";
  1517. document.getElementById(ID.settingButton).style.visibility = "hidden";
  1518. document.getElementById(ID.playButton).style.visibility = "hidden";
  1519. }, TIME.showButton);
  1520. }
  1521. }
  1522. }
  1523. setAudioUrl(audioUrl) {
  1524. this.audioUrl = audioUrl;
  1525. }
  1526. setSubtitleUrl(subtitleUrl) {
  1527. this.subtitleUrl = subtitleUrl;
  1528. }
  1529. setStartTime(startTime) {
  1530. this.startTime = Math.floor(startTime);
  1531. }
  1532. setReferer(referer) {
  1533. this.referer = referer;
  1534. }
  1535. setOrigin(origin) {
  1536. this.origin = origin;
  1537. }
  1538. setProxy(proxy) {
  1539. this.proxy = proxy;
  1540. }
  1541. setOther(other) {
  1542. this.other = other;
  1543. }
  1544. // 检查视频链接是否有效
  1545. check(videoUrl) {
  1546. if (videoUrl && videoUrl.startsWith("http")) {
  1547. if (videoUrl.match(/(\.m3u|\.m3u8)/g) && videoUrl != localStorage.iptvUrl) {
  1548. let m3u8 = "";
  1549. $.ajax({
  1550. type: "GET",
  1551. url: videoUrl,
  1552. async: false,
  1553. success: function (res) {
  1554. m3u8 = res;
  1555. }
  1556. });
  1557. if (m3u8 && m3u8.indexOf("png") != -1) {
  1558. console.log("Play-With-MPV:m3u8 链接无法播放:" + videoUrl);
  1559. return false;
  1560. }
  1561. }
  1562. if (videoUrl.startsWith("https://www.mp4")) {
  1563. return false;
  1564. }
  1565. return true;
  1566. }
  1567. console.log(`Play-With-MPV:链接无效:${videoUrl}`);
  1568. return false;
  1569. }
  1570. }
  1571. class BaseHandler {
  1572. constructor() {
  1573. loadConfig();
  1574. this.media = new Media();
  1575. this.media.setProxy(currentConfig.proxy);
  1576. for (const key in PLAYER) {
  1577. if (PLAYER[key].name == currentConfig.player) {
  1578. this.player = PLAYER[key];
  1579. break;
  1580. }
  1581. }
  1582. if (window.self == window.top) {
  1583. if (!document.getElementById(ID.buttonDiv)) {
  1584. console.log(INFO);
  1585. appendCSS();
  1586. appendHTML();
  1587. addListener();
  1588. }
  1589. document.getElementById(ID.buttonDiv).style.display = "none";
  1590. }
  1591. }
  1592. initCheck() {
  1593. return window.location.href != page.url;
  1594. }
  1595. async parse() { }
  1596. pause() {
  1597. let videos = document.getElementsByTagName("video");
  1598. if (videos && videos.length > 0) {
  1599. let i = 0;
  1600. while (i < TRY_TIME.maxPause) {
  1601. setTimeout(function () {
  1602. for (let video of videos) {
  1603. video.pause();
  1604. }
  1605. }, TIME.pauseInterval * i);
  1606. i++;
  1607. }
  1608. } else if (this.iframe) {
  1609. this.iframe.postMessage({ method: METHOD.pause }, "*");
  1610. }
  1611. }
  1612. play() {
  1613. let link = "";
  1614. for (let key in this.player.params) {
  1615. if (key == "title") {
  1616. continue;
  1617. }
  1618. if (key == "startTime") {
  1619. if (currentConfig.syncStartTime != 1) {
  1620. continue;
  1621. } else {
  1622. let videos = document.getElementsByTagName("video");
  1623. for (let i = 0; i < videos.length; i++) {
  1624. if (videos[i].currentTime != 0) {
  1625. this.media.setStartTime(videos[i].currentTime);
  1626. break;
  1627. }
  1628. }
  1629. }
  1630. }
  1631. let value = this.media[key];
  1632. if (value) {
  1633. let param = this.player.params[key];
  1634. let matchKey = '${' + key + '}';
  1635. while (param.indexOf(matchKey) != -1) {
  1636. param = param.replace(matchKey, value);
  1637. }
  1638. matchKey = '${E' + key + '}';
  1639. while (param.indexOf(matchKey) != -1) {
  1640. param = param.replace(matchKey, encodeURIComponent(value));
  1641. }
  1642. matchKey = '${D' + key + '}';
  1643. while (param.indexOf(matchKey) != -1) {
  1644. param = param.replace(matchKey, decodeURIComponent(value));
  1645. }
  1646. link = link + param;
  1647. }
  1648. }
  1649. if (this.media.title) {
  1650. let maxLength = 1950 - link.length;
  1651. let title = encodeURIComponent(this.media.title);
  1652. if (title.length > maxLength) {
  1653. title = title.substring(0, maxLength) + '...';
  1654. }
  1655. let param = this.player.params.title;
  1656. param = param.replace('${title}', title);
  1657. link = link + param;
  1658. }
  1659. window.open(link, "_self");
  1660. }
  1661. // 监听子页面事件
  1662. addIframeListener() {
  1663. let that = this;
  1664. window.addEventListener("message", function (event) {
  1665. that.iframe = event.source;
  1666. if (event.data.method == METHOD.pause) {
  1667. that.pause();
  1668. } else if (event.data.method == METHOD.report) {
  1669. that.media.setStartTime(event.data.media.startTime);
  1670. if (!that.media.videoUrl) {
  1671. that.media.setVideoUrl(event.data.media.videoUrl);
  1672. }
  1673. }
  1674. }, false);
  1675. }
  1676. // 监听顶层页面事件
  1677. addTopListener() {
  1678. let that = this;
  1679. window.addEventListener("message", function (event) {
  1680. if (event.data.method == METHOD.pause) {
  1681. that.pause();
  1682. }
  1683. }, false);
  1684. // 定时上报当前视频信息
  1685. setInterval(() => {
  1686. let video = document.getElementsByTagName("video")[0];
  1687. if (video) {
  1688. this.media.setStartTime(video.currentTime);
  1689. }
  1690. window.top.postMessage({ method: METHOD.report, media: that.media }, "*");
  1691. }, TIME.reportInterval);
  1692. }
  1693. // yt-dlp 支持网站解析器
  1694. ytDlpParser() {
  1695. return page.url;
  1696. }
  1697. // video 元素解析器
  1698. videoParser() {
  1699. let videos = document.getElementsByTagName("video");
  1700. for (let video of videos) {
  1701. let url = video.src;
  1702. if (url && url.startsWith("http")) {
  1703. return url;
  1704. }
  1705. }
  1706. }
  1707. // iframe 元素解析器
  1708. iframeParser() {
  1709. let iframes = document.getElementsByTagName("iframe");
  1710. for (let iframe of iframes) {
  1711. let urls = iframe.src.match(VIDEO_URL_REGEX);
  1712. if (urls && urls.length > 0) {
  1713. return urls[0];
  1714. }
  1715. }
  1716. }
  1717. // html 解析器
  1718. htmlParser() {
  1719. let urls = document.body.innerHTML.match(VIDEO_URL_REGEX);
  1720. if (urls && urls.length > 0) {
  1721. return urls[0];
  1722. }
  1723. }
  1724. // script 解析器
  1725. scriptParser() {
  1726. for (let script of document.scripts) {
  1727. let urls = script.innerHTML.match(VIDEO_URL_REGEX);
  1728. if (urls && urls.length > 0) {
  1729. return urls[0];
  1730. }
  1731. }
  1732. }
  1733. // url 解析器
  1734. urlParser() {
  1735. let urls = page.url.match(VIDEO_URL_REGEX);
  1736. if (urls && urls.length > 0) {
  1737. return urls[0];
  1738. }
  1739. }
  1740. // 诺讯解析
  1741. nxParser() {
  1742. handler.addIframeListener();
  1743. let nxParserIframe = document.getElementById(ID.nxParserIframe);
  1744. if (!nxParserIframe) {
  1745. nxParserIframe = document.createElement("iframe");
  1746. nxParserIframe.id = ID.nxParserIframe;
  1747. nxParserIframe.src = `https://yun.nxflv.com/?url=${page.url}`;
  1748. document.body.appendChild(nxParserIframe);
  1749. }
  1750. }
  1751. }
  1752. // 获取B站视频播放链接
  1753. async function getBilibiliPlayUrl(avid, cid) {
  1754. if (handler.player.name == PLAYER.mpv.name) {
  1755. handler.media.setOther(`--script-opts="cid=${cid}"`);
  1756. }
  1757. if (!handler.player.params.audioUrl || await getBilibiliVideoDash(avid, cid) == -1) {
  1758. getBilibiliVideoDurl(avid, cid);
  1759. }
  1760. if (currentConfig.subtitlePrefer != "off") {
  1761. getBilibiliVideoSubtitle(avid, cid);
  1762. }
  1763. }
  1764. // 获取B站 DASH 格式视频
  1765. async function getBilibiliVideoDash(avid, cid) {
  1766. let result = 0;
  1767. await $.ajax({
  1768. type: "GET",
  1769. url: `https://api.bilibili.com/x/player/playurl?qn=120&otype=json&fourk=1&fnver=0&fnval=4048&avid=${avid}&cid=${cid}`,
  1770. xhrFields: {
  1771. withCredentials: true
  1772. },
  1773. async: false,
  1774. success: function (res) {
  1775. if (!res.data) {
  1776. toast("Play-With-MPV 获取视频失败,如未登录请先登录并刷新页面", TOAST_TYPE.error);
  1777. tryTime = TRY_TIME.maxParse;
  1778. return;
  1779. }
  1780. let videoUrl = undefined;
  1781. let audioUrl = undefined;
  1782. let dash = res.data.dash;
  1783. if (!dash) {
  1784. result = -1;
  1785. return;
  1786. }
  1787. let hiRes = dash.flac;
  1788. let dolby = dash.dolby;
  1789. if (hiRes && hiRes.audio) {
  1790. audioUrl = hiRes.audio.baseUrl;
  1791. } else if (dolby && dolby.audio) {
  1792. audioUrl = dolby.audio[0].base_url;
  1793. } else if (dash.audio) {
  1794. audioUrl = dash.audio[0].baseUrl;
  1795. }
  1796. let i = 0;
  1797. while (i < dash.video.length && dash.video[i].id > BEST_QUALITY.bilibili[currentConfig.bestQuality]) {
  1798. i++;
  1799. }
  1800. videoUrl = dash.video[i].baseUrl;
  1801. let id = dash.video[i].id;
  1802. while (i < dash.video.length) {
  1803. if (dash.video[i].id != id) {
  1804. break;
  1805. }
  1806. if (dash.video[i].codecid == currentConfig.bilibiliCodecs) {
  1807. videoUrl = dash.video[i].baseUrl;
  1808. break;
  1809. }
  1810. i++;
  1811. }
  1812. handler.media.setAudioUrl(audioUrl);
  1813. handler.media.setVideoUrl(videoUrl);
  1814. result = 1;
  1815. }
  1816. });
  1817. return result;
  1818. }
  1819. // 获取B站 FLV / MP4 格式视频
  1820. function getBilibiliVideoDurl(avid, cid) {
  1821. $.ajax({
  1822. type: "GET",
  1823. url: `https://api.bilibili.com/x/player/playurl?qn=120&otype=json&fourk=1&fnver=0&fnval=128&avid=${avid}&cid=${cid}`,
  1824. xhrFields: {
  1825. withCredentials: true
  1826. },
  1827. async: false,
  1828. success: function (res) {
  1829. if (!res.data) {
  1830. toast("Play-With-MPV 获取视频失败,如未登录请先登录并刷新页面", TOAST_TYPE.error);
  1831. tryTime = TRY_TIME.maxParse;
  1832. return;
  1833. }
  1834. handler.media.setVideoUrl(res.data.durl[0].url);
  1835. }
  1836. });
  1837. }
  1838. // 获取B站视频字幕
  1839. function getBilibiliVideoSubtitle(avid, cid) {
  1840. $.ajax({
  1841. type: "GET",
  1842. url: `https://api.bilibili.com/x/player/v2?aid=${avid}&cid=${cid}`,
  1843. xhrFields: {
  1844. withCredentials: true
  1845. },
  1846. async: false,
  1847. success: function (res) {
  1848. if (res.code == 0 && res.data.subtitle && res.data.subtitle.subtitles.length > 0) {
  1849. let subtitles = res.data.subtitle.subtitles;
  1850. let url = "https:" + subtitles[0].subtitle_url;
  1851. let lan = subtitles[0].lan;
  1852. for (const subtitle of subtitles) {
  1853. if (currentConfig.subtitlePrefer.startsWith("zh") && subtitle.lan.startsWith("zh")) {
  1854. url = "https:" + subtitle.subtitle_url;
  1855. lan = subtitle.lan;
  1856. }
  1857. if (subtitle.lan == currentConfig.subtitlePrefer) {
  1858. url = "https:" + subtitle.subtitle_url;
  1859. lan = subtitle.lan;
  1860. break;
  1861. }
  1862. }
  1863. handler.media.setSubtitleUrl(`https://www.lckp.top/common/bilibili/jsonToSrt/?url=${url}&lan=${lan}`);
  1864. }
  1865. }
  1866. });
  1867. }
  1868. // 获取B站视频 aid 和 cid
  1869. function getBilibiliVideoId() {
  1870. let hasInitialState = false;
  1871. try {
  1872. if (__INITIAL_STATE__) {
  1873. hasInitialState = true;
  1874. }
  1875. } catch (error) {
  1876. hasInitialState = false;
  1877. }
  1878. if (!hasInitialState) {
  1879. return undefined;
  1880. }
  1881. let video = undefined;
  1882. if (__INITIAL_STATE__.epInfo) {
  1883. video = __INITIAL_STATE__.epInfo;
  1884. } else if (__INITIAL_STATE__.videoData) {
  1885. video = __INITIAL_STATE__.videoData;
  1886. } else if (__INITIAL_STATE__.videoInfo) {
  1887. video = __INITIAL_STATE__.videoInfo;
  1888. }
  1889. let aid = video.aid;
  1890. let cid = video.cid;
  1891. let p = __INITIAL_STATE__.p;
  1892. if (p && p > 1) {
  1893. cid = __INITIAL_STATE__.cidMap[aid].cids[p];
  1894. }
  1895. let videoId = {
  1896. aid: aid,
  1897. cid: cid
  1898. };
  1899. return videoId;
  1900. }
  1901. const BEST_QUALITY = {
  1902. bilibili: {
  1903. "unlimited": 127,
  1904. "2160p": 126,
  1905. "1440p": 116,
  1906. "1080p": 116,
  1907. "720p": 74,
  1908. "480p": 32
  1909. },
  1910. bilibiliLive: {
  1911. "unlimited": 4,
  1912. "2160p": 4,
  1913. "1440p": 4,
  1914. "1080p": 4,
  1915. "720p": 3,
  1916. "480p": 2
  1917. },
  1918. ytdlp: {
  1919. "unlimited": "",
  1920. "2160p": "--ytdl-format=bestvideo[height<=?2160]%2Bbestaudio/best",
  1921. "1440p": "--ytdl-format=bestvideo[height<=?1440]%2Bbestaudio/best",
  1922. "1080p": "--ytdl-format=bestvideo[height<=?1080]%2Bbestaudio/best",
  1923. "720p": "--ytdl-format=bestvideo[height<=?720]%2Bbestaudio/best",
  1924. "480p": "--ytdl-format=bestvideo[height<=?480]%2Bbestaudio/best"
  1925. }
  1926. }
  1927. var websiteList = [
  1928. {
  1929. // ✅ https://www.bilibili.com/bangumi/play/ep508404
  1930. // ✅ https://www.bilibili.com/bangumi/play/ep319063
  1931. name: "B站影视",
  1932. home: [
  1933. "https://www.bilibili.com"
  1934. ],
  1935. regex: /^https:\/\/www\.bilibili\.com\/bangumi\/play\/.*/g,
  1936. handler: class Handler extends BaseHandler {
  1937. constructor() {
  1938. super();
  1939. this.media.setReferer("https://www.bilibili.com");
  1940. }
  1941. async parse() {
  1942. // 直接从数据中获取 aid 和 cid
  1943. // let videoId = getBilibiliVideoId();
  1944. // if (videoId && videoId.aid && videoId.cid) {
  1945. // getBilibiliPlayUrl(videoId.aid, videoId.cid);
  1946. // return;
  1947. // }
  1948.  
  1949. // 从元素提取 epid 请求接口获取 aid 和 cid
  1950. let epid = page.url.match(/ep(\d+)/);
  1951. if (epid && epid[1]) {
  1952. epid = epid[1];
  1953. } else {
  1954. let epidElement = document.getElementsByClassName("ep-item cursor visited")[0];
  1955. if (!epidElement) {
  1956. epidElement = document.getElementsByClassName('ep-item cursor')[0];
  1957. }
  1958. if (epidElement) {
  1959. epid = epidElement.getElementsByTagName('a')[0].href.match(/ep(\d+)/)[1];
  1960. } else {
  1961. epidElement = document.getElementsByClassName("squirtle-pagelist-select-item active squirtle-blink")[0];
  1962. if (epidElement) {
  1963. epid = epidElement.dataset.value;
  1964. }
  1965. }
  1966. }
  1967. if (!epid) {
  1968. return;
  1969. }
  1970. $.ajax({
  1971. type: "GET",
  1972. url: `https://api.bilibili.com/pgc/view/web/season?ep_id=${epid}`,
  1973. xhrFields: {
  1974. withCredentials: true
  1975. },
  1976. async: false,
  1977. success: function (res) {
  1978. let currentEpisode;
  1979. let section = res.result.section;
  1980. if (!section) {
  1981. section = new Array();
  1982. }
  1983. section.push({ episodes: res.result.episodes });
  1984. for (let i = section.length - 1; i >= 0; i--) {
  1985. let episodes = section[i].episodes;
  1986. for (const episode of episodes) {
  1987. if (episode.id == epid) {
  1988. currentEpisode = episode;
  1989. break;
  1990. }
  1991. }
  1992. if (currentEpisode) {
  1993. break;
  1994. }
  1995. }
  1996. getBilibiliPlayUrl(currentEpisode.aid, currentEpisode.cid);
  1997. }
  1998. })
  1999. }
  2000. },
  2001. },
  2002. {
  2003. // ✅ https://www.bilibili.com/video/BV1Hd4y1k7Vb
  2004. // ✅ https://www.bilibili.com/video/av2
  2005. // ✅ https://www.bilibili.com/video/BV17Z4y117Qm
  2006. // ✅ https://www.bilibili.com/list/ml1806211634?oid=822115390&bvid=BV1Fg4y1p7Qe
  2007. name: "B站投稿",
  2008. regex: /^https:\/\/www\.bilibili\.com\/(video\/|list.*)(BV|av).*/g,
  2009. handler: class Handler extends BaseHandler {
  2010. constructor() {
  2011. super();
  2012. this.media.setReferer("https://www.bilibili.com");
  2013. }
  2014. initCheck() {
  2015. if (super.initCheck()) {
  2016. let newPageUrl = window.location.href;
  2017. let oldPageUrl = page.url;
  2018. let regex = /(&|\?)vd_source=\w+/;
  2019. if (regex.test(newPageUrl.replace(oldPageUrl, ""))) {
  2020. page.url = newPageUrl;
  2021. return false;
  2022. }
  2023. return true;
  2024. }
  2025. return false;
  2026. }
  2027. async parse() {
  2028. // 直接从数据中获取 aid 和 cid
  2029. // let videoId = getBilibiliVideoId();
  2030. // if (videoId && videoId.aid && videoId.cid) {
  2031. // getBilibiliPlayUrl(videoId.aid, videoId.cid);
  2032. // return;
  2033. // }
  2034.  
  2035. // 通过 bvid/avid 请求接口获取 aid 和 cid
  2036. let param = undefined;
  2037. let bvid = page.url.match(/BV([0-9a-zA-Z]+)/);
  2038. if (bvid && bvid[1]) {
  2039. param = `bvid=${bvid[1]}`;
  2040. } else {
  2041. let avid = page.url.match(/av([0-9]+)/);
  2042. if (avid && avid[1]) {
  2043. param = `aid=${avid[1]}`;
  2044. }
  2045. }
  2046. if (!param) {
  2047. return;
  2048. }
  2049. $.ajax({
  2050. type: "GET",
  2051. url: `https://api.bilibili.com/x/web-interface/view?${param}`,
  2052. xhrFields: {
  2053. withCredentials: true
  2054. },
  2055. async: false,
  2056. success: function (res) {
  2057. let aid = res.data.aid;
  2058. let cid = res.data.cid;
  2059. let index = page.url.indexOf("?p=");
  2060. if (index != -1 && res.data.pages.length > 1) {
  2061. let p = page.url.substring(index + 3);
  2062. let endIndex = p.indexOf("&");
  2063. if (endIndex != -1) {
  2064. p = p.substring(0, endIndex);
  2065. }
  2066. cid = res.data.pages[p - 1].cid;
  2067. }
  2068. getBilibiliPlayUrl(aid, cid);
  2069. }
  2070. })
  2071. }
  2072. },
  2073. },
  2074. {
  2075. // ✅ https://www.bilibili.com/festival/2023bnj?bvid=BV17G4y1X7vQ
  2076. name: "B站节日",
  2077. regex: /^https:\/\/www\.bilibili\.com\/festival\/.*/g,
  2078. handler: class Handler extends BaseHandler {
  2079. constructor() {
  2080. super();
  2081. this.media.setReferer("https://www.bilibili.com");
  2082. }
  2083. initCheck() {
  2084. if (super.initCheck()) {
  2085. return true;
  2086. }
  2087. let oldvideoId = this.videoId;
  2088. let newvideoId = getBilibiliVideoId();
  2089. if (oldvideoId && oldvideoId.cid != newvideoId.cid) {
  2090. return true;
  2091. }
  2092. return false;
  2093. }
  2094. async parse() {
  2095. let videoId = getBilibiliVideoId();
  2096. if (videoId && videoId.aid && videoId.cid) {
  2097. this.videoId = videoId;
  2098. getBilibiliPlayUrl(videoId.aid, videoId.cid);
  2099. return;
  2100. } else {
  2101. toast("Play-With-MPV 读取视频数据失败,请尝试清理B站缓存后刷新重试", TOAST_TYPE.error, 5000);
  2102. tryTime = TRY_TIME.maxParse;
  2103. }
  2104. }
  2105. },
  2106. },
  2107. {
  2108. // ✅ https://live.bilibili.com/7777
  2109. name: "B站直播",
  2110. home: [
  2111. "https://live.bilibili.com"
  2112. ],
  2113. regex: /^https:\/\/live\.bilibili\.com\/\d+.*/g,
  2114. handler: class Handler extends BaseHandler {
  2115. async parse() {
  2116. let iframes = document.getElementsByTagName("iframe");
  2117. let roomid = undefined;
  2118. for (let iframe of iframes) {
  2119. let roomids = iframe.src.match(/^https:\/\/live\.bilibili\.com.*(roomid=\d+|blanc\/\d+).*/);
  2120. if (roomids && roomids[1]) {
  2121. roomid = roomids[1].match(/\d+/)[0];
  2122. break;
  2123. }
  2124. }
  2125. if (!roomid) {
  2126. console.log("Play-With-MPV:找不到 roomid:" + roomid);
  2127. return;
  2128. }
  2129.  
  2130. let that = this;
  2131. $.ajax({
  2132. type: "GET",
  2133. url: `https://api.live.bilibili.com/room/v1/Room/playUrl?quality=${BEST_QUALITY.bilibiliLive[currentConfig.bestQuality]}&cid=${roomid}`,
  2134. async: false,
  2135. xhrFields: {
  2136. withCredentials: true
  2137. },
  2138. success: function (res) {
  2139. that.media.setVideoUrl(res.data.durl[0].url);
  2140. }
  2141. });
  2142. }
  2143. },
  2144. },
  2145. {
  2146. // ✅ https://www.ixigua.com/
  2147. name: "西瓜视频",
  2148. home: [
  2149. "https://www.ixigua.com"
  2150. ],
  2151. regex: /^https:\/\/www\.ixigua\.com\/\d.*/g,
  2152. handler: class Handler extends BaseHandler {
  2153. constructor() {
  2154. super();
  2155. this.media.setReferer("https://www.ixigua.com/");
  2156. }
  2157. async parse() {
  2158. let that = this;
  2159. $.ajax({
  2160. type: "GET",
  2161. url: page.url,
  2162. async: false,
  2163. success: function (res) {
  2164. try {
  2165. let _SSR_HYDRATED_DATA = (new Function("return " + res.match(/<script id="SSR_HYDRATED_DATA"[^<]*window._SSR_HYDRATED_DATA=({[^<]*})[^<]*<\/script>/)[1]))();
  2166. let packerData = _SSR_HYDRATED_DATA.anyVideo.gidInformation.packerData;
  2167. let main_url = undefined;
  2168. if (packerData.video) {
  2169. let videoList = packerData.video.videoResource.normal.video_list;
  2170. if (videoList) {
  2171. let video = undefined;
  2172. for (const key in videoList) {
  2173. if (!video || videoList[key].vheight > video.vheight) {
  2174. video = videoList[key];
  2175. }
  2176. }
  2177. main_url = video.main_url;
  2178. }
  2179. that.media.setVideoUrl(window.atob(main_url).replace("http://", "https://"));
  2180. return;
  2181. }
  2182. } catch (error) {
  2183. console.error("解析出错:" + error);
  2184. }
  2185. that.nxParser();
  2186. }
  2187. });
  2188. }
  2189. },
  2190. },
  2191. {
  2192. // ✅ https://yun.nxflv.com/?url=https://www.ixigua.com/7186534626612118071
  2193. name: "诺讯解析",
  2194. regex: /^https:\/\/yun\.nxflv\.com\/\?url=.+/g,
  2195. handler: class Handler extends BaseHandler {
  2196. constructor() {
  2197. super();
  2198. this.addTopListener();
  2199. }
  2200. async parse() {
  2201. let url = this.videoParser();
  2202. if (url.startsWith("blob")) {
  2203. for (let index = 0; index < sessionStorage.key.length; index++) {
  2204. url = sessionStorage.key(index);
  2205. url = url.match(/http[^#]*/g);
  2206. if (url && url.length > 0) {
  2207. url = url[0];
  2208. }
  2209. }
  2210. }
  2211. this.media.setVideoUrl(url);
  2212. }
  2213. },
  2214. },
  2215. {
  2216. // ✅ https://ddys.art/bleach-thousand-year-blood-war
  2217. name: "低端影视",
  2218. home: [
  2219. "https://ddys.art",
  2220. "https://ddys.pro"
  2221. ],
  2222. regex: /^https:\/\/(ddys\.art|ddys\.pro)\/.*/g,
  2223. handler: class Handler extends BaseHandler {
  2224. async parse() {
  2225. let video = document.getElementsByTagName("video")[0];
  2226. if (video.paused) {
  2227. document.getElementsByClassName('vjs-big-play-button')[0].click();
  2228. }
  2229. let url = this.videoParser();
  2230. if (url) {
  2231. let index = url.indexOf("?");
  2232. if (index != -1) {
  2233. url = url.substring(0, index + 1) + encodeURIComponent(url.substring(index + 1));
  2234. }
  2235. this.media.setVideoUrl(url);
  2236. let playing = document.getElementsByClassName("wp-playlist-playing")[0];
  2237. if (playing) {
  2238. let episode = playing.textContent.replace(/(\n|\t|\d\.)/g, "");
  2239. if (episode != " 全") {
  2240. this.media.title = document.getElementsByClassName("post-title")[0].textContent + episode + " - 低端影视";
  2241. }
  2242. }
  2243. }
  2244. }
  2245. },
  2246. },
  2247. {
  2248. // ✅ https://www.libvio.cc/play/714634-1-11.html
  2249. name: "LIBVIO",
  2250. home: [
  2251. "https://www.libvio.cc",
  2252. "https://libvio.fun",
  2253. "https://libvio.me",
  2254. "https://www.libvio.me"
  2255. ],
  2256. regex: /^https?:\/\/.*\.libvio\..*\/play.*/g,
  2257. handler: class Handler extends BaseHandler {
  2258. constructor() {
  2259. super();
  2260. this.addIframeListener();
  2261. }
  2262. }
  2263. },
  2264. {
  2265. name: "LIBVIO播放器",
  2266. regex: /^https:\/\/(.*\.chinaeast2\.cloudapp\.chinacloudapi\.cn|.*\.cfnode1\.xyz)\/.*\?url=.*/g,
  2267. handler: class Handler extends BaseHandler {
  2268. constructor() {
  2269. super();
  2270. this.addTopListener();
  2271. }
  2272. async parse() {
  2273. let url = urls;
  2274. let index = url.indexOf("?");
  2275. if (index != -1) {
  2276. url = url.substring(0, index + 1) + encodeURIComponent(url.substring(index + 1));
  2277. }
  2278. this.media.setVideoUrl(url);
  2279. }
  2280. }
  2281. },
  2282. {
  2283. // ✅ https://www.nivod.tv/UXEwMmLqnUjHG5e4MwmlvmVnWiAJ9rIQ-RofV7wPhhed3uoi50mYsftLPq4mYyIhB-720-0-0-play.html?x=1
  2284. name: "泥视频",
  2285. home: [
  2286. "https://www.nivod.tv",
  2287. ],
  2288. regex: /^https:\/\/www\.nivod\.tv\/.*play\.html?.*/g,
  2289. handler: class Handler extends BaseHandler {
  2290. async parse() {
  2291. this.media.setVideoUrl(__dp.options.video.url);
  2292. this.media.setTitle(document.title);
  2293. }
  2294. }
  2295. },
  2296. {
  2297. // ✅ https://www.pkmkv.com/py/268677-2-11.html
  2298. name: "片库",
  2299. home: [
  2300. "https://www.pkmkv.com",
  2301. ],
  2302. regex: /^https:\/\/www\.pkmkv\.com\/py\/.*/g,
  2303. handler: class Handler extends BaseHandler {
  2304. constructor() {
  2305. super();
  2306. this.addIframeListener();
  2307. }
  2308. async parse() {
  2309. this.media.setVideoUrl(player_aaaa.url);
  2310. }
  2311. }
  2312. },
  2313. {
  2314. name: "片库播放器",
  2315. regex: /^https:\/\/www\.pkmkv\.com\/addons\/dplayer\/\?url=.*/g,
  2316. handler: class Handler extends BaseHandler {
  2317. constructor() {
  2318. super();
  2319. this.addTopListener();
  2320. }
  2321. }
  2322. },
  2323. {
  2324. // ✅ https://www.btnull.org/py/BBnLd_9.html?167094
  2325. name: "无名小站",
  2326. home: [
  2327. "https://www.btnull.org",
  2328. "https://www.btnull.to",
  2329. "https://www.btnull.nu",
  2330. "https://www.btnull.in",
  2331. ],
  2332. regex: /^https:\/\/(www.btnull.org|www.btnull.to|www.btnull.nu|www.btnull.in)\/py\/.*/g,
  2333. handler: class Handler extends BaseHandler {
  2334. async parse() {
  2335. this.media.setVideoUrl(this.htmlParser());
  2336. }
  2337. }
  2338. },
  2339. {
  2340. // ✅ https://www.926dm.com/play/6792-1-91.html
  2341. // ✅ http://www.926dm.com/play/7800-1-10.html
  2342. // ✅ http://www.dmlaa.com/play/7696-1-10.html
  2343. // ✅ http://www.qdmsh.com/play/7663-1-10.html
  2344. // ✅ http://www.ntdm8.com/play/4973-1-1.html
  2345. name: "樱花动漫网",
  2346. home: [
  2347. "https://www.926dm.com",
  2348. "http://www.926dm.com",
  2349. "http://www.dmlaa.com",
  2350. "http://www.qdmsh.com",
  2351. "http://www.ntdm8.com"
  2352. ],
  2353. regex: /^https?:\/\/www\.(926dm|dmlaa|qdmsh|ntdm8)\.com\/play\/.*/g,
  2354. handler: class Handler extends BaseHandler {
  2355. constructor() {
  2356. super();
  2357. this.addIframeListener();
  2358. }
  2359. }
  2360. },
  2361. {
  2362. name: "樱花动漫网播放器",
  2363. regex: /^https:\/\/danmu\.yhdmjx\.com\/.*php\?url=.*/g,
  2364. handler: class Handler extends BaseHandler {
  2365. constructor() {
  2366. super();
  2367. this.addTopListener();
  2368. }
  2369. async parse() {
  2370. this.media.setVideoUrl(this.videoParser());
  2371. }
  2372. }
  2373. },
  2374. {
  2375. // ✅ https://dick.xfani.com/watch/582/1/11.html
  2376. name: "稀饭动漫",
  2377. home: [
  2378. "https://dick.xfani.com"
  2379. ],
  2380. regex: /^https:\/\/dick\.xfani\.com\/watch\/.*/g,
  2381. handler: class Handler extends BaseHandler {
  2382. constructor() {
  2383. super();
  2384. this.addIframeListener();
  2385. }
  2386. }
  2387. },
  2388. {
  2389. name: "稀饭动漫播放器",
  2390. regex: /(^https:\/\/dick\.xfani\.com\/addons\/dp\/player\/.*|^https:\/\/m3\.moedot\.net\/muiplayer\/\?url=.*)/g,
  2391. handler: class Handler extends BaseHandler {
  2392. constructor() {
  2393. super();
  2394. this.addTopListener();
  2395. }
  2396. async parse() {
  2397. if (config.url.indexOf(".m3u8") > 0 || config.url.indexOf(".mp4") > 0 || config.url.indexOf(".flv") > 0) {
  2398. this.media.setVideoUrl(config.url);
  2399. } else {
  2400. let that = this;
  2401. $.ajax({
  2402. type: "POST",
  2403. url: "api_currentConfig.php",
  2404. data: { "url": config.url, "time": config.time, "key": config.key, "title": config.title },
  2405. async: false,
  2406. success: function (res) {
  2407. if (res.code == "200") {
  2408. that.media.setVideoUrl(res.url);
  2409. }
  2410. }
  2411. });
  2412. }
  2413. }
  2414. }
  2415. },
  2416. {
  2417. name: "稀饭动漫播放器",
  2418. regex: /^https:\/\/player\.moedot\.net\/player\/.*/g,
  2419. handler: class Handler extends BaseHandler {
  2420. constructor() {
  2421. super();
  2422. this.addTopListener();
  2423. }
  2424. async parse() {
  2425. this.media.setVideoUrl(this.urlParser());
  2426. }
  2427. }
  2428. },
  2429. {
  2430. // ✅ https://www.mgnacg.com/bangumi/426-6-12
  2431. name: "橘子动漫",
  2432. home: [
  2433. "https://www.mgnacg.com"
  2434. ],
  2435. regex: /^https:\/\/www\.mgnacg\.com\/bangumi\/.*/g,
  2436. handler: class Handler extends BaseHandler {
  2437. constructor() {
  2438. super();
  2439. this.addIframeListener();
  2440. this.media.setReferer("https://play.mknacg.top:8585/");
  2441. }
  2442. }
  2443. },
  2444. {
  2445. name: "橘子动漫播放器",
  2446. regex: /^https:\/\/play\.mknacg\.top:8585\/.*/g,
  2447. handler: class Handler extends BaseHandler {
  2448. constructor() {
  2449. super();
  2450. this.addTopListener();
  2451. }
  2452. async parse() {
  2453. this.media.setVideoUrl(art.option.url);
  2454. }
  2455. }
  2456. },
  2457. {
  2458. // ✅ https://www.omofun.top/index.php/vod/play/id/17295/sid/2/nid/7.html
  2459. name: "OmoFun",
  2460. home: [
  2461. "https://www.omofun.top"
  2462. ],
  2463. regex: /^https:\/\/www\.omofun\.top\/index.php\/vod\/play\/id\/.*/g,
  2464. handler: class Handler extends BaseHandler {
  2465. constructor() {
  2466. super();
  2467. this.addIframeListener();
  2468. }
  2469. }
  2470. },
  2471. {
  2472. name: "OmoFun播放器",
  2473. regex: /^https:\/\/.*\.omofun\.top\/(player\/|)\?url=.*/g,
  2474. handler: class Handler extends BaseHandler {
  2475. constructor() {
  2476. super();
  2477. this.addTopListener();
  2478. }
  2479. async parse() {
  2480. this.media.setVideoUrl(this.urlParser());
  2481. }
  2482. }
  2483. },
  2484. {
  2485. // ✅ https://spdcat.net/vodplay/135443-1-23
  2486. name: "迅猫动漫",
  2487. home: [
  2488. "https://spdcat.net"
  2489. ],
  2490. regex: /^https:\/\/spdcat\.net\/vodplay\/.*/g,
  2491. handler: class Handler extends BaseHandler {
  2492. constructor() {
  2493. super();
  2494. this.addIframeListener();
  2495. }
  2496. }
  2497. },
  2498. {
  2499. name: "迅猫动漫播放器",
  2500. regex: /^https:\/\/spdcat\.net\/addons\/dp\/player\/.*/g,
  2501. handler: class Handler extends BaseHandler {
  2502. constructor() {
  2503. super();
  2504. this.addTopListener();
  2505. }
  2506. async parse() {
  2507. this.media.setVideoUrl(config.url);
  2508. setTimeout(() => {
  2509. let conplay = document.getElementsByClassName("conplay-jump")[0];
  2510. if (conplay) {
  2511. conplay.click();
  2512. }
  2513. }, 1000);
  2514. }
  2515. }
  2516. },
  2517. {
  2518. // ✅ http://www.dm88.me/player/8480-0-11.html
  2519. name: "樱花动漫",
  2520. home: [
  2521. "http://www.dm88.me"
  2522. ],
  2523. regex: /^http:\/\/www\.dm88\.me\/player\/.*/g,
  2524. handler: class Handler extends BaseHandler {
  2525. constructor() {
  2526. super();
  2527. this.addIframeListener();
  2528. }
  2529. }
  2530. },
  2531. {
  2532. name: "樱花动漫播放器",
  2533. regex: /^https:\/\/jianghu\.live2008\.com\/.*\?url=.*/g,
  2534. handler: class Handler extends BaseHandler {
  2535. constructor() {
  2536. super();
  2537. this.addTopListener();
  2538. }
  2539. async parse() {
  2540. this.media.setVideoUrl(url);
  2541. }
  2542. }
  2543. },
  2544. {
  2545. // ✅ https://www.kk151.com/play/15257-2-11.html
  2546. name: "动漫之家",
  2547. home: [
  2548. "https://www.kk151.com"
  2549. ],
  2550. regex: /^https:\/\/www\.kk151\.com\/play\/.*/g,
  2551. handler: class Handler extends BaseHandler {
  2552. constructor() {
  2553. super();
  2554. this.addIframeListener();
  2555. }
  2556. }
  2557. },
  2558. {
  2559. name: "动漫之家播放器",
  2560. regex: /^https:\/\/(www\.ikdmjx\.com\/|jx\.wolongzywcdn\.com:65\/m3u8\.php|hls\.kuaibofang\.com\/|jx\.jxbdzyw\.com\/m3u8\/|www\.m3u8\.tv\.cdn\.8old\.cn\/m3u8tv1127\/api\.php|jx\.wujinkk\.com\/dplayer\/|jx\.m3u8\.tv\/jiexi\/)\?url=.*/g,
  2561. handler: class Handler extends BaseHandler {
  2562. constructor() {
  2563. super();
  2564. this.addTopListener();
  2565. }
  2566. async parse() {
  2567. this.media.setVideoUrl(this.urlParser());
  2568. }
  2569. }
  2570. },
  2571. {
  2572. // ✅ https://hdzyk.com/?m=vod-play-id-27537-src-2-num-11.html
  2573. // ✅ https://1080zyk2.com/?m=vod-play-id-27537-src-2-num-11.html
  2574. name: "优质资源库",
  2575. home: [
  2576. "https://hdzyk.com",
  2577. "https://1080zyk1.com",
  2578. "https://1080zyk2.com",
  2579. "https://1080zyk3.com",
  2580. "https://1080zyk4.com",
  2581. "https://1080zyk5.com"
  2582. ],
  2583. regex: /^https:\/\/(hdzyk\.com|1080zyk[1-5]\.com)\/\?m=.*/g,
  2584. handler: class Handler extends BaseHandler {
  2585. constructor() {
  2586. super();
  2587. this.addIframeListener();
  2588. }
  2589. }
  2590. },
  2591. {
  2592. name: "优质资源库播放器",
  2593. regex: /^https:\/\/vip\.zykbf\.com\/\?url=.*/g,
  2594. handler: class Handler extends BaseHandler {
  2595. constructor() {
  2596. super();
  2597. this.addTopListener();
  2598. }
  2599. async parse() {
  2600. this.media.setVideoUrl(this.urlParser());
  2601. }
  2602. }
  2603. },
  2604. {
  2605. name: "优质资源库播放器",
  2606. regex: /^https:\/\/.*\.(yzzy-tv1|yzzy-tv-cdn)\.com\/.*/g,
  2607. handler: class Handler extends BaseHandler {
  2608. constructor() {
  2609. super();
  2610. this.addTopListener();
  2611. }
  2612. async parse() {
  2613. this.media.setVideoUrl("https://" + location.host + main);
  2614. }
  2615. }
  2616. },
  2617. {
  2618. // ✅ https://www.bdys10.com/play/22729-8.htm
  2619. name: "哔嘀影视",
  2620. home: [
  2621. "https://www.bdys10.com"
  2622. ],
  2623. regex: /^https:\/\/www\.bdys10\.com\/.*play\/.*/g,
  2624. handler: class Handler extends BaseHandler {
  2625. async parse() {
  2626. this.media.setVideoUrl(this.videoParser());
  2627. if (!this.media.videoUrl) {
  2628. this.media.setVideoUrl(this.htmlParser());
  2629. if (!this.media.videoUrl) {
  2630. this.media.setVideoUrl(this.scriptParser());
  2631. }
  2632. }
  2633. }
  2634. }
  2635. },
  2636. {
  2637. // ✅ https://www.dora-family.com/Resource:TV
  2638. name: "哆啦A梦新番",
  2639. home: [
  2640. "https://www.dora-family.com/Resource:TV"
  2641. ],
  2642. regex: /^https:\/\/www\.dora-family\.com\/Resource:TV/g,
  2643. handler: class Handler extends BaseHandler {
  2644. initCheck() {
  2645. if (super.initCheck()) {
  2646. return true;
  2647. }
  2648. let oldVideoUrl = this.media.videoUrl;
  2649. let newVideoUrl = this.videoParser();
  2650. if (oldVideoUrl && oldVideoUrl != newVideoUrl) {
  2651. return true;
  2652. }
  2653. return false;
  2654. }
  2655. async parse() {
  2656. this.media.setVideoUrl(this.videoParser());
  2657. }
  2658. }
  2659. },
  2660. {
  2661. // ✅ https://mypikpak.com/drive/all
  2662. name: "PikPak",
  2663. home: [
  2664. "https://mypikpak.com/drive/all"
  2665. ],
  2666. regex: /^https:\/\/mypikpak\.com\/drive\/.*/g,
  2667. handler: class Handler extends BaseHandler {
  2668. initCheck() {
  2669. if (super.initCheck()) {
  2670. return true;
  2671. }
  2672. let oldVideoUrl = this.media.videoUrl;
  2673. let newVideoUrl = this.videoParser();
  2674. if (oldVideoUrl && oldVideoUrl != newVideoUrl) {
  2675. return true;
  2676. }
  2677. return false;
  2678. }
  2679. async parse() {
  2680. while (document.getElementsByTagName("video").length == 0) {
  2681. await sleep(1000);
  2682. }
  2683. this.media.setVideoUrl(this.videoParser());
  2684. this.media.setTitle(document.getElementsByClassName("player-title")[0].textContent);
  2685. }
  2686. }
  2687. },
  2688. {
  2689. // ✅ https://www.olehdtv.com/player/vod/1/43671/1
  2690. name: "欧乐影院",
  2691. home: [
  2692. "https://www.olehdtv.com/"
  2693. ],
  2694. regex: /^https:\/\/www\.olehdtv\.com\/player\/vod\/\d+\/\d+\/\d+/g,
  2695. handler: class Handler extends BaseHandler {
  2696. async parse() {
  2697. let ids = page.url.match(/^https:\/\/www\.olehdtv\.com\/player\/vod\/(\d+)\/(\d+)\/(\d+)/);
  2698. let id = ids[2];
  2699. let index = ids[3];
  2700. let t = Date.parse(new Date) / 1e3, r = t % 20;
  2701. let _vv = MD5(t - r + "new.olelive.com");
  2702. let that = this;
  2703. $.ajax({
  2704. type: "GET",
  2705. url: `https://api.olelive.com/v1/pub/vod/detail?id=${id}&play=true&_vv=${_vv}`,
  2706. async: false,
  2707. success: function (res) {
  2708. if (res.code == 0) {
  2709. that.media.setVideoUrl(res.data.urls[index - 1].url);
  2710. }
  2711. }
  2712. })
  2713. }
  2714. play() {
  2715. this.media.setTitle(document.title);
  2716. super.play();
  2717. }
  2718. }
  2719. },
  2720. {
  2721. // ✅ https://www.olehdtv.com/player/live/tv/CCTV5HD/49
  2722. name: "欧乐影院直播",
  2723. home: [
  2724. "https://www.olehdtv.com/"
  2725. ],
  2726. regex: /^https:\/\/www\.olehdtv\.com\/player\/live\/tv\/[^/]+\/\d+/g,
  2727. handler: class Handler extends BaseHandler {
  2728. async parse() {
  2729. let ids = page.url.match(/^https:\/\/www\.olehdtv\.com\/player\/live\/tv\/([^/]+)\/(\d+)/);
  2730. let id = ids[2];
  2731. let streamId = ids[1];
  2732. let today = new Date();
  2733. let year = today.getFullYear();
  2734. let month = today.getMonth() + 1;
  2735. if (month < 10) {
  2736. month = "0" + month;
  2737. }
  2738. let day = today.getDate();
  2739. if (day < 10) {
  2740. day = "0" + day;
  2741. }
  2742. let date = year + "-" + month + "-" + day;
  2743. let t = Date.parse(today) / 1e3, r = t % 20;
  2744. let _vv = MD5(t - r + "new.olelive.com");
  2745. let that = this;
  2746. $.ajax({
  2747. type: "GET",
  2748. url: `https://api.olelive.com/v1/pub/live/info?date=${date}&streamId=${streamId}&type=tv&id=${id}&_vv=${_vv}`,
  2749. async: false,
  2750. success: function (res) {
  2751. if (res.code == 0) {
  2752. that.media.setVideoUrl(res.data.detail.hls.replace("_360", ""));
  2753. }
  2754. }
  2755. })
  2756. }
  2757. play() {
  2758. this.media.setTitle(document.title);
  2759. super.play();
  2760. }
  2761. }
  2762. },
  2763. {
  2764. // ✅ https://tkznp.com/vodplay/337990-1-2.html
  2765. name: "天空影视",
  2766. home: [
  2767. "https://tkznp.com/",
  2768. "https://www.tkznp1.com/",
  2769. "https://www.tkznp2.com/",
  2770. "https://www.tkznp3.com/",
  2771. "https://www.tkznp4.com/",
  2772. "https://www.tkznp5.com/",
  2773. "https://www.tkznp6.com/",
  2774. ],
  2775. regex: /^https?:\/\/(|www\.)tkznp(|1|2|3|4|5|6)\.com\/vodplay\/.*/g,
  2776. handler: class Handler extends BaseHandler {
  2777. constructor() {
  2778. super();
  2779. this.addIframeListener();
  2780. }
  2781. }
  2782. },
  2783. {
  2784. name: "天空影视播放器",
  2785. regex: /^https?:\/\/vip\.ckllk\.com\/\?url=.*/g,
  2786. handler: class Handler extends BaseHandler {
  2787. constructor() {
  2788. super();
  2789. this.addTopListener();
  2790. }
  2791. async parse() {
  2792. this.media.setVideoUrl(config.url);
  2793. this.media.setTitle(config.title);
  2794. }
  2795. }
  2796. },
  2797. {
  2798. // ✅ https://www.hdmoli.com/play/1630-1-0.html
  2799. // ✅ https://www.hdmoli.com/play/1630-0-0.html
  2800. name: "HDmoli",
  2801. home: [
  2802. "https://www.hdmoli.com"
  2803. ],
  2804. regex: /^https:\/\/www\.hdmoli\.com\/play\/.*/g,
  2805. handler: class Handler extends BaseHandler {
  2806. constructor() {
  2807. super();
  2808. this.addIframeListener();
  2809. }
  2810. }
  2811. },
  2812. {
  2813. name: "HDmoli 线路1",
  2814. regex: /^https:\/\/www\.hdmoli\.com\/js\/player\/dplayer\/dplayer\.html\?.*/g,
  2815. handler: class Handler extends BaseHandler {
  2816. constructor() {
  2817. super();
  2818. this.addTopListener();
  2819. setTimeout(() => {
  2820. let closeclick = document.getElementsByClassName("closeclick")[0];
  2821. if (closeclick) {
  2822. closeclick.click();
  2823. }
  2824. }, TIME.refresh);
  2825. }
  2826. async parse() {
  2827. this.media.setVideoUrl(this.videoParser());
  2828. }
  2829. }
  2830. },
  2831. {
  2832. name: "HDmoli 线路2",
  2833. regex: /^https:\/\/www\.hdmoli\.com\/js\/player\/duoduozy.html\?v=[\d\.]+/g,
  2834. handler: class Handler extends BaseHandler {
  2835. constructor() {
  2836. super();
  2837. setTimeout(() => {
  2838. let closeclick = document.getElementsByClassName("closeclick")[0];
  2839. if (closeclick) {
  2840. closeclick.click();
  2841. }
  2842. }, TIME.refresh);
  2843. }
  2844. }
  2845. },
  2846. {
  2847. name: "HDmoli 线路2",
  2848. regex: /^https:\/\/play\.qwertwe\.top\/xplay\/\?url=.*/g,
  2849. handler: class Handler extends BaseHandler {
  2850. constructor() {
  2851. super();
  2852. this.addTopListener();
  2853. }
  2854. async parse() {
  2855. let that = this;
  2856. $.ajax({
  2857. url: '555tZ4pvzHE3BpiO838.php',
  2858. type: 'GET',
  2859. dataType: 'JSON',
  2860. timeout: 3000,
  2861. data: {
  2862. tm: (new Date().getTime()),
  2863. url: config.url,
  2864. vkey: config.vkey,
  2865. token: config.token,
  2866. sign: 'F4penExTGogdt6U8'
  2867. },
  2868. success: function (data) {
  2869. if (data.code === 200) {
  2870. that.media.setVideoUrl(getVideoInfo(data.url));
  2871. }
  2872. }
  2873. });
  2874. }
  2875. }
  2876. },
  2877. {
  2878. // ✅ https://www.anfuns.cc/play/1572-1-1.html
  2879. name: "AnFuns",
  2880. home: [
  2881. "https://www.anfuns.cc"
  2882. ],
  2883. regex: /^https:\/\/www\.anfuns\.cc\/play\/.*/g,
  2884. handler: class Handler extends BaseHandler {
  2885. constructor() {
  2886. super();
  2887. this.addIframeListener();
  2888. }
  2889. }
  2890. },
  2891. {
  2892. name: "AnFuns播放器",
  2893. regex: /^https:\/\/www\.anfuns\.cc\/vapi\/(A0EPlayer|eden)\/\?url=.*/g,
  2894. handler: class Handler extends BaseHandler {
  2895. constructor() {
  2896. super();
  2897. this.addTopListener();
  2898. }
  2899. async parse() {
  2900. let url = config.url;
  2901. if (url) {
  2902. if (!url.startsWith("https://fata.peizq.online/cache/") && !url.startsWith("https://media-oss.anfuns.cn/m3u8/")) {
  2903. this.media.setVideoUrl(config.url);
  2904. } else {
  2905. tryTime = TRY_TIME.maxParse;
  2906. }
  2907. }
  2908. }
  2909. }
  2910. },
  2911. {
  2912. // ✅ https://www.youtube.com/watch?v=IkGuTYaTsLo
  2913. name: "YouTube",
  2914. home: [
  2915. "https://www.youtube.com"
  2916. ],
  2917. regex: /^https:\/\/www\.youtube\.com\/(watch|playlist)\?.*/g,
  2918. handler: class Handler extends BaseHandler {
  2919. async parse() {
  2920. this.media.setOther(BEST_QUALITY.ytdlp[currentConfig.bestQuality]);
  2921. this.media.setVideoUrl(this.ytDlpParser());
  2922. this.media.setTitle("");
  2923. }
  2924. },
  2925. },
  2926. {
  2927. // ✅ https://odysee.com/@jjlin:8/%E6%9E%97%E4%BF%8A%E5%82%91-jj-lin%E3%80%8Ajj20%E4%B8%96%E7%95%8C%E5%B7%A1%E8%BF%B4%E6%BC%94%E5%94%B1%E6%9C%83%E3%80%8B-2:8
  2928. name: "Odysee",
  2929. home: [
  2930. "https://odysee.com"
  2931. ],
  2932. regex: /^https:\/\/odysee\.com\/[^$].+/g,
  2933. handler: class Handler extends BaseHandler {
  2934. async parse() {
  2935. this.media.setOther(BEST_QUALITY.ytdlp[currentConfig.bestQuality]);
  2936. this.media.setVideoUrl(this.ytDlpParser());
  2937. this.media.setTitle("");
  2938. }
  2939. },
  2940. },
  2941. {
  2942. // ✅ https://rumble.com/v2mfr78-valheim-viking-survival-w-chaos-tricks-you-can-affect-my-game-chat-opinions.html
  2943. name: "Rumble",
  2944. home: [
  2945. "https://rumble.com"
  2946. ],
  2947. regex: /^https:\/\/rumble\.com\/v.+\.html/g,
  2948. handler: class Handler extends BaseHandler {
  2949. async parse() {
  2950. this.media.setOther(BEST_QUALITY.ytdlp[currentConfig.bestQuality]);
  2951. this.media.setVideoUrl(this.ytDlpParser());
  2952. this.media.setTitle("");
  2953. }
  2954. },
  2955. },
  2956. {
  2957. // ✅ https://www.bitchute.com/video/NoodZjmfKHXS/
  2958. name: "BitChute",
  2959. home: [
  2960. "https://www.bitchute.com"
  2961. ],
  2962. regex: /^https:\/\/www\.bitchute\.com\/video\/.+/g,
  2963. handler: class Handler extends BaseHandler {
  2964. async parse() {
  2965. this.media.setOther(BEST_QUALITY.ytdlp[currentConfig.bestQuality]);
  2966. this.media.setVideoUrl(this.ytDlpParser());
  2967. this.media.setTitle("");
  2968. }
  2969. },
  2970. },
  2971. {
  2972. // ✅ https://ani.gamer.com.tw/animeVideo.php?sn=32227
  2973. name: "巴哈姆特",
  2974. home: [
  2975. "https://ani.gamer.com.tw"
  2976. ],
  2977. regex: /^https:\/\/ani\.gamer\.com\.tw\/animeVideo.php\?sn=.*/g,
  2978. handler: class Handler extends BaseHandler {
  2979. async parse() {
  2980. this.media.setOrigin("https://ani.gamer.com.tw");
  2981. let index = page.url.indexOf("sn=") + 3;
  2982. if (index == -1) {
  2983. return;
  2984. }
  2985. let sn = page.url.substring(index);
  2986. index = sn.indexOf("&");
  2987. if (index != -1) {
  2988. sn = sn.substring(0, index);
  2989. }
  2990. let device = localStorage.ANIME_deviceid;
  2991. let that = this;
  2992. let res;
  2993. $.ajax({
  2994. type: "GET",
  2995. url: `https://ani.gamer.com.tw/ajax/m3u8.php?sn=${sn}&device=${device}`,
  2996. async: false,
  2997. xhrFields: {
  2998. withCredentials: true
  2999. },
  3000. success: function (json) {
  3001. res = JSON.parse(json);
  3002. }
  3003. })
  3004. if (res.error) {
  3005. if (res.error.code == 1015) {
  3006. let oldDuration = document.getElementsByClassName("vjs-duration-display")[0].innerHTML;
  3007. let newDuration = oldDuration;
  3008. while (oldDuration == newDuration) {
  3009. await sleep(1000);
  3010. newDuration = document.getElementsByClassName("vjs-duration-display")[0].innerHTML;
  3011. }
  3012. tryTime = 0;
  3013. }
  3014. } else {
  3015. that.media.setProxy(currentConfig.proxy);
  3016. that.media.setVideoUrl(res.src);
  3017. }
  3018. }
  3019. },
  3020. },
  3021. {
  3022. // ✅ https://hanime1.me/watch?v=26262
  3023. name: "Hanime1.me",
  3024. home: [
  3025. "https://hanime1.me"
  3026. ],
  3027. regex: /^https:\/\/hanime1\.me\/watch\?v=.*/g,
  3028. handler: class Handler extends BaseHandler {
  3029. async parse() {
  3030. let url = this.videoParser();
  3031. if (!url) {
  3032. url = this.iframeParser();
  3033. }
  3034. if (!url) {
  3035. url = page.url;
  3036. }
  3037. this.media.setVideoUrl(url);
  3038. }
  3039. },
  3040. },
  3041. {
  3042. // ✅ https://jable.tv/videos/fsdss-484/
  3043. name: "Jable.TV",
  3044. home: [
  3045. "https://jable.tv"
  3046. ],
  3047. regex: /^https:\/\/jable\.tv\/videos\/.*/g,
  3048. handler: class Handler extends BaseHandler {
  3049. async parse() {
  3050. let url = hls.url;
  3051. url = url ? url : hlsUrl;
  3052. url = url ? url : page.url;
  3053. this.media.setVideoUrl(url);
  3054. }
  3055. },
  3056. },
  3057. {
  3058. // ✅ https://ok.ru/video/2035990725937
  3059. name: "OK",
  3060. home: [
  3061. "https://ok.ru/video"
  3062. ],
  3063. regex: /^https:\/\/ok\.ru\/video\/\d+/g,
  3064. handler: class Handler extends BaseHandler {
  3065. async parse() {
  3066. this.media.setOther(BEST_QUALITY.ytdlp[currentConfig.bestQuality]);
  3067. this.media.setVideoUrl(this.ytDlpParser());
  3068. this.media.setTitle("");
  3069. }
  3070. },
  3071. },
  3072. {
  3073. // ✅ https://tver.jp/episodes/epsta1xs0z
  3074. name: "TVer",
  3075. home: [
  3076. "https://tver.jp"
  3077. ],
  3078. regex: /^https:\/\/tver\.jp\/episodes\/\w+/g,
  3079. handler: class Handler extends BaseHandler {
  3080. async parse() {
  3081. this.media.setOther(BEST_QUALITY.ytdlp[currentConfig.bestQuality]);
  3082. this.media.setVideoUrl(this.ytDlpParser());
  3083. this.media.setTitle("");
  3084. }
  3085. },
  3086. },
  3087. {
  3088. // ✅ https://www.lckp.top/play-with-mpv/index.html
  3089. name: "电视直播",
  3090. home: [
  3091. "https://www.lckp.top/play-with-mpv/index.html"
  3092. ],
  3093. regex: /^https?:\/\/(www.lckp.top\/play-with-mpv|127.0.0.1:5502\/web\/tampermonkey\/Play-With-MPV)\/index.html/g,
  3094. handler: class Handler extends BaseHandler {
  3095. async parse() {
  3096. this.media.setVideoUrl(localStorage.iptvUrl);
  3097. localStorage.player = JSON.stringify(this.player);
  3098. this.media.setTitle("");
  3099. }
  3100. },
  3101. },
  3102. {
  3103. // ✅ https://www.douyin.com/
  3104. name: "抖音",
  3105. home: [
  3106. "https://www.douyin.com/"
  3107. ],
  3108. regex: /^https?:\/\/www\.douyin\.com\/.*/g,
  3109. handler: class Handler extends BaseHandler {
  3110. constructor() {
  3111. super();
  3112. this.index = 0;
  3113. }
  3114. initCheck() {
  3115. if (super.initCheck()) {
  3116. return true;
  3117. }
  3118. let oldVideoUrl = this.media.videoUrl;
  3119. let newVideoUrl = this.videoParser();
  3120. if (oldVideoUrl && oldVideoUrl != newVideoUrl) {
  3121. return true;
  3122. }
  3123. return false;
  3124. }
  3125. async parse() {
  3126. this.media.setVideoUrl(this.videoParser());
  3127. }
  3128. videoParser() {
  3129. let videos = document.getElementsByTagName("video");
  3130. if (videos && videos.length > 0) {
  3131. this.index = videos.length > 2 ? 1 : 0;
  3132. }
  3133. let url = document.getElementsByTagName("video")[this.index].getElementsByTagName("source")[0].src;
  3134. if (url) {
  3135. return url;
  3136. }
  3137. }
  3138. },
  3139. },
  3140. {
  3141. // ✅ https://www.mengfan.tv/play/kx666U/1/3/
  3142. name: "萌番",
  3143. home: [
  3144. "https://www.mengfan.tv"
  3145. ],
  3146. regex: /^https:\/\/www\.mengfan\.tv\/play\/.*/g,
  3147. handler: class Handler extends BaseHandler {
  3148. constructor() {
  3149. super();
  3150. this.addIframeListener();
  3151. }
  3152. }
  3153. },
  3154. {
  3155. name: "萌番播放器",
  3156. regex: /^https:\/\/video1\.beijcloud\.com\/player\/\?url=.*/g,
  3157. handler: class Handler extends BaseHandler {
  3158. constructor() {
  3159. super();
  3160. this.addTopListener();
  3161. }
  3162. async parse() {
  3163. this.media.setVideoUrl(this.scriptParser());
  3164. }
  3165. }
  3166. },
  3167. {
  3168. // ✅ https://www.haitu.tv/vod/play/id/47100/sid/1/nid/4.html
  3169. name: "海兔影院",
  3170. home: [
  3171. "https://www.haitu.tv"
  3172. ],
  3173. regex: /^https:\/\/www\.haitu\.tv\/vod\/play\/.*/g,
  3174. handler: class Handler extends BaseHandler {
  3175. constructor() {
  3176. super();
  3177. this.addIframeListener();
  3178. }
  3179. }
  3180. },
  3181. {
  3182. name: "海兔影院播放器",
  3183. regex: /^https:\/\/www\.haitu\.tv\/static\/dmku\/player\/index\.php/g,
  3184. handler: class Handler extends BaseHandler {
  3185. constructor() {
  3186. super();
  3187. this.addTopListener();
  3188. }
  3189. async parse() {
  3190. this.media.setVideoUrl(config.url);
  3191. }
  3192. }
  3193. },
  3194. {
  3195. // ✅ https://www.icourse163.org/learn/ZJU-200001?tid=1470096517#/learn/content?type=detail&id=1254347726&cid=1285600901
  3196. name: "中国大学MOOC",
  3197. regex: /^https:\/\/www\.icourse163\.org\/learn\/.*\/learn\/content\?type=detail.*/g,
  3198. handler: class Handler extends BaseHandler {
  3199. async parse() {
  3200. let res = this.htmlParser();
  3201. if (!res) return;
  3202. this.media.setVideoUrl(res);
  3203. this.media.setTitle(document.querySelector('.current > .unit-name').innerText);
  3204. }
  3205. }
  3206. },
  3207. {
  3208. // ✅ https://www.iole.tv/vodplay/23711-1-1.html
  3209. name: "ioleTV",
  3210. regex: /^https:\/\/www\.iole\.tv\/vodplay\/.+/g,
  3211. handler: class Handler extends BaseHandler {
  3212. constructor() {
  3213. super();
  3214. this.addIframeListener();
  3215. }
  3216. }
  3217. },
  3218. {
  3219. name: "ioleTV播放器",
  3220. regex: /^https:\/\/www\.iole\.tv\/static\/player\/.*\.html/g,
  3221. handler: class Handler extends BaseHandler {
  3222. constructor() {
  3223. super();
  3224. this.addTopListener();
  3225. }
  3226. async parse() {
  3227. this.media.setVideoUrl(parent.MacPlayer.PlayUrl);
  3228. }
  3229. }
  3230. },
  3231. {
  3232. // ✅ https://www.zhihu.com/zvideo/1650574385558937600
  3233. name: "知乎视频",
  3234. regex: /^https:\/\/www\.zhihu\.com\/zvideo\/.+/g,
  3235. handler: class Handler extends BaseHandler {
  3236. constructor() {
  3237. super();
  3238. let that = this;
  3239. this.currentUrl = "";
  3240. //拦截请求以更新Url
  3241. const originOpen = XMLHttpRequest.prototype.open;
  3242. XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
  3243. originOpen.apply(this, arguments);
  3244. if (url.match(VIDEO_URL_REGEX)) {
  3245. that.currentUrl = url;
  3246. }
  3247. };
  3248. }
  3249. async parse() {
  3250. this.media.setVideoUrl(this.currentUrl);
  3251. }
  3252. }
  3253. },
  3254. {
  3255. // ✅ https://www.tucao.cam/play/h4092670/#1
  3256. name: "吐槽弹幕网",
  3257. regex: /^https:\/\/www\.tucao\.cam\/play\/.*/g,
  3258. handler: class Handler extends BaseHandler {
  3259. async parse() {
  3260. this.media.setVideoUrl(this.videoParser());
  3261. }
  3262. }
  3263. },
  3264. {
  3265. // ✅ http://www.susudm8.com/acg/69815/3.html
  3266. name: "速速电影院",
  3267. regex: /^https?:\/\/(www\.susudm8\.com|susudyy\.com|buding3\.com|buding6\.com)\/.+\.html/g,
  3268. handler: class Handler extends BaseHandler {
  3269. constructor() {
  3270. super();
  3271. this.addIframeListener();
  3272. }
  3273. }
  3274. },
  3275. {
  3276. name: "速速电影院播放器",
  3277. regex: /^https?:\/\/(v2\.shenjw\.com:\d+|u88\.xigua88ok\.com:\d+)\/wap\.php\?url=.+/g,
  3278. handler: class Handler extends BaseHandler {
  3279. constructor() {
  3280. super();
  3281. this.addTopListener();
  3282. }
  3283. async parse() {
  3284. this.media.setVideoUrl(this.videoParser());
  3285. }
  3286. }
  3287. },
  3288. {
  3289. name: "速速电影院播放器",
  3290. regex: /^https?:\/\/test3\.gqyy8\.com:\d+\/f\/aliplayer\.php\?url=.+/g,
  3291. handler: class Handler extends BaseHandler {
  3292. constructor() {
  3293. super();
  3294. this.addTopListener();
  3295. }
  3296. async parse() {
  3297. this.media.setVideoUrl(this.scriptParser());
  3298. }
  3299. }
  3300. },
  3301. {
  3302. // ✅ https://v.mksec.cn/index.php/vod/play/id/165438/sid/2/nid/1.html
  3303. name: "小见子的视频站",
  3304. regex: /^https?:\/\/v\.mksec\.cn\/index\.php\/vod\/play\/.+\.html/g,
  3305. handler: class Handler extends BaseHandler {
  3306. constructor() {
  3307. super();
  3308. this.addIframeListener();
  3309. }
  3310. async parse() {
  3311. this.media.setVideoUrl(player_aaaa.url);
  3312. }
  3313. }
  3314. },
  3315. {
  3316. name: "小见子的视频站播放器",
  3317. regex: /^https?:\/\/v\.mksec\.cn\/static\/player\/dplayer\.html/g,
  3318. handler: class Handler extends BaseHandler {
  3319. constructor() {
  3320. super();
  3321. this.addTopListener();
  3322. }
  3323. async parse() {
  3324. tryTime = TRY_TIME.maxParse;
  3325. }
  3326. }
  3327. },
  3328. {
  3329. // ✅ https://jh642t.dshryadqp.com/index.php/vod/play/id/51434/sid/1/nid/1.html
  3330. name: "大师兄电影网",
  3331. regex: /^https?:\/\/.*dsh.*\.com\/index\.php\/vod\/play\/.+\.html/g,
  3332. handler: class Handler extends BaseHandler {
  3333. constructor() {
  3334. super();
  3335. this.addIframeListener();
  3336. }
  3337. async parse() {
  3338. this.media.setVideoUrl(player_aaaa.url);
  3339. }
  3340. }
  3341. },
  3342. {
  3343. name: "大师兄电影网播放器",
  3344. regex: /^https?:\/\/.*dsh.*\.com\/static\/player\/dplayer\.html/g,
  3345. handler: class Handler extends BaseHandler {
  3346. constructor() {
  3347. super();
  3348. this.addTopListener();
  3349. }
  3350. async parse() {
  3351. tryTime = TRY_TIME.maxParse;
  3352. }
  3353. }
  3354. },
  3355. {
  3356. // ✅ https://www.twitch.tv/yulihong22
  3357. name: "Twitch",
  3358. home: [
  3359. "https://www.twitch.tv"
  3360. ],
  3361. regex: /^https:\/\/www\.twitch\.tv\/\w+/g,
  3362. handler: class Handler extends BaseHandler {
  3363. async parse() {
  3364. if (page.url.indexOf("/directory") != -1) {
  3365. tryTime = TRY_TIME.maxParse;
  3366. return;
  3367. }
  3368. this.media.setOther(BEST_QUALITY.ytdlp[currentConfig.bestQuality]);
  3369. this.media.setVideoUrl(this.ytDlpParser());
  3370. this.media.setTitle("");
  3371. }
  3372. },
  3373. },
  3374. {
  3375. // ✅ https://jojo.bdys.top/watch/264
  3376. name: "JOJO",
  3377. home: [
  3378. "https://jojo.bdys.top"
  3379. ],
  3380. regex: /^https:\/\/jojo\.bdys\.top\/watch\/.*/g,
  3381. handler: class Handler extends BaseHandler {
  3382. async parse() {
  3383. this.media.setVideoUrl(this.videoParser());
  3384. }
  3385. },
  3386. },
  3387. {
  3388. // ✅ https://www.agemys.org/play/20220403/1/1
  3389. name: "AGE动漫",
  3390. regex: /^https?:\/\/www\.agemys\.org\/play\/.+/g,
  3391. handler: class Handler extends BaseHandler {
  3392. constructor() {
  3393. super();
  3394. this.addIframeListener();
  3395. }
  3396. }
  3397. },
  3398. {
  3399. name: "AGE动漫播放器",
  3400. regex: /^https?:\/\/vip\.sp-flv\.com:\d+\/\?url=.+/g,
  3401. handler: class Handler extends BaseHandler {
  3402. constructor() {
  3403. super();
  3404. this.addTopListener();
  3405. }
  3406. async parse() {
  3407. this.media.setVideoUrl(this.videoParser());
  3408. }
  3409. }
  3410. },
  3411. {
  3412. name: "AList",
  3413. regex: /^https?:\/\/[^\/]+\/.*\.(mp4|mkv)/g,
  3414. handler: class Handler extends BaseHandler {
  3415. async parse() {
  3416. let url = this.videoParser();
  3417. if (!url && tryTime < 3) {
  3418. $.ajax({
  3419. type: "POST",
  3420. url: `/api/fs/get`,
  3421. data: {
  3422. password: "",
  3423. path: decodeURIComponent(location.pathname)
  3424. },
  3425. xhrFields: {
  3426. withCredentials: true
  3427. },
  3428. async: false,
  3429. success: function (res) {
  3430. if (res.code == 200) {
  3431. url = res.data.raw_url;
  3432. }
  3433. }
  3434. });
  3435. }
  3436. if (url) {
  3437. let index = url.indexOf("?");
  3438. if (index != -1) {
  3439. url = url.substring(0, index + 1) + encodeURIComponent(url.substring(index + 1));
  3440. }
  3441. this.media.setVideoUrl(url);
  3442. this.media.setTitle(document.title);
  3443. }
  3444. }
  3445. }
  3446. }
  3447. ];
  3448. // 初始化
  3449. async function init(flag) {
  3450. // 加载页面信息
  3451. page = {
  3452. host: window.location.host,
  3453. url: window.location.href,
  3454. isFullScreen: false
  3455. };
  3456. // 清除 handler
  3457. if (handler) {
  3458. handler = undefined;
  3459. if (document.getElementById(ID.buttonDiv)) {
  3460. document.getElementById(ID.buttonDiv).style.display = "none";
  3461. }
  3462. }
  3463. // 生成 handler
  3464. for (let i = 0; i < websiteList.length; i++) {
  3465. if (page.url.match(websiteList[i].regex)) {
  3466. handler = new websiteList[i].handler();
  3467. break;
  3468. }
  3469. }
  3470. if (flag && page.url.startsWith("https://www.bilibili.com/festival/")) {
  3471. await sleep(1500);
  3472. }
  3473. // 尝试解析页面视频
  3474. if (handler) {
  3475. tryTime = 0;
  3476. while (tryTime < TRY_TIME.maxParse) {
  3477. await sleep(tryTime * 1000 + 700);
  3478. if (!handler.media.videoUrl) {
  3479. try {
  3480. await handler.parse();
  3481. } catch (error) {
  3482. console.log('Play-With-MPV:解析失败:' + error);
  3483. }
  3484. }
  3485. tryTime++;
  3486. }
  3487. } else {
  3488. console.log("Play-With-MPV:暂无此网页解析器(" + page.url + ")");
  3489. }
  3490. }
  3491. // 开始执行
  3492. init();
  3493. setInterval(() => {
  3494. if (handler) {
  3495. if (handler.initCheck()) {
  3496. init(true);
  3497. }
  3498. } else if (window.location.href != page.url) {
  3499. init();
  3500. }
  3501. }, TIME.refresh);