// ==UserScript== // @name Bilibili 翻页评论区 // @namespace MotooriKashin // @version 2.1.5 // @description 恢复评论区翻页功能。 // @author MotooriKashin // @homepage https://github.com/MotooriKashin/Bilibili-Old // @supportURL https://github.com/MotooriKashin/Bilibili-Old/issues // @icon https://www.bilibili.com/favicon.ico // @match *://*.bilibili.com/* // @grant none // @run-at document-start // @license MIT // @downloadURL none // ==/UserScript== (function () { var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; // src/utils/typeof.ts var isArray = Array.isArray; var isNumber = (val) => !isNaN(parseFloat(val)) && isFinite(val); // src/utils/format/url.ts var URL = class { /** 锚 */ hash; /** 基链 */ base; /** 参数对象。结果会格式化`undefined``null``NaN`等特殊值,但不会处理数字,以免丢失精度。 */ params = {}; /** 参数链(不含`?`) */ get param() { return Object.entries(this.params).reduce((s, d) => { return s += `${s ? "&" : ""}${d[0]}=${d[1]}`; }, ""); } /** 提取URL参数 */ constructor(url) { const arr1 = url.split("#"); let str = arr1.shift(); this.hash = arr1.join("#"); (this.hash || url.includes("#")) && (this.hash = `#${this.hash}`); const arr2 = str.split("?"); this.base = arr2.shift(); str = arr2.join("?"); if (str) { str.split("&").forEach((d) => { const arr3 = d.split("="); const key = arr3.shift(); if (key) { let value = arr3.join("=") || ""; try { if (!isNumber(value)) { value = JSON.parse(value); } } catch { value === "undefined" && (value = void 0); value === "NaN" && (value = NaN); } this.params[key] = value; } }); } } sort() { this.params = Object.keys(this.params).sort().reduce((s, d) => { s[d] = this.params[d]; return s; }, {}); } /** 还原url链接 */ toJSON() { return `${this.base ? this.param ? this.base + "?" : this.base : ""}${this.param}${this.hash || ""}`; } }; function objUrl(url, obj) { const res = new URL(url); Object.entries(obj).forEach((d) => { if (d[1] === void 0 || d[1] === null) return; res.params[d[0]] = d[1]; }); return res.toJSON(); } function urlObj(url) { const res = new URL(url); return res.params; } // src/io/api.ts function jsonCheck(str) { const result = typeof str === "string" ? JSON.parse(str) : str; if (result.code === 0) return result; throw new Error(`${result.code} ${result.message}`, { cause: result.code }); } // src/io/urls.ts var _URLS = class { }; var URLS = _URLS; // protocol + // __publicField(URLS, "P_AUTO", "//"); __publicField(URLS, "P_HTTP", "http://"); __publicField(URLS, "P_HTTPS", "https://"); __publicField(URLS, "P_WS", "ws://"); __publicField(URLS, "P_WSS", "wss://"); // domain __publicField(URLS, "D_WWW", "www.bilibili.com"); __publicField(URLS, "D_API", "api.bilibili.com"); __publicField(URLS, "D_APP", "app.bilibili.com"); __publicField(URLS, "D_MANAGER", "manager.bilibili.co"); __publicField(URLS, "D_INTERFACE", "interface.bilibili.com"); __publicField(URLS, "D_PASSPORT", "passport.bilibili.com"); __publicField(URLS, "D_BANGUMI", "bangumi.bilibili.com"); __publicField(URLS, "D_SPACE", "space.bilibili.com"); __publicField(URLS, "D_STATIC_S", "static.hdslb.com"); __publicField(URLS, "D_CHAT", "chat.bilibili.com"); __publicField(URLS, "D_DATA", "data.bilibili.com"); __publicField(URLS, "D_COMMENT", "comment.bilibili.com"); __publicField(URLS, "D_BROADCAST", "broadcast.bilibili.com"); __publicField(URLS, "D_MISAKA_SW", "misaka-sw.bilibili.com"); __publicField(URLS, "D_MEMBER", "member.bilibili.com"); __publicField(URLS, "D_BVC", "bvc.bilivideo.com"); __publicField(URLS, "D_S1", "s1.hdslb.com"); __publicField(URLS, "D_API_GLOBAL", "api.global.bilibili.com"); __publicField(URLS, "D_ACCOUNT", "account.bilibili.com"); __publicField(URLS, "D_INTL", "apiintl.biliapi.net"); __publicField(URLS, "D_API_VC", "api.vc.bilibili.com"); __publicField(URLS, "WEBSHOW_LOCS", _URLS.P_AUTO + _URLS.D_API + "/x/web-show/res/locs"); __publicField(URLS, "INDEX_TOP_RCMD", _URLS.P_AUTO + _URLS.D_API + "/x/web-interface/index/top/rcmd"); __publicField(URLS, "PAGE_HEADER", _URLS.P_AUTO + _URLS.D_API + "/x/web-show/page/header"); __publicField(URLS, "SEASON_RANK_LIST", _URLS.P_AUTO + _URLS.D_API + "/pgc/season/rank/web/list"); __publicField(URLS, "VIDEO", _URLS.P_AUTO + _URLS.D_STATIC_S + "/js/video.min.js"); __publicField(URLS, "JQUERY", _URLS.P_AUTO + _URLS.D_STATIC_S + "/js/jquery.min.js"); __publicField(URLS, "ARTICLE_CARDS", _URLS.P_AUTO + _URLS.D_API + "/x/article/cards"); __publicField(URLS, "VIEW_DETAIL", _URLS.P_AUTO + _URLS.D_API + "/x/web-interface/view/detail"); __publicField(URLS, "VIEW", _URLS.P_AUTO + _URLS.D_API + "/view"); __publicField(URLS, "X_VIEW", _URLS.P_AUTO + _URLS.D_API + "/x/web-interface/view"); __publicField(URLS, "PAGE_LIST", _URLS.P_AUTO + _URLS.D_API + "/x/player/pagelist"); __publicField(URLS, "TAG_INFO", _URLS.P_AUTO + _URLS.D_API + "/x/tag/info"); __publicField(URLS, "TAG_TOP", _URLS.P_AUTO + _URLS.D_API + "/x/web-interface/tag/top"); __publicField(URLS, "BANGUMI_SEASON", _URLS.P_AUTO + _URLS.D_BANGUMI + "/view/web_api/season"); __publicField(URLS, "SEASON_STATUS", _URLS.P_AUTO + _URLS.D_API + "/pgc/view/web/season/user/status"); __publicField(URLS, "SEASON_SECTION", _URLS.P_AUTO + _URLS.D_API + "/pgc/web/season/section"); __publicField(URLS, "GLOBAL_OGV_VIEW", _URLS.P_AUTO + _URLS.D_API_GLOBAL + "/intl/gateway/v2/ogv/view/app/season"); __publicField(URLS, "GLOBAL_OGV_PLAYURL", _URLS.P_AUTO + _URLS.D_API_GLOBAL + "/intl/gateway/v2/ogv/playurl"); __publicField(URLS, "APP_PGC_PLAYURL", _URLS.P_AUTO + _URLS.D_API + "/pgc/player/api/playurl"); __publicField(URLS, "ACCOUNT_GETCARDBYMID", _URLS.P_AUTO + _URLS.D_ACCOUNT + "/api/member/getCardByMid"); __publicField(URLS, "LOGIN_APP_THIRD", _URLS.P_AUTO + _URLS.D_PASSPORT + "/login/app/third"); __publicField(URLS, "PLAYER", _URLS.P_AUTO + _URLS.D_API + "/x/player/v2"); __publicField(URLS, "PLAYURL_PROJ", _URLS.P_AUTO + _URLS.D_APP + "/v2/playurlproj"); __publicField(URLS, "PGC_PLAYURL_PROJ", _URLS.P_AUTO + _URLS.D_API + "/pgc/player/api/playurlproj"); __publicField(URLS, "PGC_PLAYURL_TV", _URLS.P_AUTO + _URLS.D_API + "/pgc/player/api/playurltv"); __publicField(URLS, "UGC_PLAYURL_TV", _URLS.P_AUTO + _URLS.D_API + "/x/tv/ugc/playurl"); __publicField(URLS, "PGC_PLAYURL", _URLS.P_AUTO + _URLS.D_API + "/pgc/player/web/playurl"); __publicField(URLS, "PLAYURL", _URLS.P_AUTO + _URLS.D_API + "/x/player/playurl"); __publicField(URLS, "INTL_PLAYURL", _URLS.P_AUTO + _URLS.D_APP + "/x/intl/playurl"); __publicField(URLS, "INTL_OGV_PLAYURL", _URLS.P_AUTO + _URLS.D_INTL + "/intl/gateway/ogv/player/api/playurl"); __publicField(URLS, "PLAYURL_INTERFACE", _URLS.P_AUTO + _URLS.D_INTERFACE + "/v2/playurl"); __publicField(URLS, "PLAYURL_BANGUMI", _URLS.P_AUTO + _URLS.D_BANGUMI + "/player/web_api/v2/playurl"); __publicField(URLS, "LIKE", _URLS.P_AUTO + _URLS.D_API + "/x/web-interface/archive/like"); __publicField(URLS, "HAS_LIKE", _URLS.P_AUTO + _URLS.D_API + "/x/web-interface/archive/has/like"); __publicField(URLS, "DM_WEB_VIEW", _URLS.P_AUTO + _URLS.D_API + "/x/v2/dm/web/view"); __publicField(URLS, "DM_WEB_SEG_SO", _URLS.P_AUTO + _URLS.D_API + "/x/v2/dm/web/seg.so"); __publicField(URLS, "STAT", _URLS.P_AUTO + _URLS.D_API + "/x/web-interface/archive/stat"); __publicField(URLS, "SLIDE_SHOW", _URLS.P_AUTO + _URLS.D_API + "/pgc/operation/api/slideshow"); __publicField(URLS, "SEARCH_SQUARE", _URLS.P_AUTO + _URLS.D_API + "/x/web-interface/search/square"); __publicField(URLS, "SPACE_ARC", _URLS.P_AUTO + _URLS.D_API + "/x/space/wbi/arc/search"); __publicField(URLS, "NEWLIST", _URLS.P_AUTO + _URLS.D_API + "/x/web-interface/newlist"); __publicField(URLS, "SEARCH", _URLS.P_AUTO + _URLS.D_API + "/search"); __publicField(URLS, "REPLY", _URLS.P_AUTO + _URLS.D_API + "/x/v2/reply"); __publicField(URLS, "ARTICLE_UPCOVER", _URLS.P_AUTO + _URLS.D_API + "/x/article/creative/article/upcover"); __publicField(URLS, "DRAW_IMAGE_UPLOAD", _URLS.P_AUTO + _URLS.D_API_VC + "/api/v1/drawImage/upload"); __publicField(URLS, "DYNAMIC_UPLOAD_BFS", _URLS.P_AUTO + _URLS.D_API + "/x/dynamic/feed/draw/upload_bfs"); // src/io/api-reply.ts async function apiReply(oid, pn = 1, type = 1, sort = 0) { const reply = await fetch(objUrl(URLS.REPLY, { pn, type, oid, sort }), { credentials: "include" }); const json = await reply.json(); return jsonCheck(json).data; } // src/utils/abv.ts var Abv = class { base58Table = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"; digitMap = [11, 10, 3, 8, 4, 6]; xor = 177451812; add = 8728348608; bvidTemplate = ["B", "V", 1, "", "", 4, "", 1, "", 7, "", ""]; table = {}; constructor() { for (let i = 0; i < 58; i++) this.table[this.base58Table[i]] = i; } /** * av/BV互转 * @param input av或BV,可带av/BV前缀 * @returns 转化结果 */ check(input) { if (/^[aA][vV][0-9]+$/.test(String(input)) || /^\d+$/.test(String(input))) return this.avToBv(Number(/[0-9]+/.exec(String(input))[0])); if (/^1[fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF]{9}$/.test(String(input))) return this.bvToAv("BV" + input); if (/^[bB][vV]1[fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF]{9}$/.test(String(input))) return this.bvToAv(String(input)); throw input; } bvToAv(BV) { let r = 0; for (let i = 0; i < 6; i++) r += this.table[BV[this.digitMap[i]]] * 58 ** i; return r - this.add ^ this.xor; } avToBv(av) { let bv = Array.from(this.bvidTemplate); av = (av ^ this.xor) + this.add; for (let i = 0; i < 6; i++) bv[this.digitMap[i]] = this.base58Table[parseInt(String(av / 58 ** i)) % 58]; return bv.join(""); } }; function abv(input) { return new Abv().check(input); } function BV2avAll(str) { return str.replace(/[bB][vV]1[fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF]{9}/g, (s) => "av" + abv(s)); } // src/utils/poll.ts function poll(check, callback, delay = 100, stop = 180) { let timer = setInterval(() => { const d = check(); if (d) { clearInterval(timer); callback(d); } }, delay); stop && setTimeout(() => clearInterval(timer), stop * 1e3); } // src/utils/element.ts function addElement(tag, attribute, parrent, innerHTML, top, replaced) { let element = document.createElement(tag); attribute && Object.entries(attribute).forEach((d) => element.setAttribute(d[0], d[1])); parrent = parrent || document.body; innerHTML && (element.innerHTML = innerHTML); replaced ? replaced.replaceWith(element) : top ? parrent.insertBefore(element, parrent.firstChild) : parrent.appendChild(element); return element; } async function addCss(txt, id, parrent) { if (!parrent && !document.head) { await new Promise((r) => poll(() => document.body, r)); } parrent = parrent || document.head; const style = document.createElement("style"); style.setAttribute("type", "text/css"); id && !parrent.querySelector(`#${id}`) && style.setAttribute("id", id); style.appendChild(document.createTextNode(txt)); parrent.appendChild(style); return style; } function loadScript(src, onload) { return new Promise((r, j) => { const script = document.createElement("script"); script.type = "text/javascript"; script.src = src; script.addEventListener("load", () => { script.remove(); onload && onload(); r(true); }); script.addEventListener("error", () => { script.remove(); j(); }); (document.body || document.head || document.documentElement || document).appendChild(script); }); } // src/utils/format/integer.ts function integerFormat(num, byte = 2) { return num < 10 ** byte ? (Array(byte).join("0") + num).slice(-1 * byte) : num; } // src/utils/format/time.ts function timeFormat(time = (/* @__PURE__ */ new Date()).getTime(), type) { const date = new Date(time); const arr = date.toLocaleString().split(" "); const day = arr[0].split("/"); day[1] = integerFormat(day[1], 2); day[2] = integerFormat(day[2], 2); return type ? day.join("-") + " " + arr[1] : arr[1]; } // src/utils/debug.ts var group = { /** 分组层次 */ i: 0, /** 分组栈 */ call: [] }; function debug(...data) { group.call.push(console.log.bind(console, `%c[${timeFormat()}]`, "color: blue;", ...arguments)); !group.i && setTimeout(group.call.shift()); return debug; } debug.assert = function(condition, ...data) { group.call.push(console.assert.bind(console, `[${timeFormat()}]`, ...arguments)); !group.i && setTimeout(group.call.shift()); return debug; }; debug.clear = function() { group.i = 0; group.call = []; setTimeout(console.clear.bind(console)); return debug; }; debug.debug = function(...data) { group.call.push(console.debug.bind(console, `[${timeFormat()}]`, ...arguments)); !group.i && setTimeout(group.call.shift()); return debug; }; debug.error = function(...data) { group.call.push(console.error.bind(console, `[${timeFormat()}]`, ...arguments)); !group.i && setTimeout(group.call.shift()); return debug; }; debug.group = function(...data) { group.i++; group.call.push(console.group.bind(console, `[${timeFormat()}]`, ...arguments)); return debug; }; debug.groupCollapsed = function(...data) { group.i++; group.call.push(console.groupCollapsed.bind(console, `[${timeFormat()}]`, ...arguments)); return debug; }; debug.groupEnd = function() { if (group.i) { group.i--; group.call.push(console.groupEnd.bind(console)); !group.i && (group.call.push(() => group.call = []), group.call.forEach((d) => setTimeout(d))); } return debug; }; debug.info = function(...data) { group.call.push(console.info.bind(console, `%c[${timeFormat()}]`, "color: blue;", ...arguments)); !group.i && setTimeout(group.call.shift()); return debug; }; debug.log = function(...data) { group.call.push(console.log.bind(console, `%c[${timeFormat()}]`, "color: blue;", ...arguments)); !group.i && setTimeout(group.call.shift()); return debug; }; debug.table = function(tabularData, properties) { group.call.push(console.table.bind(console, ...arguments)); !group.i && setTimeout(group.call.shift()); return debug; }; debug.time = function(label) { console.time(label); return debug; }; debug.timeEnd = function(label) { console.timeEnd(label); return debug; }; debug.timeLog = function(label, ...data) { console.timeLog(label, `[${timeFormat()}]`, ...data); return debug; }; debug.trace = function(...data) { group.call.push(console.trace.bind(console, ...arguments)); !group.i && setTimeout(group.call.shift()); return debug; }; debug.warn = function(...data) { group.call.push(console.warn.bind(console, `[${timeFormat()}]`, ...arguments)); !group.i && setTimeout(group.call.shift()); return debug; }; // src/utils/hook/node.ts var appendChild = Element.prototype.appendChild; var insertBefore = Element.prototype.insertBefore; var jsonp = []; Element.prototype.appendChild = function(newChild) { this.parentElement === document.documentElement && newChild.nodeName == "SCRIPT" && newChild.src && jsonp.forEach((d) => { d[0].every((d2) => newChild.src.includes(d2)) && d[1].call(newChild); }); return appendChild.call(this, newChild); }; Element.prototype.insertBefore = function(newChild, refChild) { this.parentElement === document.documentElement && newChild.nodeName == "SCRIPT" && newChild.src && jsonp.forEach((d) => { d[0].every((d2) => newChild.src.includes(d2)) && d[1].call(newChild); }); return insertBefore.call(this, newChild, refChild); }; function jsonpHook(url, redirect, modifyResponse, once = true) { let id; const one = Array.isArray(url) ? url : [url]; const two = function() { once && id && delete jsonp[id - 1]; if (redirect) try { this.src = redirect(this.src) || this.src; } catch (e) { debug.error("redirect of jsonphook", one, e); } if (modifyResponse) { const obj = urlObj(this.src); if (obj) { const callback = obj.callback; const call = window[callback]; const url2 = this.src; if (call) { window[callback] = function(v) { try { v = modifyResponse(v, url2, call) || v; } catch (e) { debug.error("modifyResponse of jsonphook", one, e); } return v !== true && call(v); }; } } } }; const iid = jsonp.push([one, two]); return () => { removeJsonphook(iid); }; } jsonpHook.async = (url, condition, modifyResponse, once = true) => { let id; const one = Array.isArray(url) ? url : [url]; const two = function() { try { once && id && delete jsonp[id - 1]; if (!condition || condition(this.src)) { const obj = urlObj(this.src); if (obj) { const callback = obj.callback; const call = window[callback]; if (call) { modifyResponse && modifyResponse(this.src).then((d) => { window[callback](d); this.dispatchEvent(new ProgressEvent("load")); }).catch((e) => { this.dispatchEvent(new ProgressEvent("error")); debug.error("modifyResponse of xhrhookasync", one, e); }); } this.removeAttribute("src"); } } } catch (e) { debug.error("jsonphook", one, e); } }; const iid = jsonp.push([one, two]); return () => { removeJsonphook(iid); }; }; jsonpHook.scriptBlock = (url) => { const one = Array.isArray(url) ? url : [url]; const two = function() { try { this.removeAttribute("src"); setTimeout(() => { this.dispatchEvent(new ProgressEvent("load")); try { this.remove(); } catch (e) { } }, 100); } catch (e) { debug.error("脚本拦截失败!", one, e); } }; jsonp.push([one, two]); }; jsonpHook.scriptIntercept = (url, redirect, text) => { const one = Array.isArray(url) ? url : [url]; const two = function() { try { if (text) { this.text = text(this.src); this.removeAttribute("src"); setTimeout(() => { this.dispatchEvent(new ProgressEvent("load")); this == null ? void 0 : this.remove(); }, 100); } else if (redirect) { this.src = redirect(this.src); } } catch (e) { debug.error("scriptIntercept", one, e); } }; const iid = jsonp.push([one, two]); return () => { removeJsonphook(iid); }; }; function removeJsonphook(id) { id >= 0 && delete jsonp[id - 1]; } // src/html/preview-image.html var preview_image_default = '
\r\n\r\n'; // src/svg/fork.svg var fork_default = ''; // src/svg/left.svg var left_default = ''; // src/svg/right.svg var right_default = ''; // src/core/ui/preview-image.ts var PreviewImage = class extends HTMLElement { _image; _list; constructor() { super(); const root = this.attachShadow({ mode: "closed" }); root.innerHTML = preview_image_default; const close = root.querySelector(".svg-icon.close.use-color"); const left = root.querySelector(".svg-icon.left-arrow.use-color"); const right = root.querySelector(".svg-icon.right-arrow.use-color"); close.innerHTML = fork_default; left.innerHTML = left_default; right.innerHTML = right_default; this._image = root.querySelector(".show-image-wrap"); this._list = root.querySelector(".preview-list"); close.parentElement.addEventListener("click", (e) => { this.remove(); document.body.style.overflow = ""; e.stopPropagation(); }); left.parentElement.addEventListener("click", (e) => { this.togger(e, false); e.stopPropagation(); }); right.parentElement.addEventListener("click", (e) => { this.togger(e); e.stopPropagation(); }); this._image.addEventListener("click", (e) => { if (e.target === this._image) { this.remove(); document.body.style.overflow = ""; e.stopPropagation(); } }); } togger(e, right = true) { const list = this._list.querySelectorAll(".preview-item-box"); if (list.length) { let i = 0; list.forEach((d, j) => { if (d.classList.contains("active")) { d.classList.remove("active"); if (right) { i = j + 1; i < list.length || (i = 0); } else { i = j - 1; i < 0 && (i = list.length - 1); } } }); list[i].classList.add("active"); const img = list[i].querySelector("img"); if (img) { this._image.innerHTML = `