// ==UserScript==
// @name Bilibili 翻页评论区
// @namespace MotooriKashin
// @version 0.1.4
// @description 恢复原来的翻页评论区,同时修复评论楼层号。
// @author MotooriKashin
// @match *://*.bilibili.com/*
// @require https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old@920be7569f57d638a92359a18299b052db8fe0ba/src/comment.min.js
// @resource style http://static.hdslb.com/phoenix/dist/css/comment.min.css
// @run-at document-start
// @grant GM_getResourceText
// @license MIT License
// ==/UserScript==
(function() {
'use strict';
// 拦截新版评论
Object.defineProperty(unsafeWindow, "bbComment", { get: () => {
window.commentAgent = unsafeWindow.commentAgent; // 修复评论时间戳
return window.bbComment;
}});
async function addCss(css){
if (!document.head) return setTimeout(() => { addCss(css)}, 100);
let style = document.createElement("style");
style.setAttribute("type", "text/css");
style.appendChild(document.createTextNode(css));
document.head.appendChild(style);
}
addCss(GM_getResourceText("style") + ".bb-comment .comment-header .header-page, .comment-bilibili-fold .comment-header .header-page { float: right;line-height: 36px;}"); // 补全评论样式
// 修复评论区楼层号
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) => {
if (unsafeWindow.BLOD) return;
src = "";
try {
let data;
let list_item = document.getElementsByClassName("reply-wrap");
let main_floor = document.getElementsByTagName("li");
if (!link.includes("reply?")){
let obj = search2obj(link);
obj.callback = "";
obj.jsonp = "";
obj._ = "";
link = obj2search(link.split("?")[0],obj);
data = await xhr(link);
}
else{
let mode, 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;
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<list_item.length; i++) {
let rpid = list_item[i].getAttribute("data-id");
if (rpid in floor) {
let node = list_item[i].getElementsByClassName("info")[0];
let span = document.createElement("span");
span.setAttribute("class", "floor");
span.innerText = "#" + floor[rpid];
node.insertBefore(span,node.firstChild);
}
}
}
}
catch(e) {console.error(e)}
}
if (unsafeWindow.BLOD) return;
// 监听评论初始化及翻页
document.addEventListener("DOMNodeInserted",(msg) => {
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);
});
})();