inQ_Beta wants to fix some of bilibili problem
// ==UserScript== // @name qb-fix-bilibili // @version 0.0.24 // @description inQ_Beta wants to fix some of bilibili problem // @license Apache-2.0 // @author inQ_Beta // @match https://*.bilibili.com/* // @grant GM_addStyle // @grant GM_notification // @grant unsafeWindow // @namespace no1xsyzy // ==/UserScript== (function() { "use strict"; function trace(description, center) { return center; } const $ = (x) => document.querySelector(x); function betterSelector(parentNode2, selector2) { const className = /^\.([\w_-]+)$/.exec(selector2); if (className) { return { select: () => trace(`betterSelector("${selector2}").select#class`, parentNode2.getElementsByClassName(className[1])[0]), selectAll: () => trace( `betterSelector("${selector2}").selectAll#class`, Array.from(parentNode2.getElementsByClassName(className[1])) ) }; } const elementID = /^#([\w_-]+)$/.exec(selector2); if (elementID) { return { select: () => trace(`betterSelector("${selector2}").select#id=${elementID[1]}`, document.getElementById(elementID[1])), selectAll: () => trace(`betterSelector("${selector2}").selectAll#id=${elementID[1]}`, [ document.getElementById(elementID[1]) ]) }; } const tagName = /^([\w_-]+)$/.exec(selector2); if (tagName) { return { select: () => trace(`betterSelector("${selector2}").select#tag`, parentNode2.getElementsByTagName(tagName[1])[0]), selectAll: () => trace( `betterSelector("${selector2}").selectAll#tag`, Array.from(parentNode2.getElementsByTagName(tagName[1])) ) }; } return { select: () => trace(`betterSelector("${selector2}").select#qs`, parentNode2.querySelector(selector2)), selectAll: () => trace(`betterSelector("${selector2}").selectAll#qs`, Array.from(parentNode2.querySelectorAll(selector2))) }; } function selectAncestor(childNode, selector2) { let p = childNode; while (p != null && !p.matches(selector2)) { p = p.parentElement; } return p; } function launchObserver({ parentNode: parentNode2, selector: selector2, failCallback = null, successCallback = null, stopWhenSuccess = true, config = { childList: true, subtree: true } }) { if (!parentNode2) { parentNode2 = document; } const { select, selectAll } = betterSelector(parentNode2, selector2); let _connected = false; const off = () => { if (_connected) { observer.takeRecords(); observer.disconnect(); _connected = false; } }; const on = () => { if (!_connected) { observer.observe(parentNode2, config); _connected = true; } }; const connected = () => _connected; const reroot = (newParentNode) => { parentNode2 = newParentNode; }; const wrapped = { on, off, connected, reroot }; const observeFunc = (mutationList) => { const selected = select(); if (!selected) { if (failCallback) { failCallback({ ...wrapped, mutationList }); } return; } if (stopWhenSuccess) { off(); } if (successCallback) { const maybePromise = successCallback({ ...wrapped, selected, selectAll, mutationList }); if (maybePromise instanceof Promise) { maybePromise.then(() => { }); } } }; const observer = new MutationObserver(observeFunc); on(); return wrapped; } function attrChange({ node, attributeFilter, callback, once = true }) { let _connected = false; let _resolve; const promise = new Promise((resolve) => { _resolve = resolve; }); const wrapped = { on() { if (_connected) return; _connected = true; observer.observe(node, { attributeFilter, attributeOldValue: true }); }, off() { if (!_connected) return; _connected = false; observer.disconnect(); }, connected() { return _connected; }, reroot(x) { if (_connected) { wrapped.off(); node = x; wrapped.on(); } else { node = x; } }, then(onfulfill, onrejected) { return promise.then(onfulfill, onrejected); } }; const observer = new MutationObserver((mutationList) => { if (once) { wrapped.off(); } callback(mutationList, wrapped); _resolve(mutationList[0].attributeName); }); wrapped.on(); return wrapped; } function elementEmerge(selector2, parentNode2, subtree = true) { const g = betterSelector(parentNode2 ?? document, selector2).select(); if (g) return Promise.resolve(g); return new Promise((resolve) => { launchObserver({ parentNode: parentNode2, selector: selector2, successCallback: ({ selected }) => { resolve(selected); }, config: { subtree, childList: true } }); }); } const waitAppBodyMount = async function() { const appBody = betterSelector(document, `.app-body`).select(); if (!appBody) { throw new Error("activity page"); } await new Promise((resolve) => { launchObserver({ parentNode: appBody, selector: `#sections-vm`, successCallback: ({ selected }) => { resolve(null); }, config: { childList: true } }); }); return appBody; }(); async function 关注栏尺寸() { GM_addStyle(` .section-content-cntr{height:calc(100vh - 250px)!important;} .follow-cntr{height:calc(100vh - 150px)!important;} .follow-cntr>.anchor-list{height:auto!important;} .follow-cntr>.anchor-list>.three-anchor{height:auto!important;} `); const sidebarVM = await (async () => { if (location.pathname === "/") { return betterSelector(document, `.flying-vm`).select(); } else if (location.pathname === "/p/eden/area-tags") { return betterSelector(document, `#area-tags`).select(); } else if (/^(?:\/blanc)?\/(\d+)$/.exec(location.pathname)) { const appBody = await waitAppBodyMount; return betterSelector(appBody, `#sidebar-vm`).select(); } })(); const sidebarPopup = await elementEmerge(`.side-bar-popup-cntr`, sidebarVM); launchObserver({ parentNode: sidebarPopup, selector: `*`, successCallback: ({ mutationList }) => { if (sidebarPopup.style.height !== "0px") { sidebarPopup.style.bottom = "75px"; sidebarPopup.style.height = "calc(100vh - 150px)"; } setTimeout(() => $(`.side-bar-popup-cntr.ts-dot-4 .ps`)?.dispatchEvent(new Event("scroll")), 2e3); }, stopWhenSuccess: false, config: { attributes: true, attributeFilter: ["class"] } }); } const makeTitle$1 = () => `${($(`#area-tags header img+div`) || $(`#area-tags header h2`)).innerText} - 分区列表 - 哔哩哔哩直播`; const parentNode$3 = $(`#area-tags`); const selector$4 = `header`; function 分区标题() { launchObserver({ parentNode: parentNode$3, selector: selector$4, successCallback: () => { document.title = makeTitle$1(); }, stopWhenSuccess: false }); document.title = makeTitle$1(); } const followersTextClass = (followers) => { if (followers > 1e6) { return [`${Math.round(followers / 1e5) / 10}m★`, "followers-m"]; } else if (followers > 1e3) { return [`${Math.round(followers / 100) / 10}k★`, "followers-k"]; } else { return [`${followers}★`, "followers-1"]; } }; function defaultCacheStorageFactory(id) { let store = []; const get = (key) => store.filter(([k, t, v]) => k === key).map(([k, t, v]) => [t, v])[0] ?? [0, void 0]; const set = (key, time, value) => { const i = store.findIndex(([k, t, v]) => k === key); if (i === -1) { store.push([key, time, value]); } else { store[i] = [key, time, value]; } }; const cleanup = (ttl, now) => { store = store.filter(([k, t]) => t + ttl > now); }; return { get, set, cleanup }; } function timedLRU(func, { id, version = 1, ttl = 10 * 60 * 1e3, cleanupInterval = 60 * 1e3, cacheStorageFactory = defaultCacheStorageFactory }) { const cacheStorage = cacheStorageFactory(id, version); let timeout = null; const cleanup = () => { if (timeout !== null) { clearTimeout(timeout); } cacheStorage.cleanup(ttl, (/* @__PURE__ */ new Date()).getTime()); timeout = setTimeout(cleanup, cleanupInterval); }; setTimeout(cleanup, cleanupInterval / 10); const wrapped = async (k) => { const t = (/* @__PURE__ */ new Date()).getTime(); let [_, v] = cacheStorage.get(k); if (v === void 0) { v = await func(k); } cacheStorage.set(k, t, v); return v; }; wrapped.cleanup = cleanup; return wrapped; } function localStorageCacheStorageFactory(id, version) { const get = (key) => JSON.parse(localStorage.getItem(`cacheStore__${id}__${version}__${key}`)) ?? [0, void 0]; const set = (key, time, value) => { localStorage.setItem(`cacheStore__${id}__${version}__${key}`, JSON.stringify([time, value])); }; const cleanup = (ttl, now) => { for (let i = 0; i < localStorage.length; i++) { const k = localStorage.key(i); if (!k.startsWith(`cacheStore__${id}__`)) { continue; } if (!k.startsWith(`cacheStore__${id}__${version}__`)) ; const [t, _] = JSON.parse(localStorage.getItem(k)); if (t + ttl < now) { localStorage.removeItem(k); } } }; return { get, set, cleanup }; } const getCard = timedLRU( async (uid) => { const json = await (await fetch(`https://api.bilibili.com/x/web-interface/card?mid=${uid}`, { // credentials: 'include', headers: { Accept: "application/json" }, method: "GET", mode: "cors" })).json(); if (json.code !== 0) { throw json.message; } const { data: { card: { fans, sex } } } = json; return { fans, gender: sex }; }, { id: "getCard", version: 2, ttl: 86400 * 1e3, cacheStorageFactory: localStorageCacheStorageFactory } ); const getFansCount = async (uid) => { return (await getCard(uid)).fans; }; const getSexTag = async (uid) => { const { gender } = await getCard(uid); switch (gender) { case "男": return "♂"; case "女": return "♀"; default: return "〼"; } }; const getInfoByRoom = timedLRU( async (roomid) => { const json = await (await fetch(`https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=${roomid}`, { // credentials: 'include', headers: { Accept: "application/json" }, method: "GET", mode: "cors" })).json(); if (json.code !== 0) { throw json.message; } const followers = json.data.anchor_info.relation_info.attention; return { followers }; }, { id: "getInfoByRoom", version: 2, ttl: 86400 * 1e3, cacheStorageFactory: localStorageCacheStorageFactory } ); const getRoomFollowers = async (roomid) => { return (await getInfoByRoom(roomid)).followers; }; async function* getDynamicFeed({ timezone = -480, type = "all" }) { let page = 1; let json = await (await fetch( `https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?timezone_offset=${timezone}&type=${type}&page=${page}`, { credentials: "include", headers: { Accept: "application/json" }, method: "GET", mode: "cors" } )).json(); if (json.code === 0) { for (const item of json.data.items) { yield item; } } else { throw json.message; } while (json.data.has_more) { page += 1; json = await (await fetch( `https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?timezone_offset=${timezone}&type=${type}&offset=${json.data.offset}&page=${page}`, { credentials: "include", headers: { Accept: "application/json" }, method: "GET", mode: "cors" } )).json(); if (json.code === 0) { for (const item of json.data.items) { yield item; } } else { throw json.message; } } } function compareDynamicID(a, b) { if (a === b) return 0; if (a.length < b.length) return -1; if (a.length > b.length) return 1; if (a < b) return -1; if (a > b) return 1; } function recordDynamicFeed(spec) { const registry = []; const gen = getDynamicFeed(spec); const extend = async (n = 1) => { for (let i = 0; i < n; i++) { let v; try { v = (await gen.next()).value; } catch (err) { } if (v) { registry.push(v); } else { return false; } } return true; }; const getByIndex = async (i) => { while (registry.length < i) { if (!await extend()) break; } if (registry.length > i) { return registry[i]; } }; const lastVisibleDynamic = () => { for (let i = registry.length - 1; i >= 0; i--) { if (registry[i].visible) { return registry[i]; } } return null; }; const getByDynamicID = async (did) => { if (!registry.length && !await extend()) { return null; } do { if (registry[registry.length - 1].id_str === did) { return registry[registry.length - 1]; } if (compareDynamicID(lastVisibleDynamic().id_str, did) < 0) { for (const dyn of registry) { if (dyn.id_str === did) { return dyn; } } return null; } } while (await extend()); }; const getByBVID = async (bvid) => { if (spec.type === "article") { return null; } for (const dyn of registry) { if (dyn.modules.module_dynamic.major.archive.bvid === bvid) { return dyn; } } do { if (lastVisibleDynamic()?.modules.module_dynamic.major.archive.bvid === bvid) { return lastVisibleDynamic(); } } while (await extend()); return null; }; return { getByIndex, getByDynamicID, getByBVID }; } const CARDCLS$1 = "Item_1EohdhbR"; const NAMECLS = "Item_QAOnosoB"; const parentNode$2 = $(`#area-tag-list`); const selector$3 = `.${CARDCLS$1}`; GM_addStyle(` .${NAMECLS}.processed::after { content: attr(data-followers); } .${NAMECLS}.processed.followers-m { color: purple !important; } .${NAMECLS}.processed.followers-k { color: grey !important; } .${NAMECLS}.processed.followers-1 { color: red !important; } `); function 分区添加粉丝数() { launchObserver({ parentNode: parentNode$2, selector: selector$3, successCallback: ({ selectAll }) => { for (const card of selectAll()) { (async () => { const nametag = card.querySelector(`.${NAMECLS}`); if (nametag.classList.contains("processed")) { return; } const followers = await getRoomFollowers(card.pathname.slice(1)); const [txt, cls] = followersTextClass(followers); nametag.dataset.followers = txt; nametag.title = txt; nametag.classList.add("processed"); nametag.classList.add(cls); })(); } }, stopWhenSuccess: false }); } const CARDCLS = "Item_1EohdhbR"; const TITLECLS = "Item_2GEmdhg6"; const parentNode$1 = $(`#area-tag-list`); const selector$2 = `.${CARDCLS}`; function 分区卡片直播间标题指向() { launchObserver({ parentNode: parentNode$1, selector: selector$2, successCallback: ({ selectAll }) => { for (const card of selectAll()) { (async () => { const titletag = card.querySelector(`.${TITLECLS}`); titletag.title = titletag.textContent.trim(); })(); } }, stopWhenSuccess: false }); } function 分区() { 关注栏尺寸(); 分区标题(); 分区添加粉丝数(); 分区卡片直播间标题指向(); } function liveStatus() { const liveStatus2 = betterSelector(document, `.live-status`).select().innerText; switch (liveStatus2) { case "直播": return "▶️"; case "闲置": return "⏹️"; case "轮播": return "🔁"; default: return `【${liveStatus2}】`; } } const liveTitle = () => betterSelector(document, `.live-title`).select().innerText; const liveHost = () => betterSelector(document, `.room-owner-username`).select().innerText; const makeTitle = () => `${liveStatus()} ${liveTitle()} - ${liveHost()} - 哔哩哔哩直播`; const selector$1 = `.live-title`; async function 直播间标题() { launchObserver({ parentNode: await elementEmerge(`#head-info-vm .left-header-area`), selector: selector$1, successCallback: () => { document.title = makeTitle(); }, stopWhenSuccess: false }); document.title = makeTitle(); } function 通用表情框尺寸修复() { GM_addStyle(` #control-panel-ctnr-box > .border-box.top-left[style^="transform-origin: 249px "], #control-panel-ctnr-box > .border-box.top-left[style^="transform-origin: 251px "] { height: 500px } `); } const parentNode = betterSelector(document, `#chat-items`).select(); const selector = `.user-name`; GM_addStyle(` .infoline::before{ content: attr(data-infoline); } .infoline.followers-m::before{ color: purple; } .infoline.followers-k::before{ color: red; } `); const append = async (un) => { const uid = selectAncestor(un, `.chat-item`).dataset.uid; const fans = await getFansCount(uid); const [txt, cls] = followersTextClass(fans); const sextag = await getSexTag(uid); un.dataset.infoline = `${sextag} ${txt} `; if (cls !== "") un.classList.add(cls); }; function 直播间留言者显示粉丝数() { launchObserver({ parentNode, selector, successCallback: ({ selectAll }) => { for (const un of selectAll()) { if (!un.classList.contains("infoline")) { un.classList.add("infoline"); append(un); } } }, stopWhenSuccess: false }); } async function 自动刷新崩溃直播间() { await new Promise((resolve) => setTimeout(resolve, 5e3)); const player = betterSelector(document, `#live-player`).select(); const video = elementEmerge(`video`, player, false).then((x) => void 0); const endingPanel = elementEmerge(`.web-player-ending-panel`, player, false).then( (x) => void 0 ); const errorPanel = elementEmerge(`.web-player-error-panel`, player, false).then((x) => void 0); const last = await Promise.race([video, endingPanel, errorPanel]); if (last.tagName === "VIDEO") ; else if (last.classList.contains("web-player-error-panel")) { location.reload(); } } function define(name, defaultValue) { const v = localStorage.getItem(name); if (v === null) { localStorage.setItem(name, JSON.stringify(defaultValue)); } return { get() { return localStorage.getItem(name); }, set(v2) { return localStorage.setItem(name, JSON.stringify(v2)); } }; } const oldFetch = unsafeWindow.fetch; const middlewares = []; unsafeWindow.fetch = function(input, init = {}) { const request = new Request(input, init); return processFetchWithMiddlewares(middlewares)(request); }; const processFetchWithMiddlewares = function(middlewares2) { if (middlewares2.length === 0) { return oldFetch; } const [head, ...tail] = middlewares2; const next = processFetchWithMiddlewares(tail); if (head.type === "before") { return (input) => next(head.func(input)); } else if (head.type === "after") { return (input) => head.func(next(input)); } else if (head.type === "wrap") { return (input) => head.func(next, input); } }; function hijack(m) { middlewares.push(m); } const MCDN_RE = /[xy0-9]+\.mcdn\.bilivideo\.cn:\d+/; const QUALITY_SUFFIX_RE = /(\d+)_(?:minihevc|prohevc|bluray)/g; const disableMcdn = define("disableMcdn", true); let forceHighestQuality = true; let recentErrors = 0; setInterval(() => { if (recentErrors > 0) { recentErrors /= 2; } }, 1e4); function healthChecker(promise) { promise.then((response) => { if (!response.url.match(/\.(m3u8|m4s)/)) return; if (response.status >= 400 && response.status < 500) { recentErrors++; if (recentErrors >= 5 && forceHighestQuality) { forceHighestQuality = false; GM_notification({ title: "真原画", text: "最高清晰度可能不可用,取消强制", timeout: 3e3, silent: true }); } } }); } function 真原画() { hijack({ type: "wrap", func: async (fetch2, req) => { try { if (MCDN_RE.test(req.url) && disableMcdn.get()) { return Promise.reject(new Error()); } if (QUALITY_SUFFIX_RE.test(req.url) && forceHighestQuality) { req = new Request(req.url.replace(QUALITY_SUFFIX_RE, "$1"), req); } const url = req.url; const promise = fetch2(req); healthChecker(promise); return promise; } catch (e) { } return fetch2(req); } }); } function 直播间() { 关注栏尺寸(); 直播间标题(); 直播间留言者显示粉丝数(); 通用表情框尺寸修复(); 自动刷新崩溃直播间(); 真原画(); } function 直播主页() { 关注栏尺寸(); } async function 分离视频类型() { GM_addStyle(` .qfb__subselect_list {display: none;} .bili-dyn-list-tabs__item.active:nth-child(2) ~ .qfb__subselect_list { display: flex; } #qfb_upload, #qfb_live_replay { display: none; } #qfb_upload:not(:checked) ~ .bili-dyn-list-tabs .qfb_upload::before {content:"☐"} #qfb_upload:checked ~ .bili-dyn-list-tabs .qfb_upload::before {content:"☑"} #qfb_upload:not(:checked) ~ .bili-dyn-list .qfb_upload { display: none; } #qfb_live_replay:not(:checked) ~ .bili-dyn-list-tabs .qfb_live_replay::before {content:"☐"} #qfb_live_replay:checked ~ .bili-dyn-list-tabs .qfb_live_replay::before {content:"☑"} #qfb_live_replay:not(:checked) ~ .bili-dyn-list .qfb_live_replay { display: none; } `); const listTabs = await elementEmerge(`.bili-dyn-list-tabs`); const cmbUpload = document.createElement("input"); cmbUpload.setAttribute("type", "checkbox"); cmbUpload.setAttribute("id", "qfb_upload"); cmbUpload.checked = true; listTabs.before(cmbUpload); const cmbLiveReplay = document.createElement("input"); cmbLiveReplay.setAttribute("type", "checkbox"); cmbLiveReplay.setAttribute("id", "qfb_live_replay"); cmbLiveReplay.checked = true; listTabs.before(cmbLiveReplay); const listTabsUpload = await elementEmerge(`.bili-dyn-list-tabs__item:nth-child(2)`, listTabs); const subSelect = document.createElement("div"); subSelect.classList.add("bili-dyn-list-tabs"); subSelect.classList.add("qfb__subselect_list"); subSelect.innerHTML = ` <div class="bili-dyn-list-tabs__list"> <label class="bili-dyn-list-tabs__item qfb_upload" for="qfb_upload"> 投稿视频 </label> <label class="bili-dyn-list-tabs__item qfb_live_replay" for="qfb_live_replay"> 直播回放 </label> </div> `; listTabs.after(subSelect); attrChange({ node: listTabsUpload, attributeFilter: ["class"], callback: () => { if (listTabsUpload.classList.contains("active")) { subSelect.style.display = "flex"; } else { subSelect.style.display = "none"; } }, once: false }); listTabs.addEventListener("click", (e) => { listTabsUpload.classList.contains(""); if (e.target === listTabsUpload) { subSelect.style.display = "flex"; } else { subSelect.style.display = "none"; } }); const dynList = await elementEmerge(`.bili-dyn-list__items`); launchObserver({ parentNode: dynList, selector: `.bili-dyn-list__item`, successCallback: ({ selectAll }) => { for (const div of selectAll()) { if (div.classList.contains("processed")) continue; const type = div.getElementsByClassName(`bili-dyn-card-video__badge`)[0]?.textContent.trim(); switch (type) { case "直播回放": div.classList.add("qfb_live_replay"); break; case "合作视频": case "投稿视频": div.classList.add("qfb_upload"); break; } div.classList.add("processed"); } }, config: { childList: true } }); } async function 动态首页联合投稿具名() { const record = recordDynamicFeed({ type: "video" }); launchObserver({ parentNode: document.body, selector: `.bili-dyn-item`, successCallback: async ({ selectAll }) => { for (const dynItem of selectAll()) { if (dynItem.dataset.qfb_expanded_did === "processing") { return; } if (dynItem.dataset.qfb_expanded_did && !dynItem.querySelector(`.bili-dyn-item-fold`)) { dynItem.dataset.qfb_expanded_did = "processing"; const dyn = await record.getByDynamicID(dynItem.querySelector(`.bili-dyn-card-video`).getAttribute("dyn-id")); const timediv = dynItem.querySelector(`.bili-dyn-time`); timediv.innerHTML = `${dyn.modules.module_author.pub_time} · ${dyn.modules.module_author.pub_action}`; delete dynItem.dataset.qfb_expanded_did; } else if (!dynItem.dataset.qfb_expanded_did && dynItem.querySelector(`.bili-dyn-item-fold`)) { dynItem.dataset.qfb_expanded_did = "processing"; const dyn = await record.getByDynamicID(dynItem.querySelector(`.bili-dyn-card-video`).getAttribute("dyn-id")); const timediv = dynItem.querySelector(`.bili-dyn-time`); if (!dyn.modules.module_fold) return; const description = (await Promise.all(dyn.modules.module_fold.ids.map((did) => record.getByDynamicID(did)))).map((dyn2) => `<a href="${dyn2.modules.module_author.jump_url}">${dyn2.modules.module_author.name}</a>`).join(`、`); timediv.innerHTML = `${dyn.modules.module_author.pub_time} · 与${description}联合创作`; dynItem.dataset.qfb_expanded_did = dyn.id_str; } } }, stopWhenSuccess: false }); } function 动态页面() { 动态首页联合投稿具名(); 分离视频类型(); } async function 单条动态页面$1() { const dyn = await elementEmerge(`.bili-rich-text`, document); const uname = $(`.bili-dyn-title>span`).textContent.trim(); const dtime = $(`.bili-dyn-time`).textContent.trim(); const dcont = dyn.textContent.trim(); const ddisp = dcont.length > 23 ? dcont.slice(0, 20) + "..." : dcont; document.title = `${uname} 于 ${dtime} 说道:『${ddisp}』-哔哩哔哩`; } async function opus$1() { const uname = $(`.opus-module-author__name`).textContent.trim(); const dtime = $(`.opus-module-author__pub__text`).textContent.trim(); const dcont = $(`.opus-module-content`).textContent.trim(); const ddisp = dcont.length > 23 ? dcont.slice(0, 20) + "..." : dcont; document.title = `${uname} 于 ${dtime} 说道:『${ddisp}』-哔哩哔哩`; } function 单条动态页面() { 单条动态页面$1(); } function opus() { opus$1(); } async function 排序粉丝勋章() { const list = await elementEmerge(`.medalList .list`); Array.from(list.children).sort( (a, b) => betterSelector(b, ".btn").selectAll().length - betterSelector(a, ".btn").selectAll().length || betterSelector(b, ".living").selectAll().length - betterSelector(a, ".living").selectAll().length || +betterSelector(b, ".m-medal__fans-medal-level").select().textContent - +betterSelector(a, ".m-medal__fans-medal-level").select().textContent ).forEach((node) => list.appendChild(node)); } function 粉丝勋章页() { 排序粉丝勋章(); } function 专栏可以复制() { $(".article-container").addEventListener("copy", (e) => e.stopImmediatePropagation(), true); } function 专栏() { 专栏可以复制(); } if (location.host === "live.bilibili.com") { if (location.pathname === "/") { 直播主页(); } else if (location.pathname === "/p/eden/area-tags") { 分区(); } else if (location.pathname === "/p/html/live-fansmedal-wall/") { 粉丝勋章页(); } else if (/^(?:\/blanc)?\/(\d+)$/.exec(location.pathname) && $(`.app-body`)) { 直播间(); } else ; } else if (location.host === "space.bilibili.com") ; else if (location.host === "t.bilibili.com") { if (/\/\d+/.exec(location.pathname)) { 单条动态页面(); } else { 动态页面(); } } else if (location.host === "www.bilibili.com") { if (/^\/opus\//.exec(location.pathname)) { opus(); } else if (location.pathname.startsWith("/read/cv")) { 专栏(); } } else ; })();