// ==UserScript== // @name 修复B站评论区楼层 // @namespace MotooriKashin // @version 0.0.7 // @description 修复评论区评论的楼层号,包括视频、动态、专栏、话题、活动…… // @author MotooriKashin // @match *://*.bilibili.com/* // @grant none // @license MIT License // @downloadURL none // ==/UserScript== (function() { 'use strict'; let src, mode, type; const url = { reply : "https://api.bilibili.com/x/v2/reply", replymain : "https://api.bilibili.com/x/v2/reply/main", replycursor : "https://api.bilibili.com/x/v2/reply/reply/cursor", replydialog: "https://api.bilibili.com/x/v2/reply/dialog/cursor" } const xhr = (url) => { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open('get', url, true); xhr.withCredentials = true; xhr.onload = () => { if (xhr.status >= 200 && xhr.status < 300) resolve(xhr.response) else reject({status: xhr.status, statusText: xhr.statusText}) }; xhr.onerror = () => reject({status: xhr.status, statusText: xhr.statusText}) xhr.send(); }); } const obj2search = (url, obj) =>{ if (obj) { let arr = [],i = 0; for (let key in obj) { if(obj[key] !== "" && obj[key] !== "undefined" && obj[key] !== null) { arr[i] = key + "=" + obj[key]; i++; } } url = url + "?" + arr.join("&"); } return url; } const search2obj = (url) => { url = url.split('#')[0]; url = url.split('?')[1] ? url.split('?')[1].split('&') : ""; if (!url) return; let obj = {}; for (let i = 0; i < url.length; i++) obj[url[i].split('=')[0]] = url[i].split('=')[1]; return obj; } const setReplyFloor = async (link) => { src = ""; try { let mode, data, obj = search2obj(link), oid = obj.oid, sort = obj.sort, pn = obj.pn, root = obj.root, type = obj.type; if (sort == 0) mode = 1; if (sort == 1) return; if (sort == 2) mode = 3; let list_item = document.getElementsByClassName("reply-wrap"); let main_floor = document.getElementsByTagName("li"); if (root) { if (pn < 2) data = await xhr(obj2search(url.replycursor, {"oid": oid,"root": root,"type": type})); else { let dialog; if (list_item[0]) { for (let i = 0; i < list_item.length; i++) { if (list_item[i].getAttribute("data-id") == root) { list_item = list_item[i].getElementsByClassName("reply-wrap"); if (list_item[0]) { for (let j = 0; j < list_item.length; j++) { if (!list_item[j].getElementsByClassName("floor")[0]) { dialog = list_item[j].getAttribute("data-id"); break; } } } break; } } } else if (main_floor[0]) { for (let i = 0; i < main_floor.length; i++) { if (main_floor[i].getAttribute("id") && main_floor[i].getAttribute("id").includes(root)) { main_floor = main_floor[i].getElementsByTagName("li"); if (main_floor[0]) { for (let j = 0; j < main_floor.length; j++) { if (main_floor[j].id && main_floor[j].id.includes("l_id") && !main_floor[j].getElementsByClassName("floor-num")[0]) { dialog = main_floor[j].getAttribute("id").split('_')[2]; break; } } } break; } } } data = await xhr(obj2search(url.replydialog, {"oid": oid,"root": root,"type": type, "dialog": dialog, "size": 20})); let min_id = JSON.parse(data).data.replies; if (min_id) { for (let i = 0; i < min_id.length; i++) { if (min_id[i].rpid == dialog) { min_id = min_id[i].floor; break; } } } else return; data = await xhr(obj2search(url.replycursor, {"oid": oid,"root": root,"type": type, "min_id": min_id})); } } else { if (sort == 2) data = await xhr(obj2search(url.replymain, {"oid": oid,"next": pn,"type": type,"mode": mode})); else if (pn == 1) data = await xhr(obj2search(url.replymain, {"oid": oid,"type": type,"mode": mode})); else { pn = pn - 1; data = await xhr(obj2search(url.reply, {"type": type,"sort": sort,"oid": oid,"pn": pn})); data = JSON.parse(data).data; let i = data.replies.length - 1; oid = data.replies[0].oid; let root = data.replies[i].rpid; data = await xhr(obj2search(url.replycursor, {"oid": oid,"root": root,"type": type})); data = JSON.parse(data).data; oid = data.root.oid; let next = data.root.floor; data = await xhr(obj2search(url.replymain, {"oid": oid,"next": next,"type": type,"mode": mode})); } } data = JSON.parse(data).data; let floor = {}, top = data.top, hots = data.hots, replies = data.replies, froot = data.root; if (hots && hots[0]) { for (let i = 0; i < hots.length; i++) { floor[hots[i].rpid] = hots[i].floor; if (hots[i].replies) { for (let j = 0; j < hots[i].replies.length; j++) { floor[hots[i].replies[j].rpid] = hots[i].replies[j].floor; } } } } if (replies && replies[0]) { for (let i = 0;i < replies.length; i++) { floor[replies[i].rpid] = replies[i].floor; if (replies[i].replies) { for (let j = 0; j < replies[i].replies.length; j++) { floor[replies[i].replies[j].rpid] = replies[i].replies[j].floor; } } } } if (top) { for (let key in top) { if (top[key]) { floor[top[key].rpid] = top[key].floor; if (top[key].replies) { for (let i = 0; i < top[key].replies.length; i++) { floor[top[key].replies[i].rpid] = top[key].replies[i].floor; } } } } } if (froot && froot.replies) for (let i = 0; i < froot.replies.length; i++) floor[froot.replies[i].rpid] = froot.replies[i].floor; if (main_floor[0]) { for (let i = 0; i < main_floor.length; i++) { if (main_floor[i].id && main_floor[i].id.includes("l_id")) { let rpid = main_floor[i].getAttribute("id").split('_')[2]; if (rpid in floor) { try { main_floor[i].getElementsByClassName("floor-num")[0].innerText = "#" + floor[rpid]; } catch (e) { let node = main_floor[i].getElementsByClassName("floor-date")[0].parentNode; let span = document.createElement("span"); span.setAttribute("class", "floor-num"); span.setAttribute("style", "float: left;color: #aaa;padding-right: 10px;"); span.innerText = "#" + floor[rpid]; node.insertBefore(span,node.firstChild); } } } } } if (list_item[0]) { for (let i = 0; i { if (msg.target.src && msg.target.src.startsWith('https://api.bilibili.com/x/v2/reply') && msg.target.src.includes("oid")) src = msg.target.src; if (src && ((msg.target.id && /l_id/.test(msg.target.id)) || (msg.target.className && /reply-wrap/.test(msg.target.className)))) setReplyFloor(src); }); })();