External Player

Play web video via external player

  1. // ==UserScript==
  2. // @name External Player
  3. // @name:zh-CN 外部播放器
  4. // @namespace https://github.com/LuckyPuppy514/external-player
  5. // @copyright 2024, Grant LuckyPuppy514 (https://github.com/LuckyPuppy514)
  6. // @version 1.2.3
  7. // @license MIT
  8. // @description Play web video via external player
  9. // @description:zh-CN 使用外部播放器播放网页中的视频
  10. // @icon https://www.lckp.top/gh/LuckyPuppy514/pic-bed/common/mpv.png
  11. // @author LuckyPuppy514
  12. // @homepage https://github.com/LuckyPuppy514/external-player
  13. // @include *://*
  14. // @connect v.anime1.me
  15. // @grant GM_setValue
  16. // @grant GM_getValue
  17. // @grant GM.xmlHttpRequest
  18. // @run-at document-start
  19. // @require https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-y/pako/2.0.4/pako.min.js
  20. // ==/UserScript==
  21.  
  22. 'use strict';
  23.  
  24. const DEBUG = false;
  25.  
  26. const PROJECT_NAME = 'external-player';
  27.  
  28. const SETTING_URL = DEBUG === true ? 'http://127.0.0.1:5500/setting.html' : undefined;
  29.  
  30. const VIDEO_URL_REGEX_GLOBAL = /https?:\/\/((?![^"^']*http)[^"^']+(\.|%2e)(mp4|mkv|flv|m3u8|m4s|m3u|mov|avi|wmv|webm)(\?[^"^']+|))|((?![^"^']*http)[^"^']+\?[^"^']+(\.|%2e|video_)(mp4|mkv|flv|mov|avi|wmv|webm|m3u8|m3u)[^"^']*)/ig;
  31.  
  32. const VIDEO_URL_REGEX_EXACT = /^https?:\/\/((?![^"^']*http)[^"^']+(\.|%2e)(mp4|mkv|flv|m3u8|m4s|m3u|mov|avi|wmv|webm)(\?[^"^']+|))|((?![^"^']*http)[^"^']+\?[^"^']+(\.|%2e|video_)(mp4|mkv|flv|mov|avi|wmv|webm|m3u8|m3u)[^"^']*)$/ig;
  33.  
  34. const defaultConfig = {
  35. global: {
  36. version: '1.2.3',
  37. language: (navigator.language || navigator.userLanguage) === 'zh-CN' ? 'zh' : 'en',
  38. buttonXCoord: '0',
  39. buttonYCoord: '0',
  40. buttonScale: '1.00',
  41. buttonVisibilityDuration: '5000',
  42. networkProxy: '',
  43. parser: {
  44. ytdlp: {
  45. regex: [
  46. "https://www.youtube.com/shorts/.+",
  47. "https://www.youtube.com/watch\\?.+",
  48. "https://www.youtube.com/playlist\\?list=.+",
  49. ],
  50. preferredQuality: 'unlimited',
  51. },
  52. video: {
  53. regex: [
  54. "https://www.moepoi.net/static/player/artplayer.html",
  55. "https://.*libvio\\..+/vid/plyr/vr2.php\\?url=.+",
  56. "https://danmu.yhdmjx.com/m3u8.php\\?url=.+",
  57. "https://player.cycanime.com/\\?url=.+",
  58. "https://www.tucao.my/play/.+",
  59. "https://ddys.pro/.+",
  60. "https://43.240.156.118:8443/vip/\\?url=.+",
  61. ]
  62. },
  63. url: {
  64. regex: [
  65. "https://m3u8.girigirilove.com/.+\\.php\\?.+",
  66. "https://m3u8.girigirilove.icu/.+\\.php\\?.+",
  67. "https://cnys.tv/addons/dp/player/dp.php\\?.+",
  68. ]
  69. },
  70. html: {
  71. regex: []
  72. },
  73. script: {
  74. regex: [
  75. "https://.*libvio\\..+/vid/yd.php\\?url=.+",
  76. "https://43.240.156.118:8443/m3u8/\\?url=.+"
  77. ]
  78. },
  79. request: {
  80. regex: []
  81. },
  82. bilibili: {
  83. regex: [
  84. "https://www.bilibili.com/bangumi/play/.+",
  85. "https://www.bilibili.com/video/.+",
  86. "https://www.bilibili.com/list/.+",
  87. "https://www.bilibili.com/festival/.+"
  88. ],
  89. preferredQuality: '127',
  90. preferredSubtitle: 'off',
  91. preferredCodec: '12',
  92. },
  93. bilibiliLive: {
  94. regex: [
  95. "https://live.bilibili.com/\\d+.*",
  96. "https://live.bilibili.com/roomid=\\d+.*",
  97. "https://live.bilibili.com/blanc/\\d+.*"
  98. ],
  99. preferredQuality: '4',
  100. preferredLine: '0',
  101. },
  102. aniGamer: {
  103. regex: [
  104. "https://ani.gamer.com.tw/animeVideo.php\\?sn=.+"
  105. ]
  106. },
  107. anime1: {
  108. regex: [
  109. "https://anime1.me/\\d+"
  110. ]
  111. }
  112. }
  113. },
  114. players: [{
  115. name: 'IINA',
  116. system: 'mac',
  117. icon: 'data:image/png;base64,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',
  118. iconSize: 53,
  119. playEvent: "const delimiter = '&';\n\nlet args = [\n `url=${encodeURIComponent(media.video)}`,\n media.origin ? `mpv_http-header-fields=${encodeURIComponent('origin: ' + media.origin)}` : '',\n media.referer ? `mpv_http-header-fields=${encodeURIComponent('referer: ' + media.referer)}` : '',\n]\nargs = args.filter(item => item !== '');\n\nconsole.log(args);\n\nwindow.open(`iina://weblink?${args.join(delimiter)}`, '_self');",
  120. presetEvent: {
  121. playAuto: false,
  122. pauseAuto: true,
  123. closeAuto: false,
  124. syncTime: false,
  125. },
  126. enable: true,
  127. readonly: true,
  128. },
  129. {
  130. name: 'PotPlayer',
  131. system: 'windows',
  132. icon: 'data:image/png;base64,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',
  133. iconSize: 50,
  134. playEvent: "let args = [\n `\"${media.video}\"`,\n media.subtitle ? `/sub=\"${media.subtitle}\"` : '',\n media.origin ? `/headers=\"origin: ${media.origin}\"` : '',\n media.referer ? `/referer=\"${media.referer}\"` : '',\n config.networkProxy ? `/user_agent=\"${config.networkProxy}\"` : '',\n media.title ? `/title=\"${media.title}\"` : '',\n media.time ? `/seek=\"${media.time}\"` : '',\n]\nargs = args.filter(item => item !== '');\n\nconsole.log(args);\n\nwindow.open(`ush://${player.name}?${compress(args.join(' '))}`, '_self');",
  135. presetEvent: {
  136. playAuto: false,
  137. pauseAuto: true,
  138. closeAuto: false,
  139. syncTime: false,
  140. },
  141. enable: true,
  142. readonly: true,
  143. },
  144. {
  145. name: 'MPV',
  146. system: 'windows',
  147. icon: 'data:image/png;base64,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',
  148. iconSize: 52,
  149. playEvent: "let args = [\n `\"${media.video}\"`,\n media.audio ? `--audio-file=\"${media.audio}\"` : '',\n media.subtitle ? `--sub-file=\"${media.subtitle}\"` : '',\n media.origin ? `--http-header-fields=\"origin: ${media.origin}\"` : '',\n media.referer ? `--http-header-fields=\"referer: ${media.referer}\"` : '',\n media.cookie ? `--http-header-fields=\"cookie: ${media.cookie}\"` : '',\n config.networkProxy ? `--http-proxy=\"${config.networkProxy}\"` : '',\n media.ytdlp.networkProxy ? `--ytdl-raw-options=\"proxy=[${media.ytdlp.networkProxy}]\"` : '',\n media.ytdlp.quality ? `--ytdl-format=\"bestvideo[height<=?${media.ytdlp.quality}]+bestaudio/best\"` : '',\n media.bilibili.cid ? `--script-opts-append=\"cid=${media.bilibili.cid}\"` : '',\n media.title ? `--force-media-title=\"${media.title}\"` : '',\n media.time ? `--start=\"${media.time}\"` : '',\n]\nargs = args.filter(item => item !== '');\n\nconsole.log(args);\n\nwindow.open(`ush://${player.name}?${compress(args.join(' '))}`, '_self');",
  150. presetEvent: {
  151. playAuto: false,
  152. pauseAuto: true,
  153. closeAuto: false,
  154. syncTime: false,
  155. },
  156. enable: true,
  157. readonly: true,
  158. },
  159. {
  160. name: 'MPVNET',
  161. system: 'windows',
  162. icon: 'data:image/png;base64,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',
  163. iconSize: 50,
  164. playEvent: "let args = [\n `\"${media.video}\"`,\n media.audio ? `--audio-file=\"${media.audio}\"` : '',\n media.subtitle ? `--sub-file=\"${media.subtitle}\"` : '',\n media.origin ? `--http-header-fields=\"origin: ${media.origin}\"` : '',\n media.referer ? `--http-header-fields=\"referer: ${media.referer}\"` : '',\n media.cookie ? `--http-header-fields=\"cookie: ${media.cookie}\"` : '',\n config.networkProxy ? `--http-proxy=\"${config.networkProxy}\"` : '',\n media.ytdlp.networkProxy ? `--ytdl-raw-options=\"proxy=[${media.ytdlp.networkProxy}]\"` : '',\n media.ytdlp.quality ? `--ytdl-format=\"bestvideo[height<=?${media.ytdlp.quality}]+bestaudio/best\"` : '',\n media.bilibili.cid ? `--script-opts=\"cid=${media.bilibili.cid}\"` : '',\n media.title ? `--force-media-title=\"${media.title}\"` : '',\n media.time ? `--start=\"${media.time}\"` : '',\n]\nargs = args.filter(item => item !== '');\n\nconsole.log(args);\n\nwindow.open(`ush://${player.name}?${compress(args.join(' '))}`, '_self');",
  165. presetEvent: {
  166. playAuto: false,
  167. pauseAuto: true,
  168. closeAuto: false,
  169. syncTime: false,
  170. },
  171. enable: true,
  172. readonly: true,
  173. }
  174. ]
  175. }
  176.  
  177. if (DEBUG === true) {
  178. defaultConfig.global.parser.ytdlp.regex.push(SETTING_URL);
  179. }
  180.  
  181. const translations = {
  182. en: {
  183. loadSuccessfully: 'Load successfully',
  184. loadTimeout: 'Load timeout ......',
  185. saveSuccessfully: 'Save successfully',
  186. loadFail: 'Load fail',
  187. requireLoginOrVip: 'Require login or vip',
  188. noMatchingParserFound: 'No matching parser found',
  189. onlyNewTabsCanCloseAutomatically: 'Only new tabs can close automatically'
  190. },
  191. zh: {
  192. loadSuccessfully: '加载成功',
  193. loadTimeout: '加载超时 ......',
  194. saveSuccessfully: '保存成功',
  195. loadFail: '加载失败',
  196. requireLoginOrVip: '需要登录或会员',
  197. noMatchingParserFound: '没有匹配的解析器',
  198. onlyNewTabsCanCloseAutomatically: '只有新标签页才能自动关闭'
  199. }
  200. };
  201.  
  202. const REFRESH_INTERVAL = 500;
  203. const MAX_TRY_COUNT = 5;
  204.  
  205. var currentTryCount;
  206. var currentConfig;
  207. var currentUrl;
  208. var currentParser;
  209. var currentMedia;
  210. var currentPlayer;
  211. var translation;
  212. var iframe;
  213.  
  214. class BaseParser {
  215. constructor() {
  216. currentMedia = {
  217. video: undefined,
  218. audio: undefined,
  219. subtitle: undefined,
  220. title: undefined,
  221. origin: undefined,
  222. referer: undefined,
  223. time: undefined,
  224. bilibili: {
  225. cid: undefined
  226. },
  227. ytdlp: {
  228. quality: undefined,
  229. networkProxy: undefined
  230. }
  231. }
  232. }
  233. async execute() {}
  234. async parseVideo() {
  235. currentMedia.video = location.href;
  236. }
  237. async parseAudio() {}
  238. async parseSubtitle() {}
  239. async parseTitle() {
  240. currentMedia.title = document.title;
  241. }
  242. async parseOrigin() {
  243. currentMedia.origin = location.origin || location.href;
  244. }
  245. async parseReferer() {
  246. let index = currentUrl.indexOf('?');
  247. currentMedia.referer = index > 0 ? currentUrl.substring(0, index) : currentUrl;
  248. }
  249. async parseTime() {
  250. try {
  251. for (const video of document.getElementsByTagName('video')) {
  252. currentMedia.time = video.currentTime;
  253. return;
  254. }
  255. } catch (error) {
  256. console.error("获取开始时间失败", error);
  257. }
  258. }
  259. async check(video) {
  260. if (!video) {
  261. video = currentMedia.video;
  262. }
  263. if (!video || !video.startsWith('http') || video.startsWith('https://www.mp4')) {
  264. return false;
  265. }
  266.  
  267. if (video.indexOf('.m3u8') > -1 || video.indexOf('.m3u') > -1) {
  268. try {
  269. const response = await (await fetch(video, {
  270. method: 'GET',
  271. credentials: 'include'
  272. })).body();
  273. return response && response.indexOf('png') === -1;
  274. } catch (error) {}
  275. }
  276.  
  277. return new RegExp(VIDEO_URL_REGEX_EXACT).test(video);
  278. }
  279. async pause() {
  280. for (let index = 0; index < MAX_TRY_COUNT; index++) {
  281. try {
  282. for (const video of document.getElementsByTagName('video')) {
  283. video.pause();
  284. }
  285. } catch (error) {
  286. console.error('暂停失败', error);
  287. } finally {
  288. await sleep(REFRESH_INTERVAL * 3);
  289. }
  290. }
  291. }
  292. async close() {
  293. try {
  294. await sleep(REFRESH_INTERVAL * 2);
  295. if (window.top.history.length === 1) {
  296. window.top.location.href = "about:blank";
  297. window.top.close();
  298. } else {
  299. showToast(translation.onlyNewTabsCanCloseAutomatically);
  300. }
  301. } catch (error) {
  302. console.error('关闭失败', error);
  303. }
  304. }
  305. async play(player) {
  306. try {
  307. showLoading(6000);
  308.  
  309. // 别名,方便播放事件使用
  310. currentPlayer = player;
  311. let media = currentMedia;
  312. let parser = currentParser;
  313. let config = currentConfig.global;
  314.  
  315. currentTryCount = 0;
  316. let latestError = undefined;
  317. do {
  318. currentTryCount++;
  319. try {
  320. // 低端影视
  321. if (currentUrl.startsWith("https://ddys")) {
  322. document.getElementsByClassName("vjs-big-play-button")[0].click();
  323. await this.parseReferer();
  324. }
  325.  
  326. await parser.execute();
  327. if (await parser.check()) {
  328. latestError = undefined;
  329. break;
  330. }
  331. await sleep(REFRESH_INTERVAL * 2);
  332. } catch (error) {
  333. latestError = error;
  334. console.error(`第${currentTryCount}次尝试解析失败:`, error);
  335. }
  336. }
  337. while (currentTryCount < MAX_TRY_COUNT);
  338. if (latestError) {
  339. showToast(translation.loadFail + ': ' + latestError.message);
  340. return;
  341. }
  342. if (!await parser.check()) {
  343. showToast(translation.loadFail);
  344. return;
  345. }
  346. media = currentMedia;
  347.  
  348. if (!player.presetEvent.syncTime) {
  349. media.time = undefined;
  350. }
  351.  
  352. if (player.playEvent) {
  353. eval(policy.createScript(player.playEvent));
  354. }
  355.  
  356. if (player.presetEvent.closeAuto) {
  357. parser.close();
  358. }
  359. if (player.presetEvent.pauseAuto) {
  360. parser.pause();
  361. }
  362. } catch (error) {
  363. showToast(translation.loadFail + ': ' + error.message);
  364. } finally {
  365. hideLoading();
  366. }
  367. }
  368. }
  369.  
  370. const PARSER = {
  371. YTDLP: class Parser extends BaseParser {
  372. async execute() {
  373. currentMedia.ytdlp.quality = currentConfig.global.parser.ytdlp.preferredQuality === 'unlimited' ?
  374. undefined :
  375. currentConfig.global.parser.ytdlp.preferredQuality;
  376. currentMedia.ytdlp.networkProxy = currentConfig.global.networkProxy ?
  377. currentConfig.global.networkProxy :
  378. undefined;
  379. await this.parseVideo();
  380. await this.parseTime();
  381. }
  382. async check() {
  383. return currentMedia.video ? true : false;
  384. }
  385. },
  386. VIDEO: class Parser extends BaseParser {
  387. async execute() {
  388. await this.parseVideo();
  389. await this.parseTitle();
  390. await this.parseTime();
  391. }
  392. async parseVideo() {
  393. for (const video of document.getElementsByTagName('video')) {
  394. if (await this.check(video.src)) {
  395. currentMedia.video = video.src;
  396. return;
  397. }
  398. }
  399. }
  400. async check(video) {
  401. video = video ? video : currentMedia.video;
  402. return video && video.startsWith("http") ? true : false;
  403. }
  404. },
  405. URL: class Parser extends BaseParser {
  406. async execute() {
  407. await this.parseVideo();
  408. await this.parseTitle();
  409. await this.parseTime();
  410. }
  411. async parseVideo() {
  412. let urls = currentUrl.match(VIDEO_URL_REGEX_GLOBAL) || [];
  413. for (const url of urls) {
  414. if (await this.check(url)) {
  415. currentMedia.video = url;
  416. return;
  417. }
  418. }
  419.  
  420. for (const iframe of document.getElementsByTagName('iframe')) {
  421. let urls = iframe.src.match(VIDEO_URL_REGEX_GLOBAL) || [];
  422. for (const url of urls) {
  423. if (await this.check(url)) {
  424. currentMedia.video = url;
  425. return;
  426. }
  427. }
  428. }
  429. }
  430. },
  431. HTML: class Parser extends BaseParser {
  432. async execute() {
  433. await this.parseVideo();
  434. await this.parseTitle();
  435. await this.parseTime();
  436. }
  437. async parseVideo() {
  438. let urls = document.body.innerHTML.match(VIDEO_URL_REGEX_GLOBAL) || [];
  439. for (const url of urls) {
  440. if (await this.check(url)) {
  441. currentMedia.video = url;
  442. return;
  443. }
  444. }
  445. }
  446. },
  447. SCRIPT: class Parser extends BaseParser {
  448. async execute() {
  449. await this.parseVideo();
  450. await this.parseTitle();
  451. await this.parseTime();
  452. }
  453. async parseVideo() {
  454. for (const script of document.scripts) {
  455. let urls = script.innerHTML.match(VIDEO_URL_REGEX_GLOBAL) || [];
  456. for (const url of urls) {
  457. if (await this.check(url)) {
  458. currentMedia.video = url;
  459. return;
  460. }
  461. }
  462. }
  463. }
  464. },
  465. REQUEST: class Parser extends BaseParser {
  466. constructor() {
  467. super();
  468. this.video = undefined;
  469. let that = this;
  470. const open = XMLHttpRequest.prototype.open;
  471. XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
  472. if (!that.video) {
  473. let urls = url.match(VIDEO_URL_REGEX_GLOBAL) || [];
  474. for (const vurl of urls) {
  475. that.check(vurl).check().then(
  476. result => {
  477. if (result === true) {
  478. that.video = vurl;
  479. }
  480. }
  481. )
  482. }
  483.  
  484. }
  485. return open.apply(this, arguments);
  486. };
  487.  
  488. const originalFetch = fetch;
  489.  
  490. window.fetch = function (url, options) {
  491. return originalFetch(url, options).then(response => {
  492. if (!that.video) {
  493. let urls = url.match(VIDEO_URL_REGEX_GLOBAL) || [];
  494. for (const vurl of urls) {
  495. that.check(vurl).check().then(
  496. result => {
  497. if (result === true) {
  498. that.video = vurl;
  499. }
  500. }
  501. )
  502. }
  503. }
  504. return response;
  505. });
  506. };
  507. }
  508. async execute() {
  509. await this.parseTitle();
  510. await this.parseVideo();
  511. await this.parseReferer();
  512. await this.parseTime();
  513. }
  514. async parseVideo() {
  515. currentMedia.video = this.video;
  516. }
  517. },
  518. BILIBILI: class Parser extends BaseParser {
  519. async execute() {
  520. await this.parseTitle();
  521. await this.parseVideo();
  522. await this.parseReferer();
  523. await this.parseTime();
  524. }
  525. async parseVideo() {
  526. let videoInfo = undefined;
  527. if (currentUrl.startsWith('https://www.bilibili.com/bangumi/')) {
  528. videoInfo = await this.getVideoInfoByEpid();
  529. } else if (currentUrl.startsWith('https://www.bilibili.com/video/')) {
  530. videoInfo = await this.getVideoInfoByBvid();
  531. } else {
  532. videoInfo = await this.getVideoInfo();
  533. }
  534.  
  535. if (!videoInfo || !videoInfo.aid || !videoInfo.cid) {
  536. throw new Error('can not find aid and cid');
  537. }
  538.  
  539. const aid = videoInfo.aid;
  540. const cid = videoInfo.cid;
  541. const title = videoInfo.title;
  542. const codecid = currentConfig.global.parser.bilibili.preferredCodec;
  543. const quality = currentConfig.global.parser.bilibili.preferredQuality;
  544.  
  545. currentMedia.bilibili.cid = cid;
  546. currentMedia.title = title ? title : currentMedia.title;
  547. if (currentConfig.global.parser.bilibili.preferredSubtitle &&
  548. currentConfig.global.parser.bilibili.preferredSubtitle !== 'off') {
  549. currentMedia.subtitle = await this.getSubtitle(aid, cid);
  550. }
  551. // 支持传入音频优先获取 dash 格式视频,以支持更高分辨率
  552. if (currentPlayer.playEvent && currentPlayer.playEvent.indexOf('audio') > -1) {
  553. const dash = await this.getDash(aid, cid, codecid, quality);
  554. if (dash) {
  555. currentMedia.audio = dash.audio;
  556. currentMedia.video = dash.video;
  557. return;
  558. }
  559. }
  560. currentMedia.video = await this.getFlvOrMP4(aid, cid);
  561. }
  562. async getVideoInfo() {
  563. try {
  564. const initialState = __INITIAL_STATE__;
  565. if (!initialState) {
  566. return;
  567. }
  568. const videoInfo = initialState.epInfo || initialState.videoData || initialState.videoInfo;
  569. const aid = videoInfo.aid;
  570. const page = initialState.p;
  571. let cid = videoInfo.cid;
  572. let title = videoInfo.title;
  573. if (page && page > 1) {
  574. cid = initialState.cidMap[aid].cids[page];
  575. }
  576.  
  577. return {
  578. aid: aid,
  579. cid: cid,
  580. title: title
  581. };
  582. } catch (error) {
  583. console.error(error.message);
  584. }
  585. }
  586. async getVideoInfoByBvid() {
  587. let param = undefined;
  588. const bvids = currentUrl.match(/BV([0-9a-zA-Z]+)/);
  589. if (bvids && bvids[1]) {
  590. param = `bvid=${bvids[1]}`;
  591. } else {
  592. const avids = currentUrl.match(/av([0-9]+)/);
  593. param = `aid=${avids[1]}`;
  594. }
  595.  
  596. if (!param) {
  597. throw new Error('can not find bvid or avid');
  598. }
  599.  
  600. const response = await (await fetch(`https://api.bilibili.com/x/web-interface/view?${param}`, {
  601. method: 'GET',
  602. credentials: 'include'
  603. })).json();
  604.  
  605. let aid = response.data.aid;
  606. let cid = response.data.cid;
  607. let title = response.data.title;
  608.  
  609. // 分 p 视频
  610. const ps = currentUrl.match(/[?&]p=([^&]+)/);
  611. if (ps && response.data.pages.length > 1) {
  612. const p = ps[1];
  613. const currentPage = response.data.pages[p - 1];
  614. cid = currentPage.cid;
  615. title = currentPage.part;
  616. }
  617.  
  618. return {
  619. aid: aid,
  620. cid: cid,
  621. title: title
  622. };
  623. }
  624. async getVideoInfoByEpid() {
  625. let epid = undefined;
  626. let epids = currentUrl.match(/ep(\d+)/);
  627. if (epids && epids[1]) {
  628. epid = epids[1];
  629. } else {
  630. let epidElement = undefined;
  631. let epidElementClassNames = [
  632. "ep-item cursor visited",
  633. "ep-item cursor",
  634. "numberListItem_select__WgCVr",
  635. "imageListItem_wrap__o28QW",
  636. ];
  637. for (const className of epidElementClassNames) {
  638. epidElement = document.getElementsByClassName(className)[0];
  639. if (epidElement) {
  640. epid = epidElement.getElementsByTagName("a")[0].href.match(/ep(\d+)/)[1];
  641. break;
  642. }
  643. }
  644.  
  645. if (!epid) {
  646. epidElement = document.getElementsByClassName("squirtle-pagelist-select-item active squirtle-blink")[0];
  647. if (epidElement) {
  648. epid = epidElement.dataset.value;
  649. }
  650. }
  651. }
  652.  
  653. if (!epid) {
  654. throw new Error('can not find epid');
  655. }
  656.  
  657. const response = await (await fetch(`https://api.bilibili.com/pgc/view/web/season?ep_id=${epid}`, {
  658. method: 'GET',
  659. credentials: 'include'
  660. })).json();
  661. let section = response.result.section;
  662. if (!section) {
  663. section = new Array();
  664. }
  665. section.push({
  666. episodes: response.result.episodes
  667. });
  668. let currentEpisode;
  669. for (let i = section.length - 1; i >= 0; i--) {
  670. let episodes = section[i].episodes;
  671. for (const episode of episodes) {
  672. if (episode.id == epid) {
  673. currentEpisode = episode;
  674. break;
  675. }
  676. }
  677. if (currentEpisode) {
  678. return {
  679. aid: currentEpisode.aid,
  680. cid: currentEpisode.cid,
  681. title: currentEpisode.share_copy
  682. }
  683. }
  684. }
  685. }
  686. async getDash(aid, cid, codecid, quality) {
  687. const url = `https://api.bilibili.com/x/player/playurl?qn=120&otype=json&fourk=1&fnver=0&fnval=4048&avid=${aid}&cid=${cid}`;
  688. const response = await (await fetch(url, {
  689. method: 'GET',
  690. credentials: 'include'
  691. })).json();
  692. if (!response.data) {
  693. currentTryCount = MAX_TRY_COUNT;
  694. throw new Error(translation.requireLoginOrVip);
  695. }
  696. let video = undefined;
  697. let audio = undefined;
  698. let dash = response.data.dash;
  699. if (!dash) {
  700. return undefined;
  701. }
  702. let hiRes = dash.flac;
  703. let dolby = dash.dolby;
  704. if (hiRes && hiRes.audio) {
  705. audio = hiRes.audio.baseUrl;
  706. } else if (dolby && dolby.audio) {
  707. audio = dolby.audio[0].base_url;
  708. } else if (dash.audio) {
  709. audio = dash.audio[0].baseUrl;
  710. }
  711. let i = 0;
  712. while (i < dash.video.length &&
  713. dash.video[i].id > quality) {
  714. i++;
  715. }
  716. video = dash.video[i].baseUrl;
  717. let id = dash.video[i].id;
  718. while (i < dash.video.length) {
  719. if (dash.video[i].id != id) {
  720. break;
  721. }
  722. if (dash.video[i].codecid == codecid) {
  723. video = dash.video[i].baseUrl;
  724. break;
  725. }
  726. i++;
  727. }
  728. return {
  729. video: video,
  730. audio: audio
  731. };
  732. }
  733. async getFlvOrMP4(aid, cid) {
  734. const url = `https://api.bilibili.com/x/player/playurl?qn=120&otype=json&fourk=1&fnver=0&fnval=128&avid=${aid}&cid=${cid}`;
  735. const response = await (await fetch(url, {
  736. method: 'GET',
  737. credentials: 'include'
  738. })).json();
  739. if (!response.data) {
  740. currentTryCount = MAX_TRY_COUNT;
  741. throw new Error(translation.requireLoginOrVip);
  742. }
  743. return response.data.durl[0].url;
  744. }
  745. async getSubtitle(avid, cid) {
  746. const url = `https://api.bilibili.com/x/player/wbi/v2?aid=${avid}&cid=${cid}`;
  747. const response = await (await fetch(url, {
  748. method: 'GET',
  749. credentials: 'include'
  750. })).json();
  751.  
  752. if (response.code === 0 && response.data.subtitle && response.data.subtitle.subtitles.length > 0) {
  753. let subtitles = response.data.subtitle.subtitles;
  754. let url = subtitles[0].subtitle_url;
  755. let lan = subtitles[0].lan;
  756. for (const subtitle of subtitles) {
  757. if (currentConfig.global.parser.bilibili.preferredSubtitle.startsWith("zh") &&
  758. subtitle.lan.startsWith("zh")) {
  759. url = subtitle.subtitle_url;
  760. lan = subtitle.lan;
  761. }
  762. if (subtitle.lan == currentConfig.subtitlePrefer) {
  763. url = subtitle.subtitle_url;
  764. lan = subtitle.lan;
  765. break;
  766. }
  767. }
  768. if (url) {
  769. return `https://www.lckp.top/common/bilibili/jsonToSrt/?url=https:${url}&lan=${lan}`;
  770. }
  771. }
  772. }
  773. },
  774. BILIBILI_LIVE: class Parser extends BaseParser {
  775. async execute() {
  776. await this.parseVideo();
  777. await this.parseTitle();
  778. await this.parseReferer();
  779. }
  780. async parseVideo() {
  781. const roomids = currentUrl.match(
  782. /.*(roomid=|blanc\/|live.bilibili.com\/)(\d+).*/
  783. );
  784. const roomid = roomids ? roomids[2] : undefined;
  785.  
  786. if (!roomid) {
  787. throw new Error('can not find roomid');
  788. }
  789.  
  790. const quality = currentConfig.global.parser.bilibiliLive.preferredQuality;
  791. const url = `https://api.live.bilibili.com/room/v1/Room/playUrl?quality=${quality}&cid=${roomid}`;
  792. const response = await (await fetch(url, {
  793. method: 'GET',
  794. credentials: 'include'
  795. })).json();
  796.  
  797. const durls = response.data.durl;
  798. const line = currentConfig.global.parser.bilibiliLive.preferredLine;
  799. let durl = durls[durls.length - 1];
  800. for (let index = 0; index < durls.length; index++) {
  801. if (line == index) {
  802. durl = durls[index];
  803. break;
  804. }
  805. }
  806. currentMedia.video = durl.url;
  807. }
  808. },
  809. ANI_GAMER: class Parser extends BaseParser {
  810. async execute() {
  811. await this.parseVideo();
  812. await this.parseOrigin();
  813. await this.parseTitle();
  814. await this.parseTime();
  815. }
  816. async parseVideo() {
  817. let match = currentUrl.match(/[?&]sn=([^&]+)/);
  818. const sn = match ? match[1] : undefined;
  819. if (!sn) {
  820. return;
  821. }
  822. const device = localStorage.ANIME_deviceid;
  823. const url = `https://ani.gamer.com.tw/ajax/m3u8.php?sn=${sn}&device=${device}`;
  824. const response = await (await fetch(url, {
  825. method: 'GET',
  826. credentials: 'include'
  827. })).json();
  828. if (response.error && response.error.code === 1015) {
  829. throw new Error("請先跳過廣告后再嘗試");
  830. }
  831. currentMedia.video = response ? response.src : undefined;
  832. }
  833. },
  834. ANIME1: class Parser extends BaseParser {
  835. async execute() {
  836. await this.parseVideo();
  837. await this.parseTitle();
  838. await this.parseTime();
  839. }
  840. async parseVideo() {
  841. const anime1_api_url = 'https://v.anime1.me/api';
  842. const body = `d=${document.querySelector("video").getAttribute("data-apireq")}`;
  843. const response = await new Promise(res => {
  844. GM.xmlHttpRequest({
  845. headers: {
  846. "content-type": "application/x-www-form-urlencoded",
  847. },
  848. method: "POST",
  849. url: anime1_api_url,
  850. data: body,
  851. onload: function (response) {
  852. res(response);
  853. }
  854. });
  855. });
  856.  
  857. let cookies = [];
  858. let cookieLines = response.responseHeaders.match(/set-cookie:\s*([^;]*)/gi);
  859. if (cookieLines) {
  860. cookieLines.forEach(cookieStr => {
  861. let [key, value] = cookieStr.replace(/set-cookie:\s*/i, "").split("=");
  862. cookies.push(`${key}=${value}`);
  863. });
  864. }
  865. currentMedia.cookie = cookies.join("; ");
  866.  
  867. const video = response?.responseText ? JSON.parse(response.responseText).s?. [0]?.src : undefined;
  868. currentMedia.video = video ? "https:" + video : undefined;
  869. }
  870. },
  871. IFRAME: class Parser extends BaseParser {
  872. async execute() {
  873. iframe.postMessage({
  874. name: PROJECT_NAME,
  875. method: 'execute'
  876. }, '*');
  877. await sleep(REFRESH_INTERVAL);
  878. await this.parseTitle();
  879. }
  880. async pause() {
  881. iframe.postMessage({
  882. name: PROJECT_NAME,
  883. method: 'pause'
  884. }, '*');
  885. }
  886. async check() {
  887. return currentMedia.video ? true : false;
  888. }
  889. }
  890. };
  891.  
  892. function compress(str) {
  893. return btoa(String.fromCharCode(...pako.gzip(str)));
  894. };
  895.  
  896. function sleep(ms) {
  897. return new Promise((resolve) => setTimeout(resolve, ms));
  898. }
  899.  
  900. function loadConfig() {
  901. let config = GM_getValue('config');
  902. if (config) {
  903. if (config.global.version === defaultConfig.global.version) {
  904. return config;
  905. }
  906. console.log('更新配置 ......');
  907. config = updateConfig(defaultConfig, config);
  908. config.global.version = defaultConfig.global.version;
  909. } else {
  910. console.log('初始化配置 ......');
  911. config = JSON.parse(JSON.stringify(defaultConfig));
  912. for (const key in config.global.parser) {
  913. config.global.parser[key].regex = [];
  914. }
  915. }
  916. GM_setValue('config', config);
  917. return config;
  918. }
  919.  
  920. function updateConfig(defaultConfig, config) {
  921. function mergeDefaults(defaultObj, currentObj) {
  922. if (typeof defaultObj !== 'object' || defaultObj === null) {
  923. return currentObj !== undefined ? currentObj : defaultObj;
  924. }
  925.  
  926. if (Array.isArray(defaultObj)) {
  927. return Array.isArray(currentObj) ? currentObj : defaultObj;
  928. }
  929.  
  930. const merged = {};
  931. for (const key in defaultObj) {
  932. if (key === 'regex') {
  933. merged[key] = currentObj?. [key] || [];
  934. continue;
  935. }
  936. merged[key] = mergeDefaults(defaultObj[key], currentObj?. [key]);
  937. }
  938. return merged;
  939. }
  940.  
  941. const newConfig = mergeDefaults(defaultConfig, config);
  942. for (let index = 0; index < defaultConfig.players.length; index++) {
  943. const dp = defaultConfig.players[index];
  944. const np = newConfig.players[index];
  945. if (np && dp.name === np.name) {
  946. np.icon = dp.icon;
  947. np.readonly = dp.readonly;
  948. np.playEvent = dp.playEvent;
  949. if (!np.presetEvent.syncTime) {
  950. np.presetEvent.syncTime = dp.presetEvent.syncTime;
  951. }
  952. } else {
  953. newConfig.players.unshift(dp);
  954. }
  955. }
  956.  
  957. return newConfig;
  958. }
  959.  
  960. function matchParser(parser, url) {
  961. for (const key in parser) {
  962. for (const regex of parser[key].regex) {
  963. if (!regex || regex.startsWith('#') || regex.startsWith('//')) {
  964. continue;
  965. }
  966. if (new RegExp(regex).test(url)) {
  967. console.log(`match parser regex: ${new RegExp(regex)}\n${url}`);
  968. return new PARSER[key.replace(/[A-Z]/g, letter => `_${letter}`).toUpperCase()]();
  969. }
  970. }
  971. }
  972. }
  973.  
  974. // =================================== 按钮区域和设置页面 ===================================
  975.  
  976. var policy;
  977. try {
  978. policy = window.trustedTypes.createPolicy('externalPlayer', {
  979. createHTML: (string, sink) => string,
  980. createScript: (input) => input
  981. })
  982. } catch (error) {
  983. policy = {
  984. createHTML: (string, sink) => string,
  985. createScript: (input) => input
  986. }
  987. }
  988.  
  989. const FIRST_Z_INDEX = 999999999;
  990. const SECOND_Z_INDEX = FIRST_Z_INDEX - 1;
  991. const THIRD_Z_INDEX = SECOND_Z_INDEX - 1;
  992.  
  993. const COLORS = [{
  994. // 配色方案1
  995. PRIMARY: 'rgba(245, 166, 35, 1)',
  996. TEXT: 'rgba(90, 90, 90, 1)',
  997. TEXT_ACTIVE: 'rgba(255, 255, 255, 1)',
  998. WARNING: 'rgba(233, 78, 119, 1)',
  999. BORDER: 'rgba(243, 229, 213, 1)',
  1000. }, {
  1001. // 配色方案2
  1002. PRIMARY: 'rgba(60, 179, 113, 1)',
  1003. TEXT: 'rgba(47, 79, 79, 1)',
  1004. TEXT_ACTIVE: 'rgba(255, 255, 255, 1)',
  1005. WARNING: 'rgba(255, 111, 97, 1)',
  1006. BORDER: 'rgba(204, 231, 208, 1)',
  1007. }, {
  1008. // 配色方案3
  1009. PRIMARY: 'rgba(74, 144, 226, 1)',
  1010. TEXT: 'rgba(51, 51, 51, 1)',
  1011. TEXT_ACTIVE: 'rgba(255, 255, 255, 1)',
  1012. WARNING: 'rgba(242, 95, 92, 1)',
  1013. BORDER: 'rgba(217, 227, 240, 1)',
  1014. }]
  1015. const COLOR = COLORS[2];
  1016.  
  1017. var style;
  1018. var buttonDiv;
  1019. var toastDiv;
  1020. var loadingDiv;
  1021. var settingButton;
  1022. var settingIframe;
  1023. var loadingId;
  1024. var isReloading = false;
  1025.  
  1026. function appendCss() {
  1027. if (style) {
  1028. return;
  1029. }
  1030. style = document.createElement('style');
  1031. style.innerHTML = policy.createHTML(`
  1032. #${PROJECT_NAME}-toast-div {
  1033. z-index: ${FIRST_Z_INDEX};
  1034. position: fixed;
  1035. top: 20px;
  1036. left: 50%;
  1037. transform: translate(-50%, 0);
  1038. background-color: rgba(0, 0, 0, 0.8);
  1039. color: white;
  1040. font-size: 14px;
  1041. padding: 10px 20px;
  1042. border-radius: 5px;
  1043. opacity: 0;
  1044. transition: opacity 0.5s ease;
  1045. display: none;
  1046. letter-spacing: 1px;
  1047. }
  1048. #${PROJECT_NAME}-loading-div {
  1049. z-index: ${FIRST_Z_INDEX};
  1050. display: none;
  1051. position: fixed;
  1052. bottom: 50%;
  1053. left: 50%;
  1054. transform: translate(-50%, -50%);
  1055. background-color: rgba(0, 0, 0, 0);
  1056. }
  1057. #${PROJECT_NAME}-loading-div div {
  1058. width: 50px;
  1059. height: 50px;
  1060. background-color: ${COLOR.PRIMARY};
  1061. border-radius: 0;
  1062. -webkit-animation: sk-rotateplane 1.2s infinite ease-in-out;
  1063. animation: sk-rotateplane 1.2s infinite ease-in-out;
  1064. }
  1065. @-webkit-keyframes sk-rotateplane {
  1066. 0% {
  1067. -webkit-transform: perspective(120px)
  1068. }
  1069. 50% {
  1070. -webkit-transform: perspective(120px) rotateY(180deg)
  1071. }
  1072. 100% {
  1073. -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg)
  1074. }
  1075. }
  1076. @keyframes sk-rotateplane {
  1077. 0% {
  1078. transform: perspective(120px) rotateX(0deg) rotateY(0deg);
  1079. -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg)
  1080. }
  1081. 50% {
  1082. transform: perspective(120px) rotateX(-180deg) rotateY(0deg);
  1083. -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(0deg)
  1084. }
  1085. 100% {
  1086. transform: perspective(120px) rotateX(-180deg) rotateY(-180deg);
  1087. -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-180deg);
  1088. }
  1089. }
  1090. #${PROJECT_NAME}-button-div {
  1091. z-index: ${THIRD_Z_INDEX};
  1092. position: fixed;
  1093. display: none;
  1094. align-items: center;
  1095. width: auto;
  1096. height: auto;
  1097. left: ${currentConfig.global.buttonXCoord}px;
  1098. bottom: ${currentConfig.global.buttonYCoord}px;
  1099. padding: 5px;
  1100. border: 3px solid rgba(0, 0, 0, 0);
  1101. border-radius: 5px;
  1102. cursor: move;
  1103. gap: 10px;
  1104. background-color: rgba(0, 0, 0, 0);
  1105. min-width: ${50 * currentConfig.global.buttonScale}px;
  1106. min-height: ${50 * currentConfig.global.buttonScale}px;
  1107. }
  1108. #${PROJECT_NAME}-button-div button {
  1109. color: white;
  1110. font-size: 20px;
  1111. font-weight: bold;
  1112. width: 50px;
  1113. height: 50px;
  1114. outline: none;
  1115. border: none;
  1116. border-radius: 50%;
  1117. cursor: pointer;
  1118. background-size: cover;
  1119. background-color: rgba(0, 0, 0, 0);
  1120. transition: opacity 0.5s ease, visibility 0s linear 0.5s;
  1121. }
  1122. #${PROJECT_NAME}-button-div:hover {
  1123. background-color: rgb(255, 255, 255, 0.3) !important;
  1124. }
  1125. #${PROJECT_NAME}-button-div:hover button {
  1126. visibility: visible !important;
  1127. transition: opacity 0.5s ease, visibility 0s;
  1128. }
  1129. #${PROJECT_NAME}-button-div button:hover {
  1130. transform: scale(1.06);
  1131. box-shadow: 0px 0px 16px #e6e6e6;
  1132. }
  1133. #${PROJECT_NAME}-setting-button {
  1134. visibility: hidden;
  1135. position: absolute;
  1136. right: ${-12 * currentConfig.global.buttonScale}px !important;
  1137. top: ${-12 * currentConfig.global.buttonScale}px !important;
  1138. width: ${25 * currentConfig.global.buttonScale}px !important;
  1139. height: ${25 * currentConfig.global.buttonScale}px !important;
  1140. background-image: url('data:image/svg+xml,<svg t="1731846507027" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4281" width="16" height="16"><path d="M616.533333 512.128c0-25.6-9.941333-49.536-28.16-67.669333a95.744 95.744 0 0 0-67.84-28.074667c-25.685333 0-49.706667 9.984-67.925333 28.074667a95.146667 95.146667 0 0 0-28.16 67.669333c0 25.6 10.069333 49.578667 28.16 67.712 18.218667 18.048 42.24 28.074667 67.925333 28.074667 25.642667 0 49.664-10.026667 67.84-28.074667 18.218667-18.133333 28.16-42.112 28.16-67.712z m-202.112 352.896l48-55.978667a309.290667 309.290667 0 0 0 99.029334 0l48 55.978667a27.52 27.52 0 0 0 30.208 7.978667l2.218666-0.768a380.074667 380.074667 0 0 0 118.186667-68.138667l1.834667-1.536a27.434667 27.434667 0 0 0 8.106666-30.037333l-24.746666-69.546667a298.666667 298.666667 0 0 0 49.322666-85.205333l72.874667-13.44a27.477333 27.477333 0 0 0 22.058667-22.101334l0.426666-2.304a384.64 384.64 0 0 0 0-135.936l-0.426666-2.304a27.477333 27.477333 0 0 0-22.058667-22.058666l-73.216-13.525334a302.293333 302.293333 0 0 0-49.194667-84.650666l25.002667-70.016a27.306667 27.306667 0 0 0-8.149333-30.037334l-1.834667-1.536a383.018667 383.018667 0 0 0-118.186667-68.138666l-2.218666-0.768a27.605333 27.605333 0 0 0-30.208 7.936l-48.512 56.661333a302.592 302.592 0 0 0-97.834667 0L414.592 159.146667a27.52 27.52 0 0 0-30.208-7.978667l-2.218667 0.768a381.056 381.056 0 0 0-118.186666 68.138667l-1.834667 1.536a27.434667 27.434667 0 0 0-8.106667 30.037333l24.96 69.973333a296.192 296.192 0 0 0-49.194666 84.693334l-73.216 13.525333a27.477333 27.477333 0 0 0-22.058667 22.058667l-0.426667 2.304a382.592 382.592 0 0 0 0 135.936l0.426667 2.304c2.048 11.221333 10.794667 20.053333 22.058667 22.101333l72.874666 13.44a300.672 300.672 0 0 0 49.365334 85.248l-24.832 69.504a27.306667 27.306667 0 0 0 8.149333 30.037333l1.834667 1.536a383.018667 383.018667 0 0 0 118.186666 68.138667l2.218667 0.768a27.733333 27.733333 0 0 0 30.037333-8.149333z m-44.8-352.853333A150.656 150.656 0 0 1 520.533333 361.642667a150.656 150.656 0 0 1 150.869334 150.442666A150.656 150.656 0 0 1 520.533333 662.613333a150.656 150.656 0 0 1-150.912-150.485333z" fill="${COLOR.PRIMARY}" p-id="4282"></path></svg>');
  1141. }
  1142. #${PROJECT_NAME}-setting-iframe {
  1143. z-index: ${SECOND_Z_INDEX};
  1144. position: fixed;
  1145. width: 1000px;
  1146. max-width: 100%;
  1147. height: 500px;
  1148. max-height: 90%;
  1149. top: 50%;
  1150. left: 50%;
  1151. transform: translate(-50%, -50%);
  1152. border: none;
  1153. border-radius: 5px;
  1154. box-shadow: 0 0 16px rgba(0, 0, 0, 0.6);
  1155. background-color: #fff;
  1156. display: none;
  1157. }
  1158. `);
  1159. document.head.appendChild(style);
  1160. }
  1161.  
  1162. function appendToastDiv() {
  1163. const TOAST_DIV_ID = `${PROJECT_NAME}-toast-div`;
  1164. if (document.getElementById(TOAST_DIV_ID)) {
  1165. return;
  1166. }
  1167. toastDiv = document.createElement('div');
  1168. toastDiv.id = TOAST_DIV_ID;
  1169. document.body.appendChild(toastDiv);
  1170. }
  1171.  
  1172. function showToast(message) {
  1173. toastDiv.textContent = message;
  1174. toastDiv.style.opacity = '0.9';
  1175. toastDiv.style.display = 'block';
  1176. setTimeout(() => {
  1177. toastDiv.style.opacity = '0';
  1178. toastDiv.style.display = 'none';
  1179. }, 5000);
  1180. }
  1181.  
  1182. function appendLoadingDiv() {
  1183. const LOADING_DIV_ID = `${PROJECT_NAME}-loading-div`;
  1184. if (document.getElementById(LOADING_DIV_ID)) {
  1185. return;
  1186. }
  1187. loadingDiv = document.createElement('div');
  1188. loadingDiv.id = LOADING_DIV_ID;
  1189. loadingDiv.appendChild(document.createElement('div'));
  1190. document.body.appendChild(loadingDiv);
  1191. }
  1192.  
  1193. function showLoading(timeout) {
  1194. if (loadingId) {
  1195. clearTimeout(loadingId);
  1196. loadingId = undefined;
  1197. }
  1198. if (!timeout) {
  1199. timeout = 10000;
  1200. }
  1201. loadingDiv.style.display = 'block';
  1202. loadingId = setTimeout(() => {
  1203. if (loadingDiv.style.display === 'block') {
  1204. hideLoading();
  1205. showToast(translation.loadTimeout);
  1206. }
  1207. }, timeout);
  1208. }
  1209.  
  1210. function hideLoading() {
  1211. loadingDiv.style.display = 'none';
  1212. }
  1213.  
  1214. function appendButtonDiv() {
  1215. const BUTTON_DIV_ID = `${PROJECT_NAME}-button-div`;
  1216. if (document.getElementById(BUTTON_DIV_ID)) {
  1217. buttonDiv.style.display = "none";
  1218. return;
  1219. }
  1220. buttonDiv = document.createElement('div');
  1221. buttonDiv.id = BUTTON_DIV_ID;
  1222. buttonDiv.addEventListener('mousedown', (e) => {
  1223. if (e.target.tagName === 'BUTTON') {
  1224. return;
  1225. }
  1226. let offsetX = e.clientX - buttonDiv.getBoundingClientRect().left;
  1227. let offsetY = e.clientY - buttonDiv.getBoundingClientRect().top;
  1228.  
  1229. document.addEventListener('mouseup', mouseUpHandler);
  1230. document.addEventListener('mousemove', mouseMoveHandler);
  1231.  
  1232. function mouseUpHandler() {
  1233. buttonDiv.style.border = '3px solid rgba(0, 0, 0, 0)';
  1234. document.removeEventListener('mousemove', mouseMoveHandler);
  1235. document.removeEventListener('mouseup', mouseUpHandler);
  1236. }
  1237.  
  1238. function mouseMoveHandler(e) {
  1239. buttonDiv.style.border = `3px solid ${COLOR.PRIMARY}`;
  1240. let newX = e.clientX - offsetX;
  1241. let newY = e.clientY - offsetY;
  1242.  
  1243. const windowWidth = window.innerWidth;
  1244. const windowHeight = window.innerHeight;
  1245. const divWidth = buttonDiv.offsetWidth;
  1246. const divHeight = buttonDiv.offsetHeight;
  1247.  
  1248. if (newX < 0) newX = 0;
  1249. if (newX + divWidth > windowWidth) newX = windowWidth - divWidth;
  1250. if (newY < 0) newY = 0;
  1251. if (newY + divHeight > windowHeight) newY = windowHeight - divHeight;
  1252.  
  1253. newY = windowHeight - newY - divHeight;
  1254. buttonDiv.style.left = `${newX}px`;
  1255. buttonDiv.style.bottom = `${newY}px`;
  1256. currentConfig.global.buttonXCoord = newX;
  1257. currentConfig.global.buttonYCoord = newY;
  1258. GM_setValue('config', currentConfig);
  1259. }
  1260. });
  1261. document.body.appendChild(buttonDiv);
  1262.  
  1263. appendPlayButton();
  1264. appendSettingButton();
  1265.  
  1266. // 全屏隐藏
  1267. document.addEventListener("fullscreenchange", () => {
  1268. if (document.fullscreenElement) {
  1269. buttonDiv.style.display = "none";
  1270. } else {
  1271. if (currentParser) {
  1272. buttonDiv.style.display = "flex";
  1273. }
  1274. }
  1275. });
  1276. }
  1277.  
  1278. function appendPlayButton() {
  1279. if (!currentConfig.players) {
  1280. return;
  1281. }
  1282. currentConfig.players.forEach(player => {
  1283. if (player.enable !== true) {
  1284. return;
  1285. }
  1286. const playButton = document.createElement('button');
  1287. if (player.icon) {
  1288. const image = new Image();
  1289. image.src = player.icon;
  1290. image.onload = () => playButton.style.backgroundImage = `url(${image.src})`;
  1291. image.onerror = () => {
  1292. playButton.style.backgroundColor = COLOR.PRIMARY;
  1293. playButton.textContent = player.name ? player.name.substring(0, 1) : 'P';
  1294. };
  1295. } else {
  1296. playButton.style.backgroundColor = COLOR.PRIMARY;
  1297. playButton.textContent = player.name ? player.name.substring(0, 1) : 'P';
  1298. }
  1299. playButton.style.width = `${player.iconSize * currentConfig.global.buttonScale}px`;
  1300. playButton.style.height = `${player.iconSize * currentConfig.global.buttonScale}px`;
  1301.  
  1302. // 自动隐藏
  1303. if (currentConfig.global.buttonVisibilityDuration == 0) {
  1304. playButton.style.visibility = 'hidden';
  1305. } else if (currentConfig.global.buttonVisibilityDuration > 0) {
  1306. setTimeout(() => {
  1307. playButton.style.visibility = 'hidden';
  1308. }, currentConfig.global.buttonVisibilityDuration);
  1309. }
  1310.  
  1311. playButton.addEventListener('click', async function () {
  1312. playButton.disabled = true;
  1313. if (currentParser) {
  1314. currentParser.play(player);
  1315. } else {
  1316. showToast(translation.noMatchingParserFound);
  1317. }
  1318. setTimeout(() => {
  1319. playButton.disabled = false;
  1320. }, REFRESH_INTERVAL * 3);
  1321. });
  1322.  
  1323. buttonDiv.appendChild(playButton);
  1324. });
  1325. }
  1326.  
  1327. function appendSettingButton() {
  1328. settingButton = document.createElement('button');
  1329. settingButton.id = `${PROJECT_NAME}-setting-button`;
  1330. settingButton.title = 'Ctrl + Alt + E';
  1331.  
  1332. settingButton.addEventListener('click', async () => {
  1333. await appendSettingIframe();
  1334. if (settingIframe.style.display === "block") {
  1335. settingIframe.style.display = "none";
  1336. } else {
  1337. settingIframe.contentWindow.postMessage({
  1338. name: PROJECT_NAME,
  1339. method: 'loadConfig',
  1340. defaultConfig: defaultConfig,
  1341. config: currentConfig
  1342. }, '*');
  1343. settingIframe.style.display = "block";
  1344. }
  1345. });
  1346. buttonDiv.appendChild(settingButton);
  1347.  
  1348. // 失去焦点隐藏设置页面
  1349. document.addEventListener('click', (event) => {
  1350. if (settingIframe && settingIframe.style.display === 'block' &&
  1351. !settingButton.contains(event.target) &&
  1352. !settingIframe.contains(event.target)) {
  1353. settingIframe.style.display = 'none';
  1354. }
  1355. });
  1356. }
  1357.  
  1358. async function appendSettingIframe() {
  1359. const SETTING_IFRAME_ID = `${PROJECT_NAME}-setting-iframe`;
  1360. if (document.getElementById(SETTING_IFRAME_ID)) {
  1361. return;
  1362. }
  1363. settingIframe = document.createElement('iframe');
  1364. settingIframe.id = SETTING_IFRAME_ID;
  1365. let settingIframeHtml = `
  1366. <!DOCTYPE html>
  1367. <html lang="en">
  1368.  
  1369. <head>
  1370. <meta charset="UTF-8">
  1371. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  1372. <title>External Player</title>
  1373. <style>
  1374. :root {
  1375. --primary-color: ${COLOR.PRIMARY};
  1376. --text-color: ${COLOR.TEXT};
  1377. --text-active-color: ${COLOR.TEXT_ACTIVE};
  1378. --warning-color: ${COLOR.WARNING};
  1379. --border-color: ${COLOR.BORDER};
  1380. }
  1381.  
  1382. body {
  1383. display: flex;
  1384. flex-direction: row;
  1385. height: 100vh;
  1386. margin: 0;
  1387. }
  1388.  
  1389. body,
  1390. button,
  1391. input,
  1392. textarea,
  1393. select {
  1394. font-family: auto;
  1395. color: var(--text-color);
  1396. }
  1397.  
  1398. ::placeholder {
  1399. font-family: auto;
  1400. color: var(--text-color);
  1401. opacity: 0.2;
  1402. }
  1403.  
  1404. #sidebar-container {
  1405. display: none;
  1406. flex: 0 0 200px;
  1407. flex-direction: column;
  1408. background-color: #f4f4f4;
  1409. box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
  1410. padding: 25px 20px 35px 20px;
  1411. }
  1412.  
  1413. #sidebar {
  1414. flex: 1;
  1415. overflow-y: auto;
  1416. position: relative;
  1417. border: none;
  1418. border-radius: 5px;
  1419. margin-bottom: 10px;
  1420. }
  1421.  
  1422. #sidebar::-webkit-scrollbar {
  1423. display: none !important;
  1424. }
  1425.  
  1426. .reset-button,
  1427. #add-tab-button,
  1428. #save-button,
  1429. #sidebar button {
  1430. width: 200px;
  1431. padding: 10px;
  1432. margin: 0 0 10px 0;
  1433. border: none;
  1434. border-radius: 5px;
  1435. background-color: #e0e0e0;
  1436. cursor: pointer;
  1437. font-size: 15px;
  1438. white-space: nowrap;
  1439. display: inline-flex;
  1440. position: relative;
  1441. align-items: center;
  1442. justify-content: center;
  1443. }
  1444.  
  1445. #add-tab-button,
  1446. #save-button {
  1447. background-color: var(--primary-color);
  1448. color: var(--text-active-color);
  1449. margin: 0;
  1450. }
  1451.  
  1452. #add-tab-button {
  1453. font-size: 25px;
  1454. line-height: 21.45px;
  1455. }
  1456.  
  1457. #add-tab-button:hover,
  1458. #save-button:hover {
  1459. opacity: 0.9;
  1460. }
  1461.  
  1462. #reset-button-coord-button {
  1463. padding: 7px 10px;
  1464. }
  1465.  
  1466. .reset-button {
  1467. margin: 0;
  1468. width: 80px;
  1469. background-color: var(--warning-color);
  1470. color: var(--text-active-color);
  1471. opacity: 0.6;
  1472. }
  1473.  
  1474. .reset-button:hover {
  1475. opacity: 0.8;
  1476. }
  1477.  
  1478. #sidebar button svg {
  1479. width: 20px !important;
  1480. height: 20px !important;
  1481. position: absolute;
  1482. left: 10px;
  1483. fill: var(--text-color);
  1484. }
  1485.  
  1486. #content .radio-button svg {
  1487. width: 20px !important;
  1488. height: 20px !important;
  1489. fill: var(--text-color);
  1490. }
  1491.  
  1492. #sidebar button.active svg,
  1493. #sidebar button:hover svg,
  1494. #content .radio-button.active svg,
  1495. #content .radio-button:hover svg {
  1496. fill: var(--text-active-color)
  1497. }
  1498.  
  1499. #sidebar button.active {
  1500. background-color: var(--primary-color);
  1501. color: var(--text-active-color);
  1502. }
  1503.  
  1504. #sidebar button:hover {
  1505. background-color: var(--primary-color);
  1506. color: var(--text-active-color);
  1507. }
  1508.  
  1509. #content-container {
  1510. display: none;
  1511. flex-direction: column;
  1512. flex: 1;
  1513. padding: 25px 20px 0 20px;
  1514. }
  1515.  
  1516. #content {
  1517. flex: 1;
  1518. padding: 20px;
  1519. overflow-y: auto;
  1520. position: relative;
  1521. border: 1px solid var(--border-color);
  1522. border-radius: 5px;
  1523. margin-bottom: 15px;
  1524. }
  1525.  
  1526. .tab {
  1527. display: none;
  1528. position: relative;
  1529. }
  1530.  
  1531. .tab.active {
  1532. display: block;
  1533. }
  1534.  
  1535. .input-group {
  1536. margin-bottom: 15px;
  1537. }
  1538.  
  1539. label {
  1540. display: flex;
  1541. margin-bottom: 5px;
  1542. font-weight: bold;
  1543. align-items: center;
  1544. }
  1545.  
  1546. input[type="number"] {
  1547. width: calc(100% - 16px);
  1548. font-size: 14px;
  1549. border-radius: 5px;
  1550. border: 1px solid var(--border-color);
  1551. margin-right: 15px;
  1552. padding: 8px;
  1553. }
  1554.  
  1555. input[type="text"],
  1556. input[type="search"],
  1557. textarea {
  1558. width: 100%;
  1559. min-width: 400px;
  1560. padding: 8px;
  1561. border: 1px solid var(--border-color);
  1562. border-radius: 5px;
  1563. font-size: 14px;
  1564. box-sizing: border-box;
  1565. }
  1566.  
  1567. textarea {
  1568. resize: vertical;
  1569. height: 160px;
  1570. }
  1571.  
  1572. .switch {
  1573. position: relative;
  1574. display: inline-block;
  1575. width: 54px;
  1576. height: 24px;
  1577. }
  1578.  
  1579. .switch input {
  1580. opacity: 0;
  1581. width: 0;
  1582. height: 0;
  1583. }
  1584.  
  1585. .switch-slider {
  1586. position: absolute;
  1587. cursor: pointer;
  1588. top: 0;
  1589. left: 0;
  1590. right: 0;
  1591. bottom: 0;
  1592. background-color: #ccc;
  1593. transition: 0.4s;
  1594. border-radius: 34px;
  1595. }
  1596.  
  1597. .switch-slider:before {
  1598. position: absolute;
  1599. content: "";
  1600. height: 16px;
  1601. width: 16px;
  1602. border-radius: 50%;
  1603. left: 4px;
  1604. bottom: 4px;
  1605. background-color: var(--text-active-color);
  1606. transition: 0.4s;
  1607. }
  1608.  
  1609. input:checked+.switch-slider {
  1610. background-color: var(--primary-color);
  1611. }
  1612.  
  1613. input:checked+.switch-slider:before {
  1614. transform: translateX(30px);
  1615. }
  1616.  
  1617. .remove-button {
  1618. position: absolute;
  1619. opacity: 0.9;
  1620. top: -10px;
  1621. right: 0;
  1622. background: var(--warning-color);
  1623. color: var(--text-active-color);
  1624. border: none;
  1625. padding: 5px 10px;
  1626. cursor: pointer;
  1627. border-radius: 5px;
  1628. font-size: 14px;
  1629. }
  1630.  
  1631. .remove-button:hover {
  1632. opacity: 1;
  1633. }
  1634.  
  1635. .radio-button-group,
  1636. .checkbox-group {
  1637. display: flex;
  1638. flex-wrap: wrap;
  1639. gap: 10px;
  1640. margin-bottom: 15px;
  1641. }
  1642.  
  1643. .radio-button,
  1644. .checkbox-group .chekbox-label {
  1645. padding: 8px 12px;
  1646. background-color: #e0e0e0;
  1647. cursor: pointer;
  1648. border-radius: 5px;
  1649. font-size: 14px;
  1650. font-weight: normal;
  1651. min-width: 132px;
  1652. display: inline-flex;
  1653. justify-content: center;
  1654. align-items: center;
  1655. gap: 12px;
  1656. height: 20px;
  1657. margin: 0;
  1658. }
  1659.  
  1660. .parser .radio-button,
  1661. .parser .checkbox-group .chekbox-label {
  1662. min-width: 122.5px;
  1663. }
  1664.  
  1665. .radio-button.active,
  1666. .checkbox-group input:checked+.chekbox-label {
  1667. background-color: var(--primary-color);
  1668. color: var(--text-active-color);
  1669. }
  1670.  
  1671. .radio-button:hover {
  1672. background-color: var(--primary-color);
  1673. color: var(--text-active-color);
  1674. }
  1675.  
  1676. .checkbox-group input[type="checkbox"] {
  1677. display: none;
  1678. }
  1679.  
  1680. #language {
  1681. padding: 8px;
  1682. border-radius: 5px;
  1683. cursor: pointer;
  1684. width: 100%;
  1685. border: 1px solid var(--border-color);
  1686. }
  1687.  
  1688. .parser {
  1689. border: 1px solid var(--border-color);
  1690. border-radius: 5px;
  1691. padding: 10px 20px;
  1692. }
  1693.  
  1694. .parser textarea {
  1695. margin-bottom: 10px;
  1696. resize: none;
  1697. }
  1698.  
  1699. a {
  1700. color: var(--text-color);
  1701. text-decoration: none;
  1702. font-weight: bold;
  1703. transition: color 0.3s ease, border-bottom 0.3s ease;
  1704. border-bottom: 2px solid transparent;
  1705. }
  1706.  
  1707. a:hover {
  1708. color: var(--primary-color);
  1709. border-bottom-color: var(--primary-color);
  1710. }
  1711.  
  1712. #tab-container {
  1713. flex: 1;
  1714. padding: 20px;
  1715. overflow-y: auto;
  1716. position: relative;
  1717. border: 1px solid var(--border-color);
  1718. border-radius: 5px;
  1719. margin-bottom: 10px;
  1720. }
  1721.  
  1722. :disabled {
  1723. opacity: 0.6;
  1724. }
  1725.  
  1726. div.disabled,
  1727. button:disabled {
  1728. pointer-events: none !important;
  1729. cursor: not-allowed !important;
  1730. }
  1731.  
  1732. .parser textarea:disabled {
  1733. height: 30px;
  1734. overflow-y: hidden;
  1735. line-height: 20px;
  1736. }
  1737.  
  1738. textarea:disabled::-webkit-scrollbar {
  1739. display: none;
  1740. }
  1741.  
  1742. ::-webkit-scrollbar {
  1743. width: 20px !important;
  1744. height: 20px !important;
  1745. }
  1746.  
  1747. ::-webkit-scrollbar-thumb {
  1748. background: var(--border-color) !important;
  1749. border-radius: 5px !important;
  1750. }
  1751.  
  1752. ::-webkit-scrollbar-thumb:hover {
  1753. background: var(--primary-color) !important;
  1754. }
  1755.  
  1756. ::-webkit-scrollbar-track {
  1757. background: rgb(245, 245, 245) !important;
  1758. border-radius: 5px !important;
  1759. }
  1760.  
  1761. select:focus,
  1762. input:focus,
  1763. textarea:focus {
  1764. border-color: var(--primary-color);
  1765. outline: none;
  1766. }
  1767.  
  1768. #footer {
  1769. font-size: 14px;
  1770. height: 35px;
  1771. display: flex;
  1772. justify-content: center;
  1773. align-items: center;
  1774. }
  1775.  
  1776. #footer svg {
  1777. width: 20px;
  1778. height: 20px;
  1779. margin-bottom: -3px;
  1780. }
  1781.  
  1782. #footer a,
  1783. #footer a:hover {
  1784. margin-left: 3px;
  1785. margin-right: 3px;
  1786. font-weight: normal;
  1787. border-bottom: none !important;
  1788. text-decoration: none !important;
  1789. }
  1790. </style>
  1791. </head>
  1792.  
  1793. <body>
  1794. <div id="sidebar-container">
  1795. <div id="sidebar">
  1796. <button id="global-button" class="tab-button active" data-tab="global">
  1797. <svg t="1732015880724" class="icon" viewBox="0 0 1024 1024" version="1.1"
  1798. xmlns="http://www.w3.org/2000/svg" p-id="4317" width="32" height="32">
  1799. <path
  1800. d="M386.35 112.05h-228.7c-25.2 0-45.7 20.5-45.7 45.7v228.5c0 25.2 20.4 45.7 45.6 45.8h228.6c25.2 0 45.7-20.4 45.8-45.6V157.65c0.1-25.2-20.4-45.6-45.6-45.6z"
  1801. p-id="4318"></path>
  1802. <path
  1803. d="M157.55 80.05h229c42.8 0 77.5 34.7 77.5 77.5v229c0 42.8-34.7 77.5-77.5 77.5h-229c-42.8 0-77.5-34.7-77.5-77.5v-229c0-42.8 34.7-77.5 77.5-77.5z m228.9 320.5c7.8 0 14.1-6.3 14.1-14.1v-229c0-7.8-6.3-14.1-14.1-14.1h-229c-7.8 0-14.1 6.3-14.1 14.1v229c0 7.8 6.3 14.1 14.1 14.1h229z"
  1804. p-id="4319"></path>
  1805. <path
  1806. d="M387.55 590.25h-231.1c-25.5 0-46.2 20.7-46.2 46.2v231.1c0 25.5 20.7 46.2 46.2 46.2h231.1c25.5 0 46.2-20.7 46.2-46.2v-231.1c0-25.5-20.7-46.2-46.2-46.2z"
  1807. p-id="4320"></path>
  1808. <path
  1809. d="M157.55 560.05h229c42.8 0 77.5 34.7 77.5 77.5v229c0 42.8-34.7 77.5-77.5 77.5h-229c-42.8 0-77.5-34.7-77.5-77.5v-229c0-42.8 34.7-77.5 77.5-77.5z m228.9 320.5c7.8 0 14.1-6.3 14.1-14.1v-229c0-7.8-6.3-14.1-14.1-14.1h-229c-7.8 0-14.1 6.3-14.1 14.1v229c0 7.8 6.3 14.1 14.1 14.1h229zM637.55 80.05h229c42.8 0 77.5 34.7 77.5 77.5v229c0 42.8-34.7 77.5-77.5 77.5h-229c-42.8 0-77.5-34.7-77.5-77.5v-229c0-42.8 34.7-77.5 77.5-77.5z m228.9 320.5c7.8 0 14.1-6.3 14.1-14.1v-229c0-7.8-6.3-14.1-14.1-14.1h-229c-7.8 0-14.1 6.3-14.1 14.1v229c0 7.8 6.3 14.1 14.1 14.1h229z"
  1810. p-id="4321"></path>
  1811. <path
  1812. d="M866.306 592.006h-228.6c-25.2 0-45.7 20.5-45.7 45.7v228.5c0 25.2 20.5 45.7 45.7 45.7h228.5c25.2 0 45.7-20.4 45.8-45.6v-228.6c0-25.2-20.5-45.7-45.7-45.7z"
  1813. p-id="4322"></path>
  1814. <path
  1815. d="M637.506 560.006h229c42.8 0 77.5 34.7 77.5 77.5v229c0 42.8-34.7 77.5-77.5 77.5h-229c-42.8 0-77.5-34.7-77.5-77.5v-229c0-42.8 34.7-77.5 77.5-77.5z m229 320.6c7.8 0 14.1-6.3 14.1-14.1v-229c0-7.8-6.3-14.1-14.1-14.1h-229c-7.8 0-14.1 6.3-14.1 14.1v229c0 7.8 6.3 14.1 14.1 14.1h229z"
  1816. p-id="4323"></path>
  1817. </svg>
  1818. <span data-translate="global">全局配置</span>
  1819. </button>
  1820. </div>
  1821. <button id="add-tab-button">+</button>
  1822. </div>
  1823.  
  1824. <div id="content-container">
  1825. <div id="content">
  1826. <div id="global" class="tab active">
  1827. <div class="input-group">
  1828. <label data-translate="version">版本</label>
  1829. <input type="text" id="version" readonly></input>
  1830. </div>
  1831. <div class="input-group">
  1832. <label data-translate="language">语言</label>
  1833. <select id="language">
  1834. <option value="zh" selected>中文</option>
  1835. <option value="en">English</option>
  1836. </select>
  1837. </div>
  1838. <div class="input-group">
  1839. <label data-translate="buttonCoord">按钮坐标</label>
  1840. <label>
  1841. <input type="number" id="buttonXCoord" min="0" placeholder="0">
  1842. <input type="number" id="buttonYCoord" min="0" placeholder="0">
  1843. <button id="reset-button-coord-button" class="reset-button" data-translate="reset">重置</button>
  1844. </label>
  1845. </div>
  1846. <div class="input-group">
  1847. <label data-translate="buttonScale">按钮比例</label>
  1848. <input type="number" id="buttonScale" min="0.01" max="10" step="0.01" placeholder="1.00">
  1849. </div>
  1850. <div class="input-group">
  1851. <label data-translate="buttonVisibilityDuration">按钮可见时长(毫秒,-1:一直可见)</label>
  1852. <input type="number" id="buttonVisibilityDuration" min="-1" placeholder="3000">
  1853. </div>
  1854. <div class="input-group">
  1855. <label data-translate="networkProxy">网络代理</label>
  1856. <input type="text" id="networkProxy" placeholder="http://127.0.0.1:7890"></input>
  1857. </div>
  1858. <label data-translate="parser">解析器</label>
  1859. <div class="input-group parser" id="ytdlp">
  1860. <label><a href="https://github.com/yt-dlp/yt-dlp" target="_blank">YTDLP</a></label>
  1861. <textarea name="regex" disabled></textarea>
  1862. <textarea name="regex"></textarea>
  1863. <label data-translate="preferredQuality">首选画质</label>
  1864. <div class="radio-button-group" name="preferredQuality">
  1865. <div class="radio-button active" value="unlimited" data-translate="unlimited">无限制</div>
  1866. <div class="radio-button" value="2160">2160P</div>
  1867. <div class="radio-button" value="1440">1440P</div>
  1868. <div class="radio-button" value="1080">1080P</div>
  1869. <div class="radio-button" value="720">720P</div>
  1870. </div>
  1871. </div>
  1872. <div class="input-group parser" id="video">
  1873. <label><a href="https://github.com/LuckyPuppy514/external-player" target="_blank">VIDEO</a></label>
  1874. <textarea name="regex" disabled></textarea>
  1875. <textarea name="regex"></textarea>
  1876. </div>
  1877. <div class="input-group parser" id="url">
  1878. <label><a href="https://github.com/LuckyPuppy514/external-player" target="_blank">URL</a></label>
  1879. <textarea name="regex" disabled></textarea>
  1880. <textarea name="regex"></textarea>
  1881. </div>
  1882. <div class="input-group parser" id="html">
  1883. <label><a href="https://github.com/LuckyPuppy514/external-player" target="_blank">HTML</a></label>
  1884. <textarea name="regex" disabled></textarea>
  1885. <textarea name="regex"></textarea>
  1886. </div>
  1887. <div class="input-group parser" id="script">
  1888. <label><a href="https://github.com/LuckyPuppy514/external-player" target="_blank">SCRIPT</a></label>
  1889. <textarea name="regex" disabled></textarea>
  1890. <textarea name="regex"></textarea>
  1891. </div>
  1892. <div class="input-group parser" id="request">
  1893. <label><a href="https://github.com/LuckyPuppy514/external-player"
  1894. target="_blank">REQUEST</a></label>
  1895. <textarea name="regex" disabled></textarea>
  1896. <textarea name="regex"></textarea>
  1897. </div>
  1898. <div class="input-group parser" id="bilibili">
  1899. <label><a href="https://github.com/SocialSisterYi/bilibili-API-collect"
  1900. target="_blank">BILIBILI</a></label>
  1901. <textarea name="regex" disabled></textarea>
  1902. <textarea name="regex" style="display: none;"></textarea>
  1903. <label data-translate="preferredQuality">首选画质</label>
  1904. <div class="radio-button-group" name="preferredQuality">
  1905. <div class="radio-button active" value="127" data-translate="unlimited">无限制</div>
  1906. <div class="radio-button" value="126">2160P</div>
  1907. <div class="radio-button" value="116">1080P</div>
  1908. <div class="radio-button" value="74">720P</div>
  1909. </div>
  1910. <label data-translate="preferredSubtitle">首选字幕</label>
  1911. <div class="radio-button-group" name="preferredSubtitle">
  1912. <div class="radio-button active" value="off" data-translate="off">关闭</div>
  1913. <div class="radio-button" value="zh-Hans">简体</div>
  1914. <div class="radio-button" value="zh-Hant">繁体</div>
  1915. <div class="radio-button" value="en-US">English</div>
  1916. </div>
  1917. <label data-translate="preferredCodec">首选编码</label>
  1918. <div class="radio-button-group" name="preferredCodec">
  1919. <div class="radio-button active" value="12">HEVC</div>
  1920. <div class="radio-button" value="13">AV1</div>
  1921. <div class="radio-button" value="7">AVC</div>
  1922. </div>
  1923. </div>
  1924. <div class="input-group parser" id="bilibiliLive">
  1925. <label><a href="https://github.com/SocialSisterYi/bilibili-API-collect" target="_blank">BILIBILI
  1926. LIVE</a></label>
  1927. <textarea name="regex" disabled></textarea>
  1928. <textarea name="regex" style="display: none;"></textarea>
  1929. <label data-translate="preferredQuality">首选画质</label>
  1930. <div class="radio-button-group" name="preferredQuality">
  1931. <div class="radio-button active" value="4" data-translate="original">原画</div>
  1932. <div class="radio-button active" value="3" data-translate="hd">高清</div>
  1933. <div class="radio-button active" value="2" data-translate="smooth">流畅</div>
  1934. </div>
  1935. <label data-translate="preferredLine">首选线路</label>
  1936. <div class="radio-button-group" name="preferredLine">
  1937. <div class="radio-button active" value="0" data-translate="mainLine">主线</div>
  1938. <div class="radio-button active" value="1" data-translate="backupLine1">备线1</div>
  1939. <div class="radio-button active" value="2" data-translate="backupLine2">备线2</div>
  1940. <div class="radio-button active" value="3" data-translate="backupLine3">备线3</div>
  1941. </div>
  1942. </div>
  1943. </div>
  1944. </div>
  1945.  
  1946. <div style="margin: 0 auto;">
  1947. <button id="save-button" data-translate="save">保存</button>
  1948. <button id="reset-button" class="reset-button" data-translate="reset">重置</button>
  1949. </div>
  1950.  
  1951. <div id="footer">
  1952. <span>
  1953. <a href="https://github.com/LuckyPuppy514" target="_blank">
  1954. &copy 2024 LuckyPuppy514
  1955. </a>
  1956. <svg t="1731923678389" class="icon" viewBox="0 0 1024 1024" version="1.1"
  1957. xmlns="http://www.w3.org/2000/svg" p-id="5894" width="32" height="32">
  1958. <path
  1959. d="M20.48 503.72608c0 214.4256 137.4208 396.73856 328.94976 463.6672 25.8048 6.5536 21.87264-11.8784 21.87264-24.33024v-85.07392c-148.93056 17.44896-154.86976-81.1008-164.94592-97.52576-20.23424-34.52928-67.91168-43.33568-53.69856-59.76064 33.91488-17.44896 68.48512 4.42368 108.46208 63.61088 28.95872 42.88512 85.44256 35.6352 114.15552 28.4672a138.8544 138.8544 0 0 1 38.0928-66.7648c-154.25536-27.60704-218.60352-121.77408-218.60352-233.79968 0-54.31296 17.94048-104.2432 53.0432-144.54784-22.36416-66.43712 2.08896-123.24864 5.3248-131.6864 63.81568-5.7344 130.00704 45.6704 135.168 49.68448 36.2496-9.78944 77.57824-14.9504 123.82208-14.9504 46.4896 0 88.064 5.3248 124.5184 15.23712 12.288-9.4208 73.80992-53.53472 133.12-48.128 3.15392 8.43776 27.0336 63.93856 6.02112 129.4336 35.59424 40.38656 53.69856 90.76736 53.69856 145.24416 0 112.18944-64.7168 206.4384-219.42272 233.71776a140.0832 140.0832 0 0 1 41.7792 99.9424v123.4944c0.86016 9.87136 0 19.6608 16.50688 19.6608 194.31424-65.49504 334.2336-249.15968 334.2336-465.5104C1002.57792 232.48896 782.66368 12.77952 511.5904 12.77952 240.18944 12.65664 20.48 232.40704 20.48 503.72608z"
  1960. fill="#000000" opacity=".65" p-id="5895"></path>
  1961. </svg>
  1962. <a href="https://github.com/LuckyPuppy514/external-player" target="_blank">
  1963. Powered by External Player
  1964. </a>
  1965. </span>
  1966. </div>
  1967. </div>
  1968. </body>
  1969. <script>
  1970. const translations = {
  1971. en: {
  1972. global: 'Global Config',
  1973. version: 'Version',
  1974. language: 'Language',
  1975. buttonCoord: 'Button Coord',
  1976. buttonScale: 'Button Scale',
  1977. buttonVisibilityDuration: 'Button Visibility Duration (ms, -1: Keep Visible)',
  1978. networkProxy: 'Network Proxy',
  1979. reset: 'Reset',
  1980. save: 'Save',
  1981. delete: 'Delete',
  1982. name: 'Name',
  1983. system: 'System',
  1984. icon: 'Icon',
  1985. iconSize: 'Icon Size',
  1986. playEvent: 'Play Event',
  1987. enable: 'Enable',
  1988. parser: 'Parser',
  1989. preferredQuality: 'Preferred Quality',
  1990. preferredSubtitle: 'Preferred Subtitle',
  1991. preferredCodec: 'Preferred Codec',
  1992. preferredLine: 'Preferred Line',
  1993. original: 'Original',
  1994. hd: 'HD',
  1995. smooth: 'Smooth',
  1996. mainLine: 'Main',
  1997. backupLine1: 'Backup 1',
  1998. backupLine2: 'Backup 2',
  1999. backupLine3: 'Backup 3',
  2000. unlimited: 'Unlimited',
  2001. off: 'OFF',
  2002. presetEvent: 'Preset Event',
  2003. playAuto: 'Play Automatically',
  2004. pauseAuto: 'Pause Automatically',
  2005. closeAuto: 'Close Automatically',
  2006. syncTime: 'Synchronize Time',
  2007. },
  2008. zh: {
  2009. global: '全局配置',
  2010. version: '版本',
  2011. language: '语言',
  2012. buttonCoord: '按钮坐标',
  2013. buttonScale: '按钮比例',
  2014. buttonVisibilityDuration: '按钮可见时长(毫秒,-1:一直可见)',
  2015. networkProxy: '网络代理',
  2016. reset: '重置',
  2017. save: '保存',
  2018. delete: '删除',
  2019. name: '名称',
  2020. system: '系统',
  2021. icon: '图标',
  2022. iconSize: '图标大小',
  2023. playEvent: '播放事件',
  2024. enable: '启用',
  2025. parser: '解析器',
  2026. preferredQuality: '首选画质',
  2027. preferredSubtitle: '首选字幕',
  2028. preferredCodec: '首选编码',
  2029. preferredLine: '首选线路',
  2030. original: '原画',
  2031. hd: '高清',
  2032. smooth: '流畅',
  2033. mainLine: '主线',
  2034. backupLine1: '备线1',
  2035. backupLine2: '备线2',
  2036. backupLine3: '备线3',
  2037. unlimited: '无限制',
  2038. off: '关闭',
  2039. presetEvent: '预设事件',
  2040. playAuto: '自动播放',
  2041. pauseAuto: '自动暂停',
  2042. closeAuto: '自动关闭',
  2043. syncTime: '同步时间',
  2044. }
  2045. };
  2046.  
  2047. const SYSTEM_SVG = {
  2048. windows: '<svg t="1732017849573" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5376" width="32" height="32"><path d="M523.8 191.4v288.9h382V128.1zM523.8 833.6l382 62.2v-352h-382zM120.1 480.2H443V201.9l-322.9 53.5zM120.1 770.6L443 823.2V543.8H120.1z" p-id="5377"></path></svg>',
  2049. linux: '<svg t="1732017810402" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4326" width="32" height="32"><path d="M834.198588 918.588235c-30.659765 15.661176-71.559529 50.115765-86.618353 64.572236-11.324235 10.782118-58.066824 16.203294-84.449882 2.710588-30.659765-15.661176-14.516706-40.417882-61.861647-41.923765-23.672471-0.602353-46.802824-0.602353-69.933177-0.602353-20.419765 0.602353-40.839529 1.626353-61.861647 2.108235-70.957176 1.626353-77.944471 47.405176-123.723294 45.778824-31.201882-1.084235-70.415059-25.840941-138.24-39.755294-47.344941-9.758118-93.003294-12.348235-102.761412-33.370353-9.637647-21.022118 11.866353-44.634353 13.432471-65.054118 1.626353-27.467294-20.419765-64.572235-4.276706-78.607059 13.974588-12.348235 43.550118-3.252706 62.885647-13.914352 20.419765-11.806118 29.033412-21.022118 29.033412-46.260706 7.529412 25.720471-0.542118 46.682353-17.227294 56.922353-10.24 6.445176-29.033412 9.697882-44.694588 8.131764-12.348235-1.144471-19.877647 0.481882-23.130353 5.360941-4.818824 5.903059-3.252706 16.685176 2.710588 30.659765 5.903059 13.974588 12.890353 23.130353 11.806118 40.297412-0.542118 17.227294-19.877647 37.707294-16.624942 52.224 1.084235 5.421176 6.445176 10.24 19.877647 13.974588 21.504 5.903059 60.777412 11.806118 98.966589 21.022118 42.526118 10.721882 86.618353 30.057412 114.085647 26.322823 81.739294-11.324235 34.936471-98.966588 22.046117-119.868235-69.391059-108.724706-115.109647-179.681882-151.67247-151.732706-9.155765 7.529412-9.697882-18.311529-9.155765-28.551529 1.626353-35.538824 19.395765-48.368941 30.117647-75.836236 20.419765-52.224 36.020706-111.856941 67.222588-142.516705 23.311059-30.177882 59.873882-79.088941 66.921412-104.869647-5.963294-55.958588-7.589647-115.109647-8.613647-166.671059-1.084235-55.416471 7.529412-103.905882 69.933177-137.697883C453.391059 33.310118 473.268706 30.117647 494.290824 30.117647c37.104941-0.602353 78.486588 10.24 104.869647 29.575529 41.984 31.201882 68.306824 97.340235 65.114353 144.624942-2.168471 37.104941 4.276706 75.294118 16.143058 115.109647 13.974588 46.802824 36.080941 79.570824 71.55953 117.217882 42.526118 45.176471 75.836235 133.903059 85.534117 190.343529 8.613647 52.826353-3.252706 85.594353-14.516705 87.220706-17.227294 2.590118-27.949176 56.922353-81.739295 54.814118-34.394353-1.626353-37.647059-22.046118-47.344941-39.815529-15.600941-27.407059-31.201882-18.793412-37.104941 10.24-3.252706 14.516706-1.144471 36.080941 3.734588 52.103529 9.697882 33.912471 6.445176 65.656471 0.542118 104.929882-11.324235 74.209882 52.163765 88.184471 94.689882 52.645647 41.923765-34.876235 51.079529-40.297412 103.785412-58.608941 80.112941-27.467294 53.248-51.621647 10.179765-66.138353-38.731294-12.950588-40.297412-78.064941-26.383059-90.413176 3.252706 69.933176 39.815529 80.173176 54.874353 89.810823 66.138353 41.020235-24.756706 74.932706-64.030118 94.810353z m-90.352941-259.734588c14.516706-48.489412 8.071529-67.764706-1.566118-113.543529-7.529412-34.394353-39.273412-81.257412-64.030117-95.713883 6.445176 5.360941 18.311529 20.961882 30.659764 44.574118 21.504 40.417882 43.008 100.050824 29.033412 149.564235-5.360941 19.275294-18.251294 21.985882-26.864941 22.528-37.647059 4.336941-15.600941-45.176471-31.201882-112.338823-17.769412-75.354353-36.020706-80.715294-40.297412-86.618353-22.166588-97.822118-46.320941-88.124235-53.368471-124.687059-5.903059-32.828235 28.551529-59.693176-18.251294-68.848941-14.516706-2.710588-34.936471-17.227294-43.008-18.31153-8.071529-1.024-12.408471-54.332235 17.709177-55.958588 29.575529-2.168471 34.996706 33.370353 29.575529 47.405177-8.553412 13.914353 0.542118 19.335529 15.119059 14.45647 11.806118-3.734588 4.276706-34.936471 6.987294-39.213176-7.529412-45.176471-26.383059-51.621647-45.718588-55.416471-74.270118 5.903059-40.899765 87.702588-48.429177 80.173177-10.782118-11.324235-41.923765-1.084235-41.923764-8.131765 0.542118-41.923765-13.492706-66.138353-32.828236-66.680471-21.504-0.542118-30.117647 29.575529-31.201882 46.742589-1.626353 16.143059 9.155765 50.115765 17.227294 47.405176 5.360941-1.626353 14.516706-12.408471 4.818824-11.806118-4.818824 0-12.348235-11.866353-13.432471-25.840941-0.542118-14.034824 4.879059-28.009412 23.130353-27.467294 20.961882 0.542118 20.961882 42.465882 18.793412 44.092235-6.927059 4.818824-15.600941 14.034824-16.685177 15.600942-6.927059 11.324235-20.359529 14.456471-25.780706 19.395764-9.155765 9.637647-11.264 20.419765-4.276705 24.154353 24.696471 13.974588 16.624941 30.057412 51.079529 31.262118 22.588235 1.084235 39.213176-3.252706 54.874353-8.07153 11.806118-3.734588 50.055529-11.806118 58.066823-25.840941 3.734588-5.903059 8.071529-5.903059 10.721883-4.276706 5.360941 2.650353 6.445176 12.890353-6.987294 16.143059-18.793412 5.421176-37.647059 15.661176-54.814118 22.106353-16.685176 6.927059-22.046118 9.637647-37.647059 12.288-35.478588 6.445176-61.801412-12.890353-38.189176 10.24 8.071529 7.529412 15.600941 12.348235 36.020706 11.866353 45.176471-1.626353 95.232-56.018824 100.050823-31.804235 1.024 5.360941-14.034824 11.806118-25.840941 17.769412-41.923765 20.419765-71.499294 61.319529-98.424471 47.284705-24.214588-12.890353-48.368941-72.643765-47.887058-45.658353 0.542118 41.381647-54.332235 77.944471-29.033412 125.289412-16.685176 4.216471-53.790118 83.365647-59.151059 124.205177-3.252706 23.672471 2.168471 52.705882-3.794824 68.848941-8.071529 23.672471-44.634353-22.588235-32.768-79.028706 2.108235-9.637647 0-11.866353-2.710588-6.927059-14.516706 26.322824-6.445176 63.427765 5.360941 89.208471 4.879059 11.324235 17.227294 16.143059 26.383059 25.840941 18.793412 21.443765 93.003294 76.378353 105.953883 89.810823a33.008941 33.008941 0 0 1-22.588236 55.898353c17.769412 33.370353 34.936471 36.623059 34.454588 90.895059 20.419765-10.721882 12.408471-34.394353 3.734589-49.392941-5.963294-10.842353-13.432471-15.661176-11.866353-18.311529 1.084235-1.626353 11.866353-10.842353 17.769412-3.734589 18.251294 20.419765 52.705882 24.154353 89.268705 19.33553 37.104941-4.336941 76.920471-17.227294 95.171765-46.802824 8.613647-13.974588 14.516706-18.793412 18.31153-16.143059 4.276706 2.108235 5.963294 11.806118 5.360941 27.949177-0.542118 17.227294-7.529412 34.996706-12.348236 49.513412-4.879059 16.685176-6.445176 27.949176 9.697883 28.551529 4.276706-30.177882 12.890353-59.753412 15.058823-89.871059 2.710588-34.394353-22.046118-97.822118 4.879059-129.626353 6.987294-8.613647 15.540706-9.637647 27.407059-9.637647 1.566118-43.068235 67.764706-39.755294 89.810823-22.046117 0-9.758118-20.961882-18.853647-29.575529-22.648471zM304.971294 503.988706c-3.794824 6.927059-13.432471 12.288-5.963294 13.43247 2.710588 0.542118 10.24-6.023529 13.492706-13.43247 2.650353-9.155765 5.360941-14.034824 1.084235-15.661177-4.879059-1.566118-3.794824 8.071529-8.613647 15.661177z m123.120941-291.538824c-6.445176-1.626353-5.360941 8.011294-2.108235 6.987294 2.168471 0 4.879059 3.252706 3.734588 8.07153-1.084235 6.445176-0.542118 10.842353 4.336941 10.842353 0.542118 0 1.566118 0 1.566118-1.626353 2.228706-13.552941-4.276706-23.190588-7.529412-24.274824z m14.576941 49.453177c-5.360941 0.542118-4.336941-11.866353 12.890353-10.782118-10.782118 1.084235-6.987294 10.782118-12.890353 10.782118z m44.092236-9.155765c15.600941-6.927059 20.961882 3.794824 15.600941 5.963294-5.421176 1.566118-5.963294-8.673882-15.600941-5.963294z m65.054117-43.550118c-6.987294 0.602353-4.818824 3.734588-1.566117 4.818824 4.276706 1.204706 8.613647 8.673882 9.697882 16.685176 0 1.084235 5.360941-1.084235 5.360941-2.710588 0.481882-12.830118-10.782118-19.275294-13.492706-18.793412z m31.201883-116.133647c-4.276706-4.336941-8.613647-8.131765-12.890353-8.131764-10.782118 1.084235-5.421176 12.348235-6.987294 17.769411-2.168471 5.903059-10.179765 10.782118-4.818824 15.058824 4.879059 3.734588 8.071529-5.903059 18.31153-9.637647 2.650353-1.144471 15.058824 0.481882 17.709176-5.421177 0.481882-2.710588-6.445176-5.903059-11.324235-9.637647z m59.693176 237.628236c-10.179765-6.384941-12.348235-17.167059-16.082823-13.432471-11.324235 12.348235 13.974588 38.189176 24.69647 40.417882 6.445176 1.084235 11.324235-7.589647 9.697883-15.119058-2.168471-10.179765-9.697882-6.445176-18.31153-11.866353z" p-id="4327"></path></svg>',
  2050. mac: '<svg t="1731999754869" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7764" width="32" height="32"><path d="M849.124134 704.896288c-1.040702 3.157923-17.300015 59.872622-57.250912 118.190843-34.577516 50.305733-70.331835 101.018741-126.801964 101.909018-55.532781 0.976234-73.303516-33.134655-136.707568-33.134655-63.323211 0-83.23061 32.244378-135.712915 34.110889-54.254671 2.220574-96.003518-54.951543-130.712017-105.011682-70.934562-102.549607-125.552507-290.600541-52.30118-416.625816 36.040844-63.055105 100.821243-103.135962 171.364903-104.230899 53.160757-1.004887 103.739712 36.012192 136.028093 36.012192 33.171494 0 94.357018-44.791136 158.90615-38.089503 27.02654 1.151219 102.622262 11.298324 151.328567 81.891102-3.832282 2.607384-90.452081 53.724599-89.487104 157.76107C739.079832 663.275355 847.952448 704.467523 849.124134 704.896288M633.69669 230.749408c29.107945-35.506678 48.235584-84.314291 43.202964-132.785236-41.560558 1.630127-92.196819 27.600615-122.291231 62.896492-26.609031 30.794353-50.062186 80.362282-43.521213 128.270409C557.264926 291.935955 604.745311 264.949324 633.69669 230.749408" p-id="7765"></path></svg>'
  2051. };
  2052.  
  2053. var policy;
  2054. try {
  2055. policy = window.trustedTypes.createPolicy('default', {
  2056. createHTML: (string, sink) => string,
  2057. createScript: (input) => input
  2058. })
  2059. } catch (error) {
  2060. policy = {
  2061. createHTML: (string, sink) => string,
  2062. createScript: (input) => input
  2063. }
  2064. }
  2065.  
  2066. var defaultConfig;
  2067. var tabCount = 0;
  2068. var projectName;
  2069.  
  2070. function translatePage(language) {
  2071. const trans = translations[language];
  2072. document.querySelectorAll('[data-translate]').forEach(el => {
  2073. el.textContent = trans[el.getAttribute('data-translate')] || el.textContent;
  2074. });
  2075. }
  2076.  
  2077. function createTab(tabId, tabName = \`Player \${tabCount}\`, config = {}) {
  2078. const tabButton = document.createElement('button');
  2079. tabButton.className = 'tab-button';
  2080. tabButton.textContent = tabName;
  2081. tabButton.dataset.tab = tabId;
  2082. sidebar.insertBefore(tabButton, document.getElementById('global-button').nextSibling);
  2083.  
  2084. const tab = document.createElement('div');
  2085. tab.id = tabId;
  2086. tab.name = tabName;
  2087. tab.className = 'tab';
  2088. tab.setAttribute('readonly', config.readonly === true)
  2089. const disabled = config.readonly === true ? 'disabled' : '';
  2090. config.presetEvent = config.presetEvent || {
  2091. pauseAuto: true,
  2092. syncTime: false,
  2093. };
  2094. tab.innerHTML = policy.createHTML(\`
  2095. <div class="header">
  2096. <button class="remove-button" data-translate="delete" \${disabled}>删除</button>
  2097. </div>
  2098. <div class="input-group">
  2099. <label data-translate="name">名称</label>
  2100. <input type="text" value="\${config.name || tabName}" name="name" placeholder="\${tabName}" required \${disabled}>
  2101. </div>
  2102. <div class="input-group">
  2103. <label data-translate="system">系统</label>
  2104. <div class="radio-button-group" name="system">
  2105. <div class="radio-button active \${disabled}" value="windows">\${SYSTEM_SVG.windows} Windows</div>
  2106. <div class="radio-button \${disabled}" value="linux">\${SYSTEM_SVG.linux} Linux</div>
  2107. <div class="radio-button \${disabled}" value="mac">\${SYSTEM_SVG.mac} Mac</div>
  2108. </div>
  2109. </div>
  2110. <div class="input-group">
  2111. <label data-translate="iconSize">图标大小</label>
  2112. <input type="number" value="\${config.iconSize || 50}" name="iconSize" min="1" required>
  2113. </div>
  2114. <div class="input-group">
  2115. <label data-translate="icon">图标</label>
  2116. <input type="search" value="\${config.icon || ''}" name="icon" required>
  2117. </div>
  2118. <div class="input-group">
  2119. <label data-translate="presetEvent">预设事件</label>
  2120. <div class="checkbox-group">
  2121. <input type="checkbox" id="\${tabId}-play-auto" name="playAuto" \${config.presetEvent.playAuto ? 'checked' : ''}/>
  2122. <label for="\${tabId}-play-auto" data-translate="playAuto" class="chekbox-label">自动播放</label>
  2123. <input type="checkbox" id="\${tabId}-pause-auto" name="pauseAuto" \${config.presetEvent.pauseAuto ? 'checked' : ''}/>
  2124. <label for="\${tabId}-pause-auto" data-translate="pauseAuto" class="chekbox-label">自动暂停</label>
  2125. <input type="checkbox" id="\${tabId}-close-auto" name="closeAuto" \${config.presetEvent.closeAuto ? 'checked' : ''}/>
  2126. <label for="\${tabId}-close-auto" data-translate="closeAuto" class="chekbox-label">自动关闭</label>
  2127. <input type="checkbox" id="\${tabId}-sync-time" name="syncTime" \${config.presetEvent.syncTime ? 'checked' : ''}/>
  2128. <label for="\${tabId}-sync-time" data-translate="syncTime" class="chekbox-label">同步时间</label>
  2129. </div>
  2130. </div>
  2131. <div class="input-group">
  2132. <label data-translate="playEvent">播放事件</label>
  2133. <textarea class="tab-textarea" wrap="off" \${disabled} name="playEvent">\${config.playEvent || ''}</textarea>
  2134. </div>
  2135. <div class="input-group">
  2136. <label data-translate="enable">启用</label>
  2137. <label class="switch">
  2138. <input type="checkbox" class="tab-switch" \${config.enable || config.enable === undefined ? 'checked' : ''} name="enable"><span class="switch-slider"></span>
  2139. </label>
  2140. </div>
  2141. \`);
  2142. content.appendChild(tab);
  2143.  
  2144. tab.querySelector('.remove-button').onclick = () => {
  2145. const previousElement = tabButton.previousElementSibling;
  2146. sidebar.removeChild(tabButton);
  2147. content.removeChild(tab);
  2148. activateTab(previousElement.getAttribute('data-tab'));
  2149. };
  2150.  
  2151. const nameInput = tab.querySelector('[name="name"]');
  2152. nameInput.oninput = () => {
  2153. tabButton.innerHTML = policy.createHTML(SYSTEM_SVG[tab.querySelector('[name=system] .active').getAttribute('value')] + (
  2154. nameInput.value || tabName));
  2155. };
  2156.  
  2157. config.system = config.system || 'windows';
  2158. tab.querySelectorAll('[name=system]').forEach(radioButtonGroup => {
  2159. const radioButtons = radioButtonGroup.querySelectorAll('.radio-button');
  2160. radioButtons.forEach(radioButton => {
  2161. radioButton.onclick = () => {
  2162. radioButtons.forEach(btn => btn.classList.remove('active'));
  2163. radioButton.classList.add('active');
  2164. tabButton.innerHTML = policy.createHTML(SYSTEM_SVG[radioButton.getAttribute('value')] + (nameInput
  2165. .value || tabName));
  2166. };
  2167. if (radioButton.getAttribute('value') === config.system) {
  2168. radioButton.classList.add('active');
  2169. tabButton.innerHTML = policy.createHTML(SYSTEM_SVG[radioButton.getAttribute('value')] + (nameInput
  2170. .value || tabName));
  2171. } else {
  2172. radioButton.classList.remove('active');
  2173. }
  2174. });
  2175. })
  2176.  
  2177. tabButton.onclick = () => activateTab(tabId);
  2178.  
  2179. activateTab(tabId);
  2180. }
  2181.  
  2182. function activateTab(tabId) {
  2183. document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
  2184. document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active'));
  2185. document.getElementById(tabId).classList.add('active');
  2186. document.querySelector(\`[data-tab="\${tabId}"]\`).classList.add('active');
  2187. document.querySelector('#content').scrollTop = 0;
  2188. }
  2189.  
  2190. function saveConfig() {
  2191. const ytdlp = document.querySelector('#ytdlp');
  2192. const ytdlpRegex = ytdlp.querySelector('[name="regex"]:not([disabled])').value;
  2193.  
  2194. const bilibili = document.querySelector('#bilibili');
  2195. const bilibiliRegex = bilibili.querySelector('[name="regex"]:not([disabled])').value;
  2196.  
  2197. const config = {
  2198. global: {
  2199. parser: {}
  2200. },
  2201. players: []
  2202. };
  2203.  
  2204. for (const id in defaultConfig.global.parser) {
  2205. const parser = document.getElementById(id);
  2206. if (!parser) {
  2207. continue;
  2208. }
  2209. const regex = parser.querySelector('[name="regex"]:not([disabled])').value;
  2210. config.global.parser[id] = {};
  2211. config.global.parser[id].regex = regex ? regex.split('\\n') : [];
  2212. for (const name in defaultConfig.global.parser[id]) {
  2213. if (name === 'regex') {
  2214. continue;
  2215. }
  2216. config.global.parser[id][name] = parser.querySelector(\`[name=\${name}] .active\`).getAttribute('value');
  2217. }
  2218. }
  2219.  
  2220. for (const key in defaultConfig.global) {
  2221. if (key === 'parser') {
  2222. continue;
  2223. }
  2224. config.global[key] = document.getElementById(key)?.value || defaultConfig.global[key];
  2225. }
  2226.  
  2227. document.querySelectorAll('.tab').forEach(tab => {
  2228. if (tab.id !== 'global') {
  2229. config.players.push({
  2230. readonly: tab.getAttribute('readonly') === "true",
  2231. name: tab.querySelector('[name="name"]').value || tab.name || 'Player',
  2232. system: tab.querySelector('[name="system"] .active').getAttribute('value') ||
  2233. 'windows',
  2234. icon: tab.querySelector('[name="icon"]').value || '',
  2235. iconSize: tab.querySelector('[name="iconSize"]').value || 50,
  2236. playEvent: tab.querySelector('[name="playEvent"]').value || '',
  2237. presetEvent: {
  2238. playAuto: tab.querySelector('[name="playAuto"]').checked,
  2239. pauseAuto: tab.querySelector('[name="pauseAuto"]').checked,
  2240. closeAuto: tab.querySelector('[name="closeAuto"]').checked,
  2241. syncTime: tab.querySelector('[name="syncTime"]').checked,
  2242. },
  2243. enable: tab.querySelector('[name="enable"]').checked,
  2244. });
  2245. }
  2246. });
  2247.  
  2248. parent.postMessage({
  2249. name: projectName,
  2250. method: 'saveConfig',
  2251. config: config
  2252. }, '*');
  2253. };
  2254.  
  2255. function resetButtonCoord() {
  2256. document.getElementById('buttonXCoord').value = defaultConfig.global.buttonXCoord;
  2257. document.getElementById('buttonYCoord').value = defaultConfig.global.buttonYCoord;
  2258. }
  2259.  
  2260. function loadConfig(config) {
  2261. // 全局配置
  2262. for (const key in config.global) {
  2263. if (key === 'parser' || !document.getElementById(key)) {
  2264. continue;
  2265. }
  2266. document.getElementById(key).value = config.global[key];
  2267. }
  2268.  
  2269. document.getElementById('language').value = config.global.language;
  2270. language.dispatchEvent(new Event("change"));
  2271.  
  2272. document.querySelectorAll('.parser').forEach(parser => {
  2273. parser.querySelectorAll('.radio-button-group').forEach(radioButtonGroup => {
  2274. const radioButtons = radioButtonGroup.querySelectorAll('.radio-button');
  2275. radioButtons.forEach(radioButton => {
  2276. if (radioButton.getAttribute('value') === config.global.parser[parser.id][
  2277. radioButtonGroup.getAttribute('name')
  2278. ]) {
  2279. radioButton.classList.add('active');
  2280. } else {
  2281. radioButton.classList.remove('active');
  2282. }
  2283. });
  2284. })
  2285. parser.querySelectorAll('textarea').forEach(textarea => {
  2286. if (textarea.disabled) {
  2287. const regex = defaultConfig.global.parser[parser.id][textarea.getAttribute(
  2288. 'name')] || [];
  2289. if (regex.length > 0) {
  2290. textarea.value = regex.join('\\n');
  2291. textarea.style.height = regex.length * 20 + 20 + 'px';
  2292. } else {
  2293. textarea.style.display = 'none';
  2294. }
  2295. } else {
  2296. const regex = config.global.parser[parser.id][textarea.getAttribute('name')] || [];
  2297. textarea.value = regex.join('\\n');
  2298. }
  2299. })
  2300. })
  2301.  
  2302. // 播放器配置
  2303. removeAllTab();
  2304. config.players.forEach(player => createTab(\`player\${tabCount++}\`, player.name, player));
  2305.  
  2306. // 默认选中全局配置
  2307. activateTab('global');
  2308. }
  2309.  
  2310. function removeAllTab() {
  2311. document.querySelectorAll('.tab-button').forEach(tabButton => {
  2312. if (tabButton.id === 'global-button') {
  2313. return;
  2314. }
  2315. sidebar.removeChild(tabButton);
  2316. })
  2317. document.querySelectorAll('.tab').forEach(tab => {
  2318. if (tab.id === 'global') {
  2319. return;
  2320. }
  2321. content.removeChild(tab);
  2322. })
  2323. }
  2324.  
  2325. function resetConfig() {
  2326. let config = JSON.parse(JSON.stringify(defaultConfig));
  2327. for (const key in config.global.parser) {
  2328. config.global.parser[key].regex = [];
  2329. }
  2330. loadConfig(config);
  2331. }
  2332.  
  2333. function init() {
  2334. if (window.self === window.top) {
  2335. return;
  2336. }
  2337.  
  2338. window.addEventListener('message', function (event) {
  2339. const data = event.data;
  2340. if (!data || !data.name || data.method !== 'loadConfig') {
  2341. return;
  2342. }
  2343. projectName = data.name;
  2344. defaultConfig = data.defaultConfig;
  2345. loadConfig(data.config);
  2346. translatePage(data.config.global.language);
  2347. document.getElementById('sidebar-container').style.display = 'flex';
  2348. document.getElementById('content-container').style.display = 'flex';
  2349. });
  2350.  
  2351. document.getElementById('language').addEventListener('change', (e) => {
  2352. translatePage(e.target.value);
  2353. });
  2354. document.getElementById('add-tab-button').onclick = () => createTab(\`tab\${tabCount++}\`);
  2355. document.getElementById('global-button').onclick = () => activateTab('global');
  2356. document.getElementById('save-button').onclick = () => saveConfig();
  2357. document.getElementById('reset-button').onclick = () => resetConfig();
  2358. document.getElementById('reset-button-coord-button').onclick = () => resetButtonCoord();
  2359.  
  2360. document.querySelectorAll('#global .radio-button-group').forEach(radioButtonGroup => {
  2361. const radioButtons = radioButtonGroup.querySelectorAll('.radio-button');
  2362. radioButtons.forEach(radioButton => {
  2363. radioButton.onclick = () => {
  2364. radioButtons.forEach(btn => btn.classList.remove('active'));
  2365. radioButton.classList.add('active');
  2366. };
  2367. });
  2368. })
  2369. }
  2370.  
  2371. init();
  2372. </script>
  2373.  
  2374. </html>
  2375. `;
  2376. if (SETTING_URL) {
  2377. const response = await fetch(SETTING_URL);
  2378. settingIframeHtml = await response.text();
  2379. }
  2380. settingIframe.onload = function () {
  2381. const doc = settingIframe.contentDocument || settingIframe.contentWindow.document;
  2382. doc.open();
  2383. doc.write(policy.createHTML(settingIframeHtml));
  2384. doc.close();
  2385. };
  2386. document.body.appendChild(settingIframe);
  2387.  
  2388. try {
  2389. showLoading();
  2390. await sleep(REFRESH_INTERVAL);
  2391. } finally {
  2392. hideLoading();
  2393. }
  2394. }
  2395.  
  2396. function saveConfig(config) {
  2397. // 保存配置
  2398. currentConfig = config;
  2399. GM_setValue('config', currentConfig);
  2400. showToast(translation.saveSuccessfully);
  2401.  
  2402. // 移除旧元素
  2403. if (style) {
  2404. document.head.removeChild(style);
  2405. style = undefined;
  2406. }
  2407. if(buttonDiv) {
  2408. document.body.removeChild(buttonDiv);
  2409. buttonDiv = undefined;
  2410. }
  2411.  
  2412. // 重新初始化
  2413. isReloading = true;
  2414. init(currentUrl);
  2415. }
  2416.  
  2417. function startFlashing(element) {
  2418. let visibility = element.style.visibility;
  2419. let transition = element.style.transition;
  2420. let boxShadow = element.style.boxShadow;
  2421.  
  2422. element.style.visibility = 'visible';
  2423. element.style.transition = 'box-shadow 0.5s ease';
  2424. let isGlowing = false;
  2425. const interval = setInterval(() => {
  2426. isGlowing = !isGlowing;
  2427. element.style.boxShadow = isGlowing ? `0 0 10px 10px ${COLOR.PRIMARY}` : 'none';
  2428. }, 500);
  2429.  
  2430. setTimeout(() => {
  2431. clearInterval(interval);
  2432. element.style.visibility = visibility;
  2433. element.transition = transition;
  2434. element.boxShadow = boxShadow;
  2435. }, 5000);
  2436. }
  2437.  
  2438. function showButtonDiv() {
  2439. buttonDiv.style.display = 'flex';
  2440. if (!isReloading) {
  2441. for (const player of currentConfig.players) {
  2442. if (player.presetEvent.playAuto === true) {
  2443. setTimeout(() => {
  2444. currentParser.play(player);
  2445. }, REFRESH_INTERVAL);
  2446. }
  2447. }
  2448. }
  2449. isReloading = false;
  2450. }
  2451.  
  2452. // ======================================== 开始执行 =======================================
  2453.  
  2454. function appendAll() {
  2455. appendCss();
  2456. appendToastDiv();
  2457. appendLoadingDiv();
  2458. appendButtonDiv();
  2459. }
  2460.  
  2461. function initTop() {
  2462. if (currentParser) {
  2463. appendAll();
  2464. showButtonDiv();
  2465. }
  2466.  
  2467. // 监听子页面事件
  2468. window.addEventListener('message', function (event) {
  2469. const data = event.data;
  2470. if (!data) {
  2471. return;
  2472. }
  2473. if (!data.name || data.name !== PROJECT_NAME) {
  2474. return;
  2475. }
  2476. if (data.method === 'init') {
  2477. iframe = event.source;
  2478. // 子页面覆盖父页面解析器
  2479. currentParser = new PARSER.IFRAME();
  2480. isReloading = data.isReloading;
  2481. appendAll();
  2482. showButtonDiv();
  2483. return;
  2484. }
  2485. if (data.method === 'currentMedia') {
  2486. currentMedia = data.currentMedia;
  2487. return;
  2488. }
  2489. });
  2490.  
  2491. // 快捷键
  2492. document.addEventListener('keydown', (event) => {
  2493. // 打开设置:Ctrl + Alt + E
  2494. if (event.ctrlKey && event.altKey && (event.key === 'e' || event.key === 'E')) {
  2495. event.preventDefault();
  2496. startFlashing(settingButton);
  2497. settingButton.click();
  2498. }
  2499. });
  2500.  
  2501. // 保存配置
  2502. window.addEventListener('message', function (event) {
  2503. const data = event.data;
  2504. if (!data || data.name !== PROJECT_NAME || data.method !== 'saveConfig') {
  2505. return;
  2506. }
  2507. saveConfig(data.config);
  2508. if (iframe) {
  2509. iframe.postMessage({
  2510. name: PROJECT_NAME,
  2511. method: 'reload'
  2512. }, '*');
  2513. }
  2514. });
  2515. }
  2516.  
  2517. function initIframe() {
  2518. if (currentParser) {
  2519. // 通知顶层窗口初始化按钮
  2520. setTimeout(() => {
  2521. parent.postMessage({
  2522. name: PROJECT_NAME,
  2523. method: 'init',
  2524. isReloading: isReloading
  2525. }, '*');
  2526. isReloading = false;
  2527. }, REFRESH_INTERVAL);
  2528.  
  2529. // 监听父页面事件
  2530. window.addEventListener("message", async function (event) {
  2531. const data = event.data;
  2532. if (!data) {
  2533. return;
  2534. }
  2535. if (!data.name || data.name !== PROJECT_NAME) {
  2536. return;
  2537. }
  2538. if (data.method === 'execute') {
  2539. await currentParser.execute();
  2540. parent.postMessage({
  2541. name: PROJECT_NAME,
  2542. method: 'currentMedia',
  2543. currentMedia: currentMedia
  2544. }, '*');
  2545. return;
  2546. }
  2547. if (data.method === 'pause') {
  2548. currentParser.pause();
  2549. return;
  2550. }
  2551. if (data.method === 'reload') {
  2552. isReloading = true;
  2553. init(currentUrl);
  2554. }
  2555. });
  2556. }
  2557. }
  2558.  
  2559. async function init(url) {
  2560. currentConfig = loadConfig();
  2561. translation = translations[currentConfig.global.language];
  2562. currentParser = matchParser(currentConfig.global.parser, url) || matchParser(defaultConfig.global.parser, url);
  2563. if (self === top) {
  2564. initTop();
  2565. } else {
  2566. initIframe();
  2567. }
  2568. currentUrl = url;
  2569. }
  2570.  
  2571. setInterval(() => {
  2572. const url = location.href;
  2573. if (currentUrl !== url) {
  2574. console.log(`current url update: ${currentUrl ? currentUrl + ' => ' : ''}${url}`);
  2575. if (currentUrl && currentUrl.indexOf('?') > -1 &&
  2576. url.replace(/\/\?/, '?').startsWith(currentUrl.replace(/\/\?/, '?'))) {
  2577. currentUrl = url;
  2578. return;
  2579. }
  2580. init(url);
  2581. }
  2582. }, REFRESH_INTERVAL);