完全自定义你的视频标题下方信息栏,排序,增加,删除!
// ==UserScript== // @name 哔哩哔哩自定义视频信息条 // @namespace ckylin-bilibili-display-video-id // @version 1.19.3 // @description 完全自定义你的视频标题下方信息栏,排序,增加,删除! // @author CKylinMC // @match https://www.bilibili.com/video* // @match https://www.bilibili.com/medialist/play/* // @resource cktools https://greasyfork.org/scripts/429720-cktools/code/CKTools.js?version=1034581 // @resource popjs https://cdn.jsdelivr.net/gh/CKylinMC/PopNotify.js@master/PopNotify.js // @resource popcss https://cdn.jsdelivr.net/gh/CKylinMC/PopNotify.js@master/PopNotify.css // @resource timeago https://unpkg.com/[email protected]/dist/timeago.min.js // @grant unsafeWindow // @grant GM_setValue // @grant GM_getValue // @grant GM_getResourceText // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @license GPL-3.0-only // ==/UserScript== (function () { //======[Apply all resources] const resourceList = [ { name: 'cktools', type: 'js' }, { name: 'timeago', type: 'js' }, { name: 'popjs', type: 'js' }, { name: 'popcss', type: 'css' }, { name: 'popcsspatch', type: 'rawcss', content: "div.popNotifyUnitFrame{z-index:110000!important;}.CKTOOLS-modal-content{color: #616161!important;max-height: 80vh;overflow: auto;}" }, ] function applyResource() { resloop: for (let res of resourceList) { if (!document.querySelector("#" + res.name)) { let el; switch (res.type) { case 'js': case 'rawjs': el = document.createElement("script"); break; case 'css': case 'rawcss': el = document.createElement("style"); break; default: console.log('Err:unknown type', res); continue resloop; } el.id = res.name; el.innerHTML = res.type.startsWith('raw') ? res.content : GM_getResourceText(res.name); document.head.appendChild(el); } } } applyResource(); //====== const wait = (t) => new Promise(r => setTimeout(r, t)); const waitForPageVisible = async () => { return document.hidden && new Promise(r => document.addEventListener("visibilitychange", r)) } const log = (...m) => console.log('[ShowAV]', ...m); const getAPI = (bvid) => fetch('https://api.bilibili.com/x/web-interface/view?bvid=' + bvid).then(raw => raw.json()); const getAidAPI = (aid) => fetch('https://api.bilibili.com/x/web-interface/view?aid=' + aid).then(raw => raw.json()); const config = { defaultAv: true, hideTime: true, firstTimeLoad: true, defaultTextTime: true, foldedWarningTip: true, forceRemoveAllItem: true, showInNewLine: false, forceGap: false, nobreakline: false, jssafetyWarning: true, pnmaxlength: 18, orders: ['openGUI', 'showArgue', 'showPic', 'showAv', 'showPn'], all: ['showAv', 'showSAv', 'showSBv', 'showPn', 'showCid', 'showCate', 'openGUI', 'showPic', 'showSize', 'showMore', 'showCTime', 'showViews', 'showDmk', 'showTop', 'showArgue'], copyitems: ['currTime', 'short', 'shareTime', 'vid'], copyitemsAll: ['curr', 'currTime', 'short', 'share', 'shareTime', 'md', 'bb', 'html', 'vid'], customcopyitems: {}, customComponents: {}, vduration: 0, running: {} }; const ignoredConfigKeys = ["all", "vduration", "firstTimeLoad", "running"]; const menuId = { defaultAv: -1, foldedWarningTip: -1, showInNewLine: -1, }; const txtCn = { showAv: "可切换视频编号和高级复制", showSAv: "视频AV号和高级复制", showSBv: "视频BV号和高级复制", showPn: "视频分P名", showCid: "视频CID编号", showCate: "视频所在分区", showPic: "视频封面", showSize: "视频分辨率", showMore: "更多信息", showCTime: "视频投稿时间", showViews: "替换视频播放量", showDmk: "替换视频弹幕量", showTop: "替换全站排名提示", showArgue: "显示危险提示", curr: "当前视频地址", currTime: "当前视频地址(含视频进度)", short: "短地址", share: "快速分享", shareTime: "快速分享(含视频进度)", md: "Markdown 格式", bb: "BBCode 格式", html: "HTML 格式", vid: "视频编号", openGUI: "设置选项" }; const descCn = { showAv: "展示视频号(AV号/BV号右键单击可切换),左键单击快速复制(包含当前播放时间),左键长按打开更多格式复制窗口", showSAv: "展示视频AV号,左键单击快速复制(包含当前播放时间),左键长按打开更多格式复制窗口", showSBv: "展示视频BV号,左键单击快速复制(包含当前播放时间),左键长按打开更多格式复制窗口", showPn: "展示视频分P信息以及缓存名(分P名)。可能较长,建议放在最下面,并调整最大长度。", showCid: "展示视频资源CID编号,通常不需要展示。", showCate: "展示视频所在的子分区。", showPic: "提供按钮一键查看封面,长按可以在新标签页打开大图。", showSize: "展示视频当前分辨率(宽高信息)。", showMore: "查看视频更多信息。", showCTime: "用文字方式描述投稿时间,如:一周前", showViews: "替换展示视频播放量(由于内容相同,将自动隐藏原版播放量信息)", showDmk: "替换展示视频弹幕量(由于内容相同,将自动隐藏原版弹幕量信息)", showTop: "替换原版全站排名信息", showArgue: "如果视频有危险提示,则显示危险提示", curr: "提供当前视频纯净地址", currTime: "提供当前视频地址,并在播放时提供含跳转时间的地址(可以直接跳转到当前进度)。", short: "提供当前视频的b23.tv短地址", share: "提供当前视频的标题和地址组合文本。", shareTime: "提供当前视频的标题和地址组合文本,在播放时提供含跳转时间的地址(可以直接跳转到当前进度)。", md: "提供Markdown特殊语法的快速复制。", bb: "提供BBCode特殊语法的快速复制。", html: "提供HTML格式的快速复制。", vid: "提供当前视频av号/BV号/CID号。请注意此项目不支持快速复制。", openGUI: "提供按钮快速进入设置选项。" }; const idTn = { showAv: 2, showSAv: 2, showSBv: 2, showPn: 5, showCid: 2, showCate: 3, showPic: 1, showSize: 2, showMore: 1, showCTime: -4, showViews: -2, showDmk: -2, showTop: 0, showArgue: 1, openGUI: 1 }; let globalinfos = {}; // https://stackoverflow.com/questions/10726638 String.prototype.mapReplace = function (map) { var regex = []; for (var key in map) regex.push(key.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")); return this.replace(new RegExp(regex.join('|'), "g"), function (word) { return map[word]; }); }; // CSDN https://blog.csdn.net/namechenfl/article/details/91968396 function numberFormat(value) { let param = {}; let k = 10000, sizes = ['', '万', '亿', '万亿'], i; if (value < k) { param.value = value param.unit = '' } else { i = Math.floor(Math.log(value) / Math.log(k)); param.value = ((value / Math.pow(k, i))).toFixed(2); param.unit = sizes[i]; } return param; } function exec(code,binding=this){ return (async function(){ eval(code); }).bind(binding); } async function saveAllConfig() { for (let configKey of Object.keys(config)) { if (ignoredConfigKeys.includes(configKey)) continue; await GM_setValue(configKey, config[configKey]); } popNotify.success("配置保存成功"); } async function initScript(flag = false) { for (let menuitem of Object.keys(menuId)) { if (menuId[menuitem] != -1) GM_unregisterMenuCommand(menuId[menuitem]); } for (let configKey of Object.keys(config)) { if (ignoredConfigKeys.includes(configKey)) continue; if (typeof (await GM_getValue(configKey)) === 'undefined') { await GM_setValue(configKey, config[configKey]); } else { config[configKey] = await GM_getValue(configKey); } } GM_registerMenuCommand("打开设置", async () => { await GUISettings(); }); CKTools.addStyle(` #bilibiliShowPN{ max-width: ${config.pnmaxlength}em!important; } `, "showav_pnlen", "update", document.head); tryInject(flag); } async function playerReady() { let i = 150; while (--i > 0) { await wait(100); console.log('ShowAV waiting for player ready - loop') if (unsafeWindow.player?.isInitialized()??false) break; } console.log('ShowAV waiting for player ready - loop end') if (i < 0) return false; console.log('ShowAV waiting for player ready - wait visible') await waitForPageVisible(); while (1) { await wait(200); console.log('ShowAV waiting for player controlls') if (document.querySelector(".bilibili-player-video-control-wrap, .bpx-player-control-wrap")) return true; } } async function waitForDom(q) { let i = 50; let dom; while (--i >= 0) { if (dom = document.querySelector(q)) break; await wait(100); } return dom; } function getUrlParam(key) { return (new URL(location.href)).searchParams.get(key); } function getOrNew(id, parent,) { let target = document.querySelector("#" + id); if (!target) { target = document.createElement("span"); target.id = id; parent.appendChild(target); } return target; } async function getPlayerSeeks() { const video = await waitForDom(".bilibili-player-video video"); let seconds = 0; if (video) { seconds = Math.floor(video.currentTime); } if (seconds == 0) { let fromParam = getUrlParam("t") || 0; return fromParam; } else return seconds; } function getHEVC(){ return document.querySelector(".bilibili-player-video bwp-video") } async function registerVideoChangeHandler() { const video = await waitForDom(".bilibili-player-video video"); const HEVCplayer = getHEVC();// must behind video loaded(no more waitfordom) let target = HEVCplayer || video; if(!target) return; const observer = new MutationObserver(async e => { if (e[0].target.src) { tryInject(true); } }); observer.observe(target, { attribute: true, attributes: true, childList: false }); } function getPageFromCid(cid, infos) { if (!cid || !infos || !infos.pages) return 1; let pages = infos.pages; if (pages.length == 1) return 1; let page; for (page of pages) { if (!page) continue; if (page.cid == cid) return page.page; } return 1; } async function feat_showCate() { const { av_root, infos } = this; const cate_span = getOrNew("bilibiliShowCate", av_root); //if (config.showCate) { cate_span.style.textOverflow = "ellipsis"; cate_span.style.whiteSpace = "nowarp"; cate_span.style.overflow = "hidden"; cate_span.title = "分区: " + infos.tname; cate_span.innerText = "分区: " + infos.tname; //} else cate_span.remove(); } async function feat_showStaticAv() { const func = feat_showAv.bind(this); func(true); } async function feat_showStaticBv() { const func = feat_showAv.bind(this); func(true, 'bv'); } async function prepareData(infos,el=null){ const defaultVid = el?el.innerText:'av'+infos.aid; const currpage = new URL(location.href); let part = infos.p==currpage.searchParams.get("p") ? infos.p : (currpage.searchParams.get("p") ? currpage.searchParams.get("p") : infos.p); let url = new URL(location.protocol + "//" + location.hostname + "/video/" + defaultVid); part == 1 || url.searchParams.append("p", part); let vidurl = new URL(url); let shorturl = new URL(location.protocol + "//b23.tv/" + defaultVid); let t = await getPlayerSeeks(); if (t && t != "0" && t != ("" + config.vduration)) url.searchParams.append("t", t); try { partinfo = infos.pages[infos.p - 1]; } catch (e) { partinfo = infos.pages[0]; } return {currpage,partinfo,url,vidurl,shorturl,part,t,infos} } async function getCopyItem(copyitem,infos,av_span=null){ const {partinfo,url,vidurl,shorturl,part,t} = await prepareData(infos,av_span); switch (copyitem) { case "curr": return { title: "当前地址", content: vidurl, type: "copiable" }; case "currTime": return { title: "含视频进度地址(仅在播放时提供)", content: url, type: "copiable" }; case "short": return { title: "短地址格式", content: shorturl, type: "copiable" }; case "share": return { title: "快速分享", content: `${infos.title}_地址:${shorturl}`, type: "copiable" }; case "shareTime": return { title: "快速分享(含视频进度)", content: `${infos.title}_地址:${url}`, type: "copiable" }; case "md": return { title: "MarkDown格式", content: `[${infos.title}](${vidurl})`, type: "copiable" }; case "bb": return { title: "BBCode格式", content: `[url=${vidurl}]${infos.title}[/url]`, type: "copiable" }; case "html": return { title: "HTML格式", content: `<a href="${vidurl}">${infos.title}</a>`, type: "copiable" }; case "vid": return { title: "视频编号", content: `<div class="shoav_expandinfo"> <div> AV号 <input class="shortinput" readonly value="av${infos.aid}" onclick="showav_fastcopy(this);" /> </div> <div> BV号 <input class="shortinput" readonly value="${infos.bvid}" onclick="showav_fastcopy(this);" /> </div> <div> 资源CID <input class="shortinput" readonly value="${infos.cid}" onclick="showav_fastcopy(this);" /> </div> </div> `, type: "component", copyaction: function(){ copy(this.av_span.innerText); popNotify.success("已复制到剪贴板",this.av_span.innerText); } }; default: if (Object.keys(config.customcopyitems).includes(copyitem)) { let ccopyitem = config.customcopyitems[copyitem]; let pat = ccopyitem.content ? ccopyitem.content : "无效内容"; pat = apiBasedVariablesReplacement(pat.mapReplace({ "%timeurl%": url, "%vidurl%": vidurl, "%shorturl%": shorturl, "%seek%": t, "%title%": infos.title, "%av%": infos.aid, "%bv%": infos.bvid, "%cid%": infos.cid, "%p%": part, "%pname%": partinfo.part, "'": "\'" })); return { title: `(自定义) ${ccopyitem.title}`, content: pat, type: "copiable" } }else return null; } }; function apiBasedVariablesReplacement(txt='',infos=globalinfos){ log("apiBasedVariablesReplacement",infos); const vars = [...txt.matchAll(/\%[a-zA-Z.]+\%/g)].map(k=>k[0]); if(vars.length==0) return txt; const map = {}; for(let replaceVar of vars){ const varName = replaceVar.substring(1,replaceVar.length-1); const apiR###lt = recursiveApiResolver(varName,infos); if(!apiR###lt) continue; map[replaceVar] = apiR###lt; } return txt.mapReplace(map); } function recursiveApiResolver(name,infos=globalinfos){ const pargs = name.split(".").filter(arr=>arr.length); if([pargs[0],pargs[pargs.length-1]].includes("")) return null; let target = infos; for(let parg of pargs){ if(target.hasOwnProperty(parg)){ target = target[parg]; }else return null; } return target.toString(); } async function feat_showAv(force = false, mode = 'av'/* 'bv' */) { const { av_root, infos } = this; const av_span = getOrNew("bilibiliShowAV" + (force ? mode : ''), av_root); //if (config.showAv) { if (force) { if (mode == 'bv') { av_span.innerText = infos.bvid; } else { av_span.innerText = 'av' + infos.aid; } } else if (config.defaultAv) av_span.innerText = 'av' + infos.aid; else av_span.innerText = infos.bvid; av_span.style.overflow = "hidden"; const video = await waitForDom("video"); if (video) { config.vduration = Math.floor(video.duration); } let title = `左键单击复制,${force?'右键单击切换显示,':''}长按打开窗口`; if(config.copyitems.length){ const firstCopyItem = config.copyitems[0]; const firstInfo = await getCopyItem(firstCopyItem,globalinfos,av_span); if(firstInfo!==null){ if(firstInfo.type=="copiable"||firstInfo.type=="component"){ av_span.setAttribute('title',title + '\n默认复制: '+firstInfo.title); } }else av_span.setAttribute('title',title + '\n没有默认复制行为'); }else{ av_span.setAttribute('title',title + '\n没有默认复制行为'); } if (av_span.getAttribute("setup") != globalinfos.cid) { if (!force) av_span.oncontextmenu = e => { if (e.target.innerText.startsWith('av')) e.target.innerText = infos.bvid; else av_span.innerText = 'av' + infos.aid; e.preventDefault(); }; let runningCfg = config.running['avspanHC'+(force ? mode : '')]; if(runningCfg) runningCfg.uninstall(); runningCfg = new CKTools.HoldClick(av_span); runningCfg.onclick(async e => { for (let copyitem of config.copyitems) { const copyiteminfo = await getCopyItem(copyitem,globalinfos,av_span); if(copyiteminfo===null) { log(`[ADVCOPY] warning: unknown custom copy item id "${copyitem}", maybe should clean settings up.`); continue; } if(copyiteminfo.type=="copiable"){ copy(copyiteminfo.content); popNotify.success(copyiteminfo.title+"复制成功", copyiteminfo.content); return; } else if(copyiteminfo.type=="component"){ if(typeof(copyiteminfo.copyaction)==="function"){ try{ const func = copyiteminfo.copyaction.bind({av_span}); func(); }catch(e){log(copyiteminfo,e);} }else{ copy(copyiteminfo.copyaction.toString()); popNotify.success("已复制到剪贴板",copyiteminfo.copyaction.toString()) } return; } else continue; } popNotify.error("快速复制失败","没有任何已启用的可用快速复制设定"); }); runningCfg.onhold(async e => { let modalcontent = ` <style scoped> input:not(.shortinput){ width:100%; display:block; } .shoav_expandinfo>div { text-align: center; flex: 1; } input.shortinput { width: 7.8em; text-align: center; } .CKTOOLS-modal-content>div>div{ width: 440px!important; } .shoav_expandinfo{ display: flex; flex-direction: row; flex-wrap: nowrap; align-content: center; justify-content: space-around; align-items: stretch; } </style> <b>点击输入框可以快速复制</b><br>`; for (let copyitem of config.copyitems) { const copyiteminfo = await getCopyItem(copyitem,globalinfos,av_span); if(copyiteminfo.type=="copiable"){ modalcontent+=`<span class="copyitem-title">${copyiteminfo.title}</span><input readonly value="${copyiteminfo.content}" onclick="showav_fastcopy(this);" />` } else{ modalcontent+=copyiteminfo.content; } } modalcontent += `<br><hr><a href="javascript:void(0)" onclick="showav_guisettings_advcopy()">⚙ 复制设置</a><br> <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> `; modalcontent+= closeButton().outerHTML; CKTools.modal.alertModal("高级复制", modalcontent, "关闭"); }); av_span.setAttribute("setup", globalinfos.cid); config.running['avspanHC'+(force ? mode : '')] = runningCfg; } //} else av_span.remove(); } async function feat_showMore() { const { av_root } = this; const more_span = getOrNew("bilibiliShowMore", av_root); more_span.innerHTML = '⋯'; more_span.title = "展示更多信息"; more_span.style.cursor = "pointer"; if (more_span.getAttribute("setup") != globalinfos.cid) { more_span.addEventListener('click', async e => { let part, videoData = globalinfos; try { part = videoData.pages[globalinfos.p - 1]; } catch (e) { part = videoData.pages[0]; } let currentPageName = part.part.length ? part.part : ''; let currentPageNum; if (videoData.videos != 1) { currentPageNum = `P ${globalinfos.p}/${videoData.videos}`; } else { currentPageNum = "P 1/1"; } CKTools.modal.alertModal("视频信息", ` <style scoped> li{ line-height: 2em; } </style> <li> <b>AV号: </b>av${globalinfos.aid} </li> <li> <b>BV号: </b>${globalinfos.bvid} </li> <li> <b>CID: </b>${globalinfos.cid} </li> <li> <b>分P: </b>${currentPageNum} </li> <li> <b>P名: </b>${currentPageName} </li> <li> <b>长度: </b>${globalinfos.duration}s </li> <li> <b>投稿: </b>${timeago.format(globalinfos.ctime * 1000, 'zh_CN')} </li> <li> <b>分区: </b>${globalinfos.tname} </li> <li> <b>大小: </b>${globalinfos.dimension.width}x${globalinfos.dimension.height} </li> <li> <b>封面: </b><a href="${globalinfos.pic}" target="_blank">点击查看</a> </li> `, "确定"); }) more_span.setAttribute("setup", globalinfos.cid); } } async function feat_showCTime() { const { av_root, infos } = this; const ct_span = getOrNew("bilibiliShowCTime", av_root); ct_span.style.textOverflow = "ellipsis"; ct_span.style.whiteSpace = "nowarp"; ct_span.style.overflow = "hidden"; const d = new Date(infos.pubdate * 1000); let txttime = timeago.format(infos.pubdate * 1000, 'zh_CN'); 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()}`; ct_span.title = "投稿时间 " + (config.defaultTextTime ? rawtime : txttime); ct_span.innerHTML = config.defaultTextTime ? txttime : rawtime if (config.hideTime) ct_span.innerHTML += ` <style> .video-info-detail span:nth-child(3),.video-info-detail span.pupdate{ display:none!important; } </style>`; } async function feat_showViews() { const { av_root, infos } = this; const v_span = getOrNew("bilibiliShowViews", av_root); v_span.style.textOverflow = "ellipsis"; v_span.style.whiteSpace = "nowarp"; v_span.style.overflow = "hidden"; v_span.title = `播放量 ${infos.stat.view}`; v_span.innerHTML = (() => { const res = numberFormat(infos.stat.view); return `${res.value}${res.unit}播放`; })(); v_span.innerHTML += ` <style> .video-info-detail span.view{ display:none!important; } </style>`; } async function feat_showDmk() { const { av_root, infos } = this; const dmk_span = getOrNew("bilibiliShowDmk", av_root); dmk_span.style.textOverflow = "ellipsis"; dmk_span.style.whiteSpace = "nowarp"; dmk_span.style.overflow = "hidden"; dmk_span.title = `${infos.stat.danmaku}条弹幕`; dmk_span.innerHTML = (() => { const res = numberFormat(infos.stat.danmaku); return `${res.value}${res.unit}条弹幕`; })(); dmk_span.innerHTML += ` <style> .video-info-detail span.dm{ display:none!important; } </style>`; } async function feat_showTop() { const { av_root, infos } = this; const top_span = getOrNew("bilibiliShowTop", av_root); top_span.style.textOverflow = "ellipsis"; top_span.style.whiteSpace = "nowarp"; top_span.style.overflow = "hidden"; top_span.title = `全站最高排行第${infos.stat.his_rank}名`; top_span.innerHTML = '' top_span.innerHTML += ` <style> .video-info-detail span.rank,.video-info-detail a.honor{ display:none!important; } </style>`; if (infos.stat.his_rank === 0) { top_span.style.display = "none"; setTimeout(() => { if (top_span.nextElementSibling) { top_span.nextElementSibling.style.marginLeft = 0; } }, 100); } else { top_span.innerHTML += '📊 ' + infos.stat.his_rank; } } async function feat_showPic() { const { av_root, infos } = this; const pic_span = getOrNew("bilibiliShowPic", av_root); pic_span.style.textOverflow = "ellipsis"; pic_span.style.whiteSpace = "nowarp"; pic_span.style.overflow = "hidden"; pic_span.title = "查看封面"; pic_span.innerHTML = "🖼️"; pic_span.style.cursor = "pointer"; if (pic_span.getAttribute("setup") != globalinfos.cid) { config.running.picHC && config.running.picHC.uninstall(); config.running.picHC = new CKTools.HoldClick(pic_span); config.running.picHC.onclick(() => { CKTools.modal.alertModal("封面", ` <img src="${globalinfos.pic}" style="width:100%" onload="this.parentElement.style.width='100%'" /> `, "关闭"); }); config.running.picHC.onhold(() => { open(globalinfos.pic); }); pic_span.setAttribute("setup", globalinfos.cid); } } async function feat_showCid() { const { av_root, infos } = this; const cid_span = getOrNew("bilibiliShowCID", av_root); //if (config.showCid) { cid_span.style.textOverflow = "ellipsis"; cid_span.style.whiteSpace = "nowarp"; cid_span.style.overflow = "hidden"; cid_span.title = "CID:" + infos.cid; cid_span.innerText = "CID:" + infos.cid; if (cid_span.getAttribute("setup") != globalinfos.cid) { config.running.cidspanHC && config.running.cidspanHC.uninstall(); config.running.cidspanHC = new CKTools.HoldClick(cid_span); config.running.cidspanHC.onclick(() => { copy(currentPageName); popNotify.success("CID复制成功", globalinfos.cid); }); config.running.cidspanHC.onhold(() => { CKTools.modal.alertModal("CID信息", ` <input readonly style="width:440px" value="${globalinfos.cid}" /> `, "关闭"); }); cid_span.setAttribute("setup", globalinfos.cid); } //} else cid_span.remove(); } async function feat_showSize() { const { av_root, infos } = this; const size_span = getOrNew("bilibiliShowSize", av_root); //if (config.showCid) { size_span.style.textOverflow = "ellipsis"; size_span.style.whiteSpace = "nowarp"; size_span.style.overflow = "hidden"; size_span.title = `${infos.dimension.width}x${infos.dimension.height}`; size_span.innerText = `${infos.dimension.width}x${infos.dimension.height}`; //} else cid_span.remove(); } async function feat_openGUI() { const { av_root, infos } = this; const gui_span = getOrNew("bilibiliShowGUISettings", av_root); gui_span.innerHTML = "⚙"; gui_span.title = "ShowAV 设置"; gui_span.style.overflow = "hidden"; gui_span.style.cursor = "pointer"; gui_span.onclick = e => GUISettings(); } async function feat_showArgue() { const { av_root, infos } = this; const argue_span = getOrNew("bilibiliShowArgue", av_root); const original = document.querySelector(".argue.item"); if(!original) argue_span.style.display = "none"; else argue_span.style.display = "block"; argue_span.style.color = "rgb(255, 170, 44)"; argue_span.innerHTML = "<i class='van-icon-info_warning'></i>"; argue_span.title = (original&&original.title)||"警告"; argue_span.style.overflow = "hidden"; } async function feat_showPn() { const { av_root, infos } = this; const pn_span = getOrNew("bilibiliShowPN", av_root); //if (config.showPn) { const videoData = infos; if (!videoData) return; let part = { part: 'P' + infos.p } try { part = videoData.pages[infos.p - 1]; } catch (e) { part = videoData.pages[0]; } let currentPageName = part.part.length ? part.part : ''; let currentPageNum; let delimiters; if (videoData.videos != 1) { currentPageNum = `P ${infos.p}/${videoData.videos}`; delimiters = ["\n", " "]; } else { currentPageNum = ""; delimiters = ["", ""]; } pn_span.style.textOverflow = "ellipsis"; pn_span.style.whiteSpace = "nowarp"; pn_span.style.overflow = "hidden"; pn_span.title = currentPageNum + delimiters[0] + currentPageName pn_span.innerText = currentPageNum + delimiters[1] + currentPageName; if (pn_span.getAttribute("setup") != globalinfos.cid) { config.running.pnspanHC && config.running.pnspanHC.uninstall(); config.running.pnspanHC = new CKTools.HoldClick(pn_span); config.running.pnspanHC.onclick(() => { copy(currentPageName); popNotify.success("分P标题复制成功", currentPageName); }); config.running.pnspanHC.onhold(() => { CKTools.modal.alertModal("分P标题", ` <input readonly style="width:440px" value="${currentPageName}" /> `, "关闭"); }); pn_span.setAttribute("setup", globalinfos.cid); } //} else pn_span.remove(); } async function feat_custom(itemid){ const { av_root, infos } = this; const that = this; that.window = unsafeWindow; const custom_span = getOrNew("bilibili_"+itemid, av_root); const {partinfo,url,vidurl,shorturl,part,t} = await prepareData(infos); const parseTxt = txt=>apiBasedVariablesReplacement(txt.mapReplace({ "%timeurl%": url, "%vidurl%": vidurl, "%shorturl%": shorturl, "%seek%": t, "%title%": infos.title, "%av%": infos.aid, "%bv%": infos.bvid, "%cid%": infos.cid, "%p%": part, "%pname%": partinfo.part, "'": "\'" })); if(Object.keys(config.customComponents).includes(itemid)){ const item = config.customComponents[itemid]; let content = item.content; if(item.content.startsWith("js:")){ content = item.content.replace("js:",""); } else content = parseTxt(item.content); custom_span.style.overflow = "hidden"; try{ if(item.title.startsWith("js:")){ let itemtitle = item.title.substr(3); custom_span.innerHTML = eval(parseTxt(itemtitle)); }else custom_span.innerHTML = parseTxt(item.title); }catch(e){ custom_span.innerHTML = parseTxt(item.title); } custom_span.title = `自定义组件: ${item.title}\n长按管理自定义组件`; if(custom_span.getAttribute("setup")!=globalinfos.cid){ custom_span.setAttribute("setup",globalinfos.cid); config.running[itemid] && config.running[itemid].uninstall(); config.running[itemid] = new CKTools.HoldClick(custom_span); config.running[itemid].onclick(e => { console.log(item.content) if(item.content.startsWith("js:")){ log("executing:",content); exec(content,that)(); }else{ copy(content); popNotify.success("已复制"+item.title,content); } }); config.running[itemid].onhold(e=>{ GUISettings_customcomponents(); }) } }else{ log("Errored while handling custom components:",k,"not found"); custom_span.remove(); } } function getSideloadModules(){ if(!unsafeWindow.ShowAVModules) return {}; const mods = {}; for(const modName of Object.keys(unsafeWindow.ShowAVModules)){ const mod = unsafeWindow.ShowAVModules[modName]; if(mod&&(typeof(mod.name)==='string')&&(typeof(mod.onload)==='function')&&(typeof(mod.onclick)==='function')&&(typeof(mod.onhold)==='function')){ mods[modName] = mod; } } return mods; } function mappedSideloadModules(){ const sideloads = getSideloadModules(); const mods = {}; for(const modName of Object.keys(sideloads)){ mods['sideload_'+modName] = sideloads[modName]; } return mods; } async function runSideloadModule(module,moduleInternalID = (Math.floor(Math.random()*10000))){ let slm_span = null; try{ const { av_root }=this; const onloadFn = module.onload.bind(this); const onclickFn = module.onclick.bind(this); const onholdFn = module.onhold.bind(this); const name = "showav_slm_" + moduleInternalID; slm_span = getOrNew(name, av_root); slm_span.innerHTML = ''; slm_span.style.textOverflow = "ellipsis"; slm_span.style.whiteSpace = "nowarp"; slm_span.style.overflow = "hidden"; slm_span.title = "模块:" + module.name; if(module.tip){ if(typeof(module.tip)=='function') slm_span.title+='\n'+module.tip.bind(this)(); else slm_span.title+='\n'+module.tip; }else if(module.description){ slm_span.title+='\n'+module.description; } slm_span.appendChild(await onloadFn(slm_span)); if (slm_span.getAttribute("setup") != globalinfos.cid) { config.running[name] && config.running[name].uninstall(); config.running[name] = new CKTools.HoldClick(slm_span); config.running[name].onclick(onclickFn); config.running[name].onhold(onholdFn); slm_span.setAttribute("setup", globalinfos.cid); } }catch(e){ log('[ERR]',module.name,e); (slm_span&&(slm_span instanceof HTMLElement)&&slm_span.remove()); } } async function tryInject(flag) { console.log('ShowAV waiting for player ready') if (flag && config.orders.length === 0) return log('Terminated because no option is enabled.'); if (!(await playerReady())) return log('Can not load player in time.'); if (config.firstTimeLoad) { registerVideoChangeHandler(); config.firstTimeLoad = false; } console.log('ShowAV start inject') 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); if (config.forceGap) { CKTools.addStyle(`#bilibiliShowInfos{margin-left: 12px!important;}`,"showav_forceGapCss","update",document.head); }else{ CKTools.addStyle(``,"showav_forceGapCss","update",document.head); } if(config.forceRemoveAllItem){ 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); }else{ CKTools.addStyle(``,"showav_hideall", "update", document.head); } if(config.nobreakline){ CKTools.addStyle(`#bilibiliShowInfos{max-width: 100%;flex-wrap: nowrap!important;}`,"showav_nobreak", "update", document.head); }else{ CKTools.addStyle(``,"showav_nobreak", "update", document.head); } if (location.pathname.startsWith("/medialist")) { let aid = unsafeWindow.aid; if (!aid) { log("Variable 'aid' is not available from unsafeWindow."); let activeVideo = await waitForDom(".player-auxiliary-playlist-item-active"); aid = activeVideo.getAttribute("data-aid"); } let apidata = await getAidAPI(aid); globalinfos = apidata.data; } else { if (flag) globalinfos = (await getAPI(unsafeWindow.vd?.bvid)).data; else globalinfos = unsafeWindow.vd; } globalinfos.p = getUrlParam("p") || getPageFromCid(unsafeWindow.cid, globalinfos); //const av_infobar = await waitForDom(".video-data"); const av_infobar = await waitForDom(".video-info-detail"); if (!av_infobar) return log('Can not load info-bar in time.'); let av_root; if (config.showInNewLine) { av_root = getOrNew("bilibiliShowInfos", av_infobar.parentElement); } else { let rootel = document.querySelector("#bilibiliShowInfos"); if (!rootel) { rootel = document.createElement("span"); rootel.id = "bilibiliShowInfos"; av_infobar.appendChild(rootel); } av_root = rootel; } //const av_root = getOrNew("bilibiliShowInfos",av_infobar); //const av_root = av_infobar; av_root.style.textOverflow = "ellipsis"; av_root.style.whiteSpace = "nowrap!important"; // av_root.style.overflow = "hidden"; const that = { av_root, config, av_infobar, infos : globalinfos, CKTools, popNotify, tools:{ copy, wait, waitForPageVisible, log, getPlayerSeeks, getHEVC, waitForDom, getOrNew, playerReady, variablesReplace:apiBasedVariablesReplacement }, }; const functions = { showAv: feat_showAv.bind(that), showSAv: feat_showStaticAv.bind(that), showSBv: feat_showStaticBv.bind(that), showCate: feat_showCate.bind(that), showCid: feat_showCid.bind(that), showPn: feat_showPn.bind(that), showPic: feat_showPic.bind(that), showSize: feat_showSize.bind(that), showMore: feat_showMore.bind(that), showCTime: feat_showCTime.bind(that), showDmk: feat_showDmk.bind(that), showViews: feat_showViews.bind(that), showTop: feat_showTop.bind(that), showArgue: feat_showArgue.bind(that), openGUI: feat_openGUI.bind(that), customDriver: feat_custom.bind(that) } const sideloads = mappedSideloadModules(); config.orders.forEach(async k => { if(Object.keys(functions).includes(k)) await functions[k](); else if(Object.keys(sideloads).includes(k)) await runSideloadModule.bind(that)(sideloads[k], k); else{ try{ await functions.customDriver(k); }catch(e){ log(`Custom component "${k}" throwed an error:`,e) }; } }); const titleobj = document.querySelector("span.tit"); if(titleobj&&!titleobj.getAttribute("data-copy-action-registered")){ titleobj.onclick = e => { let content = e.target.innerText; let tip = "已复制视频标题"; if(unsafeWindow.getSelection().toString().length){ content = unsafeWindow.getSelection().toString(); tip = "已复制视频标题选中部分"; } copy(content); popNotify.success(tip,content); } titleobj.setAttribute("data-copy-action-registered",true); } setupWarningAutoFolding(); } function setupWarningAutoFolding(){ //if(config.foldedWarningTip) /*CKTools.addStyle( "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}", "showav_foldWarningTip","update");*/ CKTools.addStyle( "span.argue{display:none!important}", "showav_foldWarningTip","update"); /*else CKTools.addStyle( "span.argue{margin-right: 10px !important;margin-left: 0 !important;}", "showav_foldWarningTip","update");*/ } function closeButton(){ const closebtn = document.createElement("div"); closebtn.innerHTML = " × "; closebtn.style.position = "absolute"; closebtn.style.top = "10px"; closebtn.style.right = "10px"; closebtn.style.cursor = "pointer"; closebtn.style.fontWeight = 900; closebtn.style.fontSize = "larger"; closebtn.setAttribute("onclick","CKTools.modal.hideModal()"); return closebtn; } async function GUISettings() { if (CKTools.modal.isModalShowing()) { CKTools.modal.hideModal(); await wait(300); } CKTools.modal.openModal("ShowAV / 设置", await CKTools.domHelper("div", async container => { container.style.alignItems = "stretch"; container.style.minWidth = "300px"; [ closeButton(), await CKTools.domHelper("div", async tip => { tip.style.lineHeight = "2em"; tip.style.fontSize = "small"; tip.style.fontStyle = "italic"; tip.style.width = "100%"; tip.innerText = "修改设置后记得点击保存哦"; }), await CKTools.domHelper("li", async list => { list.classList.add("showav_menuitem"); list.onclick = e => GUISettings_options(); [ await CKTools.domHelper("label", label => { label.innerHTML = "功能选项"; }), await CKTools.domHelper("span", label => { label.innerHTML = "调整每个功能模块的单独选项"; label.style.marginLeft = "6px"; }), ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("li", async list => { list.classList.add("showav_menuitem"); list.onclick = e => GUISettings_components(); [ await CKTools.domHelper("label", label => { label.innerHTML = "组件设置"; }), await CKTools.domHelper("span", label => { label.innerHTML = "启用/排序/自定义功能组件"; label.style.marginLeft = "6px"; }), ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("li", async list => { list.classList.add("showav_menuitem"); list.onclick = e => GUISettings_customcomponents(()=>GUISettings()); [ await CKTools.domHelper("label", label => { label.innerHTML = "自定义组件设置"; }), await CKTools.domHelper("span", label => { label.innerHTML = "添加或删除自定义的信息栏组件"; label.style.marginLeft = "6px"; }), ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("li", async list => { list.classList.add("showav_menuitem"); list.onclick = e => GUISettings_advcopy(()=>GUISettings()); [ await CKTools.domHelper("label", label => { label.innerHTML = "高级复制设置"; }), await CKTools.domHelper("span", label => { label.innerHTML = "自定义复制弹窗和默认动作"; label.style.marginLeft = "6px"; }), ].forEach(e => list.appendChild(e)); }), ].forEach(e => container.appendChild(e)); })); } async function GUISettings_options() { if (CKTools.modal.isModalShowing()) { CKTools.modal.hideModal(); await wait(300); } CKTools.modal.openModal("ShowAV / 设置 / 功能选项", await CKTools.domHelper("div", async container => { container.style.alignItems = "stretch"; [ closeButton(), await CKTools.domHelper("li", sectiontitle=>{ sectiontitle.innerText = "信息栏"; sectiontitle.className = "showav_settings_sectiontitle"; }), await CKTools.domHelper("li", async list => { list.style.lineHeight = "2em"; [ await CKTools.domHelper("input", input => { input.type = "checkbox"; input.id = "showav_forcegap"; input.name = "showav_forcegap"; input.style.display = "none"; input.checked = config.forceGap; input.addEventListener("change",e=>{ const label = document.querySelector("#showav_forcegaptip"); if(!label) return; if(input.checked){ label.innerHTML = "在第一个组件前<b>强制添加</b>间隔(点击切换)" }else{ label.innerHTML = "在第一个组件前<b>保持默认</b>间隔(点击切换)" } }) }), await CKTools.domHelper("label", label => { label.id = "showav_forcegaptip"; label.setAttribute('for', "showav_forcegap"); if(config.forceGap){ label.innerHTML = "在第一个组件前<b>强制添加</b>间隔(点击切换)" }else{ label.innerHTML = "在第一个组件前<b>保持默认</b>间隔(点击切换)" } }), await CKTools.domHelper("div", div => { div.style.paddingLeft = "20px"; div.style.color = "#919191"; div.innerHTML = `可选扩展信息栏和原版信息栏之间强制添加一个间隔,或保持默认`; }) ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("li", async list => { list.style.lineHeight = "2em"; [ await CKTools.domHelper("input", input => { input.type = "checkbox"; input.id = "showav_newline"; input.style.display = "none"; input.name = "showav_newline"; input.checked = config.showInNewLine; input.addEventListener("change",e=>{ const label = document.querySelector("#showav_showinnewlinetip"); if(!label) return; if(input.checked){ label.innerHTML = "在<b>新的一行中</b>显示扩展信息栏(点击切换)" }else{ label.innerHTML = "在<b>当前位置后</b>显示扩展信息栏(点击切换)" } }) }), await CKTools.domHelper("label", label => { label.id = "showav_showinnewlinetip"; label.setAttribute('for', "showav_newline"); if(config.showInNewLine){ label.innerHTML = "在<b>新的一行中</b>显示扩展信息栏(点击切换)" }else{ label.innerHTML = "在<b>当前位置后</b>显示扩展信息栏(点击切换)" } }), await CKTools.domHelper("div", div => { div.style.paddingLeft = "20px"; div.style.color = "#919191"; div.innerHTML = `可选将扩展信息栏显示在下一行,尽量减少对原信息栏的修改`; }) ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("li", async list => { list.style.lineHeight = "2em"; [ await CKTools.domHelper("label", label => { label.style.paddingLeft = "3px"; label.id = "showav_nobreakline_tip"; label.setAttribute('for', "showav_nobreakline"); if (config.nobreakline) label.innerHTML = "默认 <b>禁止</b> 信息栏换行(点击切换)"; else label.innerHTML = "默认 <b>允许</b> 信息栏换行"; }), await CKTools.domHelper("input", input => { input.type = "checkbox"; input.id = "showav_nobreakline"; input.name = "showav_nobreakline"; input.style.display = "none"; input.checked = config.nobreakline; input.addEventListener('change', e => { const label = document.querySelector("#showav_nobreakline_tip"); if (!label) return; if (input.checked) label.innerHTML = "默认 <b>禁止</b> 信息栏换行(点击切换)"; else label.innerHTML = "默认 <b>允许</b> 信息栏换行(点击切换)"; }) }), await CKTools.domHelper("div", div => { div.style.paddingLeft = "20px"; div.style.color = "#919191"; div.innerHTML = `是否要求信息栏尽量不换行,可能其中的文本会被截断。`; }) ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("li", async list => { list.style.lineHeight = "2em"; [ await CKTools.domHelper("label", label => { label.style.paddingLeft = "3px"; label.id = "showav_foldvidwarn_tip"; label.setAttribute('for', "showav_foldvidwarn"); //if (config.foldedWarningTip) //label.innerHTML = "默认 <b>隐藏</b> 视频警告文字(点击切换)"; //else label.innerHTML = "默认 <b>隐藏</b> 视频警告文字"; }), /*await CKTools.domHelper("input", input => { input.type = "checkbox"; input.id = "showav_foldvidwarn"; input.name = "showav_foldvidwarn"; input.style.display = "none"; input.checked = config.foldedWarningTip; input.addEventListener('change', e => { const label = document.querySelector("#showav_foldvidwarn_tip"); if (!label) return; if (input.checked) label.innerHTML = "默认 <b>折叠</b> 视频警告文字(点击切换)"; else label.innerHTML = "默认 <b>展示</b> 视频警告文字(点击切换)"; }) }),*/ await CKTools.domHelper("div", div => { div.style.paddingLeft = "20px"; div.style.color = "#919191"; div.innerHTML = `将视频警告(如 含有危险行为)折叠为图标,防止占用过多信息栏空间。<br>由于新版本播放器适配问题,默认隐藏原版提示。<br>请前往组件管理开启或关闭组件中的警告提示。`; }) ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("li", async list => { list.style.lineHeight = "2em"; [ await CKTools.domHelper("label", label => { label.style.paddingLeft = "3px"; label.id = "showav_forceRemoveAllItem_tip"; label.setAttribute('for', "showav_forceRemoveAllItem"); if (config.forceRemoveAllItem) label.innerHTML = "默认 <b>隐藏</b> 原版所有组件(点击切换)"; else label.innerHTML = "默认 <b>不隐藏</b> 原版所有组件"; }), await CKTools.domHelper("input", input => { input.type = "checkbox"; input.id = "showav_forceRemoveAllItem"; input.name = "showav_forceRemoveAllItem"; input.style.display = "none"; input.checked = config.forceRemoveAllItem; input.addEventListener('change', e => { const label = document.querySelector("#showav_forceRemoveAllItem_tip"); if (!label) return; if (input.checked) label.innerHTML = "默认 <b>隐藏</b> 原版所有组件(点击切换)"; else label.innerHTML = "默认 <b>不隐藏</b> 原版所有组件(点击切换)"; }) }), // await CKTools.domHelper("div", div => { // div.style.paddingLeft = "20px"; // div.style.color = "#919191"; // div.innerHTML = `是否尽量隐藏B站原本信息条中的组件。仅对新版本播放器生效。`; // }) ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("li", sectiontitle=>{ sectiontitle.innerText = "组件: 显示视频分P信息"; sectiontitle.className = "showav_settings_sectiontitle"; }), await CKTools.domHelper("li", async list => { list.style.lineHeight = "2em"; [ await CKTools.domHelper("label", label => { label.style.paddingLeft = "3px"; label.setAttribute('for', "showav_pnwid"); label.innerHTML = "字数限制"; }), await CKTools.domHelper("input", input => { input.type = "number"; input.id = "showav_pnwid"; input.name = "showav_pnwid"; input.setAttribute('min', 5); input.setAttribute('max', 100); input.style.width = "3em"; input.style.textAlign = "center"; input.style.marginLeft = "1em"; input.style.lineHeight = "1em"; input.value = config.pnmaxlength; const updatePreview = () => wait(2).then(() => CKTools.addStyle(` #showav_lengthpreview{ max-width: ${input.value}em !important; } `, "showav_lengthpreviewcss", "update")); input.addEventListener("input", updatePreview); wait(300).then(updatePreview); }), await CKTools.domHelper("span", span => { span.id = "showav_lengthpreview"; span.innerText = "这里是一条长度预览,你可以在这里查看长度限制的效果。好吧,我承认,后面这几个字只是为了凑个字数而已的。等等,你还要更长???相信我,你不会想要这么长的。"; span.style.maxWidth = "0em"; span.style.marginLeft = "30px"; span.style.textOverflow = "ellipsis"; span.style.whiteSpace = "nowarp"; span.style.overflow = "hidden"; span.style.whiteSpace = "nowrap"; span.style.display = "block"; span.style.fontSize = "12px"; span.style.transition = "all .5s"; }), await CKTools.domHelper("div", div => { div.style.paddingLeft = "20px"; div.style.color = "#919191"; div.innerHTML = `限制分P信息显示时的最大长度`; }) ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("li", sectiontitle=>{ sectiontitle.innerText = "组件: 显示视频编号和高级复制"; sectiontitle.className = "showav_settings_sectiontitle"; }), await CKTools.domHelper("li", async list => { list.style.lineHeight = "2em"; [ await CKTools.domHelper("label", label => { label.style.paddingLeft = "3px"; label.id = "showav_defaultav_tip"; label.setAttribute('for', "showav_defaultav"); if (config.defaultAv) label.innerHTML = "默认展示 <b>视频AV号</b> (点击切换)"; else label.innerHTML = "默认展示 <b>视频BV号</b> (点击切换)"; }), await CKTools.domHelper("input", input => { input.type = "checkbox"; input.id = "showav_defaultav"; input.name = "showav_defaultav"; input.style.display = "none"; input.checked = config.defaultAv; input.addEventListener('change', e => { const label = document.querySelector("#showav_defaultav_tip"); if (!label) return; if (input.checked) label.innerHTML = "默认展示 <b>视频AV号</b> (点击切换)"; else label.innerHTML = "默认展示 <b>视频BV号</b> (点击切换)"; }) }), await CKTools.domHelper("div", div => { div.style.paddingLeft = "20px"; div.style.color = "#919191"; div.innerHTML = `仅对<b>可切换视频编号和高级复制</b>功能起效。<br> 可切换视频编号和高级复制组件可以使用右键临时切换显示内容。<br> 高级复制和快速复制默认读取对应组件显示内容,因此此处设置也会影响可切换视频编号组件的默认复制内容。`; }) ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("li", sectiontitle=>{ sectiontitle.innerText = "组件: 显示视频投稿时间"; sectiontitle.className = "showav_settings_sectiontitle"; }), await CKTools.domHelper("li", async list => { list.style.lineHeight = "2em"; [ await CKTools.domHelper("label", label => { label.style.paddingLeft = "3px"; label.id = "showav_hidetime_tip"; label.setAttribute('for', "showav_hidetime"); if (config.hideTime) label.innerHTML = "<b>隐藏</b>原版发布时间 (点击切换)"; else label.innerHTML = "<b>显示</b>原版发布时间 (点击切换)"; }), await CKTools.domHelper("input", input => { input.type = "checkbox"; input.id = "showav_hidetime"; input.name = "showav_hidetime"; input.style.display = "none"; input.checked = config.hideTime; input.addEventListener('change', e => { const label = document.querySelector("#showav_hidetime_tip"); if (!label) return; if (input.checked) label.innerHTML = "<b>隐藏</b>原版发布时间 (点击切换)"; else label.innerHTML = "<b>显示</b>原版发布时间 (点击切换)"; }) }), await CKTools.domHelper("div", div => { div.style.paddingLeft = "20px"; div.style.color = "#919191"; div.innerHTML = `仅在开启<b>视频投稿时间</b>功能时起效。<br> 插件添加的视频投稿时间可以选择显示两种时间格式,并且可排序。`; }) ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("li", async list => { list.style.lineHeight = "2em"; [ await CKTools.domHelper("label", label => { label.style.paddingLeft = "3px"; label.id = "showav_deftxttime_tip"; label.setAttribute('for', "showav_deftxttime"); if (config.defaultTextTime) label.innerHTML = "显示<b>相对时间</b> (点击切换)"; else label.innerHTML = "显示<b>完整时间戳</b> (点击切换)"; }), await CKTools.domHelper("input", input => { input.type = "checkbox"; input.id = "showav_deftxttime"; input.name = "showav_deftxttime"; input.style.display = "none"; input.checked = config.defaultTextTime; input.addEventListener('change', e => { const label = document.querySelector("#showav_deftxttime_tip"); if (!label) return; if (input.checked) label.innerHTML = "显示<b>相对时间</b> (点击切换)"; else label.innerHTML = "显示<b>完整时间戳</b> (点击切换)"; }) }), await CKTools.domHelper("div", div => { div.style.paddingLeft = "20px"; div.style.color = "#919191"; div.innerHTML = `<b>相对时间格式:</b> 如 1周前<br><b>完整时间戳格式:</b> 如 2021-09-10 11:21:03<br>仅对<b>视频投稿时间</b>功能起效。`; }) ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("div", async btns => { btns.style.display = "flex"; btns.style.alignItems = "flex-end"; btns.appendChild(await CKTools.domHelper("button", btn => { btn.className = "CKTOOLS-toolbar-btns"; btn.innerHTML = "保存并返回"; btn.onclick = e => { config.defaultAv = document.querySelector("#showav_defaultav").checked; config.forceGap = document.querySelector("#showav_forcegap").checked; config.hideTime = document.querySelector("#showav_hidetime").checked; config.defaultTextTime = document.querySelector("#showav_deftxttime").checked; config.forceRemoveAllItem = document.querySelector("#showav_forceRemoveAllItem").checked; config.nobreakline = document.querySelector("#showav_nobreakline").checked; config.pnmaxlength = parseInt(document.querySelector("#showav_pnwid").value); config.showInNewLine = document.querySelector("#showav_newline").checked; saveAllConfig(); CKTools.addStyle(``, "showav_lengthpreviewcss", "update"); CKTools.modal.hideModal(); let old = document.querySelector("#bilibiliShowInfos") if (old) old.remove(); initScript(true); wait(300).then(()=>GUISettings()); } })) btns.appendChild(await CKTools.domHelper("button", btn => { btn.className = "CKTOOLS-toolbar-btns"; btn.innerHTML = "返回"; btn.style.background = "#ececec"; btn.style.color = "black"; btn.onclick = e => { CKTools.addStyle(``, "showav_lengthpreviewcss", "update"); CKTools.modal.hideModal(); wait(300).then(()=>GUISettings()); } })) }) ].forEach(e => container.appendChild(e)); })); } async function GUISettings_components() { if (CKTools.modal.isModalShowing()) { CKTools.modal.hideModal(); await wait(300); } CKTools.modal.openModal("ShowAV / 设置 / 组件", await CKTools.domHelper("div", async container => { container.style.alignItems = "stretch"; [ closeButton(), // dragable code from ytb v=jfYWwQrtzzY await CKTools.domHelper("li", async list => { const makeDragable = async id => { return await CKTools.domHelper("div", draggable => { draggable.className = "showav_dragableitem"; draggable.setAttribute("draggable", true); draggable.setAttribute("data-id", id); if (id.split("_")[0] === "custom") { draggable.innerHTML = config.customComponents[id].title; const node = document.createElement("div"); node.appendChild(document.createTextNode(config.customComponents[id].content)); draggable.appendChild(node); }else if (id.split("_")[0] == "sideload") { let ids = id.split("_"); ids.shift(); const modname = ids.join('_'); draggable.innerHTML = getSideloadModules()[modname].name; const node = document.createElement("div"); node.appendChild(document.createTextNode(getSideloadModules()[modname].description??'外挂组件')); draggable.appendChild(node); } else { draggable.innerHTML = txtCn[id]; draggable.innerHTML += `<div>${descCn[id]}</div>`; } let expanded = false; draggable.addEventListener('dragstart', e => { if (expanded) draggable.classList.remove('showav_expand'); draggable.classList.add('showav_dragging'); [...document.querySelectorAll('.showav_dragablediv')].forEach(e => e.classList.add('showav_child_dragging')) }) draggable.addEventListener('dragend', e => { if (expanded) draggable.classList.add('showav_expand'); draggable.classList.remove('showav_dragging'); [...document.querySelectorAll('.showav_child_dragging')].forEach(e => e.classList.remove('showav_child_dragging')) }) draggable.addEventListener('click', e => { expanded = draggable.classList.toggle('showav_expand'); }) }) }; function getClosestItem(container, y) { const draggables = [...container.querySelectorAll(".showav_dragableitem:not(.showav_dragging)")]; return draggables.reduce((closest, child) => { const box = child.getBoundingClientRect(); const offset = y - box.top - box.height / 2; if (offset < 0 && offset > closest.offset) return { offset, element: child }; else return closest; }, { offset: Number.NEGATIVE_INFINITY }).element; } function registerDragEvent(draggablediv) { draggablediv.addEventListener('dragover', e => { e.preventDefault(); const closestElement = getClosestItem(draggablediv, e.clientY); const dragging = document.querySelector(".showav_dragging"); if (closestElement === null) { draggablediv.appendChild(dragging); } else { draggablediv.insertBefore(dragging, closestElement); } }) } [ await CKTools.domHelper("div", div => { div.innerHTML = `<b>拖动下面的功能模块进行排序</b>`; }), await CKTools.domHelper("div", async enableddiv => { enableddiv.innerHTML = `<b>启用</b>`; enableddiv.className = "showav_dragablediv showav_enableddiv"; config.orders.forEach(async k => { enableddiv.appendChild(await makeDragable(k)); }); registerDragEvent(enableddiv); }), await CKTools.domHelper("div", async disableddiv => { disableddiv.innerHTML = `<b>禁用</b>`; disableddiv.className = "showav_dragablediv showav_disableddiv"; const sideloads = getSideloadModules(); const sideloaditems = Object.keys(sideloads).map(k => 'sideload_'+k); [...config.all,...sideloaditems].forEach(async k => { if (config.orders.includes(k)) return; disableddiv.appendChild(await makeDragable(k)); }); registerDragEvent(disableddiv); }), await CKTools.domHelper("div", async div => { div.style.lineHeight = "2em"; div.style.cursor = "pointer"; div.style.color = "#1976d2"; div.style.fontWeight = "bold"; div.innerHTML = `功能设置`; div.onclick = e => GUISettings_options(); }), await CKTools.domHelper("div", async div => { div.style.lineHeight = "2em"; div.style.cursor = "pointer"; div.style.color = "#1976d2"; div.style.fontWeight = "bold"; div.innerHTML = `管理自定义组件`; div.onclick = e => GUISettings_customcomponents(); }), await CKTools.domHelper("div", async div => { div.style.lineHeight = "2em"; 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>` }), await CKTools.domHelper("div", async div => { div.appendChild(await CKTools.domHelper("div", async btns => { btns.style.display = "flex"; btns.appendChild(await CKTools.domHelper("button", btn => { btn.className = "CKTOOLS-toolbar-btns"; btn.innerHTML = "保存并返回"; btn.onclick = e => { const enableddiv = document.querySelector(".showav_enableddiv"); const elements = enableddiv.querySelectorAll(".showav_dragableitem"); let enabledArray = []; for (let element of [...elements]) { enabledArray.push(element.getAttribute('data-id')); } config.orders = enabledArray; saveAllConfig(); CKTools.modal.hideModal(); let old = document.querySelector("#bilibiliShowInfos") if (old) old.remove(); initScript(true); wait(310).then(()=>GUISettings()); } })) btns.appendChild(await CKTools.domHelper("button", btn => { btn.className = "CKTOOLS-toolbar-btns"; btn.innerHTML = "返回"; btn.style.background = "#ececec"; btn.style.color = "black"; btn.onclick = e => { CKTools.modal.hideModal(); wait(310).then(()=>GUISettings()); } })) })) }), ].forEach(e => list.appendChild(e)); }) ].forEach(e => container.appendChild(e)); })); } async function GUISettings_advcopy(back=null) { if (CKTools.modal.isModalShowing()) { CKTools.modal.hideModal(); await wait(300); } CKTools.modal.openModal("ShowAV / 设置 / 快速复制设置", await CKTools.domHelper("div", async container => { container.style.alignItems = "stretch"; [ closeButton(), // dragable code from ytb v=jfYWwQrtzzY await CKTools.domHelper("li", async list => { const makeDragable = async id => { return await CKTools.domHelper("div", draggable => { draggable.className = "showav_dragableitem copyitem"; draggable.setAttribute("draggable", true); draggable.setAttribute("data-id", id); if (id.split("_")[0] === "custom") { draggable.innerHTML = config.customcopyitems[id].title; const node = document.createElement("div"); node.appendChild(document.createTextNode(config.customcopyitems[id].content)); draggable.appendChild(node); } else { draggable.innerHTML = txtCn[id]; draggable.innerHTML += `<div>${descCn[id]}</div>`; } draggable.removeItem = draggable.remove; let expanded = false; draggable.addEventListener('dragstart', e => { if (expanded) draggable.classList.remove('showav_expand'); draggable.classList.add('showav_dragging'); [...document.querySelectorAll('.showav_dragablediv')].forEach(e => e.classList.add('showav_child_dragging')) }) draggable.addEventListener('dragend', e => { if (expanded) draggable.classList.add('showav_expand'); draggable.classList.remove('showav_dragging'); [...document.querySelectorAll('.showav_child_dragging')].forEach(e => e.classList.remove('showav_child_dragging')) }) draggable.addEventListener('click', e => { expanded = draggable.classList.toggle('showav_expand'); }) }) }; function getClosestItem(container, y) { const draggables = [...container.querySelectorAll(".showav_dragableitem:not(.showav_dragging)")]; return draggables.reduce((closest, child) => { const box = child.getBoundingClientRect(); const offset = y - box.top - box.height / 2; if (offset < 0 && offset > closest.offset) return { offset, element: child }; else return closest; }, { offset: Number.NEGATIVE_INFINITY }).element; } function registerDragEvent(draggablediv) { draggablediv.addEventListener('dragover', e => { e.preventDefault(); const closestElement = getClosestItem(draggablediv, e.clientY); const dragging = document.querySelector(".showav_dragging"); if (closestElement === null) { draggablediv.appendChild(dragging); } else { draggablediv.insertBefore(dragging, closestElement); } }) } [ await CKTools.domHelper("div", div => { div.innerHTML = `<b>拖动下面的功能模块进行排序</b>,第一个单项将成为默认快速复制项目。`; }), await CKTools.domHelper("div", async enableddiv => { enableddiv.innerHTML = `<b>启用</b>`; enableddiv.className = "showav_dragablediv showav_enableddiv"; config.copyitems.forEach(async k => { enableddiv.appendChild(await makeDragable(k)); }); registerDragEvent(enableddiv); }), await CKTools.domHelper("div", async disableddiv => { disableddiv.innerHTML = `<b>禁用</b>`; disableddiv.className = "showav_dragablediv showav_disableddiv"; config.copyitemsAll.forEach(async k => { if (config.copyitems.includes(k)) return; disableddiv.appendChild(await makeDragable(k)); }); registerDragEvent(disableddiv); }), await CKTools.domHelper("li", async list => { const makeItem = (copyitemid,focus=false) => { const item = config.customcopyitems[copyitemid]; const node = document.createElement("li"); node.className = "copyitem"; if(focus){ node.classList.add("actionpending"); setTimeout(() => { node.classList.remove("actionpending"); node.scrollIntoView(); },20); } node.setAttribute("data-id", copyitemid); node.innerHTML = `${item.title}<br>`; node.style.borderRadius = "3px"; node.style.border = "solid 2px grey"; node.style.padding = "3px"; node.style.margin = "1px"; const smallp = document.createElement("p"); smallp.style.fontSize = "small"; smallp.style.color = "grey"; smallp.style.overflow = "hidden"; smallp.style.wordWrap = "nowarp"; smallp.appendChild(document.createTextNode(item.content)); node.appendChild(smallp); node.removeItem = ()=>{ node.classList.add("actionpending"); setTimeout(()=>node.remove(),350); }; node.onclick = async e => { if(node.classList.contains("preremove")){ if (config.copyitems.includes(copyitemid)) { config.copyitems.splice(config.copyitems.indexOf(copyitemid), 1); } if (config.copyitemsAll.includes(copyitemid)) { config.copyitemsAll.splice(config.copyitemsAll.indexOf(copyitemid), 1); } delete config.customcopyitems[copyitemid]; saveAllConfig(); [...document.querySelectorAll(`.copyitem[data-id="${copyitemid}"]`)].forEach(e => e.removeItem()); }else{ [...document.querySelectorAll("li.copyitem.preremove")].forEach(e=>{ e.classList.remove("preremove"); try{if(e.clearTimer){ clearTimeout(e.clearTimer); }}catch(e){}; }); node.classList.add("preremove"); node.clearTimer = setTimeout(() => { node.classList.remove("preremove"); node.clearTimer = null; },5000); } } return node; }; [ await CKTools.domHelper("label", label => { label.style.paddingLeft = "3px"; label.style.fontWeight = "bold"; label.innerHTML = "添加自定义复制项目"; }), await CKTools.domHelper("div", async div => { div.style.paddingLeft = "20px"; [ await CKTools.domHelper("input", async input => { input.id = "showav_customcopytitle"; input.setAttribute("type", "text"); input.style.width = "60%"; input.style.margin = "6px 0 0 0"; input.style.padding = "6px"; input.style.borderRadius = "6px"; input.style.border = "solid 2px grey"; input.setAttribute("placeholder", "自定义标题"); }), await CKTools.domHelper("input", async input => { input.id = "showav_customcopycontent"; input.setAttribute("type", "text"); input.style.width = "60%"; input.style.margin = "6px 0 0 0"; input.style.padding = "6px"; input.style.borderRadius = "6px"; input.style.border = "solid 2px grey"; input.setAttribute("placeholder", "自定义内容"); }), await CKTools.domHelper("div", div => { div.style.paddingLeft = "20px"; div.style.color = "#919191"; div.innerHTML = `变量提示<br><ul> <li>%timeurl% => 包含时间的完整地址</li> <li>%vidurl% => 视频纯净地址</li> <li>%shorturl% => 短地址</li> <li>%seek% => 当前视频播放秒数</li> <li>%title% => 视频标题</li> <li>%av% => av号</li> <li>%bv% => BV号</li> <li>%cid% => CID号</li> <li>%p% => 分P</li> <li>%pname% => 分P名</li> <li>%tname% => 分区名</li> </ul>`; div.style.maxHeight = '2rem'; div.style.overflow = 'hidden'; div.style.transition = 'all .3s'; let expanded = false; div.onclick = e => { expanded = !expanded; if (expanded) { div.style.maxHeight = "30rem"; } else { div.style.maxHeight = '2rem'; } } }), await CKTools.domHelper("button", btn => { btn.className = "CKTOOLS-toolbar-btns"; btn.innerHTML = "添加"; btn.style.background = "#ececec"; btn.style.color = "black"; btn.onclick = async e => { const ccid = "custom_" + Math.random().toString(36).replace('.', ''); const title = document.querySelector("#showav_customcopytitle").value; const content = document.querySelector("#showav_customcopycontent").value; if (title.trim().length < 1 || content.trim().length < 1) { popNotify.warn("无法添加自定义项目", "标题或内容为空"); return; } config.customcopyitems[ccid] = { title, content }; if (!config.copyitemsAll.includes(ccid)) config.copyitemsAll.push(ccid); saveAllConfig(); const disablediv = document.querySelector(".showav_disableddiv"); disablediv && disablediv.appendChild(await makeDragable(ccid)); const customlist = document.querySelector("#showav_customitems"); customlist && customlist.appendChild(makeItem(ccid,true)); document.querySelector("#showav_customcopytitle").value = ""; document.querySelector("#showav_customcopycontent").value = ""; } }) ].forEach(e => div.appendChild(e)); }), await CKTools.domHelper("label", label => { label.style.paddingLeft = "3px"; label.style.fontWeight = "bold"; label.innerHTML = "已有自定义复制项目 <small>(点击移除)</small>"; }), await CKTools.domHelper("ul", ul => { ul.style.paddingLeft = "3px"; ul.id = "showav_customitems"; for (let copyitemid of Object.keys(config.customcopyitems)) { ul.appendChild(makeItem(copyitemid)); } }), ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("div", async div => { div.appendChild(await CKTools.domHelper("div", async btns => { btns.style.display = "flex"; btns.appendChild(await CKTools.domHelper("button", btn => { btn.className = "CKTOOLS-toolbar-btns"; btn.innerHTML = "保存并关闭"; if(back!=null) btn.innerHTML = "保存并返回"; btn.onclick = e => { const enableddiv = document.querySelector(".showav_enableddiv"); const elements = enableddiv.querySelectorAll(".showav_dragableitem"); let enabledArray = []; for (let element of [...elements]) { enabledArray.push(element.getAttribute('data-id')); } config.copyitems = enabledArray; saveAllConfig(); initScript(true); if(back!=null) back(); else CKTools.modal.hideModal(); } })) btns.appendChild(await CKTools.domHelper("button", btn => { btn.className = "CKTOOLS-toolbar-btns"; btn.innerHTML = "关闭"; if(back!=null) btn.innerHTML = "返回"; btn.onclick = e => { if(back!=null) back(); else CKTools.modal.hideModal(); } })) })) }), ].forEach(e => list.appendChild(e)); }) ].forEach(e => container.appendChild(e)); })); } async function GUISettings_customcomponents(back=GUISettings_components) { if (CKTools.modal.isModalShowing()) { CKTools.modal.hideModal(); await wait(300); } CKTools.modal.openModal("ShowAV / 设置 / 组件 / 自定义组件", await CKTools.domHelper("div", async container => { container.style.alignItems = "stretch"; [ closeButton(), // dragable code from ytb v=jfYWwQrtzzY await CKTools.domHelper("li", async list => { [ await CKTools.domHelper("li", async list => { const makeItem = (customitemid,focus=false) => { const item = config.customComponents[customitemid]; const node = document.createElement("li"); node.className = "copyitem"; if(focus){ node.classList.add("actionpending"); setTimeout(() => { node.classList.remove("actionpending"); node.scrollIntoView(); },20); } node.setAttribute("data-id", customitemid); node.innerHTML = `${item.title}<br>`; node.style.borderRadius = "3px"; node.style.border = "solid 2px grey"; node.style.padding = "3px"; node.style.margin = "1px"; const smallp = document.createElement("p"); smallp.style.fontSize = "small"; smallp.style.color = "grey"; smallp.style.overflow = "hidden"; smallp.style.wordWrap = "nowarp"; smallp.appendChild(document.createTextNode(item.content)); node.appendChild(smallp); node.removeItem = ()=>{ node.classList.add("actionpending"); setTimeout(()=>node.remove(),350); }; node.onclick = async e => { if(node.classList.contains("preremove")){ if (config.orders.includes(customitemid)) { config.orders.splice(config.orders.indexOf(customitemid), 1); } if (config.all.includes(customitemid)) { config.all.splice(config.all.indexOf(customitemid), 1); } delete config.customComponents[customitemid]; saveAllConfig(); [...document.querySelectorAll(`.copyitem[data-id="${customitemid}"]`)].forEach(e => e.removeItem()); }else{ [...document.querySelectorAll("li.copyitem.preremove")].forEach(e=>{ e.classList.remove("preremove"); try{if(e.clearTimer){ clearTimeout(e.clearTimer); }}catch(e){}; }); node.classList.add("preremove"); node.clearTimer = setTimeout(() => { node.classList.remove("preremove"); node.clearTimer = null; },5000); } } return node; }; [ await CKTools.domHelper("label", label => { label.style.paddingLeft = "3px"; label.style.fontWeight = "bold"; label.innerHTML = "添加组件"; }), await CKTools.domHelper("div", async div => { div.style.paddingLeft = "20px"; [ await CKTools.domHelper("input", async input => { input.id = "showav_customcopntitle"; input.setAttribute("type", "text"); input.style.width = "60%"; input.style.margin = "6px 0 0 0"; input.style.padding = "6px"; input.style.borderRadius = "6px"; input.style.border = "solid 2px grey"; input.setAttribute("placeholder", "自定义显示文本"); input.addEventListener("keydown",e=>{ const contentel = document.querySelector("#showav_customcopncontent"); if(!contentel) return; if(contentel.getAttribute("data-sync")!=="1") return; setTimeout(()=>contentel.value = input.value,10); }) }), await CKTools.domHelper("input", async input => { input.id = "showav_customcopncontent"; input.setAttribute("type", "text"); input.style.width = "60%"; input.style.margin = "6px 0 0 0"; input.style.padding = "6px"; input.style.borderRadius = "6px"; input.style.border = "solid 2px grey"; input.title = `默认与自定义显示文本同步\n使用"js:"开头时将在点击时执行脚本`; input.setAttribute("data-sync","1"); input.setAttribute("placeholder", "自定义复制内容或脚本"); input.addEventListener("keydown",e=>input.setAttribute("data-sync","0")); input.addEventListener("keydown",async e=>{ await wait(1); if(input.value.startsWith("js:")){ if(config.jssafetyWarning){ config.jssafetyWarning = !confirm(`安全性警告:\n\n"js:"开头的内容将作为JS脚本执行。\n\nJS脚本拥有您在当前页面的所有权限,请勿复制和执行未知来源的脚本!\n请仅在了解你输入的内容情况下使用此功能!\n\n如果不点击确定,则每次输入"js:"时都会弹出此消息。\n\n继续输入吗?`); if(config.jssafetyWarning){ saveAllConfig(); }else{ input.value = input.value.replace("js:",""); } }else{ document.querySelector("#showav_custom_txttip").style.display = "none"; document.querySelector("#showav_custom_jstip").style.display = "block"; } }else{ document.querySelector("#showav_custom_jstip").style.display = "none"; document.querySelector("#showav_custom_txttip").style.display = "block"; } }) }), await CKTools.domHelper("div", div => { div.style.paddingLeft = "20px"; div.id = "showav_custom_txttip"; div.style.color = "#919191"; div.innerHTML = `变量提示<br><ul> <li>%timeurl% => 包含时间的完整地址</li> <li>%vidurl% => 视频纯净地址</li> <li>%shorturl% => 短地址</li> <li>%seek% => 当前视频播放秒数</li> <li>%title% => 视频标题</li> <li>%av% => av号</li> <li>%bv% => BV号</li> <li>%cid% => CID号</li> <li>%p% => 分P</li> <li>%pname% => 分P名</li> <li>%tname% => 分区名</li> </ul>`; div.style.maxHeight = '2rem'; div.style.overflow = 'hidden'; div.style.transition = 'all .3s'; let expanded = false; div.onclick = e => { expanded = !expanded; if (expanded) { div.style.maxHeight = "30rem"; } else { div.style.maxHeight = '2rem'; } } }), await CKTools.domHelper("div", div => { div.style.paddingLeft = "20px"; div.id = "showav_custom_jstip"; div.style.display = "none"; div.style.color = "#919191"; div.innerHTML = `脚本提示<br><ul> <li>变量 infos => 视频信息</li> <li>方法 parseTxt("string") => 解析文本</li> <li>方法 copy("string") => 复制文字</li> </ul>`; div.style.maxHeight = '2rem'; div.style.overflow = 'hidden'; div.style.transition = 'all .3s'; let expanded = false; div.onclick = e => { expanded = !expanded; if (expanded) { div.style.maxHeight = "30rem"; } else { div.style.maxHeight = '2rem'; } } }), await CKTools.domHelper("button", btn => { btn.className = "CKTOOLS-toolbar-btns"; btn.innerHTML = "添加"; btn.style.background = "#ececec"; btn.style.color = "black"; btn.onclick = async e => { const ccid = "custom_" + Math.random().toString(36).replace('.', ''); const title = document.querySelector("#showav_customcopntitle").value; const content = document.querySelector("#showav_customcopncontent").value; if (title.trim().length < 1 || content.trim().length < 1) { popNotify.warn("无法添加自定义组件", "标题或内容为空"); return; } config.customComponents[ccid] = { title, content }; if (!config.all.includes(ccid)) config.all.push(ccid); saveAllConfig(); const customlist = document.querySelector("#showav_customitems"); customlist && customlist.appendChild(makeItem(ccid,true)); document.querySelector("#showav_customcopntitle").value = ""; document.querySelector("#showav_customcopncontent").value = ""; } }) ].forEach(e => div.appendChild(e)); }), await CKTools.domHelper("label", label => { label.style.paddingLeft = "3px"; label.style.fontWeight = "bold"; label.innerHTML = "已有自定义组件 <small>(点击移除)</small>"; }), await CKTools.domHelper("ul", ul => { ul.style.paddingLeft = "3px"; ul.id = "showav_customitems"; for (let itemid of Object.keys(config.customComponents)) { ul.appendChild(makeItem(itemid)); } }), ].forEach(e => list.appendChild(e)); }), await CKTools.domHelper("label", label => { label.style.width = "100%"; label.style.display = "block"; label.style.textAlign = "center"; label.innerHTML = "此页面内容自动保存"; }), await CKTools.domHelper("div", async div => { div.appendChild(await CKTools.domHelper("div", async btns => { btns.style.display = "flex"; btns.appendChild(await CKTools.domHelper("button", btn => { btn.className = "CKTOOLS-toolbar-btns"; btn.innerHTML = "返回"; btn.onclick = e => { saveAllConfig(); back(); } })) btns.appendChild(await CKTools.domHelper("button", btn => { btn.className = "CKTOOLS-toolbar-btns"; btn.innerHTML = "关闭"; btn.onclick = e => { saveAllConfig(); CKTools.modal.hideModal(); } })) })) }), ].forEach(e => list.appendChild(e)); }) ].forEach(e => container.appendChild(e)); })); } const copy = function copy(text) { if (!navigator.clipboard) { prompt('请手动复制', text); return; } navigator.clipboard.writeText(text).then(function () { log('Copy OK'); }, function (err) { log('Auto Copy Failed:', err); prompt('请手动复制', text); }); } unsafeWindow.showav_fastcopy = (el) => { copy(el.value); popNotify.success("复制成功", el.value); } unsafeWindow.showav_guisettings = GUISettings; unsafeWindow.showav_guisettings_advcopy = GUISettings_advcopy; unsafeWindow.showav_guisettings_customcomponents = GUISettings_customcomponents; CKTools.modal.initModal(); CKTools.modal.hideModal(); const blockwin = CKTools.get("#CKTOOLS-blockWindow"); blockwin&&(blockwin.onclick = CKTools.modal.hideModal); CKTools.addStyle(` #CKTOOLS-modal{ width: fit-content!important; max-width: 80%!important; } .CKTOOLS-modal-content li label b { color: #1976d2!important; } .showav_menuitem{ line-height: 2em; width: 100%; transition: all .3s; cursor: pointer; } .showav_menuitem:hover{ transform: translateX(6px); } .showav_menuitem>label{ color: #1976d2; font-weight: bold; font-size: large; display: block; } .showav_dragablediv { width: 400px; max-width: 80%; max-width: 400px; min-height: 60px; border: dotted; border-radius: 8px; padding: 12px; margin: 5px; position: relative; margin: 3px auto; } .showav_dragableitem { background: white; margin: 3px; padding: 3px; border-radius: 4px; border: solid #bdbdbd 2px; color: black; transition: all .3s; max-height: 2rem; } .showav_dragableitem.showav_expand { max-height: 8rem; } .showav_dragableitem>div { color: #adadad; margin: 0 6px; opacity: 0; transition: all .3s ease-in-out; transform: translateX(-10px); font-size: small; overflow: hidden; max-height: 0; } .showav_dragableitem.showav_expand>div{ transform: translateX(0px); max-height: 8rem; opacity: 1; } .showav_dragableitem::before { content: "⋮⋮"; float: right; font-size: xx-small; padding: 3px; color: #bbbbbb !important; } .showav_dragging { background: grey; color: white; border: solid #515050 2px; transform: scale(1.1); transition: all .3s; } .showav_dragablediv:not(.showav_child_dragging) .showav_dragableitem:hover:not(.showav_dragging) { background: grey; color: white; border: solid #515050 2px; transform: scale(1.03); transition: all .3s; } .showav_dragablediv>b { position: absolute; left: -4rem; } .showav_disableddiv .showav_dragableitem { color: #a9a8a8; } .showav_enableddiv{ background: #dcedc8; } .showav_disableddiv{ background: #ffcdd2; } .showav_settings_sectiontitle{ display: block; width: 100%; font-weight: bold; color: #1976d2; border-bottom: 2px solid #1976d2; margin: 18px 0 3px 0; } .showav_settings_sectiontitle:first-of-type{ margin-top: 0!important; } #showav_newlinetip{ font-size: small; display: inline-block; padding: 0 2px; line-height: 1.5em; border-radius: 3px; background: #ff5722; color: white; overflow: hidden; transition: all .3s; opacity: 0; } #showav_newlinetip.showav_newlinetip_ok{ background: #0288d1!important; } #showav_newlinetip.showav_newlinetip{ opacity: 1; } ul#showav_customitems{ min-height: 60px; } ul#showav_customitems::after{ content:"目前没有自定义项目。当添加了自定义项目时,可以在这里删除。"; padding: 6px; display: block; opacity: 0; transition: all.3s; overflow: hidden; height: 0px; } ul#showav_customitems:empty::after{ opacity: 1; height: 4rem!important; } li.copyitem{ transition: all 0.3s; opacity: 1; max-height: 8em; } li.copyitem.preremove{ color: red!important; border-color: red!important; } li.copyitem::after{ transition: all 0.3s; line-height: 0px!important; content:"再次点击以移除"; display: block; overflow: hidden; color: red!important; opacity: 0; max-height: 8em; } li.copyitem.actionpending{ transition: all 0.5s; padding: 0px!important; border-width: 0px; margin-top: 0px!important; margin-bottom: 0px!important; max-height: 0em!important; opacity: 0; } li.copyitem.preremove::after{ line-height: 2rem!important; opacity: 1; } #bilibiliShowInfos { display: flex; column-gap: 12px; flex-wrap: wrap; } `, 'showav_dragablecss', "unique", document.head); CKTools.addStyle(` .video-info-detail-list{ display: none!important; } #bilibiliShowInfos{ white-space: nowrap !important; } #CKTOOLS-modal li, #CKTOOLS-modal ul{ list-style: none !important; } `,'showav_css_patch', 'unique', document.head); console.log('ShowAV loaded') initScript(false); })();