哔哩哔哩自定义视频信息条

完全自定义你的视频标题下方信息栏,排序,增加,删除!

安装此脚本?
作者推荐脚本

您可能也喜欢[Bilibili] 关注管理器

安装此脚本
  1. // ==UserScript==
  2. // @name 哔哩哔哩自定义视频信息条
  3. // @namespace ckylin-bilibili-display-video-id
  4. // @version 1.19.3
  5. // @description 完全自定义你的视频标题下方信息栏,排序,增加,删除!
  6. // @author CKylinMC
  7. // @match https://www.bilibili.com/video*
  8. // @match https://www.bilibili.com/medialist/play/*
  9. // @resource cktools https://gf.qytechs.cn/scripts/429720-cktools/code/CKTools.js?version=1034581
  10. // @resource popjs https://cdn.jsdelivr.net/gh/CKylinMC/PopNotify.js@master/PopNotify.js
  11. // @resource popcss https://cdn.jsdelivr.net/gh/CKylinMC/PopNotify.js@master/PopNotify.css
  12. // @resource timeago https://unpkg.com/timeago.js@4.0.2/dist/timeago.min.js
  13. // @grant unsafeWindow
  14. // @grant GM_setValue
  15. // @grant GM_getValue
  16. // @grant GM_getResourceText
  17. // @grant GM_registerMenuCommand
  18. // @grant GM_unregisterMenuCommand
  19. // @license GPL-3.0-only
  20. // ==/UserScript==
  21.  
  22. (function () {
  23. //======[Apply all resources]
  24. const resourceList = [
  25. { name: 'cktools', type: 'js' },
  26. { name: 'timeago', type: 'js' },
  27. { name: 'popjs', type: 'js' },
  28. { name: 'popcss', type: 'css' },
  29. { name: 'popcsspatch', type: 'rawcss', content: "div.popNotifyUnitFrame{z-index:110000!important;}.CKTOOLS-modal-content{color: #616161!important;max-height: 80vh;overflow: auto;}" },
  30. ]
  31. function applyResource() {
  32. resloop: for (let res of resourceList) {
  33. if (!document.querySelector("#" + res.name)) {
  34. let el;
  35. switch (res.type) {
  36. case 'js':
  37. case 'rawjs':
  38. el = document.createElement("script");
  39. break;
  40. case 'css':
  41. case 'rawcss':
  42. el = document.createElement("style");
  43. break;
  44. default:
  45. console.log('Err:unknown type', res);
  46. continue resloop;
  47. }
  48. el.id = res.name;
  49. el.innerHTML = res.type.startsWith('raw') ? res.content : GM_getResourceText(res.name);
  50. document.head.appendChild(el);
  51. }
  52. }
  53. }
  54. applyResource();
  55. //======
  56. const wait = (t) => new Promise(r => setTimeout(r, t));
  57. const waitForPageVisible = async () => {
  58. return document.hidden && new Promise(r => document.addEventListener("visibilitychange", r))
  59. }
  60. const log = (...m) => console.log('[ShowAV]', ...m);
  61. const getAPI = (bvid) => fetch('https://api.bilibili.com/x/web-interface/view?bvid=' + bvid).then(raw => raw.json());
  62. const getAidAPI = (aid) => fetch('https://api.bilibili.com/x/web-interface/view?aid=' + aid).then(raw => raw.json());
  63. const config = {
  64. defaultAv: true,
  65. hideTime: true,
  66. firstTimeLoad: true,
  67. defaultTextTime: true,
  68. foldedWarningTip: true,
  69. forceRemoveAllItem: true,
  70. showInNewLine: false,
  71. forceGap: false,
  72. nobreakline: false,
  73. jssafetyWarning: true,
  74. pnmaxlength: 18,
  75. orders: ['openGUI', 'showArgue', 'showPic', 'showAv', 'showPn'],
  76. all: ['showAv', 'showSAv', 'showSBv', 'showPn', 'showCid', 'showCate', 'openGUI', 'showPic', 'showSize', 'showMore', 'showCTime', 'showViews', 'showDmk', 'showTop', 'showArgue'],
  77. copyitems: ['currTime', 'short', 'shareTime', 'vid'],
  78. copyitemsAll: ['curr', 'currTime', 'short', 'share', 'shareTime', 'md', 'bb', 'html', 'vid'],
  79. customcopyitems: {},
  80. customComponents: {},
  81. vduration: 0,
  82. running: {}
  83. };
  84. const ignoredConfigKeys = ["all", "vduration", "firstTimeLoad", "running"];
  85. const menuId = {
  86. defaultAv: -1,
  87. foldedWarningTip: -1,
  88. showInNewLine: -1,
  89. };
  90. const txtCn = {
  91. showAv: "可切换视频编号和高级复制",
  92. showSAv: "视频AV号和高级复制",
  93. showSBv: "视频BV号和高级复制",
  94. showPn: "视频分P名",
  95. showCid: "视频CID编号",
  96. showCate: "视频所在分区",
  97. showPic: "视频封面",
  98. showSize: "视频分辨率",
  99. showMore: "更多信息",
  100. showCTime: "视频投稿时间",
  101. showViews: "替换视频播放量",
  102. showDmk: "替换视频弹幕量",
  103. showTop: "替换全站排名提示",
  104. showArgue: "显示危险提示",
  105. curr: "当前视频地址",
  106. currTime: "当前视频地址(含视频进度)",
  107. short: "短地址",
  108. share: "快速分享",
  109. shareTime: "快速分享(含视频进度)",
  110. md: "Markdown 格式",
  111. bb: "BBCode 格式",
  112. html: "HTML 格式",
  113. vid: "视频编号",
  114. openGUI: "设置选项"
  115. };
  116. const descCn = {
  117. showAv: "展示视频号(AV号/BV号右键单击可切换),左键单击快速复制(包含当前播放时间),左键长按打开更多格式复制窗口",
  118. showSAv: "展示视频AV号,左键单击快速复制(包含当前播放时间),左键长按打开更多格式复制窗口",
  119. showSBv: "展示视频BV号,左键单击快速复制(包含当前播放时间),左键长按打开更多格式复制窗口",
  120. showPn: "展示视频分P信息以及缓存名(分P名)。可能较长,建议放在最下面,并调整最大长度。",
  121. showCid: "展示视频资源CID编号,通常不需要展示。",
  122. showCate: "展示视频所在的子分区。",
  123. showPic: "提供按钮一键查看封面,长按可以在新标签页打开大图。",
  124. showSize: "展示视频当前分辨率(宽高信息)。",
  125. showMore: "查看视频更多信息。",
  126. showCTime: "用文字方式描述投稿时间,如:一周前",
  127. showViews: "替换展示视频播放量(由于内容相同,将自动隐藏原版播放量信息)",
  128. showDmk: "替换展示视频弹幕量(由于内容相同,将自动隐藏原版弹幕量信息)",
  129. showTop: "替换原版全站排名信息",
  130. showArgue: "如果视频有危险提示,则显示危险提示",
  131. curr: "提供当前视频纯净地址",
  132. currTime: "提供当前视频地址,并在播放时提供含跳转时间的地址(可以直接跳转到当前进度)。",
  133. short: "提供当前视频的b23.tv短地址",
  134. share: "提供当前视频的标题和地址组合文本。",
  135. shareTime: "提供当前视频的标题和地址组合文本,在播放时提供含跳转时间的地址(可以直接跳转到当前进度)。",
  136. md: "提供Markdown特殊语法的快速复制。",
  137. bb: "提供BBCode特殊语法的快速复制。",
  138. html: "提供HTML格式的快速复制。",
  139. vid: "提供当前视频av号/BV号/CID号。请注意此项目不支持快速复制。",
  140. openGUI: "提供按钮快速进入设置选项。"
  141. };
  142. const idTn = {
  143. showAv: 2,
  144. showSAv: 2,
  145. showSBv: 2,
  146. showPn: 5,
  147. showCid: 2,
  148. showCate: 3,
  149. showPic: 1,
  150. showSize: 2,
  151. showMore: 1,
  152. showCTime: -4,
  153. showViews: -2,
  154. showDmk: -2,
  155. showTop: 0,
  156. showArgue: 1,
  157. openGUI: 1
  158. };
  159. let globalinfos = {};
  160. // https://stackoverflow.com/questions/10726638
  161. String.prototype.mapReplace = function (map) {
  162. var regex = [];
  163. for (var key in map)
  164. regex.push(key.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"));
  165. return this.replace(new RegExp(regex.join('|'), "g"), function (word) {
  166. return map[word];
  167. });
  168. };
  169.  
  170. // CSDN https://blog.csdn.net/namechenfl/article/details/91968396
  171. function numberFormat(value) {
  172. let param = {};
  173. let k = 10000,
  174. sizes = ['', '万', '亿', '万亿'],
  175. i;
  176. if (value < k) {
  177. param.value = value
  178. param.unit = ''
  179. } else {
  180. i = Math.floor(Math.log(value) / Math.log(k));
  181. param.value = ((value / Math.pow(k, i))).toFixed(2);
  182. param.unit = sizes[i];
  183. }
  184. return param;
  185. }
  186.  
  187. function exec(code,binding=this){
  188. return (async function(){
  189. eval(code);
  190. }).bind(binding);
  191. }
  192.  
  193. async function saveAllConfig() {
  194. for (let configKey of Object.keys(config)) {
  195. if (ignoredConfigKeys.includes(configKey)) continue;
  196. await GM_setValue(configKey, config[configKey]);
  197. }
  198. popNotify.success("配置保存成功");
  199. }
  200.  
  201. async function initScript(flag = false) {
  202. for (let menuitem of Object.keys(menuId)) {
  203. if (menuId[menuitem] != -1) GM_unregisterMenuCommand(menuId[menuitem]);
  204. }
  205. for (let configKey of Object.keys(config)) {
  206. if (ignoredConfigKeys.includes(configKey)) continue;
  207. if (typeof (await GM_getValue(configKey)) === 'undefined') {
  208. await GM_setValue(configKey, config[configKey]);
  209. } else {
  210. config[configKey] = await GM_getValue(configKey);
  211. }
  212. }
  213. GM_registerMenuCommand("打开设置", async () => {
  214. await GUISettings();
  215. });
  216. CKTools.addStyle(`
  217. #bilibiliShowPN{
  218. max-width: ${config.pnmaxlength}em!important;
  219. }
  220. `, "showav_pnlen", "update", document.head);
  221. tryInject(flag);
  222. }
  223.  
  224. async function playerReady() {
  225. let i = 150;
  226. while (--i > 0) {
  227. await wait(100);
  228. console.log('ShowAV waiting for player ready - loop')
  229. if (unsafeWindow.player?.isInitialized()??false) break;
  230. }
  231. console.log('ShowAV waiting for player ready - loop end')
  232. if (i < 0) return false;
  233. console.log('ShowAV waiting for player ready - wait visible')
  234. await waitForPageVisible();
  235. while (1) {
  236. await wait(200);
  237. console.log('ShowAV waiting for player controlls')
  238. if (document.querySelector(".bilibili-player-video-control-wrap, .bpx-player-control-wrap")) return true;
  239. }
  240. }
  241.  
  242. async function waitForDom(q) {
  243. let i = 50;
  244. let dom;
  245. while (--i >= 0) {
  246. if (dom = document.querySelector(q)) break;
  247. await wait(100);
  248. }
  249. return dom;
  250. }
  251.  
  252. function getUrlParam(key) {
  253. return (new URL(location.href)).searchParams.get(key);
  254. }
  255.  
  256. function getOrNew(id, parent,) {
  257. let target = document.querySelector("#" + id);
  258. if (!target) {
  259. target = document.createElement("span");
  260. target.id = id;
  261. parent.appendChild(target);
  262. }
  263. return target;
  264. }
  265.  
  266. async function getPlayerSeeks() {
  267. const video = await waitForDom(".bilibili-player-video video");
  268. let seconds = 0;
  269. if (video) {
  270. seconds = Math.floor(video.currentTime);
  271. }
  272. if (seconds == 0) {
  273. let fromParam = getUrlParam("t") || 0;
  274. return fromParam;
  275. } else return seconds;
  276. }
  277.  
  278. function getHEVC(){
  279. return document.querySelector(".bilibili-player-video bwp-video")
  280. }
  281.  
  282. async function registerVideoChangeHandler() {
  283. const video = await waitForDom(".bilibili-player-video video");
  284. const HEVCplayer = getHEVC();// must behind video loaded(no more waitfordom)
  285. let target = HEVCplayer || video;
  286. if(!target) return;
  287. const observer = new MutationObserver(async e => {
  288. if (e[0].target.src) {
  289. tryInject(true);
  290. }
  291. });
  292. observer.observe(target, { attribute: true, attributes: true, childList: false });
  293. }
  294.  
  295. function getPageFromCid(cid, infos) {
  296. if (!cid || !infos || !infos.pages) return 1;
  297. let pages = infos.pages;
  298. if (pages.length == 1) return 1;
  299. let page;
  300. for (page of pages) {
  301. if (!page) continue;
  302. if (page.cid == cid) return page.page;
  303. }
  304. return 1;
  305. }
  306.  
  307. async function feat_showCate() {
  308. const { av_root, infos } = this;
  309. const cate_span = getOrNew("bilibiliShowCate", av_root);
  310. //if (config.showCate) {
  311. cate_span.style.textOverflow = "ellipsis";
  312. cate_span.style.whiteSpace = "nowarp";
  313. cate_span.style.overflow = "hidden";
  314. cate_span.title = "分区: " + infos.tname;
  315. cate_span.innerText = "分区: " + infos.tname;
  316. //} else cate_span.remove();
  317. }
  318.  
  319. async function feat_showStaticAv() {
  320. const func = feat_showAv.bind(this);
  321. func(true);
  322. }
  323.  
  324. async function feat_showStaticBv() {
  325. const func = feat_showAv.bind(this);
  326. func(true, 'bv');
  327. }
  328.  
  329. async function prepareData(infos,el=null){
  330. const defaultVid = el?el.innerText:'av'+infos.aid;
  331. const currpage = new URL(location.href);
  332. let part = infos.p==currpage.searchParams.get("p")
  333. ? infos.p
  334. : (currpage.searchParams.get("p") ? currpage.searchParams.get("p") : infos.p);
  335. let url = new URL(location.protocol + "//" + location.hostname + "/video/" + defaultVid);
  336. part == 1 || url.searchParams.append("p", part);
  337. let vidurl = new URL(url);
  338. let shorturl = new URL(location.protocol + "//b23.tv/" + defaultVid);
  339. let t = await getPlayerSeeks();
  340. if (t && t != "0" && t != ("" + config.vduration)) url.searchParams.append("t", t);
  341. try {
  342. partinfo = infos.pages[infos.p - 1];
  343. } catch (e) {
  344. partinfo = infos.pages[0];
  345. }
  346. return {currpage,partinfo,url,vidurl,shorturl,part,t,infos}
  347. }
  348. async function getCopyItem(copyitem,infos,av_span=null){
  349. const {partinfo,url,vidurl,shorturl,part,t} = await prepareData(infos,av_span);
  350. switch (copyitem) {
  351. case "curr":
  352. return {
  353. title: "当前地址",
  354. content: vidurl,
  355. type: "copiable"
  356. };
  357. case "currTime":
  358. return {
  359. title: "含视频进度地址(仅在播放时提供)",
  360. content: url,
  361. type: "copiable"
  362. };
  363. case "short":
  364. return {
  365. title: "短地址格式",
  366. content: shorturl,
  367. type: "copiable"
  368. };
  369. case "share":
  370. return {
  371. title: "快速分享",
  372. content: `${infos.title}_地址:${shorturl}`,
  373. type: "copiable"
  374. };
  375. case "shareTime":
  376. return {
  377. title: "快速分享(含视频进度)",
  378. content: `${infos.title}_地址:${url}`,
  379. type: "copiable"
  380. };
  381. case "md":
  382. return {
  383. title: "MarkDown格式",
  384. content: `[${infos.title}](${vidurl})`,
  385. type: "copiable"
  386. };
  387. case "bb":
  388. return {
  389. title: "BBCode格式",
  390. content: `[url=${vidurl}]${infos.title}[/url]`,
  391. type: "copiable"
  392. };
  393. case "html":
  394. return {
  395. title: "HTML格式",
  396. content: `<a href="${vidurl}">${infos.title}</a>`,
  397. type: "copiable"
  398. };
  399. case "vid":
  400. return {
  401. title: "视频编号",
  402. content: `<div class="shoav_expandinfo">
  403. <div>
  404. AV
  405. <input class="shortinput" readonly value="av${infos.aid}" onclick="showav_fastcopy(this);" />
  406. </div>
  407. <div>
  408. BV
  409. <input class="shortinput" readonly value="${infos.bvid}" onclick="showav_fastcopy(this);" />
  410. </div>
  411. <div>
  412. 资源CID
  413. <input class="shortinput" readonly value="${infos.cid}" onclick="showav_fastcopy(this);" />
  414. </div>
  415. </div>
  416. `,
  417. type: "component",
  418. copyaction: function(){
  419. copy(this.av_span.innerText);
  420. popNotify.success("已复制到剪贴板",this.av_span.innerText);
  421. }
  422. };
  423. default:
  424. if (Object.keys(config.customcopyitems).includes(copyitem)) {
  425. let ccopyitem = config.customcopyitems[copyitem];
  426. let pat = ccopyitem.content ? ccopyitem.content : "无效内容";
  427. pat = apiBasedVariablesReplacement(pat.mapReplace({
  428. "%timeurl%": url,
  429. "%vidurl%": vidurl,
  430. "%shorturl%": shorturl,
  431. "%seek%": t,
  432. "%title%": infos.title,
  433. "%av%": infos.aid,
  434. "%bv%": infos.bvid,
  435. "%cid%": infos.cid,
  436. "%p%": part,
  437. "%pname%": partinfo.part,
  438. "'": "\'"
  439. }));
  440. return {
  441. title: `(自定义) ${ccopyitem.title}`,
  442. content: pat,
  443. type: "copiable"
  444. }
  445. }else return null;
  446. }
  447. };
  448.  
  449. function apiBasedVariablesReplacement(txt='',infos=globalinfos){
  450. log("apiBasedVariablesReplacement",infos);
  451. const vars = [...txt.matchAll(/\%[a-zA-Z.]+\%/g)].map(k=>k[0]);
  452. if(vars.length==0) return txt;
  453. const map = {};
  454. for(let replaceVar of vars){
  455. const varName = replaceVar.substring(1,replaceVar.length-1);
  456. const apiResult = recursiveApiResolver(varName,infos);
  457. if(!apiResult) continue;
  458. map[replaceVar] = apiResult;
  459. }
  460. return txt.mapReplace(map);
  461. }
  462.  
  463. function recursiveApiResolver(name,infos=globalinfos){
  464. const pargs = name.split(".").filter(arr=>arr.length);
  465. if([pargs[0],pargs[pargs.length-1]].includes("")) return null;
  466. let target = infos;
  467. for(let parg of pargs){
  468. if(target.hasOwnProperty(parg)){
  469. target = target[parg];
  470. }else return null;
  471. }
  472. return target.toString();
  473. }
  474.  
  475. async function feat_showAv(force = false, mode = 'av'/* 'bv' */) {
  476. const { av_root, infos } = this;
  477. const av_span = getOrNew("bilibiliShowAV" + (force ? mode : ''), av_root);
  478. //if (config.showAv) {
  479. if (force) {
  480. if (mode == 'bv') {
  481. av_span.innerText = infos.bvid;
  482. } else {
  483. av_span.innerText = 'av' + infos.aid;
  484. }
  485. } else if (config.defaultAv)
  486. av_span.innerText = 'av' + infos.aid;
  487. else
  488. av_span.innerText = infos.bvid;
  489. av_span.style.overflow = "hidden";
  490. const video = await waitForDom("video");
  491. if (video) {
  492. config.vduration = Math.floor(video.duration);
  493. }
  494. let title = `左键单击复制,${force?'右键单击切换显示,':''}长按打开窗口`;
  495. if(config.copyitems.length){
  496. const firstCopyItem = config.copyitems[0];
  497. const firstInfo = await getCopyItem(firstCopyItem,globalinfos,av_span);
  498. if(firstInfo!==null){
  499. if(firstInfo.type=="copiable"||firstInfo.type=="component"){
  500. av_span.setAttribute('title',title + '\n默认复制: '+firstInfo.title);
  501. }
  502. }else av_span.setAttribute('title',title + '\n没有默认复制行为');
  503. }else{
  504. av_span.setAttribute('title',title + '\n没有默认复制行为');
  505. }
  506. if (av_span.getAttribute("setup") != globalinfos.cid) {
  507. if (!force)
  508. av_span.oncontextmenu = e => {
  509. if (e.target.innerText.startsWith('av')) e.target.innerText = infos.bvid;
  510. else av_span.innerText = 'av' + infos.aid;
  511. e.preventDefault();
  512. };
  513. let runningCfg = config.running['avspanHC'+(force ? mode : '')];
  514. if(runningCfg) runningCfg.uninstall();
  515. runningCfg = new CKTools.HoldClick(av_span);
  516. runningCfg.onclick(async e => {
  517. for (let copyitem of config.copyitems) {
  518. const copyiteminfo = await getCopyItem(copyitem,globalinfos,av_span);
  519. if(copyiteminfo===null) {
  520. log(`[ADVCOPY] warning: unknown custom copy item id "${copyitem}", maybe should clean settings up.`);
  521. continue;
  522. }
  523. if(copyiteminfo.type=="copiable"){
  524. copy(copyiteminfo.content);
  525. popNotify.success(copyiteminfo.title+"复制成功", copyiteminfo.content);
  526. return;
  527. }
  528. else if(copyiteminfo.type=="component"){
  529. if(typeof(copyiteminfo.copyaction)==="function"){
  530. try{
  531. const func = copyiteminfo.copyaction.bind({av_span});
  532. func();
  533. }catch(e){log(copyiteminfo,e);}
  534. }else{
  535. copy(copyiteminfo.copyaction.toString());
  536. popNotify.success("已复制到剪贴板",copyiteminfo.copyaction.toString())
  537. }
  538. return;
  539. }
  540. else continue;
  541. }
  542. popNotify.error("快速复制失败","没有任何已启用的可用快速复制设定");
  543. });
  544. runningCfg.onhold(async e => {
  545. let modalcontent = `
  546. <style scoped>
  547. input:not(.shortinput){
  548. width:100%;
  549. display:block;
  550. }
  551. .shoav_expandinfo>div {
  552. text-align: center;
  553. flex: 1;
  554. }
  555. input.shortinput {
  556. width: 7.8em;
  557. text-align: center;
  558. }
  559. .CKTOOLS-modal-content>div>div{
  560. width: 440px!important;
  561. }
  562. .shoav_expandinfo{
  563. display: flex;
  564. flex-direction: row;
  565. flex-wrap: nowrap;
  566. align-content: center;
  567. justify-content: space-around;
  568. align-items: stretch;
  569. }
  570. </style>
  571. <b>点击输入框可以快速复制</b><br>`;
  572. for (let copyitem of config.copyitems) {
  573. const copyiteminfo = await getCopyItem(copyitem,globalinfos,av_span);
  574. if(copyiteminfo.type=="copiable"){
  575. modalcontent+=`<span class="copyitem-title">${copyiteminfo.title}</span><input readonly value="${copyiteminfo.content}" onclick="showav_fastcopy(this);" />`
  576. }
  577. else{
  578. modalcontent+=copyiteminfo.content;
  579. }
  580. }
  581. modalcontent += `<br><hr><a href="javascript:void(0)" onclick="showav_guisettings_advcopy()">⚙ 复制设置</a><br>
  582. <a href="https://github.com/CKylinMC/UserJS/issues/new?assignees=CKylinMC&labels=&template=feature-request.yaml&title=%5BIDEA%5D+ShowAV%E8%84%9A%E6%9C%AC%E9%A2%84%E8%AE%BE%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B7%E6%B1%82&target=[%E8%84%9A%E6%9C%AC%EF%BC%9A%E8%A7%86%E9%A2%91%E9%A1%B5%E9%9D%A2%E5%B8%B8%E9%A9%BB%E6%98%BE%E7%A4%BAAV/BV%E5%8F%B7]&desp=%E6%88%91%E5%B8%8C%E6%9C%9B%E6%B7%BB%E5%8A%A0%E6%96%B0%E7%9A%84%E9%A2%84%E8%AE%BE%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%EF%BC%8C%E5%A6%82%E4%B8%8B...">缺少你需要的格式?反馈来添加...</a>
  583. `;
  584. modalcontent+= closeButton().outerHTML;
  585. CKTools.modal.alertModal("高级复制", modalcontent, "关闭");
  586. });
  587. av_span.setAttribute("setup", globalinfos.cid);
  588. config.running['avspanHC'+(force ? mode : '')] = runningCfg;
  589. }
  590. //} else av_span.remove();
  591. }
  592.  
  593. async function feat_showMore() {
  594. const { av_root } = this;
  595. const more_span = getOrNew("bilibiliShowMore", av_root);
  596. more_span.innerHTML = '⋯';
  597. more_span.title = "展示更多信息";
  598. more_span.style.cursor = "pointer";
  599. if (more_span.getAttribute("setup") != globalinfos.cid) {
  600. more_span.addEventListener('click', async e => {
  601. let part, videoData = globalinfos;
  602. try {
  603. part = videoData.pages[globalinfos.p - 1];
  604. } catch (e) {
  605. part = videoData.pages[0];
  606. }
  607. let currentPageName = part.part.length ? part.part : '';
  608. let currentPageNum;
  609. if (videoData.videos != 1) {
  610. currentPageNum = `P ${globalinfos.p}/${videoData.videos}`;
  611. } else {
  612. currentPageNum = "P 1/1";
  613. }
  614. CKTools.modal.alertModal("视频信息", `
  615. <style scoped>
  616. li{
  617. line-height: 2em;
  618. }
  619. </style>
  620. <li>
  621. <b>AV号: </b>av${globalinfos.aid}
  622. </li>
  623. <li>
  624. <b>BV号: </b>${globalinfos.bvid}
  625. </li>
  626. <li>
  627. <b>CID: </b>${globalinfos.cid}
  628. </li>
  629. <li>
  630. <b>分P: </b>${currentPageNum}
  631. </li>
  632. <li>
  633. <b>P名: </b>${currentPageName}
  634. </li>
  635. <li>
  636. <b>长度: </b>${globalinfos.duration}s
  637. </li>
  638. <li>
  639. <b>投稿: </b>${timeago.format(globalinfos.ctime * 1000, 'zh_CN')}
  640. </li>
  641. <li>
  642. <b>分区: </b>${globalinfos.tname}
  643. </li>
  644. <li>
  645. <b>大小: </b>${globalinfos.dimension.width}x${globalinfos.dimension.height}
  646. </li>
  647. <li>
  648. <b>封面: </b><a href="${globalinfos.pic}" target="_blank">点击查看</a>
  649. </li>
  650. `, "确定");
  651. })
  652. more_span.setAttribute("setup", globalinfos.cid);
  653. }
  654. }
  655.  
  656. async function feat_showCTime() {
  657. const { av_root, infos } = this;
  658. const ct_span = getOrNew("bilibiliShowCTime", av_root);
  659. ct_span.style.textOverflow = "ellipsis";
  660. ct_span.style.whiteSpace = "nowarp";
  661. ct_span.style.overflow = "hidden";
  662. const d = new Date(infos.pubdate * 1000);
  663. let txttime = timeago.format(infos.pubdate * 1000, 'zh_CN');
  664. let rawtime = `${d.getFullYear()}-${(d.getMonth() + 1) < 10 ? '0' + (d.getMonth() + 1) : d.getMonth() + 1}-${d.getDate() < 10 ? '0' + d.getDate() : d.getDate()} ${d.getHours() < 10 ? '0' + d.getHours() : d.getHours()}:${d.getMinutes() < 10 ? '0' + d.getMinutes() : d.getMinutes()}:${d.getSeconds() < 10 ? '0' + d.getSeconds() : d.getSeconds()}`;
  665.  
  666. ct_span.title = "投稿时间 " + (config.defaultTextTime ? rawtime : txttime);
  667. ct_span.innerHTML = config.defaultTextTime ? txttime : rawtime
  668. if (config.hideTime) ct_span.innerHTML += `
  669. <style>
  670. .video-info-detail span:nth-child(3),.video-info-detail span.pupdate{
  671. display:none!important;
  672. }
  673. </style>`;
  674. }
  675.  
  676. async function feat_showViews() {
  677. const { av_root, infos } = this;
  678. const v_span = getOrNew("bilibiliShowViews", av_root);
  679. v_span.style.textOverflow = "ellipsis";
  680. v_span.style.whiteSpace = "nowarp";
  681. v_span.style.overflow = "hidden";
  682. v_span.title = `播放量 ${infos.stat.view}`;
  683. v_span.innerHTML = (() => {
  684. const res = numberFormat(infos.stat.view);
  685. return `${res.value}${res.unit}播放`;
  686. })();
  687. v_span.innerHTML += `
  688. <style>
  689. .video-info-detail span.view{
  690. display:none!important;
  691. }
  692. </style>`;
  693. }
  694.  
  695. async function feat_showDmk() {
  696. const { av_root, infos } = this;
  697. const dmk_span = getOrNew("bilibiliShowDmk", av_root);
  698. dmk_span.style.textOverflow = "ellipsis";
  699. dmk_span.style.whiteSpace = "nowarp";
  700. dmk_span.style.overflow = "hidden";
  701. dmk_span.title = `${infos.stat.danmaku}条弹幕`;
  702. dmk_span.innerHTML = (() => {
  703. const res = numberFormat(infos.stat.danmaku);
  704. return `${res.value}${res.unit}条弹幕`;
  705. })();
  706. dmk_span.innerHTML += `
  707. <style>
  708. .video-info-detail span.dm{
  709. display:none!important;
  710. }
  711. </style>`;
  712. }
  713.  
  714. async function feat_showTop() {
  715. const { av_root, infos } = this;
  716. const top_span = getOrNew("bilibiliShowTop", av_root);
  717. top_span.style.textOverflow = "ellipsis";
  718. top_span.style.whiteSpace = "nowarp";
  719. top_span.style.overflow = "hidden";
  720. top_span.title = `全站最高排行第${infos.stat.his_rank}名`;
  721. top_span.innerHTML = ''
  722. top_span.innerHTML += `
  723. <style>
  724. .video-info-detail span.rank,.video-info-detail a.honor{
  725. display:none!important;
  726. }
  727. </style>`;
  728. if (infos.stat.his_rank === 0) {
  729. top_span.style.display = "none";
  730. setTimeout(() => {
  731. if (top_span.nextElementSibling) {
  732. top_span.nextElementSibling.style.marginLeft = 0;
  733. }
  734. }, 100);
  735. } else {
  736. top_span.innerHTML += '📊 ' + infos.stat.his_rank;
  737. }
  738. }
  739.  
  740. async function feat_showPic() {
  741. const { av_root, infos } = this;
  742. const pic_span = getOrNew("bilibiliShowPic", av_root);
  743. pic_span.style.textOverflow = "ellipsis";
  744. pic_span.style.whiteSpace = "nowarp";
  745. pic_span.style.overflow = "hidden";
  746. pic_span.title = "查看封面";
  747. pic_span.innerHTML = "🖼️";
  748. pic_span.style.cursor = "pointer";
  749. if (pic_span.getAttribute("setup") != globalinfos.cid) {
  750. config.running.picHC && config.running.picHC.uninstall();
  751. config.running.picHC = new CKTools.HoldClick(pic_span);
  752. config.running.picHC.onclick(() => {
  753. CKTools.modal.alertModal("封面", `
  754. <img src="${globalinfos.pic}" style="width:100%" onload="this.parentElement.style.width='100%'" />
  755. `, "关闭");
  756. });
  757. config.running.picHC.onhold(() => {
  758. open(globalinfos.pic);
  759. });
  760. pic_span.setAttribute("setup", globalinfos.cid);
  761. }
  762. }
  763.  
  764. async function feat_showCid() {
  765. const { av_root, infos } = this;
  766. const cid_span = getOrNew("bilibiliShowCID", av_root);
  767. //if (config.showCid) {
  768. cid_span.style.textOverflow = "ellipsis";
  769. cid_span.style.whiteSpace = "nowarp";
  770. cid_span.style.overflow = "hidden";
  771. cid_span.title = "CID:" + infos.cid;
  772. cid_span.innerText = "CID:" + infos.cid;
  773. if (cid_span.getAttribute("setup") != globalinfos.cid) {
  774. config.running.cidspanHC && config.running.cidspanHC.uninstall();
  775. config.running.cidspanHC = new CKTools.HoldClick(cid_span);
  776. config.running.cidspanHC.onclick(() => {
  777. copy(currentPageName);
  778. popNotify.success("CID复制成功", globalinfos.cid);
  779. });
  780. config.running.cidspanHC.onhold(() => {
  781. CKTools.modal.alertModal("CID信息", `
  782. <input readonly style="width:440px" value="${globalinfos.cid}" />
  783. `, "关闭");
  784. });
  785. cid_span.setAttribute("setup", globalinfos.cid);
  786. }
  787. //} else cid_span.remove();
  788. }
  789.  
  790. async function feat_showSize() {
  791. const { av_root, infos } = this;
  792. const size_span = getOrNew("bilibiliShowSize", av_root);
  793. //if (config.showCid) {
  794. size_span.style.textOverflow = "ellipsis";
  795. size_span.style.whiteSpace = "nowarp";
  796. size_span.style.overflow = "hidden";
  797. size_span.title = `${infos.dimension.width}x${infos.dimension.height}`;
  798. size_span.innerText = `${infos.dimension.width}x${infos.dimension.height}`;
  799. //} else cid_span.remove();
  800. }
  801.  
  802. async function feat_openGUI() {
  803. const { av_root, infos } = this;
  804. const gui_span = getOrNew("bilibiliShowGUISettings", av_root);
  805. gui_span.innerHTML = "⚙";
  806. gui_span.title = "ShowAV 设置";
  807. gui_span.style.overflow = "hidden";
  808. gui_span.style.cursor = "pointer";
  809. gui_span.onclick = e => GUISettings();
  810. }
  811.  
  812. async function feat_showArgue() {
  813. const { av_root, infos } = this;
  814. const argue_span = getOrNew("bilibiliShowArgue", av_root);
  815. const original = document.querySelector(".argue.item");
  816. if(!original) argue_span.style.display = "none";
  817. else argue_span.style.display = "block";
  818. argue_span.style.color = "rgb(255, 170, 44)";
  819. argue_span.innerHTML = "<i class='van-icon-info_warning'></i>";
  820. argue_span.title = (original&&original.title)||"警告";
  821. argue_span.style.overflow = "hidden";
  822. }
  823.  
  824. async function feat_showPn() {
  825. const { av_root, infos } = this;
  826. const pn_span = getOrNew("bilibiliShowPN", av_root);
  827. //if (config.showPn) {
  828. const videoData = infos;
  829. if (!videoData) return;
  830. let part = {
  831. part: 'P' + infos.p
  832. }
  833. try {
  834. part = videoData.pages[infos.p - 1];
  835. } catch (e) {
  836. part = videoData.pages[0];
  837. }
  838. let currentPageName = part.part.length ? part.part : '';
  839. let currentPageNum;
  840. let delimiters;
  841. if (videoData.videos != 1) {
  842. currentPageNum = `P ${infos.p}/${videoData.videos}`;
  843. delimiters = ["\n", " "];
  844. } else {
  845. currentPageNum = "";
  846. delimiters = ["", ""];
  847. }
  848. pn_span.style.textOverflow = "ellipsis";
  849. pn_span.style.whiteSpace = "nowarp";
  850. pn_span.style.overflow = "hidden";
  851. pn_span.title = currentPageNum + delimiters[0] + currentPageName
  852. pn_span.innerText = currentPageNum + delimiters[1] + currentPageName;
  853.  
  854. if (pn_span.getAttribute("setup") != globalinfos.cid) {
  855. config.running.pnspanHC && config.running.pnspanHC.uninstall();
  856. config.running.pnspanHC = new CKTools.HoldClick(pn_span);
  857. config.running.pnspanHC.onclick(() => {
  858. copy(currentPageName);
  859. popNotify.success("分P标题复制成功", currentPageName);
  860. });
  861. config.running.pnspanHC.onhold(() => {
  862. CKTools.modal.alertModal("分P标题", `
  863. <input readonly style="width:440px" value="${currentPageName}" />
  864. `, "关闭");
  865. });
  866. pn_span.setAttribute("setup", globalinfos.cid);
  867. }
  868. //} else pn_span.remove();
  869. }
  870.  
  871. async function feat_custom(itemid){
  872. const { av_root, infos } = this;
  873. const that = this;
  874. that.window = unsafeWindow;
  875. const custom_span = getOrNew("bilibili_"+itemid, av_root);
  876. const {partinfo,url,vidurl,shorturl,part,t} = await prepareData(infos);
  877. const parseTxt = txt=>apiBasedVariablesReplacement(txt.mapReplace({
  878. "%timeurl%": url,
  879. "%vidurl%": vidurl,
  880. "%shorturl%": shorturl,
  881. "%seek%": t,
  882. "%title%": infos.title,
  883. "%av%": infos.aid,
  884. "%bv%": infos.bvid,
  885. "%cid%": infos.cid,
  886. "%p%": part,
  887. "%pname%": partinfo.part,
  888. "'": "\'"
  889. }));
  890. if(Object.keys(config.customComponents).includes(itemid)){
  891. const item = config.customComponents[itemid];
  892. let content = item.content;
  893. if(item.content.startsWith("js:")){
  894. content = item.content.replace("js:","");
  895. }
  896. else content = parseTxt(item.content);
  897. custom_span.style.overflow = "hidden";
  898. try{
  899. if(item.title.startsWith("js:")){
  900. let itemtitle = item.title.substr(3);
  901. custom_span.innerHTML = eval(parseTxt(itemtitle));
  902. }else
  903. custom_span.innerHTML = parseTxt(item.title);
  904. }catch(e){
  905. custom_span.innerHTML = parseTxt(item.title);
  906. }
  907. custom_span.title = `自定义组件: ${item.title}\n长按管理自定义组件`;
  908. if(custom_span.getAttribute("setup")!=globalinfos.cid){
  909. custom_span.setAttribute("setup",globalinfos.cid);
  910. config.running[itemid] && config.running[itemid].uninstall();
  911. config.running[itemid] = new CKTools.HoldClick(custom_span);
  912. config.running[itemid].onclick(e => {
  913. console.log(item.content)
  914. if(item.content.startsWith("js:")){
  915. log("executing:",content);
  916. exec(content,that)();
  917. }else{
  918. copy(content);
  919. popNotify.success("已复制"+item.title,content);
  920. }
  921. });
  922. config.running[itemid].onhold(e=>{
  923. GUISettings_customcomponents();
  924. })
  925. }
  926. }else{
  927. log("Errored while handling custom components:",k,"not found");
  928. custom_span.remove();
  929. }
  930. }
  931.  
  932. function getSideloadModules(){
  933. if(!unsafeWindow.ShowAVModules) return {};
  934. const mods = {};
  935. for(const modName of Object.keys(unsafeWindow.ShowAVModules)){
  936. const mod = unsafeWindow.ShowAVModules[modName];
  937. if(mod&&(typeof(mod.name)==='string')&&(typeof(mod.onload)==='function')&&(typeof(mod.onclick)==='function')&&(typeof(mod.onhold)==='function')){
  938. mods[modName] = mod;
  939. }
  940. }
  941. return mods;
  942. }
  943.  
  944. function mappedSideloadModules(){
  945. const sideloads = getSideloadModules();
  946. const mods = {};
  947. for(const modName of Object.keys(sideloads)){
  948. mods['sideload_'+modName] = sideloads[modName];
  949. }
  950. return mods;
  951. }
  952.  
  953. async function runSideloadModule(module,moduleInternalID = (Math.floor(Math.random()*10000))){
  954. let slm_span = null;
  955. try{
  956. const { av_root }=this;
  957. const onloadFn = module.onload.bind(this);
  958. const onclickFn = module.onclick.bind(this);
  959. const onholdFn = module.onhold.bind(this);
  960. const name = "showav_slm_" + moduleInternalID;
  961. slm_span = getOrNew(name, av_root);
  962. slm_span.innerHTML = '';
  963. slm_span.style.textOverflow = "ellipsis";
  964. slm_span.style.whiteSpace = "nowarp";
  965. slm_span.style.overflow = "hidden";
  966. slm_span.title = "模块:" + module.name;
  967. if(module.tip){
  968. if(typeof(module.tip)=='function') slm_span.title+='\n'+module.tip.bind(this)();
  969. else slm_span.title+='\n'+module.tip;
  970. }else if(module.description){
  971. slm_span.title+='\n'+module.description;
  972. }
  973. slm_span.appendChild(await onloadFn(slm_span));
  974. if (slm_span.getAttribute("setup") != globalinfos.cid) {
  975. config.running[name] && config.running[name].uninstall();
  976. config.running[name] = new CKTools.HoldClick(slm_span);
  977. config.running[name].onclick(onclickFn);
  978. config.running[name].onhold(onholdFn);
  979. slm_span.setAttribute("setup", globalinfos.cid);
  980. }
  981. }catch(e){
  982. log('[ERR]',module.name,e);
  983. (slm_span&&(slm_span instanceof HTMLElement)&&slm_span.remove());
  984. }
  985. }
  986.  
  987. async function tryInject(flag) {
  988. console.log('ShowAV waiting for player ready')
  989. if (flag && config.orders.length === 0) return log('Terminated because no option is enabled.');
  990. if (!(await playerReady())) return log('Can not load player in time.');
  991.  
  992. if (config.firstTimeLoad) {
  993. registerVideoChangeHandler();
  994. config.firstTimeLoad = false;
  995. }
  996. console.log('ShowAV start inject')
  997. CKTools.addStyle(`.video-container-v1 .copyright.item{display:none!important;}.video-container-v1 .video-info-detail{flex-wrap: wrap!important;}`,"showav_patchNewPlayer","update",document.head);
  998.  
  999. if (config.forceGap) {
  1000. CKTools.addStyle(`#bilibiliShowInfos{margin-left: 12px!important;}`,"showav_forceGapCss","update",document.head);
  1001. }else{
  1002. CKTools.addStyle(``,"showav_forceGapCss","update",document.head);
  1003. }
  1004. if(config.forceRemoveAllItem){
  1005. CKTools.addStyle(`.video-container-v1 .video-info-detail>.item{display:none!important}.video-info-detail>span:not(#bilibiliShowInfos){display:none!important}`,"showav_hideall", "update", document.head);
  1006. }else{
  1007. CKTools.addStyle(``,"showav_hideall", "update", document.head);
  1008. }
  1009. if(config.nobreakline){
  1010. CKTools.addStyle(`#bilibiliShowInfos{max-width: 100%;flex-wrap: nowrap!important;}`,"showav_nobreak", "update", document.head);
  1011. }else{
  1012. CKTools.addStyle(``,"showav_nobreak", "update", document.head);
  1013. }
  1014.  
  1015. if (location.pathname.startsWith("/medialist")) {
  1016. let aid = unsafeWindow.aid;
  1017. if (!aid) {
  1018. log("Variable 'aid' is not available from unsafeWindow.");
  1019. let activeVideo = await waitForDom(".player-auxiliary-playlist-item-active");
  1020. aid = activeVideo.getAttribute("data-aid");
  1021. }
  1022. let apidata = await getAidAPI(aid);
  1023. globalinfos = apidata.data;
  1024. } else {
  1025. if (flag)
  1026. globalinfos = (await getAPI(unsafeWindow.vd?.bvid)).data;
  1027. else globalinfos = unsafeWindow.vd;
  1028. }
  1029. globalinfos.p = getUrlParam("p") || getPageFromCid(unsafeWindow.cid, globalinfos);
  1030.  
  1031. //const av_infobar = await waitForDom(".video-data");
  1032. const av_infobar = await waitForDom(".video-info-detail");
  1033. if (!av_infobar) return log('Can not load info-bar in time.');
  1034. let av_root;
  1035. if (config.showInNewLine) {
  1036. av_root = getOrNew("bilibiliShowInfos", av_infobar.parentElement);
  1037. } else {
  1038. let rootel = document.querySelector("#bilibiliShowInfos");
  1039. if (!rootel) {
  1040. rootel = document.createElement("span");
  1041. rootel.id = "bilibiliShowInfos";
  1042. av_infobar.appendChild(rootel);
  1043. }
  1044. av_root = rootel;
  1045. }
  1046. //const av_root = getOrNew("bilibiliShowInfos",av_infobar);
  1047. //const av_root = av_infobar;
  1048.  
  1049. av_root.style.textOverflow = "ellipsis";
  1050. av_root.style.whiteSpace = "nowrap!important";
  1051. // av_root.style.overflow = "hidden";
  1052. const that = {
  1053. av_root, config, av_infobar, infos : globalinfos, CKTools, popNotify, tools:{
  1054. copy, wait, waitForPageVisible, log, getPlayerSeeks, getHEVC, waitForDom, getOrNew, playerReady, variablesReplace:apiBasedVariablesReplacement
  1055. },
  1056. };
  1057.  
  1058. const functions = {
  1059. showAv: feat_showAv.bind(that),
  1060. showSAv: feat_showStaticAv.bind(that),
  1061. showSBv: feat_showStaticBv.bind(that),
  1062. showCate: feat_showCate.bind(that),
  1063. showCid: feat_showCid.bind(that),
  1064. showPn: feat_showPn.bind(that),
  1065. showPic: feat_showPic.bind(that),
  1066. showSize: feat_showSize.bind(that),
  1067. showMore: feat_showMore.bind(that),
  1068. showCTime: feat_showCTime.bind(that),
  1069. showDmk: feat_showDmk.bind(that),
  1070. showViews: feat_showViews.bind(that),
  1071. showTop: feat_showTop.bind(that),
  1072. showArgue: feat_showArgue.bind(that),
  1073. openGUI: feat_openGUI.bind(that),
  1074. customDriver: feat_custom.bind(that)
  1075. }
  1076.  
  1077. const sideloads = mappedSideloadModules();
  1078.  
  1079. config.orders.forEach(async k => {
  1080. if(Object.keys(functions).includes(k)) await functions[k]();
  1081. else if(Object.keys(sideloads).includes(k)) await runSideloadModule.bind(that)(sideloads[k], k);
  1082. else{
  1083. try{
  1084. await functions.customDriver(k);
  1085. }catch(e){
  1086. log(`Custom component "${k}" throwed an error:`,e)
  1087. };
  1088. }
  1089. });
  1090. const titleobj = document.querySelector("span.tit");
  1091. if(titleobj&&!titleobj.getAttribute("data-copy-action-registered")){
  1092. titleobj.onclick = e => {
  1093. let content = e.target.innerText;
  1094. let tip = "已复制视频标题";
  1095. if(unsafeWindow.getSelection().toString().length){
  1096. content = unsafeWindow.getSelection().toString();
  1097. tip = "已复制视频标题选中部分";
  1098. }
  1099. copy(content);
  1100. popNotify.success(tip,content);
  1101. }
  1102. titleobj.setAttribute("data-copy-action-registered",true);
  1103. }
  1104.  
  1105. setupWarningAutoFolding();
  1106. }
  1107.  
  1108. function setupWarningAutoFolding(){
  1109. //if(config.foldedWarningTip)
  1110. /*CKTools.addStyle(
  1111. "span.argue{margin-right: 10px !important;margin-left: 0 !important;overflow: hidden !important;width: 15px !important;text-overflow: clip !important;padding: 3px 4px !important}span.argue>i{margin-right: 5px!important}",
  1112. "showav_foldWarningTip","update");*/
  1113. CKTools.addStyle(
  1114. "span.argue{display:none!important}",
  1115. "showav_foldWarningTip","update");
  1116. /*else
  1117. CKTools.addStyle(
  1118. "span.argue{margin-right: 10px !important;margin-left: 0 !important;}",
  1119. "showav_foldWarningTip","update");*/
  1120. }
  1121.  
  1122. function closeButton(){
  1123. const closebtn = document.createElement("div");
  1124. closebtn.innerHTML = " × ";
  1125. closebtn.style.position = "absolute";
  1126. closebtn.style.top = "10px";
  1127. closebtn.style.right = "10px";
  1128. closebtn.style.cursor = "pointer";
  1129. closebtn.style.fontWeight = 900;
  1130. closebtn.style.fontSize = "larger";
  1131. closebtn.setAttribute("onclick","CKTools.modal.hideModal()");
  1132. return closebtn;
  1133. }
  1134.  
  1135. async function GUISettings() {
  1136. if (CKTools.modal.isModalShowing()) {
  1137. CKTools.modal.hideModal();
  1138. await wait(300);
  1139. }
  1140. CKTools.modal.openModal("ShowAV / 设置", await CKTools.domHelper("div", async container => {
  1141. container.style.alignItems = "stretch";
  1142. container.style.minWidth = "300px";
  1143. [
  1144. closeButton(),
  1145. await CKTools.domHelper("div", async tip => {
  1146. tip.style.lineHeight = "2em";
  1147. tip.style.fontSize = "small";
  1148. tip.style.fontStyle = "italic";
  1149. tip.style.width = "100%";
  1150. tip.innerText = "修改设置后记得点击保存哦";
  1151. }),
  1152. await CKTools.domHelper("li", async list => {
  1153. list.classList.add("showav_menuitem");
  1154. list.onclick = e => GUISettings_options();
  1155. [
  1156. await CKTools.domHelper("label", label => {
  1157. label.innerHTML = "功能选项";
  1158. }),
  1159. await CKTools.domHelper("span", label => {
  1160. label.innerHTML = "调整每个功能模块的单独选项";
  1161. label.style.marginLeft = "6px";
  1162. }),
  1163. ].forEach(e => list.appendChild(e));
  1164. }),
  1165. await CKTools.domHelper("li", async list => {
  1166. list.classList.add("showav_menuitem");
  1167. list.onclick = e => GUISettings_components();
  1168. [
  1169. await CKTools.domHelper("label", label => {
  1170. label.innerHTML = "组件设置";
  1171. }),
  1172. await CKTools.domHelper("span", label => {
  1173. label.innerHTML = "启用/排序/自定义功能组件";
  1174. label.style.marginLeft = "6px";
  1175. }),
  1176. ].forEach(e => list.appendChild(e));
  1177. }),
  1178. await CKTools.domHelper("li", async list => {
  1179. list.classList.add("showav_menuitem");
  1180. list.onclick = e => GUISettings_customcomponents(()=>GUISettings());
  1181. [
  1182. await CKTools.domHelper("label", label => {
  1183. label.innerHTML = "自定义组件设置";
  1184. }),
  1185. await CKTools.domHelper("span", label => {
  1186. label.innerHTML = "添加或删除自定义的信息栏组件";
  1187. label.style.marginLeft = "6px";
  1188. }),
  1189. ].forEach(e => list.appendChild(e));
  1190. }),
  1191. await CKTools.domHelper("li", async list => {
  1192. list.classList.add("showav_menuitem");
  1193. list.onclick = e => GUISettings_advcopy(()=>GUISettings());
  1194. [
  1195. await CKTools.domHelper("label", label => {
  1196. label.innerHTML = "高级复制设置";
  1197. }),
  1198. await CKTools.domHelper("span", label => {
  1199. label.innerHTML = "自定义复制弹窗和默认动作";
  1200. label.style.marginLeft = "6px";
  1201. }),
  1202. ].forEach(e => list.appendChild(e));
  1203. }),
  1204. ].forEach(e => container.appendChild(e));
  1205. }));
  1206. }
  1207.  
  1208. async function GUISettings_options() {
  1209. if (CKTools.modal.isModalShowing()) {
  1210. CKTools.modal.hideModal();
  1211. await wait(300);
  1212. }
  1213. CKTools.modal.openModal("ShowAV / 设置 / 功能选项", await CKTools.domHelper("div", async container => {
  1214. container.style.alignItems = "stretch";
  1215. [
  1216. closeButton(),
  1217. await CKTools.domHelper("li", sectiontitle=>{
  1218. sectiontitle.innerText = "信息栏";
  1219. sectiontitle.className = "showav_settings_sectiontitle";
  1220. }),
  1221. await CKTools.domHelper("li", async list => {
  1222. list.style.lineHeight = "2em";
  1223. [
  1224. await CKTools.domHelper("input", input => {
  1225. input.type = "checkbox";
  1226. input.id = "showav_forcegap";
  1227. input.name = "showav_forcegap";
  1228. input.style.display = "none";
  1229. input.checked = config.forceGap;
  1230. input.addEventListener("change",e=>{
  1231. const label = document.querySelector("#showav_forcegaptip");
  1232. if(!label) return;
  1233. if(input.checked){
  1234. label.innerHTML = "在第一个组件前<b>强制添加</b>间隔(点击切换)"
  1235. }else{
  1236. label.innerHTML = "在第一个组件前<b>保持默认</b>间隔(点击切换)"
  1237. }
  1238. })
  1239. }),
  1240. await CKTools.domHelper("label", label => {
  1241. label.id = "showav_forcegaptip";
  1242. label.setAttribute('for', "showav_forcegap");
  1243. if(config.forceGap){
  1244. label.innerHTML = "在第一个组件前<b>强制添加</b>间隔(点击切换)"
  1245. }else{
  1246. label.innerHTML = "在第一个组件前<b>保持默认</b>间隔(点击切换)"
  1247. }
  1248. }),
  1249. await CKTools.domHelper("div", div => {
  1250. div.style.paddingLeft = "20px";
  1251. div.style.color = "#919191";
  1252. div.innerHTML = `可选扩展信息栏和原版信息栏之间强制添加一个间隔,或保持默认`;
  1253. })
  1254. ].forEach(e => list.appendChild(e));
  1255. }),
  1256. await CKTools.domHelper("li", async list => {
  1257. list.style.lineHeight = "2em";
  1258. [
  1259. await CKTools.domHelper("input", input => {
  1260. input.type = "checkbox";
  1261. input.id = "showav_newline";
  1262. input.style.display = "none";
  1263. input.name = "showav_newline";
  1264. input.checked = config.showInNewLine;
  1265. input.addEventListener("change",e=>{
  1266. const label = document.querySelector("#showav_showinnewlinetip");
  1267. if(!label) return;
  1268. if(input.checked){
  1269. label.innerHTML = "在<b>新的一行中</b>显示扩展信息栏(点击切换)"
  1270. }else{
  1271. label.innerHTML = "在<b>当前位置后</b>显示扩展信息栏(点击切换)"
  1272. }
  1273. })
  1274. }),
  1275. await CKTools.domHelper("label", label => {
  1276. label.id = "showav_showinnewlinetip";
  1277. label.setAttribute('for', "showav_newline");
  1278. if(config.showInNewLine){
  1279. label.innerHTML = "在<b>新的一行中</b>显示扩展信息栏(点击切换)"
  1280. }else{
  1281. label.innerHTML = "在<b>当前位置后</b>显示扩展信息栏(点击切换)"
  1282. }
  1283. }),
  1284. await CKTools.domHelper("div", div => {
  1285. div.style.paddingLeft = "20px";
  1286. div.style.color = "#919191";
  1287. div.innerHTML = `可选将扩展信息栏显示在下一行,尽量减少对原信息栏的修改`;
  1288. })
  1289. ].forEach(e => list.appendChild(e));
  1290. }),
  1291. await CKTools.domHelper("li", async list => {
  1292. list.style.lineHeight = "2em";
  1293. [
  1294. await CKTools.domHelper("label", label => {
  1295. label.style.paddingLeft = "3px";
  1296. label.id = "showav_nobreakline_tip";
  1297. label.setAttribute('for', "showav_nobreakline");
  1298. if (config.nobreakline)
  1299. label.innerHTML = "默认 <b>禁止</b> 信息栏换行(点击切换)";
  1300. else
  1301. label.innerHTML = "默认 <b>允许</b> 信息栏换行";
  1302. }),
  1303. await CKTools.domHelper("input", input => {
  1304. input.type = "checkbox";
  1305. input.id = "showav_nobreakline";
  1306. input.name = "showav_nobreakline";
  1307. input.style.display = "none";
  1308. input.checked = config.nobreakline;
  1309. input.addEventListener('change', e => {
  1310. const label = document.querySelector("#showav_nobreakline_tip");
  1311. if (!label) return;
  1312. if (input.checked)
  1313. label.innerHTML = "默认 <b>禁止</b> 信息栏换行(点击切换)";
  1314. else
  1315. label.innerHTML = "默认 <b>允许</b> 信息栏换行(点击切换)";
  1316. })
  1317. }),
  1318. await CKTools.domHelper("div", div => {
  1319. div.style.paddingLeft = "20px";
  1320. div.style.color = "#919191";
  1321. div.innerHTML = `是否要求信息栏尽量不换行,可能其中的文本会被截断。`;
  1322. })
  1323. ].forEach(e => list.appendChild(e));
  1324. }),
  1325. await CKTools.domHelper("li", async list => {
  1326. list.style.lineHeight = "2em";
  1327. [
  1328. await CKTools.domHelper("label", label => {
  1329. label.style.paddingLeft = "3px";
  1330. label.id = "showav_foldvidwarn_tip";
  1331. label.setAttribute('for', "showav_foldvidwarn");
  1332. //if (config.foldedWarningTip)
  1333. //label.innerHTML = "默认 <b>隐藏</b> 视频警告文字(点击切换)";
  1334. //else
  1335. label.innerHTML = "默认 <b>隐藏</b> 视频警告文字";
  1336. }),
  1337. /*await CKTools.domHelper("input", input => {
  1338. input.type = "checkbox";
  1339. input.id = "showav_foldvidwarn";
  1340. input.name = "showav_foldvidwarn";
  1341. input.style.display = "none";
  1342. input.checked = config.foldedWarningTip;
  1343. input.addEventListener('change', e => {
  1344. const label = document.querySelector("#showav_foldvidwarn_tip");
  1345. if (!label) return;
  1346. if (input.checked)
  1347. label.innerHTML = "默认 <b>折叠</b> 视频警告文字(点击切换)";
  1348. else
  1349. label.innerHTML = "默认 <b>展示</b> 视频警告文字(点击切换)";
  1350. })
  1351. }),*/
  1352. await CKTools.domHelper("div", div => {
  1353. div.style.paddingLeft = "20px";
  1354. div.style.color = "#919191";
  1355. div.innerHTML = `将视频警告(如 含有危险行为)折叠为图标,防止占用过多信息栏空间。<br>由于新版本播放器适配问题,默认隐藏原版提示。<br>请前往组件管理开启或关闭组件中的警告提示。`;
  1356. })
  1357. ].forEach(e => list.appendChild(e));
  1358. }),
  1359. await CKTools.domHelper("li", async list => {
  1360. list.style.lineHeight = "2em";
  1361. [
  1362. await CKTools.domHelper("label", label => {
  1363. label.style.paddingLeft = "3px";
  1364. label.id = "showav_forceRemoveAllItem_tip";
  1365. label.setAttribute('for', "showav_forceRemoveAllItem");
  1366. if (config.forceRemoveAllItem)
  1367. label.innerHTML = "默认 <b>隐藏</b> 原版所有组件(点击切换)";
  1368. else
  1369. label.innerHTML = "默认 <b>不隐藏</b> 原版所有组件";
  1370. }),
  1371. await CKTools.domHelper("input", input => {
  1372. input.type = "checkbox";
  1373. input.id = "showav_forceRemoveAllItem";
  1374. input.name = "showav_forceRemoveAllItem";
  1375. input.style.display = "none";
  1376. input.checked = config.forceRemoveAllItem;
  1377. input.addEventListener('change', e => {
  1378. const label = document.querySelector("#showav_forceRemoveAllItem_tip");
  1379. if (!label) return;
  1380. if (input.checked)
  1381. label.innerHTML = "默认 <b>隐藏</b> 原版所有组件(点击切换)";
  1382. else
  1383. label.innerHTML = "默认 <b>不隐藏</b> 原版所有组件(点击切换)";
  1384. })
  1385. }),
  1386. // await CKTools.domHelper("div", div => {
  1387. // div.style.paddingLeft = "20px";
  1388. // div.style.color = "#919191";
  1389. // div.innerHTML = `是否尽量隐藏B站原本信息条中的组件。仅对新版本播放器生效。`;
  1390. // })
  1391. ].forEach(e => list.appendChild(e));
  1392. }),
  1393. await CKTools.domHelper("li", sectiontitle=>{
  1394. sectiontitle.innerText = "组件: 显示视频分P信息";
  1395. sectiontitle.className = "showav_settings_sectiontitle";
  1396. }),
  1397. await CKTools.domHelper("li", async list => {
  1398. list.style.lineHeight = "2em";
  1399. [
  1400. await CKTools.domHelper("label", label => {
  1401. label.style.paddingLeft = "3px";
  1402. label.setAttribute('for', "showav_pnwid");
  1403. label.innerHTML = "字数限制";
  1404. }),
  1405. await CKTools.domHelper("input", input => {
  1406. input.type = "number";
  1407. input.id = "showav_pnwid";
  1408. input.name = "showav_pnwid";
  1409. input.setAttribute('min', 5);
  1410. input.setAttribute('max', 100);
  1411. input.style.width = "3em";
  1412. input.style.textAlign = "center";
  1413. input.style.marginLeft = "1em";
  1414. input.style.lineHeight = "1em";
  1415. input.value = config.pnmaxlength;
  1416. const updatePreview = () =>
  1417. wait(2).then(() => CKTools.addStyle(`
  1418. #showav_lengthpreview{
  1419. max-width: ${input.value}em !important;
  1420. }
  1421. `, "showav_lengthpreviewcss", "update"));
  1422. input.addEventListener("input", updatePreview);
  1423. wait(300).then(updatePreview);
  1424. }),
  1425. await CKTools.domHelper("span", span => {
  1426. span.id = "showav_lengthpreview";
  1427. span.innerText = "这里是一条长度预览,你可以在这里查看长度限制的效果。好吧,我承认,后面这几个字只是为了凑个字数而已的。等等,你还要更长???相信我,你不会想要这么长的。";
  1428. span.style.maxWidth = "0em";
  1429. span.style.marginLeft = "30px";
  1430. span.style.textOverflow = "ellipsis";
  1431. span.style.whiteSpace = "nowarp";
  1432. span.style.overflow = "hidden";
  1433. span.style.whiteSpace = "nowrap";
  1434. span.style.display = "block";
  1435. span.style.fontSize = "12px";
  1436. span.style.transition = "all .5s";
  1437. }),
  1438. await CKTools.domHelper("div", div => {
  1439. div.style.paddingLeft = "20px";
  1440. div.style.color = "#919191";
  1441. div.innerHTML = `限制分P信息显示时的最大长度`;
  1442. })
  1443. ].forEach(e => list.appendChild(e));
  1444. }),
  1445. await CKTools.domHelper("li", sectiontitle=>{
  1446. sectiontitle.innerText = "组件: 显示视频编号和高级复制";
  1447. sectiontitle.className = "showav_settings_sectiontitle";
  1448. }),
  1449. await CKTools.domHelper("li", async list => {
  1450. list.style.lineHeight = "2em";
  1451. [
  1452. await CKTools.domHelper("label", label => {
  1453. label.style.paddingLeft = "3px";
  1454. label.id = "showav_defaultav_tip";
  1455. label.setAttribute('for', "showav_defaultav");
  1456. if (config.defaultAv)
  1457. label.innerHTML = "默认展示 <b>视频AV号</b> (点击切换)";
  1458. else
  1459. label.innerHTML = "默认展示 <b>视频BV号</b> (点击切换)";
  1460. }),
  1461. await CKTools.domHelper("input", input => {
  1462. input.type = "checkbox";
  1463. input.id = "showav_defaultav";
  1464. input.name = "showav_defaultav";
  1465. input.style.display = "none";
  1466. input.checked = config.defaultAv;
  1467. input.addEventListener('change', e => {
  1468. const label = document.querySelector("#showav_defaultav_tip");
  1469. if (!label) return;
  1470. if (input.checked)
  1471. label.innerHTML = "默认展示 <b>视频AV号</b> (点击切换)";
  1472. else
  1473. label.innerHTML = "默认展示 <b>视频BV号</b> (点击切换)";
  1474.  
  1475. })
  1476. }),
  1477. await CKTools.domHelper("div", div => {
  1478. div.style.paddingLeft = "20px";
  1479. div.style.color = "#919191";
  1480. div.innerHTML = `仅对<b>可切换视频编号和高级复制</b>功能起效。<br>
  1481. 可切换视频编号和高级复制组件可以使用右键临时切换显示内容。<br>
  1482. 高级复制和快速复制默认读取对应组件显示内容,因此此处设置也会影响可切换视频编号组件的默认复制内容。`;
  1483. })
  1484. ].forEach(e => list.appendChild(e));
  1485. }),
  1486. await CKTools.domHelper("li", sectiontitle=>{
  1487. sectiontitle.innerText = "组件: 显示视频投稿时间";
  1488. sectiontitle.className = "showav_settings_sectiontitle";
  1489. }),
  1490. await CKTools.domHelper("li", async list => {
  1491. list.style.lineHeight = "2em";
  1492. [
  1493. await CKTools.domHelper("label", label => {
  1494. label.style.paddingLeft = "3px";
  1495. label.id = "showav_hidetime_tip";
  1496. label.setAttribute('for', "showav_hidetime");
  1497. if (config.hideTime)
  1498. label.innerHTML = "<b>隐藏</b>原版发布时间 (点击切换)";
  1499. else
  1500. label.innerHTML = "<b>显示</b>原版发布时间 (点击切换)";
  1501. }),
  1502. await CKTools.domHelper("input", input => {
  1503. input.type = "checkbox";
  1504. input.id = "showav_hidetime";
  1505. input.name = "showav_hidetime";
  1506. input.style.display = "none";
  1507. input.checked = config.hideTime;
  1508. input.addEventListener('change', e => {
  1509. const label = document.querySelector("#showav_hidetime_tip");
  1510. if (!label) return;
  1511. if (input.checked)
  1512. label.innerHTML = "<b>隐藏</b>原版发布时间 (点击切换)";
  1513. else
  1514. label.innerHTML = "<b>显示</b>原版发布时间 (点击切换)";
  1515. })
  1516. }),
  1517. await CKTools.domHelper("div", div => {
  1518. div.style.paddingLeft = "20px";
  1519. div.style.color = "#919191";
  1520. div.innerHTML = `仅在开启<b>视频投稿时间</b>功能时起效。<br>
  1521. 插件添加的视频投稿时间可以选择显示两种时间格式,并且可排序。`;
  1522. })
  1523. ].forEach(e => list.appendChild(e));
  1524. }),
  1525. await CKTools.domHelper("li", async list => {
  1526. list.style.lineHeight = "2em";
  1527. [
  1528. await CKTools.domHelper("label", label => {
  1529. label.style.paddingLeft = "3px";
  1530. label.id = "showav_deftxttime_tip";
  1531. label.setAttribute('for', "showav_deftxttime");
  1532. if (config.defaultTextTime)
  1533. label.innerHTML = "显示<b>相对时间</b> (点击切换)";
  1534. else
  1535. label.innerHTML = "显示<b>完整时间戳</b> (点击切换)";
  1536. }),
  1537. await CKTools.domHelper("input", input => {
  1538. input.type = "checkbox";
  1539. input.id = "showav_deftxttime";
  1540. input.name = "showav_deftxttime";
  1541. input.style.display = "none";
  1542. input.checked = config.defaultTextTime;
  1543. input.addEventListener('change', e => {
  1544. const label = document.querySelector("#showav_deftxttime_tip");
  1545. if (!label) return;
  1546. if (input.checked)
  1547. label.innerHTML = "显示<b>相对时间</b> (点击切换)";
  1548. else
  1549. label.innerHTML = "显示<b>完整时间戳</b> (点击切换)";
  1550. })
  1551. }),
  1552. await CKTools.domHelper("div", div => {
  1553. div.style.paddingLeft = "20px";
  1554. div.style.color = "#919191";
  1555. div.innerHTML = `<b>相对时间格式:</b> 如 1周前<br><b>完整时间戳格式:</b> 2021-09-10 11:21:03<br>仅对<b>视频投稿时间</b>功能起效。`;
  1556. })
  1557. ].forEach(e => list.appendChild(e));
  1558. }),
  1559. await CKTools.domHelper("div", async btns => {
  1560. btns.style.display = "flex";
  1561. btns.style.alignItems = "flex-end";
  1562. btns.appendChild(await CKTools.domHelper("button", btn => {
  1563. btn.className = "CKTOOLS-toolbar-btns";
  1564. btn.innerHTML = "保存并返回";
  1565. btn.onclick = e => {
  1566. config.defaultAv = document.querySelector("#showav_defaultav").checked;
  1567. config.forceGap = document.querySelector("#showav_forcegap").checked;
  1568. config.hideTime = document.querySelector("#showav_hidetime").checked;
  1569. config.defaultTextTime = document.querySelector("#showav_deftxttime").checked;
  1570. config.forceRemoveAllItem = document.querySelector("#showav_forceRemoveAllItem").checked;
  1571. config.nobreakline = document.querySelector("#showav_nobreakline").checked;
  1572. config.pnmaxlength = parseInt(document.querySelector("#showav_pnwid").value);
  1573. config.showInNewLine = document.querySelector("#showav_newline").checked;
  1574. saveAllConfig();
  1575. CKTools.addStyle(``, "showav_lengthpreviewcss", "update");
  1576. CKTools.modal.hideModal();
  1577. let old = document.querySelector("#bilibiliShowInfos")
  1578. if (old) old.remove();
  1579. initScript(true);
  1580. wait(300).then(()=>GUISettings());
  1581. }
  1582. }))
  1583. btns.appendChild(await CKTools.domHelper("button", btn => {
  1584. btn.className = "CKTOOLS-toolbar-btns";
  1585. btn.innerHTML = "返回";
  1586. btn.style.background = "#ececec";
  1587. btn.style.color = "black";
  1588. btn.onclick = e => {
  1589. CKTools.addStyle(``, "showav_lengthpreviewcss", "update");
  1590. CKTools.modal.hideModal();
  1591. wait(300).then(()=>GUISettings());
  1592. }
  1593. }))
  1594. })
  1595. ].forEach(e => container.appendChild(e));
  1596. }));
  1597. }
  1598.  
  1599. async function GUISettings_components() {
  1600. if (CKTools.modal.isModalShowing()) {
  1601. CKTools.modal.hideModal();
  1602. await wait(300);
  1603. }
  1604. CKTools.modal.openModal("ShowAV / 设置 / 组件", await CKTools.domHelper("div", async container => {
  1605. container.style.alignItems = "stretch";
  1606. [
  1607. closeButton(),
  1608. // dragable code from ytb v=jfYWwQrtzzY
  1609. await CKTools.domHelper("li", async list => {
  1610. const makeDragable = async id => {
  1611. return await CKTools.domHelper("div", draggable => {
  1612. draggable.className = "showav_dragableitem";
  1613. draggable.setAttribute("draggable", true);
  1614. draggable.setAttribute("data-id", id);
  1615. if (id.split("_")[0] === "custom") {
  1616. draggable.innerHTML = config.customComponents[id].title;
  1617. const node = document.createElement("div");
  1618. node.appendChild(document.createTextNode(config.customComponents[id].content));
  1619. draggable.appendChild(node);
  1620. }else if (id.split("_")[0] == "sideload") {
  1621. let ids = id.split("_");
  1622. ids.shift();
  1623. const modname = ids.join('_');
  1624. draggable.innerHTML = getSideloadModules()[modname].name;
  1625. const node = document.createElement("div");
  1626. node.appendChild(document.createTextNode(getSideloadModules()[modname].description??'外挂组件'));
  1627. draggable.appendChild(node);
  1628. } else {
  1629. draggable.innerHTML = txtCn[id];
  1630. draggable.innerHTML += `<div>${descCn[id]}</div>`;
  1631. }
  1632. let expanded = false;
  1633. draggable.addEventListener('dragstart', e => {
  1634. if (expanded) draggable.classList.remove('showav_expand');
  1635. draggable.classList.add('showav_dragging');
  1636. [...document.querySelectorAll('.showav_dragablediv')].forEach(e => e.classList.add('showav_child_dragging'))
  1637. })
  1638. draggable.addEventListener('dragend', e => {
  1639. if (expanded) draggable.classList.add('showav_expand');
  1640. draggable.classList.remove('showav_dragging');
  1641. [...document.querySelectorAll('.showav_child_dragging')].forEach(e => e.classList.remove('showav_child_dragging'))
  1642. })
  1643. draggable.addEventListener('click', e => {
  1644. expanded = draggable.classList.toggle('showav_expand');
  1645. })
  1646. })
  1647. };
  1648. function getClosestItem(container, y) {
  1649. const draggables = [...container.querySelectorAll(".showav_dragableitem:not(.showav_dragging)")];
  1650. return draggables.reduce((closest, child) => {
  1651. const box = child.getBoundingClientRect();
  1652. const offset = y - box.top - box.height / 2;
  1653. if (offset < 0 && offset > closest.offset) return { offset, element: child };
  1654. else return closest;
  1655. }, { offset: Number.NEGATIVE_INFINITY }).element;
  1656. }
  1657. function registerDragEvent(draggablediv) {
  1658. draggablediv.addEventListener('dragover', e => {
  1659. e.preventDefault();
  1660. const closestElement = getClosestItem(draggablediv, e.clientY);
  1661. const dragging = document.querySelector(".showav_dragging");
  1662. if (closestElement === null) {
  1663. draggablediv.appendChild(dragging);
  1664. } else {
  1665. draggablediv.insertBefore(dragging, closestElement);
  1666. }
  1667. })
  1668. }
  1669. [
  1670. await CKTools.domHelper("div", div => {
  1671. div.innerHTML = `<b>拖动下面的功能模块进行排序</b>`;
  1672. }),
  1673. await CKTools.domHelper("div", async enableddiv => {
  1674. enableddiv.innerHTML = `<b>启用</b>`;
  1675. enableddiv.className = "showav_dragablediv showav_enableddiv";
  1676. config.orders.forEach(async k => {
  1677. enableddiv.appendChild(await makeDragable(k));
  1678. });
  1679. registerDragEvent(enableddiv);
  1680. }),
  1681. await CKTools.domHelper("div", async disableddiv => {
  1682. disableddiv.innerHTML = `<b>禁用</b>`;
  1683. disableddiv.className = "showav_dragablediv showav_disableddiv";
  1684. const sideloads = getSideloadModules();
  1685. const sideloaditems = Object.keys(sideloads).map(k => 'sideload_'+k);
  1686. [...config.all,...sideloaditems].forEach(async k => {
  1687. if (config.orders.includes(k)) return;
  1688. disableddiv.appendChild(await makeDragable(k));
  1689. });
  1690. registerDragEvent(disableddiv);
  1691. }),
  1692. await CKTools.domHelper("div", async div => {
  1693. div.style.lineHeight = "2em";
  1694. div.style.cursor = "pointer";
  1695. div.style.color = "#1976d2";
  1696. div.style.fontWeight = "bold";
  1697. div.innerHTML = `功能设置`;
  1698. div.onclick = e => GUISettings_options();
  1699. }),
  1700. await CKTools.domHelper("div", async div => {
  1701. div.style.lineHeight = "2em";
  1702. div.style.cursor = "pointer";
  1703. div.style.color = "#1976d2";
  1704. div.style.fontWeight = "bold";
  1705. div.innerHTML = `管理自定义组件`;
  1706. div.onclick = e => GUISettings_customcomponents();
  1707. }),
  1708. await CKTools.domHelper("div", async div => {
  1709. div.style.lineHeight = "2em";
  1710. div.innerHTML = `<a href="https://github.com/CKylinMC/UserJS/issues/new?assignees=CKylinMC&labels=&template=feature-request.yaml&title=%5BIDEA%5D+ShowAV%E8%84%9A%E6%9C%AC%E6%98%BE%E7%A4%BA%E5%8A%9F%E8%83%BD%E8%AF%B7%E6%B1%82&target=[%E8%84%9A%E6%9C%AC%EF%BC%9A%E8%A7%86%E9%A2%91%E9%A1%B5%E9%9D%A2%E5%B8%B8%E9%A9%BB%E6%98%BE%E7%A4%BAAV/BV%E5%8F%B7]&desp=%E6%88%91%E5%B8%8C%E6%9C%9B%E6%B7%BB%E5%8A%A0%E6%96%B0%E7%9A%84%E5%BF%AB%E6%8D%B7%E5%B1%95%E7%A4%BA%E5%8A%9F%E8%83%BD%EF%BC%8C%E5%8A%9F%E8%83%BD%E7%9A%84%E4%BD%9C%E7%94%A8%E5%92%8C%E6%95%88%E6%9E%9C%E5%A6%82%E4%B8%8B...">需要添加其他的显示或快捷功能?反馈来添加...</a>`
  1711. }),
  1712. await CKTools.domHelper("div", async div => {
  1713. div.appendChild(await CKTools.domHelper("div", async btns => {
  1714. btns.style.display = "flex";
  1715. btns.appendChild(await CKTools.domHelper("button", btn => {
  1716. btn.className = "CKTOOLS-toolbar-btns";
  1717. btn.innerHTML = "保存并返回";
  1718. btn.onclick = e => {
  1719. const enableddiv = document.querySelector(".showav_enableddiv");
  1720. const elements = enableddiv.querySelectorAll(".showav_dragableitem");
  1721. let enabledArray = [];
  1722. for (let element of [...elements]) {
  1723. enabledArray.push(element.getAttribute('data-id'));
  1724. }
  1725. config.orders = enabledArray;
  1726. saveAllConfig();
  1727. CKTools.modal.hideModal();
  1728. let old = document.querySelector("#bilibiliShowInfos")
  1729. if (old) old.remove();
  1730. initScript(true);
  1731. wait(310).then(()=>GUISettings());
  1732. }
  1733. }))
  1734. btns.appendChild(await CKTools.domHelper("button", btn => {
  1735. btn.className = "CKTOOLS-toolbar-btns";
  1736. btn.innerHTML = "返回";
  1737. btn.style.background = "#ececec";
  1738. btn.style.color = "black";
  1739. btn.onclick = e => {
  1740. CKTools.modal.hideModal();
  1741. wait(310).then(()=>GUISettings());
  1742. }
  1743. }))
  1744. }))
  1745. }),
  1746. ].forEach(e => list.appendChild(e));
  1747. })
  1748. ].forEach(e => container.appendChild(e));
  1749. }));
  1750. }
  1751.  
  1752. async function GUISettings_advcopy(back=null) {
  1753. if (CKTools.modal.isModalShowing()) {
  1754. CKTools.modal.hideModal();
  1755. await wait(300);
  1756. }
  1757. CKTools.modal.openModal("ShowAV / 设置 / 快速复制设置", await CKTools.domHelper("div", async container => {
  1758. container.style.alignItems = "stretch";
  1759. [
  1760. closeButton(),
  1761. // dragable code from ytb v=jfYWwQrtzzY
  1762. await CKTools.domHelper("li", async list => {
  1763. const makeDragable = async id => {
  1764. return await CKTools.domHelper("div", draggable => {
  1765. draggable.className = "showav_dragableitem copyitem";
  1766. draggable.setAttribute("draggable", true);
  1767. draggable.setAttribute("data-id", id);
  1768. if (id.split("_")[0] === "custom") {
  1769. draggable.innerHTML = config.customcopyitems[id].title;
  1770. const node = document.createElement("div");
  1771. node.appendChild(document.createTextNode(config.customcopyitems[id].content));
  1772. draggable.appendChild(node);
  1773. } else {
  1774. draggable.innerHTML = txtCn[id];
  1775. draggable.innerHTML += `<div>${descCn[id]}</div>`;
  1776. }
  1777. draggable.removeItem = draggable.remove;
  1778. let expanded = false;
  1779. draggable.addEventListener('dragstart', e => {
  1780. if (expanded) draggable.classList.remove('showav_expand');
  1781. draggable.classList.add('showav_dragging');
  1782. [...document.querySelectorAll('.showav_dragablediv')].forEach(e => e.classList.add('showav_child_dragging'))
  1783. })
  1784. draggable.addEventListener('dragend', e => {
  1785. if (expanded) draggable.classList.add('showav_expand');
  1786. draggable.classList.remove('showav_dragging');
  1787. [...document.querySelectorAll('.showav_child_dragging')].forEach(e => e.classList.remove('showav_child_dragging'))
  1788. })
  1789. draggable.addEventListener('click', e => {
  1790. expanded = draggable.classList.toggle('showav_expand');
  1791. })
  1792. })
  1793. };
  1794. function getClosestItem(container, y) {
  1795. const draggables = [...container.querySelectorAll(".showav_dragableitem:not(.showav_dragging)")];
  1796. return draggables.reduce((closest, child) => {
  1797. const box = child.getBoundingClientRect();
  1798. const offset = y - box.top - box.height / 2;
  1799. if (offset < 0 && offset > closest.offset) return { offset, element: child };
  1800. else return closest;
  1801. }, { offset: Number.NEGATIVE_INFINITY }).element;
  1802. }
  1803. function registerDragEvent(draggablediv) {
  1804. draggablediv.addEventListener('dragover', e => {
  1805. e.preventDefault();
  1806. const closestElement = getClosestItem(draggablediv, e.clientY);
  1807. const dragging = document.querySelector(".showav_dragging");
  1808. if (closestElement === null) {
  1809. draggablediv.appendChild(dragging);
  1810. } else {
  1811. draggablediv.insertBefore(dragging, closestElement);
  1812. }
  1813. })
  1814. }
  1815. [
  1816. await CKTools.domHelper("div", div => {
  1817. div.innerHTML = `<b>拖动下面的功能模块进行排序</b>,第一个单项将成为默认快速复制项目。`;
  1818. }),
  1819. await CKTools.domHelper("div", async enableddiv => {
  1820. enableddiv.innerHTML = `<b>启用</b>`;
  1821. enableddiv.className = "showav_dragablediv showav_enableddiv";
  1822. config.copyitems.forEach(async k => {
  1823. enableddiv.appendChild(await makeDragable(k));
  1824. });
  1825. registerDragEvent(enableddiv);
  1826. }),
  1827. await CKTools.domHelper("div", async disableddiv => {
  1828. disableddiv.innerHTML = `<b>禁用</b>`;
  1829. disableddiv.className = "showav_dragablediv showav_disableddiv";
  1830. config.copyitemsAll.forEach(async k => {
  1831. if (config.copyitems.includes(k)) return;
  1832. disableddiv.appendChild(await makeDragable(k));
  1833. });
  1834. registerDragEvent(disableddiv);
  1835. }),
  1836. await CKTools.domHelper("li", async list => {
  1837. const makeItem = (copyitemid,focus=false) => {
  1838. const item = config.customcopyitems[copyitemid];
  1839. const node = document.createElement("li");
  1840. node.className = "copyitem";
  1841. if(focus){
  1842. node.classList.add("actionpending");
  1843. setTimeout(() => {
  1844. node.classList.remove("actionpending");
  1845. node.scrollIntoView();
  1846. },20);
  1847. }
  1848. node.setAttribute("data-id", copyitemid);
  1849. node.innerHTML = `${item.title}<br>`;
  1850. node.style.borderRadius = "3px";
  1851. node.style.border = "solid 2px grey";
  1852. node.style.padding = "3px";
  1853. node.style.margin = "1px";
  1854. const smallp = document.createElement("p");
  1855. smallp.style.fontSize = "small";
  1856. smallp.style.color = "grey";
  1857. smallp.style.overflow = "hidden";
  1858. smallp.style.wordWrap = "nowarp";
  1859. smallp.appendChild(document.createTextNode(item.content));
  1860. node.appendChild(smallp);
  1861. node.removeItem = ()=>{
  1862. node.classList.add("actionpending");
  1863. setTimeout(()=>node.remove(),350);
  1864. };
  1865. node.onclick = async e => {
  1866. if(node.classList.contains("preremove")){
  1867. if (config.copyitems.includes(copyitemid)) {
  1868. config.copyitems.splice(config.copyitems.indexOf(copyitemid), 1);
  1869. }
  1870. if (config.copyitemsAll.includes(copyitemid)) {
  1871. config.copyitemsAll.splice(config.copyitemsAll.indexOf(copyitemid), 1);
  1872. }
  1873. delete config.customcopyitems[copyitemid];
  1874. saveAllConfig();
  1875. [...document.querySelectorAll(`.copyitem[data-id="${copyitemid}"]`)].forEach(e => e.removeItem());
  1876. }else{
  1877. [...document.querySelectorAll("li.copyitem.preremove")].forEach(e=>{
  1878. e.classList.remove("preremove");
  1879. try{if(e.clearTimer){
  1880. clearTimeout(e.clearTimer);
  1881. }}catch(e){};
  1882. });
  1883. node.classList.add("preremove");
  1884. node.clearTimer = setTimeout(() => {
  1885. node.classList.remove("preremove");
  1886. node.clearTimer = null;
  1887. },5000);
  1888. }
  1889. }
  1890. return node;
  1891. };
  1892. [
  1893. await CKTools.domHelper("label", label => {
  1894. label.style.paddingLeft = "3px";
  1895. label.style.fontWeight = "bold";
  1896. label.innerHTML = "添加自定义复制项目";
  1897. }),
  1898. await CKTools.domHelper("div", async div => {
  1899. div.style.paddingLeft = "20px";
  1900. [
  1901. await CKTools.domHelper("input", async input => {
  1902. input.id = "showav_customcopytitle";
  1903. input.setAttribute("type", "text");
  1904. input.style.width = "60%";
  1905. input.style.margin = "6px 0 0 0";
  1906. input.style.padding = "6px";
  1907. input.style.borderRadius = "6px";
  1908. input.style.border = "solid 2px grey";
  1909. input.setAttribute("placeholder", "自定义标题");
  1910. }),
  1911. await CKTools.domHelper("input", async input => {
  1912. input.id = "showav_customcopycontent";
  1913. input.setAttribute("type", "text");
  1914. input.style.width = "60%";
  1915. input.style.margin = "6px 0 0 0";
  1916. input.style.padding = "6px";
  1917. input.style.borderRadius = "6px";
  1918. input.style.border = "solid 2px grey";
  1919. input.setAttribute("placeholder", "自定义内容");
  1920. }),
  1921. await CKTools.domHelper("div", div => {
  1922. div.style.paddingLeft = "20px";
  1923. div.style.color = "#919191";
  1924. div.innerHTML = `变量提示<br><ul>
  1925. <li>%timeurl% => 包含时间的完整地址</li>
  1926. <li>%vidurl% => 视频纯净地址</li>
  1927. <li>%shorturl% => 短地址</li>
  1928. <li>%seek% => 当前视频播放秒数</li>
  1929. <li>%title% => 视频标题</li>
  1930. <li>%av% => av号</li>
  1931. <li>%bv% => BV号</li>
  1932. <li>%cid% => CID号</li>
  1933. <li>%p% => P</li>
  1934. <li>%pname% => P名</li>
  1935. <li>%tname% => 分区名</li>
  1936. </ul>`;
  1937. div.style.maxHeight = '2rem';
  1938. div.style.overflow = 'hidden';
  1939. div.style.transition = 'all .3s';
  1940. let expanded = false;
  1941. div.onclick = e => {
  1942. expanded = !expanded;
  1943. if (expanded) {
  1944. div.style.maxHeight = "30rem";
  1945. } else {
  1946. div.style.maxHeight = '2rem';
  1947. }
  1948. }
  1949. }),
  1950. await CKTools.domHelper("button", btn => {
  1951. btn.className = "CKTOOLS-toolbar-btns";
  1952. btn.innerHTML = "添加";
  1953. btn.style.background = "#ececec";
  1954. btn.style.color = "black";
  1955. btn.onclick = async e => {
  1956. const ccid = "custom_" + Math.random().toString(36).replace('.', '');
  1957. const title = document.querySelector("#showav_customcopytitle").value;
  1958. const content = document.querySelector("#showav_customcopycontent").value;
  1959. if (title.trim().length < 1 || content.trim().length < 1) {
  1960. popNotify.warn("无法添加自定义项目", "标题或内容为空");
  1961. return;
  1962. }
  1963. config.customcopyitems[ccid] = { title, content };
  1964. if (!config.copyitemsAll.includes(ccid)) config.copyitemsAll.push(ccid);
  1965. saveAllConfig();
  1966. const disablediv = document.querySelector(".showav_disableddiv");
  1967. disablediv && disablediv.appendChild(await makeDragable(ccid));
  1968. const customlist = document.querySelector("#showav_customitems");
  1969. customlist && customlist.appendChild(makeItem(ccid,true));
  1970. document.querySelector("#showav_customcopytitle").value = "";
  1971. document.querySelector("#showav_customcopycontent").value = "";
  1972. }
  1973. })
  1974. ].forEach(e => div.appendChild(e));
  1975. }),
  1976. await CKTools.domHelper("label", label => {
  1977. label.style.paddingLeft = "3px";
  1978. label.style.fontWeight = "bold";
  1979. label.innerHTML = "已有自定义复制项目 <small>(点击移除)</small>";
  1980. }),
  1981. await CKTools.domHelper("ul", ul => {
  1982. ul.style.paddingLeft = "3px";
  1983. ul.id = "showav_customitems";
  1984. for (let copyitemid of Object.keys(config.customcopyitems)) {
  1985. ul.appendChild(makeItem(copyitemid));
  1986. }
  1987. }),
  1988. ].forEach(e => list.appendChild(e));
  1989. }),
  1990. await CKTools.domHelper("div", async div => {
  1991. div.appendChild(await CKTools.domHelper("div", async btns => {
  1992. btns.style.display = "flex";
  1993. btns.appendChild(await CKTools.domHelper("button", btn => {
  1994. btn.className = "CKTOOLS-toolbar-btns";
  1995. btn.innerHTML = "保存并关闭";
  1996. if(back!=null)
  1997. btn.innerHTML = "保存并返回";
  1998. btn.onclick = e => {
  1999. const enableddiv = document.querySelector(".showav_enableddiv");
  2000. const elements = enableddiv.querySelectorAll(".showav_dragableitem");
  2001. let enabledArray = [];
  2002. for (let element of [...elements]) {
  2003. enabledArray.push(element.getAttribute('data-id'));
  2004. }
  2005. config.copyitems = enabledArray;
  2006. saveAllConfig();
  2007. initScript(true);
  2008. if(back!=null) back();
  2009. else CKTools.modal.hideModal();
  2010. }
  2011. }))
  2012. btns.appendChild(await CKTools.domHelper("button", btn => {
  2013. btn.className = "CKTOOLS-toolbar-btns";
  2014. btn.innerHTML = "关闭";
  2015. if(back!=null)
  2016. btn.innerHTML = "返回";
  2017. btn.onclick = e => {
  2018. if(back!=null) back();
  2019. else CKTools.modal.hideModal();
  2020. }
  2021. }))
  2022. }))
  2023. }),
  2024. ].forEach(e => list.appendChild(e));
  2025. })
  2026. ].forEach(e => container.appendChild(e));
  2027. }));
  2028. }
  2029.  
  2030. async function GUISettings_customcomponents(back=GUISettings_components) {
  2031. if (CKTools.modal.isModalShowing()) {
  2032. CKTools.modal.hideModal();
  2033. await wait(300);
  2034. }
  2035. CKTools.modal.openModal("ShowAV / 设置 / 组件 / 自定义组件", await CKTools.domHelper("div", async container => {
  2036. container.style.alignItems = "stretch";
  2037. [
  2038. closeButton(),
  2039. // dragable code from ytb v=jfYWwQrtzzY
  2040. await CKTools.domHelper("li", async list => {
  2041. [
  2042. await CKTools.domHelper("li", async list => {
  2043. const makeItem = (customitemid,focus=false) => {
  2044. const item = config.customComponents[customitemid];
  2045. const node = document.createElement("li");
  2046. node.className = "copyitem";
  2047. if(focus){
  2048. node.classList.add("actionpending");
  2049. setTimeout(() => {
  2050. node.classList.remove("actionpending");
  2051. node.scrollIntoView();
  2052. },20);
  2053. }
  2054. node.setAttribute("data-id", customitemid);
  2055. node.innerHTML = `${item.title}<br>`;
  2056. node.style.borderRadius = "3px";
  2057. node.style.border = "solid 2px grey";
  2058. node.style.padding = "3px";
  2059. node.style.margin = "1px";
  2060. const smallp = document.createElement("p");
  2061. smallp.style.fontSize = "small";
  2062. smallp.style.color = "grey";
  2063. smallp.style.overflow = "hidden";
  2064. smallp.style.wordWrap = "nowarp";
  2065. smallp.appendChild(document.createTextNode(item.content));
  2066. node.appendChild(smallp);
  2067. node.removeItem = ()=>{
  2068. node.classList.add("actionpending");
  2069. setTimeout(()=>node.remove(),350);
  2070. };
  2071. node.onclick = async e => {
  2072. if(node.classList.contains("preremove")){
  2073. if (config.orders.includes(customitemid)) {
  2074. config.orders.splice(config.orders.indexOf(customitemid), 1);
  2075. }
  2076. if (config.all.includes(customitemid)) {
  2077. config.all.splice(config.all.indexOf(customitemid), 1);
  2078. }
  2079. delete config.customComponents[customitemid];
  2080. saveAllConfig();
  2081. [...document.querySelectorAll(`.copyitem[data-id="${customitemid}"]`)].forEach(e => e.removeItem());
  2082. }else{
  2083. [...document.querySelectorAll("li.copyitem.preremove")].forEach(e=>{
  2084. e.classList.remove("preremove");
  2085. try{if(e.clearTimer){
  2086. clearTimeout(e.clearTimer);
  2087. }}catch(e){};
  2088. });
  2089. node.classList.add("preremove");
  2090. node.clearTimer = setTimeout(() => {
  2091. node.classList.remove("preremove");
  2092. node.clearTimer = null;
  2093. },5000);
  2094. }
  2095. }
  2096. return node;
  2097. };
  2098. [
  2099. await CKTools.domHelper("label", label => {
  2100. label.style.paddingLeft = "3px";
  2101. label.style.fontWeight = "bold";
  2102. label.innerHTML = "添加组件";
  2103. }),
  2104. await CKTools.domHelper("div", async div => {
  2105. div.style.paddingLeft = "20px";
  2106. [
  2107. await CKTools.domHelper("input", async input => {
  2108. input.id = "showav_customcopntitle";
  2109. input.setAttribute("type", "text");
  2110. input.style.width = "60%";
  2111. input.style.margin = "6px 0 0 0";
  2112. input.style.padding = "6px";
  2113. input.style.borderRadius = "6px";
  2114. input.style.border = "solid 2px grey";
  2115. input.setAttribute("placeholder", "自定义显示文本");
  2116. input.addEventListener("keydown",e=>{
  2117. const contentel = document.querySelector("#showav_customcopncontent");
  2118. if(!contentel) return;
  2119. if(contentel.getAttribute("data-sync")!=="1") return;
  2120. setTimeout(()=>contentel.value = input.value,10);
  2121. })
  2122. }),
  2123. await CKTools.domHelper("input", async input => {
  2124. input.id = "showav_customcopncontent";
  2125. input.setAttribute("type", "text");
  2126. input.style.width = "60%";
  2127. input.style.margin = "6px 0 0 0";
  2128. input.style.padding = "6px";
  2129. input.style.borderRadius = "6px";
  2130. input.style.border = "solid 2px grey";
  2131. input.title = `默认与自定义显示文本同步\n使用"js:"开头时将在点击时执行脚本`;
  2132. input.setAttribute("data-sync","1");
  2133. input.setAttribute("placeholder", "自定义复制内容或脚本");
  2134. input.addEventListener("keydown",e=>input.setAttribute("data-sync","0"));
  2135. input.addEventListener("keydown",async e=>{
  2136. await wait(1);
  2137. if(input.value.startsWith("js:")){
  2138. if(config.jssafetyWarning){
  2139. config.jssafetyWarning = !confirm(`安全性警告:\n\n"js:"开头的内容将作为JS脚本执行。\n\nJS脚本拥有您在当前页面的所有权限,请勿复制和执行未知来源的脚本!\n请仅在了解你输入的内容情况下使用此功能!\n\n如果不点击确定,则每次输入"js:"时都会弹出此消息。\n\n继续输入吗?`);
  2140. if(config.jssafetyWarning){
  2141. saveAllConfig();
  2142. }else{
  2143. input.value = input.value.replace("js:","");
  2144. }
  2145. }else{
  2146. document.querySelector("#showav_custom_txttip").style.display = "none";
  2147. document.querySelector("#showav_custom_jstip").style.display = "block";
  2148. }
  2149. }else{
  2150. document.querySelector("#showav_custom_jstip").style.display = "none";
  2151. document.querySelector("#showav_custom_txttip").style.display = "block";
  2152. }
  2153. })
  2154. }),
  2155. await CKTools.domHelper("div", div => {
  2156. div.style.paddingLeft = "20px";
  2157. div.id = "showav_custom_txttip";
  2158. div.style.color = "#919191";
  2159. div.innerHTML = `变量提示<br><ul>
  2160. <li>%timeurl% => 包含时间的完整地址</li>
  2161. <li>%vidurl% => 视频纯净地址</li>
  2162. <li>%shorturl% => 短地址</li>
  2163. <li>%seek% => 当前视频播放秒数</li>
  2164. <li>%title% => 视频标题</li>
  2165. <li>%av% => av号</li>
  2166. <li>%bv% => BV号</li>
  2167. <li>%cid% => CID号</li>
  2168. <li>%p% => P</li>
  2169. <li>%pname% => P名</li>
  2170. <li>%tname% => 分区名</li>
  2171. </ul>`;
  2172. div.style.maxHeight = '2rem';
  2173. div.style.overflow = 'hidden';
  2174. div.style.transition = 'all .3s';
  2175. let expanded = false;
  2176. div.onclick = e => {
  2177. expanded = !expanded;
  2178. if (expanded) {
  2179. div.style.maxHeight = "30rem";
  2180. } else {
  2181. div.style.maxHeight = '2rem';
  2182. }
  2183. }
  2184. }),
  2185. await CKTools.domHelper("div", div => {
  2186. div.style.paddingLeft = "20px";
  2187. div.id = "showav_custom_jstip";
  2188. div.style.display = "none";
  2189. div.style.color = "#919191";
  2190. div.innerHTML = `脚本提示<br><ul>
  2191. <li>变量 infos => 视频信息</li>
  2192. <li>方法 parseTxt("string") => 解析文本</li>
  2193. <li>方法 copy("string") => 复制文字</li>
  2194. </ul>`;
  2195. div.style.maxHeight = '2rem';
  2196. div.style.overflow = 'hidden';
  2197. div.style.transition = 'all .3s';
  2198. let expanded = false;
  2199. div.onclick = e => {
  2200. expanded = !expanded;
  2201. if (expanded) {
  2202. div.style.maxHeight = "30rem";
  2203. } else {
  2204. div.style.maxHeight = '2rem';
  2205. }
  2206. }
  2207. }),
  2208. await CKTools.domHelper("button", btn => {
  2209. btn.className = "CKTOOLS-toolbar-btns";
  2210. btn.innerHTML = "添加";
  2211. btn.style.background = "#ececec";
  2212. btn.style.color = "black";
  2213. btn.onclick = async e => {
  2214. const ccid = "custom_" + Math.random().toString(36).replace('.', '');
  2215. const title = document.querySelector("#showav_customcopntitle").value;
  2216. const content = document.querySelector("#showav_customcopncontent").value;
  2217. if (title.trim().length < 1 || content.trim().length < 1) {
  2218. popNotify.warn("无法添加自定义组件", "标题或内容为空");
  2219. return;
  2220. }
  2221. config.customComponents[ccid] = { title, content };
  2222. if (!config.all.includes(ccid)) config.all.push(ccid);
  2223. saveAllConfig();
  2224. const customlist = document.querySelector("#showav_customitems");
  2225. customlist && customlist.appendChild(makeItem(ccid,true));
  2226. document.querySelector("#showav_customcopntitle").value = "";
  2227. document.querySelector("#showav_customcopncontent").value = "";
  2228. }
  2229. })
  2230. ].forEach(e => div.appendChild(e));
  2231. }),
  2232. await CKTools.domHelper("label", label => {
  2233. label.style.paddingLeft = "3px";
  2234. label.style.fontWeight = "bold";
  2235. label.innerHTML = "已有自定义组件 <small>(点击移除)</small>";
  2236. }),
  2237. await CKTools.domHelper("ul", ul => {
  2238. ul.style.paddingLeft = "3px";
  2239. ul.id = "showav_customitems";
  2240. for (let itemid of Object.keys(config.customComponents)) {
  2241. ul.appendChild(makeItem(itemid));
  2242. }
  2243. }),
  2244. ].forEach(e => list.appendChild(e));
  2245. }),
  2246. await CKTools.domHelper("label", label => {
  2247. label.style.width = "100%";
  2248. label.style.display = "block";
  2249. label.style.textAlign = "center";
  2250. label.innerHTML = "此页面内容自动保存";
  2251. }),
  2252. await CKTools.domHelper("div", async div => {
  2253. div.appendChild(await CKTools.domHelper("div", async btns => {
  2254. btns.style.display = "flex";
  2255. btns.appendChild(await CKTools.domHelper("button", btn => {
  2256. btn.className = "CKTOOLS-toolbar-btns";
  2257. btn.innerHTML = "返回";
  2258. btn.onclick = e => {
  2259. saveAllConfig();
  2260. back();
  2261. }
  2262. }))
  2263. btns.appendChild(await CKTools.domHelper("button", btn => {
  2264. btn.className = "CKTOOLS-toolbar-btns";
  2265. btn.innerHTML = "关闭";
  2266. btn.onclick = e => {
  2267. saveAllConfig();
  2268. CKTools.modal.hideModal();
  2269. }
  2270. }))
  2271. }))
  2272. }),
  2273. ].forEach(e => list.appendChild(e));
  2274. })
  2275. ].forEach(e => container.appendChild(e));
  2276. }));
  2277. }
  2278.  
  2279. const copy = function copy(text) {
  2280. if (!navigator.clipboard) {
  2281. prompt('请手动复制', text);
  2282. return;
  2283. }
  2284. navigator.clipboard.writeText(text).then(function () {
  2285. log('Copy OK');
  2286. }, function (err) {
  2287. log('Auto Copy Failed:', err);
  2288. prompt('请手动复制', text);
  2289. });
  2290. }
  2291.  
  2292. unsafeWindow.showav_fastcopy = (el) => {
  2293. copy(el.value);
  2294. popNotify.success("复制成功", el.value);
  2295. }
  2296.  
  2297. unsafeWindow.showav_guisettings = GUISettings;
  2298. unsafeWindow.showav_guisettings_advcopy = GUISettings_advcopy;
  2299. unsafeWindow.showav_guisettings_customcomponents = GUISettings_customcomponents;
  2300.  
  2301. CKTools.modal.initModal();
  2302. CKTools.modal.hideModal();
  2303. const blockwin = CKTools.get("#CKTOOLS-blockWindow");
  2304. blockwin&&(blockwin.onclick = CKTools.modal.hideModal);
  2305. CKTools.addStyle(`
  2306. #CKTOOLS-modal{
  2307. width: fit-content!important;
  2308. max-width: 80%!important;
  2309. }
  2310. .CKTOOLS-modal-content li label b {
  2311. color: #1976d2!important;
  2312. }
  2313. .showav_menuitem{
  2314. line-height: 2em;
  2315. width: 100%;
  2316. transition: all .3s;
  2317. cursor: pointer;
  2318. }
  2319. .showav_menuitem:hover{
  2320. transform: translateX(6px);
  2321. }
  2322. .showav_menuitem>label{
  2323. color: #1976d2;
  2324. font-weight: bold;
  2325. font-size: large;
  2326. display: block;
  2327. }
  2328. .showav_dragablediv {
  2329. width: 400px;
  2330. max-width: 80%;
  2331. max-width: 400px;
  2332. min-height: 60px;
  2333. border: dotted;
  2334. border-radius: 8px;
  2335. padding: 12px;
  2336. margin: 5px;
  2337. position: relative;
  2338. margin: 3px auto;
  2339. }
  2340. .showav_dragableitem {
  2341. background: white;
  2342. margin: 3px;
  2343. padding: 3px;
  2344. border-radius: 4px;
  2345. border: solid #bdbdbd 2px;
  2346. color: black;
  2347. transition: all .3s;
  2348. max-height: 2rem;
  2349. }
  2350. .showav_dragableitem.showav_expand {
  2351. max-height: 8rem;
  2352. }
  2353. .showav_dragableitem>div {
  2354. color: #adadad;
  2355. margin: 0 6px;
  2356. opacity: 0;
  2357. transition: all .3s ease-in-out;
  2358. transform: translateX(-10px);
  2359. font-size: small;
  2360. overflow: hidden;
  2361. max-height: 0;
  2362. }
  2363. .showav_dragableitem.showav_expand>div{
  2364. transform: translateX(0px);
  2365. max-height: 8rem;
  2366. opacity: 1;
  2367. }
  2368. .showav_dragableitem::before {
  2369. content: "⋮⋮";
  2370. float: right;
  2371. font-size: xx-small;
  2372. padding: 3px;
  2373. color: #bbbbbb !important;
  2374. }
  2375. .showav_dragging {
  2376. background: grey;
  2377. color: white;
  2378. border: solid #515050 2px;
  2379. transform: scale(1.1);
  2380. transition: all .3s;
  2381. }
  2382. .showav_dragablediv:not(.showav_child_dragging) .showav_dragableitem:hover:not(.showav_dragging) {
  2383. background: grey;
  2384. color: white;
  2385. border: solid #515050 2px;
  2386. transform: scale(1.03);
  2387. transition: all .3s;
  2388. }
  2389. .showav_dragablediv>b {
  2390. position: absolute;
  2391. left: -4rem;
  2392. }
  2393. .showav_disableddiv .showav_dragableitem {
  2394. color: #a9a8a8;
  2395. }
  2396. .showav_enableddiv{
  2397. background: #dcedc8;
  2398. }
  2399. .showav_disableddiv{
  2400. background: #ffcdd2;
  2401. }
  2402. .showav_settings_sectiontitle{
  2403. display: block;
  2404. width: 100%;
  2405. font-weight: bold;
  2406. color: #1976d2;
  2407. border-bottom: 2px solid #1976d2;
  2408. margin: 18px 0 3px 0;
  2409. }
  2410. .showav_settings_sectiontitle:first-of-type{
  2411. margin-top: 0!important;
  2412. }
  2413. #showav_newlinetip{
  2414. font-size: small;
  2415. display: inline-block;
  2416. padding: 0 2px;
  2417. line-height: 1.5em;
  2418. border-radius: 3px;
  2419. background: #ff5722;
  2420. color: white;
  2421. overflow: hidden;
  2422. transition: all .3s;
  2423. opacity: 0;
  2424. }
  2425. #showav_newlinetip.showav_newlinetip_ok{
  2426. background: #0288d1!important;
  2427. }
  2428. #showav_newlinetip.showav_newlinetip{
  2429. opacity: 1;
  2430. }
  2431. ul#showav_customitems{
  2432. min-height: 60px;
  2433. }
  2434. ul#showav_customitems::after{
  2435. content:"目前没有自定义项目。当添加了自定义项目时,可以在这里删除。";
  2436. padding: 6px;
  2437. display: block;
  2438. opacity: 0;
  2439. transition: all.3s;
  2440. overflow: hidden;
  2441. height: 0px;
  2442. }
  2443. ul#showav_customitems:empty::after{
  2444. opacity: 1;
  2445. height: 4rem!important;
  2446. }
  2447. li.copyitem{
  2448. transition: all 0.3s;
  2449. opacity: 1;
  2450. max-height: 8em;
  2451. }
  2452. li.copyitem.preremove{
  2453. color: red!important;
  2454. border-color: red!important;
  2455. }
  2456. li.copyitem::after{
  2457. transition: all 0.3s;
  2458. line-height: 0px!important;
  2459. content:"再次点击以移除";
  2460. display: block;
  2461. overflow: hidden;
  2462. color: red!important;
  2463. opacity: 0;
  2464. max-height: 8em;
  2465. }
  2466. li.copyitem.actionpending{
  2467. transition: all 0.5s;
  2468. padding: 0px!important;
  2469. border-width: 0px;
  2470. margin-top: 0px!important;
  2471. margin-bottom: 0px!important;
  2472. max-height: 0em!important;
  2473. opacity: 0;
  2474. }
  2475. li.copyitem.preremove::after{
  2476. line-height: 2rem!important;
  2477. opacity: 1;
  2478. }
  2479. #bilibiliShowInfos {
  2480. display: flex;
  2481. column-gap: 12px;
  2482. flex-wrap: wrap;
  2483. }
  2484. `, 'showav_dragablecss', "unique", document.head);
  2485.  
  2486. CKTools.addStyle(`
  2487. .video-info-detail-list{
  2488. display: none!important;
  2489. }
  2490. #bilibiliShowInfos{
  2491. white-space: nowrap !important;
  2492. }
  2493. #CKTOOLS-modal li, #CKTOOLS-modal ul{
  2494. list-style: none !important;
  2495. }
  2496. `,'showav_css_patch', 'unique', document.head);
  2497.  
  2498. console.log('ShowAV loaded')
  2499.  
  2500. initScript(false);
  2501. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址