您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
恢复原生的旧版页面,包括主页和播放页。
当前为
// ==UserScript== // @name Bilibili 旧播放页 // @namespace MotooriKashin // @version 3.6.9 // @description 恢复原生的旧版页面,包括主页和播放页。 // @author MotooriKashin, wly5556 // @supportURL https://github.com/MotooriKashin/Bilibili-Old/issues // @match *://*.bilibili.com/* // @connect bilibili.com // @connect * // @icon https://static.hdslb.com/images/favicon.ico // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/protobuf.js // @resource av https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/av.html // @resource watchlater https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/watchlater.html // @resource bangumi https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/bangumi.html // @resource cinema https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/cinema.html // @resource playlist https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/playlist.html // @resource playlistdetail https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/playlistdetail.html // @resource index https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/index.html // @resource ranking https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/ranking.html // @resource css https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/ui.css // @resource comment https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/bilibili/js-css/comment.min.js // @resource video https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/bilibili/edit/video.min.js // @resource crc https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/crc.js // @resource md5 https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/md5.js // @resource config https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/config.json // @resource playlistjson https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/playlist.json // @resource sort https://cdn.jsdelivr.net/gh/MotooriKashin/Bilibili-Old/src/sort.json // @grant GM_xmlhttpRequest // @grant GM_getResourceText // @grant GM_getResourceURL // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @run-at document-start // @license MIT License // ==/UserScript== (function(){ 'use strict'; const config = JSON.parse(GM_getResourceText("config")); const root = window.protobuf.Root.fromJSON(JSON.parse('{"nested":{"bilibili":{"nested":{"DmWebViewReply":{"fields":{"state":{"type":"int32","id":1},"text":{"type":"string","id":2},"textSide":{"type":"string","id":3},"dmSge":{"type":"DmSegConfig","id":4},"flag":{"type":"DanmakuFlagConfig","id":5},"specialDms":{"rule":"repeated","type":"string","id":6},"checkBox":{"type":"bool","id":7},"count":{"type":"int64","id":8},"commandDms":{"rule":"repeated","type":"CommandDm","id":9},"dmSetting":{"type":"DanmuWebPlayerConfig","id":10}}},"CommandDm":{"fields":{"id":{"type":"int64","id":1},"oid":{"type":"int64","id":2},"mid":{"type":"int64","id":3},"command":{"type":"string","id":4},"content":{"type":"string","id":5},"progress":{"type":"int32","id":6},"ctime":{"type":"string","id":7},"mtime":{"type":"string","id":8},"extra":{"type":"string","id":9},"idStr":{"type":"string","id":10}}},"DmSegConfig":{"fields":{"pageSize":{"type":"int64","id":1},"total":{"type":"int64","id":2}}},"DanmakuFlagConfig":{"fields":{"recFlag":{"type":"int32","id":1},"recText":{"type":"string","id":2},"recSwitch":{"type":"int32","id":3}}},"DmSegMobileReply":{"fields":{"elems":{"rule":"repeated","type":"DanmakuElem","id":1}}},"DanmakuElem":{"fields":{"id":{"type":"int64","id":1},"progress":{"type":"int32","id":2},"mode":{"type":"int32","id":3},"fontsize":{"type":"int32","id":4},"color":{"type":"uint32","id":5},"midHash":{"type":"string","id":6},"content":{"type":"string","id":7},"ctime":{"type":"int64","id":8},"weight":{"type":"int32","id":9},"action":{"type":"string","id":10},"pool":{"type":"int32","id":11},"idStr":{"type":"string","id":12}}},"DanmuWebPlayerConfig":{"fields":{"dmSwitch":{"type":"bool","id":1},"aiSwitch":{"type":"bool","id":2},"aiLevel":{"type":"int32","id":3},"blocktop":{"type":"bool","id":4},"blockscroll":{"type":"bool","id":5},"blockbottom":{"type":"bool","id":6},"blockcolor":{"type":"bool","id":7},"blockspecial":{"type":"bool","id":8},"preventshade":{"type":"bool","id":9},"dmask":{"type":"bool","id":10},"opacity":{"type":"float","id":11},"dmarea":{"type":"int32","id":12},"speedplus":{"type":"float","id":13},"fontsize":{"type":"float","id":14},"screensync":{"type":"bool","id":15},"speedsync":{"type":"bool","id":16},"fontfamily":{"type":"string","id":17},"bold":{"type":"bool","id":18},"fontborder":{"type":"int32","id":19},"drawType":{"type":"string","id":20}}}}}}}')); const protoSeg = root.lookupType('bilibili.DmSegMobileReply'); const protoView = root.lookupType('bilibili.DmWebViewReply'); let aid, cid, bvid; // 暴露接口 const BLOD = unsafeWindow.BLOD = { xmlhttpRequest: GM_xmlhttpRequest, setValue: GM_setValue, getValue: GM_getValue, getResourceText: GM_getResourceText, getResourceURL: GM_getResourceURL, deleteValue: GM_deleteValue, aid: aid, cid: cid, bvid: bvid, hash: [], ids: [], bloburl: {} } const xhrHook = { init: () => { // 分别hook WebSocket、worker、XMLHttpRequest.open、XMLHttpRequest.send // jQuery的jsonp非原生对象,由Object.defineProperty捕捉到再hook // XMLHttpRequest.open主修复旧版各种失效接口只能常开 if (config.reset.livechat) xhrHook.webSocket(); if (config.reset.danmuku && Worker) xhrHook.worker(); xhrHook.open(); if (config.reset.xhrhook) xhrHook.send(); }, webSocket: () => { let decoder = new TextDecoder(); let encoder = new TextEncoder(); let liveChatOld; // 对旧播放器建立的ws对象的引用 let liveChat; // 为了获取ws对象的引用,hook WebSocket.send let wsHookRunOnce = true; const wssend = WebSocket.prototype.send; WebSocket.prototype.send = function (...arg) { if (wsHookRunOnce && this.url == 'wss://broadcast.chat.bilibili.com:4095/sub') { liveChatOld = this; // 切p和掉线之后需要重新启动hook,获得新的引用 let onclose = liveChatOld.onclose; liveChatOld.onclose = function () { wsHookRunOnce = true; clearTimeout(liveChat.heatTimer); liveChat.close(); onclose.call(this); } // 从bilibiliPlayer.js > b.prototype.xx复制过来 // 编码一个数据包 // body[Object] : 要发送的信息 // option[Number] : 数据包对应的行为 // =5 一条弹幕数据 // =7 首个数据包,建立与服务器的连接 // return[Buffer] : 包装好的数据 liveChatOld.convertToArrayBuffer = function (body, option) { let header = [{ "name": "Header Length", "key": "headerLen", "qg": 2, "offset": 4, "value": 16 }, { "name": "Protocol Version", "key": "ver", "qg": 2, "offset": 6, "value": 1 }, { "name": "Operation", "key": "op", "qg": 4, "offset": 8, "value": option }, { "name": "Sequence Id", "key": "seq", "qg": 4, "offset": 12, "value": 1 }]; let headerBuf = new ArrayBuffer(16); let viewer = new DataView(headerBuf, 0); let bodyBuf = encoder.encode(JSON.stringify(body)); viewer.setInt32(0, 16 + bodyBuf.byteLength); header.forEach(function (b) { 4 === b.qg ? viewer.setInt32(b.offset, b.value) : 2 === b.qg && viewer.setInt16(b.offset, b.value) }) return mergeArrayBuffer(headerBuf, bodyBuf); } wsHookRunOnce = false; initLiveChat(); } wssend.call(this, ...arg); } // 原函数位于bilibiliPlayer.js > c.a.eK 和 jsc-player > Dl.mergeArrayBuffer // 连接两个buffer function mergeArrayBuffer(headerBuf, bodyBuf) { headerBuf = new Uint8Array(headerBuf); bodyBuf = new Uint8Array(bodyBuf); var d = new Uint8Array(headerBuf.byteLength + bodyBuf.byteLength); d.set(headerBuf, 0); d.set(bodyBuf, headerBuf.byteLength); return d.buffer; } function initLiveChat() { // 数据包对应的Operation常量表 let Pl = { "WS_OP_HEARTBEAT": 2, "WS_OP_HEARTBEAT_REPLY": 3, "WS_OP_DATA": 1000, "WS_OP_BATCH_DATA": 9, "WS_OP_DISCONNECT_REPLY": 6, "WS_OP_USER_AUTHENTICATION": 7, "WS_OP_CONNECT_SUCCESS": 8, "WS_OP_CHANGEROOM": 12, "WS_OP_CHANGEROOM_REPLY": 13, "WS_OP_REGISTER": 14, "WS_OP_REGISTER_REPLY": 15, "WS_OP_UNREGISTER": 16, "WS_OP_UNREGISTER_REPLY": 17, "WS_OP_OGVCMD_REPLY": 1015, "WS_PACKAGE_HEADER_TOTAL_LENGTH": 18, "WS_PACKAGE_OFFSET": 0, "WS_HEADER_OFFSET": 4, "WS_VERSION_OFFSET": 6, "WS_OPERATION_OFFSET": 8, "WS_SEQUENCE_OFFSET": 12, "WS_COMPRESS_OFFSET": 16, "WS_CONTENTTYPE_OFFSET": 17, "WS_BODY_PROTOCOL_VERSION": 1, "WS_HEADER_DEFAULT_VERSION": 1, "WS_HEADER_DEFAULT_OPERATION": 1, "ws_header_default_sequence": 1, "WS_HEADER_DEFAULT_COMPRESS": 0, "WS_HEADER_DEFAULT_CONTENTTYPE": 0 }; // 请求头的参数表 let wsBinaryHeaderList = [{ "name": "Header Length", "key": "headerLen", "bytes": 2, "offset": 4, "value": 18 }, { "name": "Protocol Version", "key": "ver", "bytes": 2, "offset": 6, "value": 1 }, { "name": "Operation", "key": "op", "bytes": 4, "offset": 8, "value": 7 }, { "name": "Sequence Id", "key": "seq", "bytes": 4, "offset": 12, "value": 2 }, { "name": "Compress", "key": "compress", "bytes": 1, "offset": 16, "value": 0 }, { "name": "ContentType", "key": "contentType", "bytes": 1, "offset": 17, "value": 0 }] liveChat = new WebSocket('wss://broadcast.chat.bilibili.com:7823/sub'); liveChat.binaryType = "arraybuffer"; liveChat.heatTimer = -1; // 每30秒一个心跳包 liveChat.heartBeat = function () { var i = this; clearTimeout(this.heatTimer); var e = this.convertToArrayBuffer({}, Pl.WS_OP_HEARTBEAT); this.send(e); this.heatTimer = window.setTimeout((function () { i.heartBeat(); }), 1e3 * 30); } liveChat.onopen = function () { let body = { "room_id": "video://" + aid + "/" + cid, "platform": "web", "accepts": [1000, 1015] }; return this.send(this.convertToArrayBuffer(body, 7)); } liveChat.onmessage = function (i) { try { var t = this.convertToObject(i.data); if (t) { switch (t.op) { case Pl.WS_OP_HEARTBEAT_REPLY: // 接收到心跳包后,服务器响应当前在线人数的数据 // 旧播放器连接的4095端口,虽然不再下发实时弹幕,但依然照常响应在线人数 // 所以暂时不用替换成新版 // this.onHeartBeatReply(t.body); break; case Pl.WS_OP_CONNECT_SUCCESS: this.heartBeat(); break; // 旧播放器只能处理(连接成功,心跳响应,实时弹幕)三种响应信息 // 新播放器新增的指令和功能就不管了 case Pl.WS_OP_CHANGEROOM_REPLY: //0 === Number(t.body.code) && this.options.onChangeRoomReply({ data : t && t.body }); break; case Pl.WS_OP_REGISTER_REPLY: //0 === Number(t.body.code) && this.options.onRegisterReply({ data : t && t.body }); break; case Pl.WS_OP_UNREGISTER_REPLY: //0 === Number(t.body.code) && this.options.onUnRegisterReply({ data : t && t.body }); break; case Pl.WS_OP_DATA: case Pl.WS_OP_BATCH_DATA: t.body.forEach(function (v) { // 记录实时弹幕哈希值 BLOD.hash.push(v[0].split(",")[7]); liveChatOld.onmessage({ data: liveChatOld.convertToArrayBuffer({ cmd: 'DM', info: [v[0], v[1]] }, 5) }); }); break; case Pl.WS_OP_OGVCMD_REPLY: //this.onOgvCmdReply(t); break; default: //this.msgReply(t) } } } catch (i) { console.error("WebSocket Error : ", i) } return this; } // jsc-player > i.prototype.convertToArrayBuffer,新版播放器的请求头信息更多,需要18字节 // 基本与liveChatOld.convertToArrayBuffer相同 liveChat.convertToArrayBuffer = function (body, option) { let headerBuf = new ArrayBuffer(Pl.WS_PACKAGE_HEADER_TOTAL_LENGTH); let viewer = new DataView(headerBuf, Pl.WS_PACKAGE_OFFSET); let bodyBuf = encoder.encode(JSON.stringify(body)); viewer.setInt32(Pl.WS_PACKAGE_OFFSET, Pl.WS_PACKAGE_HEADER_TOTAL_LENGTH + bodyBuf.byteLength); wsBinaryHeaderList[2].value = option; wsBinaryHeaderList.forEach((function (i) { 4 === i.bytes ? (viewer.setInt32(i.offset, i.value), "seq" === i.key && ++i.value) : 2 === i.bytes ? viewer.setInt16(i.offset, i.value) : 1 === i.bytes && viewer.setInt8(i.offset, i.value) })); return mergeArrayBuffer(headerBuf, bodyBuf); } // jsc-player > i.prototype.convertToObject // convertToArrayBuffer对应的解码函数 liveChat.convertToObject = function (i) { var e = new DataView(i), t = {}; t.packetLen = e.getInt32(Pl.WS_PACKAGE_OFFSET); wsBinaryHeaderList.forEach((function (i) { 4 === i.bytes ? t[i.key] = e.getInt32(i.offset) : 2 === i.bytes ? t[i.key] = e.getInt16(i.offset) : 1 === i.bytes && (t[i.key] = e.getInt8(i.offset)) })); if (t.op && t.op === Pl.WS_OP_BATCH_DATA) { t.body = this.parseDanmaku(i, e, Pl.WS_PACKAGE_HEADER_TOTAL_LENGTH, t.packetLen); } else if (t.op && Pl.WS_OP_DATA === t.op) { t.body = this.parseDanmaku(i, e, Pl.WS_PACKAGE_OFFSET, t.packetLen); } else if (t.op && t.op === Pl.WS_OP_OGVCMD_REPLY) { t.body = ""; // this.parseOgvCmd(i, e, Pl.WS_PACKAGE_OFFSET, t.packetLen); } else if (t.op) { t.body = []; for (var a = Pl.WS_PACKAGE_OFFSET, r = t.packetLen, n = "", l = ""; a < i.byteLength; a += r) { r = e.getInt32(a); n = e.getInt16(a + Pl.WS_HEADER_OFFSET); try { l = JSON.parse(decoder.decode(i.slice(a + n, a + r))); t.body = l; } catch (e) { l = decoder.decode(i.slice(a + n, a + r)); console.error("decode body error:", new Uint8Array(i), t); } } } return t; } // jsc-player > i.prototype.parseDanmaku liveChat.parseDanmaku = function (i, e, t, a) { for (var r, n = [], l = t; l < i.byteLength; l += a) { a = e.getInt32(l); r = e.getInt16(l + Pl.WS_HEADER_OFFSET); try { n.push(JSON.parse(decoder.decode(i.slice(l + r, l + a)))); } catch (e) { n.push(decoder.decode(i.slice(l + r, l + a))); console.error("decode body error:", new Uint8Array(i)); } } return n; } } }, worker: () => { // hook postMessage来得到旧播放器创建的 获取list.so 的worker对象 let workerPostMsg = Worker.prototype.postMessage; let list_so; let loadTime, parseTime; // 旧播放器需要得到耗时数据(网络请求,数据处理) Worker.prototype.postMessage = function (aMessage, transferList) { if (aMessage.url && aMessage.url.includes("list.so")) { list_so = this; loadTime = new Date(); let Segments = []; getSegDanmaku(function (protoSegments) { loadTime = new Date() - loadTime; parseTime = new Date(); protoSegments.forEach(function (seg) { Segments = Segments.concat(protoSeg.decode(new Uint8Array(seg)).elems); }); Segments.sort(function (a, b) { return a.progress - b.progress; }); // 下载功能开启时,把分段弹幕转换到xml if (config.reset.download) { toXml(Segments, cid).then(function (result) { // 备份弹幕 BLOD.xml = result; }); } // 将弹幕转换为旧格式 Segments = Segments.map(function (v) { // 记录弹幕池哈希值 BLOD.hash.push(v.midHash); return { class: 0, color: v.color, date: v.ctime, dmid: v.idStr, mode: v.mode, size: v.fontsize, stime: v.progress / 1000, text: v.content, uid: v.midHash }; }); parseTime = new Date() - parseTime; list_so.onmessage({ data: { code: 0, danmakuArray: Segments, loadTime: loadTime, parseTime: parseTime, sendTip: "", state: 0, textSide: "", total: Segments.length.toString() } }); }); } else { workerPostMsg.call(this, aMessage, transferList); } } }, open: () => { const open = XMLHttpRequest.prototype.open; xhrHook.segRequestOnlyOnce = true; XMLHttpRequest.prototype.open = function (method, url, ...rest) { let _url = url, hook = [_url, ""]; let obj = urlObj(url); // 替换视频心跳 if (url.includes('api.bilibili.com/x/report/web/heartbeat') && config.reset.heartbeat) { url = url.replace('api.bilibili.com/x/report/web/heartbeat', 'api.bilibili.com/x/click-interface/web/heartbeat'); debug.debug("XHR重定向", "替换视频心跳", [_url, url]); } // 显示历史视频 if (url.includes('api.bilibili.com/x/web-interface/history/cursor') && url.includes("business") && config.reset.history) { let max = obj.max || "", view_at = obj.view_at || ""; url = objUrl("//api.bilibili.com/x/web-interface/history/cursor", { max: max, view_at: view_at, type: "archive", ps: 20 }); debug.debug("XHR重定向", "显示历史视频", [_url, url]); } // 修改正在直播 if (url.includes('api.live.bilibili.com/room/v1/RoomRecommend/biliIndexRecList')) { this.addEventListener('readystatechange', () => { if (this.readyState === 4) xhrHook.biliIndexRec(this, hook) }); url = hook[1] = url.replace('api.live.bilibili.com/room/v1/RoomRecommend/biliIndexRecList', 'api.live.bilibili.com/xlive/web-interface/v1/webMain/getList?platform=web'); } // 修改直播动态 if (url.includes('api.live.bilibili.com/room/v1/RoomRecommend/biliIndexRecMore')) { this.addEventListener('readystatechange', () => { if (this.readyState === 4) xhrHook.biliIndexRec(this, hook) }); url = hook[1] = url.replace('api.live.bilibili.com/room/v1/RoomRecommend/biliIndexRecMore', 'api.live.bilibili.com/xlive/web-interface/v1/webMain/getMoreRecList?platform=web'); } // 重定向番剧信息 if (url.includes('bangumi.bilibili.com/view/web_api/season?')) { this.addEventListener('readystatechange', () => { if (this.readyState === 4) xhrHook.season(this, hook) }); url = hook[1] = url.replace('bangumi.bilibili.com/view/web_api/season', 'api.bilibili.com/pgc/view/web/season'); } // 重定向追番信息 if (url.includes('bangumi.bilibili.com/ext/web_api/season_count?')) { this.addEventListener('readystatechange', () => { if (this.readyState === 4) xhrHook.stat(this, hook) }); url = hook[1] = url.replace('bangumi.bilibili.com/ext/web_api/season_count', 'api.bilibili.com/pgc/web/season/stat'); } // 修改番剧推荐 if (url.includes('api.bilibili.com/pgc/web/recommend/related/recommend')) { this.addEventListener('readystatechange', () => { if (this.readyState === 4) xhrHook.recommend(this) }); } // 修改直播数据 if (url.includes('api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo')) { this.addEventListener('readystatechange', () => { if (this.readyState === 4) xhrHook.getRoomPlayInfo(this) }); } // 修改播放器通知 if (url.includes('api.bilibili.com/x/player/carousel')) { this.addEventListener('readystatechange', () => { if (this.readyState === 4) xhrHook.carousel(this) }); } // 修改区域限制 if (url.includes('season/user/status?')) { this.addEventListener('readystatechange', () => { if (this.readyState === 4) xhrHook.status(this) }); } // 监听视频链接 if (url.includes("/playurl?")) { obj.fourk = obj.sign ? "" : 1; obj.fnval = obj.fnval ? 80 : ""; url = objUrl(url.split("?")[0], obj); cid = obj.cid || cid; aid = obj.avid || aid; BLOD.bvid = bvid = obj.bvid || abv(aid) || bvid; BLOD.pgc = url.includes("pgc") ? true : false; BLOD.vip = BLOD.big > 1 ? true : BLOD.vip; if (BLOD.big > 1 || (BLOD.vip && BLOD.ids.indexOf(1 * cid) >= 0)) this.url = url; if (BLOD.limit) this.url = url; this.addEventListener('readystatechange', () => { if (this.readyState === 4) xhrHook.playinfo(this, url) }); } // 修改弹幕链接 if (url.includes("list.so") && config.reset.danmuku) { // 这时pakku.js已经修改了xhr对象,需要另做处理 if (this.pakku_url) { xhrHook.segRequestOnlyOnce = true; let pid = aid; // 更改pakku.js请求的url,使它过滤分段弹幕 this.pakku_url = url = "https://api.bilibili.com/x/v2/dm/web/seg.so?type=1&oid=" + cid + "&pid=" + pid + "&segment_index=1"; this.responseType = "arraybuffer"; let xhr = this; let cb = []; for (let i in this.pakku_load_callback) { cb[i] = this.pakku_load_callback[i]; } for (let i in this.pakku_load_callback) { // 将pakku.js返回的数据转换回xml this.pakku_load_callback[i] = function () { toXml(protoSeg.decode(new Uint8Array(xhr.response)).elems, pid).then(function (xml) { xhr.response = xhr.responseText = xml; cb[i].call(xhr); }); } } } else { this.reqURL = url; } } return open.call(this, method, url, ...rest); } }, send: () => { const send = XMLHttpRequest.prototype.send; const addEventListener = XMLHttpRequest.prototype.addEventListener; XMLHttpRequest.prototype.send = async function (...arg) { // 新版弹幕兼容pakku.js // pakku.js休眠中,钩子捕捉到首次对seg.so发起请求时触发 // (pakku.js正常运行时这个send()不会被调用) if (config.reset.danmuku && (this.pakku_url && this.pakku_url.includes("seg.so") && xhrHook.segRequestOnlyOnce)) { xhrHook.segRequestOnlyOnce = false; // pakku.js会禁用Worker,这时需要模拟一个xhr响应 Object.defineProperty(this, "response", { writable: true }); Object.defineProperty(this, "responseText", { writable: true }); Object.defineProperty(this, "readyState", { writable: true }); Object.defineProperty(this, "status", { writable: true }); this.readyState = 4; this.status = 200; this.abort(); let callBack = this.callBack; let xhr = this; getSegDanmaku(function (protoSegments) { let Segments = []; protoSegments.forEach(function (seg) { Segments = Segments.concat(protoSeg.decode(new Uint8Array(seg)).elems); }); toXml(Segments, cid).then(function (toXml) { callBack.forEach(function (f) { xhr.response = xhr.responseText = toXml; f.call(xhr); }); // 备份弹幕 BLOD.xml = xhr.response; }); }); } else if (this.url) { try { // 解除限制 Object.defineProperty(this, "response", { writable: true }); Object.defineProperty(this, "responseText", { writable: true }); Object.defineProperty(this, "readyState", { writable: true }); Object.defineProperty(this, "status", { writable: true }); this.readyState = 2; this.status = 200; this.abort(); let response, accesskey = ""; try { if (BLOD.limit) { // 区域限制 + APP限制的DASH似乎缺少码率信息,现默认启用flv以规避,platform用于伪装成APP if (BLOD.uid && (BLOD.ids.indexOf(1 * cid) >= 0) && config.reset.accesskey) accesskey = GM_getValue("access_key") || ""; let obj = Object.assign(urlObj(this.url), BLOD.__INITIAL_STATE__.rightsInfo.watch_platform ? { access_key: accesskey, balh_ajax: 1, fnval: "", fnver: "", module: "pgc", platform: "android_i" } : { access_key: accesskey, balh_ajax: 1, module: "pgc" }) response = jsonCheck(await xhr.true(objUrl("https://www.biliplus.com/BPplayurl.php", obj))); response = { "code": 0, "message": "success", "result": response }; } } catch (e) { debug.msg("解除限制失败 ಥ_ಥ", e); response = { "code": -404, "message": e, "data": null }; } this.response = this.responseText = JSON.stringify(response); this.readyState = 4; this.onreadystatechange(); if (response.code !== 0) throw response.message; BLOD.__playinfo__ = response; debug.log("解除限制", "aid=", aid, "cid=", cid); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("解除限制", ...e) } } else { send.call(this, ...arg); } } XMLHttpRequest.prototype.addEventListener = function (name, callback) { if (name == "load") { this.callBack = this.callBack || []; this.callBack.push(callback); } return addEventListener.call(this, name, callback); } }, jsonp: () => { const ajax = unsafeWindow.$.ajax; unsafeWindow.$.ajax = function (obj, ...rest) { if (obj) { if (obj.dataType == "jsonp") { let _obj = JSON.parse(JSON.stringify(obj)); if (obj.url.includes("region") && obj.data.rid == 165) { // 替换广告区rid为资讯区rid obj.data.rid = 202; debug.debug("JSONP重定向", "替换广告区", [_obj, obj]); } if (obj.url.includes("region") && obj.data.original == 1) { // 替换原创排行为全部排行 obj.data.original = 0; debug.debug("JSONP重定向", "修复原创排行", [_obj, obj]); } if (obj.url.includes('api.bilibili.com/x/web-interface/ranking/index')) { // 修改置顶推荐 obj.url = obj.url.replace('ranking/index', 'index/top'); debug.debug("JSONP重定向", "修复置顶推荐", [_obj, obj]); } } } return ajax.call(this, obj, ...rest); } }, biliIndexRec: (obj, hook = []) => { try { hook.push(jsonCheck(obj.responseText)); let response = obj.responseText.replace(/preview_banner_list/, "preview").replace(/ranking_list/, "ranking").replace(/recommend_room_list/, "recommend"); response = JSON.parse(response); response.data.text_link = { text: "233秒居然能做这些!", link: "//vc.bilibili.com" }; if (response.data.recommend) { for (let i = 0; i < response.data.recommend.length; i++) { response.data.recommend[i].pic = response.data.recommend[i].cover; response.data.recommend[i].link = "//live.bilibili.com" + response.data.recommend[i].link; } } if (response.data.preview) for (let i = 0; i < response.data.preview.length; i++) response.data.preview[i].url = response.data.preview[i].link; hook.push(response); debug.debug("XHR重定向", "修复正在直播", hook); Object.defineProperty(obj, 'response', { writable: true }); Object.defineProperty(obj, 'responseText', { writable: true }); obj.response = obj.responseText = JSON.stringify(response); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("首页推荐", ...e) } }, // 修复番剧季度信息 season: (obj, hook = []) => { try { hook.push(jsonCheck(obj.responseText)); let response = jsonCheck(obj.responseText); for (let i = 0; i < response.result.section.length; i++) response.result.episodes.push(...response.result.section[i].episodes); for (let i = 0; i < response.result.episodes.length; i++) { response.result.episodes[i].ep_id = response.result.episodes[i].id; response.result.episodes[i].episode_status = response.result.episodes[i].status; response.result.episodes[i].index = response.result.episodes[i].title; response.result.episodes[i].index_title = response.result.episodes[i].long_title; } hook.push(response); debug.debug("XHR重定向", "番剧季度信息", hook); Object.defineProperty(obj, 'response', { writable: true }); Object.defineProperty(obj, 'responseText', { writable: true }); obj.response = obj.responseText = JSON.stringify(response); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("番剧季度信息", ...e) } }, // 修复番剧追番信息 stat: (obj, hook = []) => { try { hook.push(jsonCheck(obj.responseText)); let response = jsonCheck(obj.responseText); response.result.favorites = response.result.follow; hook.push(response); debug.debug("XHR重定向", "番剧追番信息", hook); Object.defineProperty(obj, 'response', { writable: true }); Object.defineProperty(obj, 'responseText', { writable: true }); obj.response = obj.responseText = JSON.stringify(response); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("番剧季度信息", ...e) } }, // 修改直播数据 getRoomPlayInfo: (obj, hook = []) => { if (!config.reset.roomplay) return; try { hook.push(jsonCheck(obj.responseText)); let response = jsonCheck(obj.responseText); if (response.data) { response.data.live_status = 0; response.data.live_time = -1; response.data.playurl_info = null; } hook.push(response); debug.debug("XHR重定向", "拦截直播媒体", hook); Object.defineProperty(obj, 'response', { writable: true }); Object.defineProperty(obj, 'responseText', { writable: true }); obj.response = obj.responseText = JSON.stringify(response); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("直播拦截", ...e) } }, // 修改番剧推荐 recommend: (obj, hook = []) => { try { hook.push(jsonCheck(obj.responseText)); let response = jsonCheck(obj.responseText); if (response.result && response.result.season) response.result = response.result.season; hook.push(response); debug.debug("XHR重定向", "修改番剧推荐", hook); Object.defineProperty(obj, 'response', { writable: true }); Object.defineProperty(obj, 'responseText', { writable: true }); obj.response = obj.responseText = JSON.stringify(response); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("番剧推荐", ...e) } }, // 生成播放信息 carousel: (obj) => { if (!config.reset.carousel) return; try { let msg = randomArray([ ['https://www.bilibili.com/blackboard/activity-4KPC.html', '解锁超清4K画质'], ['https://www.bilibili.com/blackboard/activity-4K120FPS-PC.html', '4K120FPS投稿全量开放'], ['https://www.bilibili.com/blackboard/bilibili2009.html', '十年前的B站长啥样'], ['https://www.bilibili.com/blackboard/html5playerhelp.html', 'HTML5播放器试用'], ], 2); let xmltext = '<msg><item tooltip="" bgcolor="#000000" catalog="system" resourceid="2319" srcid="2320" id="314825"><![CDATA[<a href="' + msg[0][0] + '" target="_blank"><font color="#FFFFFF">' + msg[0][1] + '</font></a>]]></item><item tooltip="" bgcolor="#000000" catalog="system" resourceid="2319" srcid="2321" id="314372"><![CDATA[<a href="' + msg[1][0] + '" target="_blank"><font color="#FFFFFF">' + msg[1][1] + '</font></a>]]></item></msg>'; let parser = new DOMParser(), responseXML = parser.parseFromString(xmltext, "text/xml"); Object.defineProperty(obj, 'responseXML', { writable: true }); obj.responseXML = responseXML; } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("播放通知", ...e) } }, // 强制载入播放器 status: (obj) => { try { let response = jsonCheck(obj.responseText); if (response.result) { if (config.reset.limit && response.result.area_limit) { response.result.area_limit = 0; response.ban_area_show = 1; BLOD.limit = true; } if (response.result.pay) BLOD.vip = 0; if (!response.result.pay && BLOD.big && response.result.dialog) { response.result.pay = 1; BLOD.vip = true; } if (BLOD.limit || BLOD.vip) { Object.defineProperty(obj, 'response', { writable: true }); Object.defineProperty(obj, 'responseText', { writable: true }); obj.response = obj.responseText = JSON.stringify(response); } } } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("强制启用播放器", ...e) } }, // 监听视频地址 playinfo: async (obj) => { try { if (!obj.response) throw obj; BLOD.__playinfo__ = typeof obj.response == "object" ? obj.response : jsonCheck(obj.response); // 刷新下载面板 if (document.getElementById("bili-old-download-table")) download.setTable(); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("视频监听", ...e) } } } const download = BLOD.download = { // 创建播放器右键菜单 init: async (node) => { if (!config.reset.download) return; let li = document.createElement("li"); li.innerHTML = '<a class="context-menu-a js-action" href="javascript:void(0);">下载视频</a>'; li.setAttribute("class", "context-line context-menu-function bili-old-download"); node.firstChild.appendChild(li); li.firstChild.onclick = download.setTable; }, // 配置下载数据 setTable: async () => { debug.msg("正在获取视频链接", ">>>"); let qua = { 125: "HDR", 120: "4K", 116: "1080P60", 112: "1080P+", 80: "1080P", 74: "720P60", 64: "720P", 48: "720P", 32: "480P", 16: "360P", 15: "360P" }; let bps = { 30216: "64kbps", 30232: "128kbps", 30280: "320kbps" } if (!BLOD.mdf) { let path = BLOD.__playinfo__ ? (BLOD.__playinfo__.data || (BLOD.__playinfo__.durl && BLOD.__playinfo__) || BLOD.__playinfo__.result) : ""; BLOD.mdf = {}; BLOD.mdf.quee = BLOD.mdf.quee || ((path && path.durl) ? [await download.geturl()] : await Promise.all([download.geturl(), download.geturl("flv")])); download.quee(BLOD.mdf.quee, qua, bps); download.durl(path, qua); download.dash(path, qua, bps); download.other(); } download.item(); }, // 创建下载面板 item: () => { let timer, top = document.getElementById("bili-old-download-table"); if (top) { // 刷新下载面板 top.remove(); // 释放bolb残留 if (BLOD.bloburl.xml) { window.URL.revokeObjectURL(BLOD.bloburl.xml); BLOD.bloburl.xml = ""; } } if (!BLOD.mdf.mp4 && !BLOD.mdf.flv && !BLOD.mdf.dash) throw (debug.msg("未找到任何视频链接 ಥ_ಥ"), BLOD.mdf); top = document.createElement("div"); top.setAttribute("id", "bili-old-download-table"); if (BLOD.mdf.mp4) download.addBox(top, BLOD.mdf.mp4, "mp4", "download-mp4"); if (BLOD.mdf.dash) { if (BLOD.mdf.dash.avc) download.addBox(top, BLOD.mdf.dash.avc, "avc", "download-avc"); if (BLOD.mdf.dash.hev) download.addBox(top, BLOD.mdf.dash.hev, "hev", "download-hev"); if (BLOD.mdf.dash.aac) download.addBox(top, BLOD.mdf.dash.aac, "aac", "download-aac"); } if (BLOD.mdf.flv) download.addBox(top, BLOD.mdf.flv, "flv", "download-flv"); if (BLOD.mdf.xml) download.addBox(top, BLOD.mdf.xml, "其他", "download-xml", "360P"); document.body.appendChild(top); debug.msg("右键另存为或右键IDM下载", "详见脚本简介", 3000); top.onmouseover = () => window.clearTimeout(timer); top.onmouseout = () => { timer = window.setTimeout(() => { top.remove(); if (BLOD.bloburl.xml) { window.URL.revokeObjectURL(BLOD.bloburl.xml); BLOD.bloburl.xml = ""; } }, 1000) }; }, quee: (path, qua, bps) => { if (path[0] && path[0].durl) { BLOD.mdf.mp4 = BLOD.mdf.mp4 || []; BLOD.mdf.mp4.push(["1080P", path[0].durl[0].url.replace("http:", ""), sizeFormat(path[0].durl[0].size),".mp4"]); navigator.clipboard.writeText(path[0].durl[0].url); } if (path[1]) { path = path[1].data || (path[1].durl && path[1]) || path[1].result || {}; BLOD.mdf.flvq = path.quality || (path.data ? path.data.quality : (path.result ? path.result.quality : "")); download.durl(path, qua); download.dash(path, qua, bps); } }, dash: (path, qua, bps) => { if (!path.dash) return; BLOD.mdf.dash = BLOD.mdf.dash || {}; if (path.dash.video) { for (let i = 0; i < path.dash.video.length; i++) { if (path.dash.video[i].codecs.startsWith("avc")) { BLOD.mdf.dash.avc = BLOD.mdf.dash.avc || []; BLOD.mdf.dash.avc.push([qua[path.dash.video[i].id], path.dash.video[i].baseUrl.replace("http:", ""), sizeFormat(path.dash.video[i].bandwidth * path.dash.duration / 8), ".m4v"]); } else { BLOD.mdf.dash.hev = BLOD.mdf.dash.hev || []; BLOD.mdf.dash.hev.push([qua[path.dash.video[i].id], path.dash.video[i].baseUrl.replace("http:", ""), sizeFormat(path.dash.video[i].bandwidth * path.dash.duration / 8), ".m4v"]); } } } if (path.dash.audio) { for (let i = 0; i < path.dash.audio.length; i++) { BLOD.mdf.dash.aac = BLOD.mdf.dash.aac || []; BLOD.mdf.dash.aac.push([path.dash.audio[i].id, path.dash.audio[i].baseUrl.replace("http:", ""), sizeFormat(path.dash.audio[i].bandwidth * path.dash.duration / 8), ".m4a"]); } BLOD.mdf.dash.aac = bubbleSort(BLOD.mdf.dash.aac, true); for (let i = 0; i < BLOD.mdf.dash.aac.length; i++) if (BLOD.mdf.dash.aac[i][0] in bps) BLOD.mdf.dash.aac[i][0] = bps[BLOD.mdf.dash.aac[i][0]]; } }, durl: (path, qua) => { if (!path.durl) return; if (path.durl[0] && path.durl[0].url.includes("mp4?")) { BLOD.mdf.mp4 = BLOD.mdf.mp4 || []; BLOD.mdf.mp4.push([qua[path.quality], path.durl[0].url.replace("http:", ""), sizeFormat(path.durl[0].size), ".mp4"]); } else { BLOD.mdf.flv = []; for (let i = 0; i < path.durl.length; i++) BLOD.mdf.flv.push([qua[BLOD.mdf.flvq || path.quality], path.durl[i].url.replace("http:", ""), sizeFormat(path.durl[i].size), ".flv"]); } }, other: () => { if (!config.reset.dlother) return; if (BLOD.xml) { let blob = new Blob([BLOD.xml]); BLOD.mdf.xml = []; BLOD.bloburl.xml = URL.createObjectURL(blob); BLOD.mdf.xml.push(["弹幕", BLOD.bloburl.xml, sizeFormat(blob.size), ".xml"]); } else { BLOD.mdf.xml = []; BLOD.mdf.xml.push(["弹幕", "//api.bilibili.com/x/v1/dm/list.so?oid=" + cid, "--------", ".xml"]); } if (BLOD.__INITIAL_STATE__) { BLOD.mdf.xml = BLOD.mdf.xml || []; BLOD.mdf.xml.push(["封面", (BLOD.__INITIAL_STATE__.videoData && BLOD.__INITIAL_STATE__.videoData.pic || BLOD.__INITIAL_STATE__.mediaInfo.cover).replace("http:", ""), "--------", ".jpg"]); if (BLOD.__INITIAL_STATE__.mediaInfo && BLOD.__INITIAL_STATE__.mediaInfo.bkg_cover) BLOD.mdf.xml.push(["海报", BLOD.__INITIAL_STATE__.mediaInfo.bkg_cover.replace("http:", ""), "--------", ".jpg"]); if (BLOD.__INITIAL_STATE__.mediaInfo && BLOD.__INITIAL_STATE__.mediaInfo.specialCover) BLOD.mdf.xml.push(["海报", BLOD.__INITIAL_STATE__.mediaInfo.specialCover.replace("http:", ""), "--------"], ".jpg"); if (BLOD.__INITIAL_STATE__.videoData && BLOD.__INITIAL_STATE__.videoData.subtitle && BLOD.__INITIAL_STATE__.videoData.subtitle.list) for (let i = 0; i < BLOD.__INITIAL_STATE__.videoData.subtitle.list.length; i++) BLOD.mdf.xml.push([BLOD.__INITIAL_STATE__.videoData.subtitle.list[i].lan_doc, BLOD.__INITIAL_STATE__.videoData.subtitle.list[i].subtitle_url.replace("http:", ""), "--------", ".json"]); } }, // 拉取视频链接 geturl: async (...arg) => { let url = await download.playurl(...arg); try { if (!url) throw url; let data = await xhr.GM(url); return jsonCheck(data); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("下载拉取", ...e); } }, // 配置视频链接 playurl: async (type, qn) => { let obj = {}, sign = appkeySign(); BLOD.md5 = BLOD.md5 || (await import(await GM_getResourceURL("md5"))).default; aid = aid || unsafeWindow.aid; cid = cid || unsafeWindow.cid; qn = qn || 120; type = type || "mp4"; if (!cid) return; switch (type) { case 'dash': if (BLOD.pgc) return objUrl("https://api.bilibili.com/pgc/player/web/playurl", { avid: aid, cid: cid, qn: qn, fourk: 1, otype: 'json', fnver: 0, fnval: 16 }); else return objUrl("https://api.bilibili.com/x/player/playurl", { avid: aid, cid: cid, qn: qn, fourk: 1, otype: 'json', fnver: 0, fnval: 16 }); break; case 'flv': if (BLOD.pgc) return objUrl("https://api.bilibili.com/pgc/player/web/playurl", { avid: aid, cid: cid, qn: qn, fourk: 1, otype: 'json' }); else return objUrl("https://api.bilibili.com/x/player/playurl", { avid: aid, cid: cid, qn: qn, fourk: 1, otype: 'json' }); break; case 'off': obj = { appkey: sign[0], cid: cid, otype: 'json', qn: qn, quality: qn, type: '' } obj.sign = BLOD.md5(objUrl("", obj) + sign[1]); return objUrl("https://interface.bilibili.com/v2/playurl", obj); break; case 'mp4': obj = { appkey: sign[0], cid: cid, otype: 'json', platform: 'android_i', qn: 208 } obj.sign = BLOD.md5(objUrl("", obj) + sign[1]); if (BLOD.pgc) return objUrl("https://api.bilibili.com/pgc/player/web/playurl", obj); return objUrl("https://app.bilibili.com/v2/playurlproj", obj); break; } }, addBox: (top, item, name, type, quatily) => { let qua = quatily; let box = document.createElement("div"); box.setAttribute("class", "download-box"); box.innerHTML = '<div class="download-type ' + type + '">' + name + '</div>'; top.appendChild(box); item.forEach(d => { switch (qua || d[0]) { case "HDR": quatily = "quality-tops"; break; case "4K": quatily = "quality-top"; break; case "1080P60": quatily = "quality-highs"; break; case "720P60": quatily = "quality-high"; break; case "1080P+": quatily = "quality-1080ps"; break; case "1080P": quatily = "quality-1080p"; break; case "720P": quatily = "quality-720p"; break; case "480P": quatily = "quality-480p"; break; case "360P": quatily = "quality-360p"; break; case "320kbps": quatily = "quality-720p"; break; case "128kbps": quatily = "quality-480p"; break; case "64kbps": quatily = "quality-360p"; break; default: quatily = "quality-high"; } box.innerHTML += '<a download="' + "av" + aid + d[3] + '" href="' + d[1] + '" target="_blank"><div class="download-quality ' + quatily + '">' + d[0] + '</div><div class="download-size">' + d[2] + '</div></a>'; }) } } // 对象捕获 const getVariable = (value) => { function read(arr) { switch (arr[0]) { case "aid": BLOD.aid = aid = arr[1]; break; case "cid": BLOD.cid = cid = arr[1]; break; case "__playinfo__": BLOD.__playinfo__ = BLOD.__playinfo__ || arr[1]; break; } } if (unsafeWindow.$ && unsafeWindow.$.ajax) xhrHook.jsonp(); else { let timer = setInterval(() => { if (unsafeWindow.$) { clearInterval(timer); xhrHook.jsonp(); } }, 10); setTimeout(() => clearInterval(timer), 5000); } Object.defineProperty(unsafeWindow, "aid", { set: (value) => { read(["aid", value]) }, get: () => { return aid }, configurable: true }); Object.defineProperty(unsafeWindow, "cid", { set: (value) => { read(["cid", value]) }, get: () => { return cid }, configurable: true }); Object.defineProperty(unsafeWindow, "__playinfo__", { set: (value) => { read(["__playinfo__", value]) }, get: () => { return BLOD.__playinfo__ }, configurable: true }); Object.defineProperty(unsafeWindow, "__BILI_CONFIG__", { get: () => { return { "show_bv": false } }, configurable: true }); if (BLOD.path[2] == "live.bilibili.com" && config.reset.roomplay) Object.defineProperty(unsafeWindow, "__NEPTUNE_IS_MY_WAIFU__", { get: () => { return undefined }, configurable: true }); } // 时间格式化 const timeFormat = BLOD.timeFormat = (time, type) => { let date = new Date(time); let Y = date.getFullYear() + '-'; let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'; let D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '; let h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':'; let m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':'; let s = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()); return type ? Y + M + D + h + m + s : h + m + s; } // 格式化存储 const sizeFormat = BLOD.sizeFormat = (size) => { let unit = ["B", "K", "M", "G"], i = unit.length - 1, dex = 1024 ** i, vor = 1000 ** i; while (dex > 1) { if (size >= vor) { size = (size / dex).toFixed(2); break; } dex = dex / 1024; vor = vor / 1000; i--; } return size + unit[i]; } // 格式化进位 const unitFormat = BLOD.unitFormat = (num) => { let unit = ["", "万", "亿"], i = unit.length - 1, dex = 10000 ** i; while (dex > 1) { if (num >= dex) { num = (num / dex).toFixed(1); break; } dex = dex / 10000; i--; } return num + unit[i]; } // 冒泡排序 const bubbleSort = BLOD.bubbleSort = (arr) => { let temp = []; for (let i = 0; i < arr.length - 1; i++) { let bool = true; for (let j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; bool = false; } } if (bool) break; } return arr; } // 随机抽取 const randomArray = BLOD.randomArray = (arr, num) => { let out = []; num = num || 1; num = num < arr.length ? num : arr.length; while (out.length < num) { var temp = (Math.random() * arr.length) >> 0; out.push(arr.splice(temp, 1)[0]); } return out; } // av/BV互转 // https://www.zhihu.com/question/381784377/answer/1099438784 const abv = BLOD.abv = (str) => { let table = 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'; let tr = {}, s = [11, 10, 3, 8, 4, 6], xor = 177451812, add = 8728348608; for (let i = 0; i < 58; i++) tr[table[i]] = i; if (!(1 * str)) { let r = 0; for (let i = 0; i < 6; i++) r += tr[str[s[i]]] * 58 ** i; return (r - add) ^ xor; } else { str = (str ^ xor) + add; let r = ['B', 'V', 1, '', '', 4, '', 1, '', 7, '', '']; for (let i = 0; i < 6; i++) r[s[i]] = table[parseInt(str / 58 ** i) % 58]; return r.join(""); } } // 加密密钥 const appkeySign = BLOD.appkeySign = () => { let table = 'rbMCKn@KuamXWlPMoJGsKcbiJKUfkPF_8dABscJntvqhRSETg', str = ''; for (let i = table.length - 1; i >= 0; i--) str = str + String.fromCharCode(table[i].charCodeAt() + 2); return str.split(':') } // 对象转链接 const objUrl = BLOD.objUrl = (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++; } } if (url) url = url + "?" + arr.join("&"); else url = arr.join("&"); } return url; } // 链接转对象 const urlObj = BLOD.urlObj = (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; } // cookie对象 const getCookies = BLOD.getCookies = () => { let cookies = document.cookie.split('; '); let obj = cookies.reduce((pre, next) => { let key = next.split('=')[0]; let val = next.split('=')[1]; pre[key] = val; return pre; }, {}); return obj; } // proto => xml const toXml = BLOD.toXml = (danmaku, cid) => { return new Promise(function (resolve) { danmaku.sort(function (a, b) { return a.progress - b.progress; }); let dom = (new DOMParser()).parseFromString('<?xml version="1.0" encoding="UTF-8"?><i><chatserver>chat.bilibili.com</chatserver><chatid>' + cid + '</chatid><mission>0</mission><maxlimit>99999</maxlimit><state>0</state><real_name>0</real_name><source>e-r</source></i>', "text/xml"); let root = dom.childNodes[0]; let d, attr, dmk; for (let i in danmaku) { dmk = danmaku[i]; d = dom.createElement("d"); attr = [dmk.progress / 1000, dmk.mode, dmk.fontsize, dmk.color, dmk.ctime, 0, dmk.midHash, dmk.idStr]; d.setAttribute("p", attr.join(",")); d.appendChild(dom.createTextNode(dmk.content)); root.appendChild(d); } resolve(new XMLSerializer().serializeToString(dom)); }); } const getSegDanmaku = BLOD.getSegDanmaku = (onload) => { let protoSegments = []; getSegConfig().then(getAllSeg); function getSegConfig() { return new Promise(function (resolve) { let xhr = new XMLHttpRequest(); xhr.addEventListener("load", function () { let res = protoView.decode(new Uint8Array(xhr.response)); resolve(res); }); xhr.open("get", "https://api.bilibili.com/x/v2/dm/web/view?type=1&oid=" + cid + "&pid=" + aid); xhr.responseType = "arraybuffer"; xhr.send(); }); } // 获得所有分段 function getAllSeg(config) { let total = config.dmSge.total; let allrequset = []; let reqUrl = "https://api.bilibili.com/x/v2/dm/web/seg.so?type=1&oid=" + cid + "&pid=" + aid; for (let index = 1; index <= total; index++) { allrequset.push(new Promise(function (resolve) { let xhr = new XMLHttpRequest(); xhr.addEventListener("load", function () { protoSegments[index] = xhr.response; resolve(); }); xhr.open("get", reqUrl + "&segment_index=" + index); xhr.responseType = "arraybuffer"; xhr.send(); })); } // 完成所有的网络请求大概要300ms return Promise.all(allrequset).then(function () { onload(protoSegments); }); } } // 添加样式 const addCss = BLOD.addCss = async (css) => { let style = document.createElement("style"); style.setAttribute("type", "text/css"); style.appendChild(document.createTextNode(css)); setTimeout(() => { if (document.head) document.head.appendChild(style) }); } // json校验 const jsonCheck = BLOD.jsonCheck = (data, toast) => { data = JSON.parse(data); if ("code" in data && data.code !== 0) { let msg = data.msg || data.message || ""; if (toast) debug.msg("xhr错误:", data.code + " " + msg); throw [data.code, msg, data] } return data; } // 节点垂直偏移 const getTotalTop = BLOD.getTotalTop = (node) => { var sum = 0; do { sum += node.offsetTop; node = node.offsetParent; } while (node); return sum; } // 重写页面 const write = BLOD.write = (html) => { document.open(); document.write(html); document.close(); } // 原生脚本替换 const oldScript = (str) => { str = str.replace("//static.hdslb.com/js/video.min.js", BLOD.getResourceURL("video")); str = str.replace("//static.hdslb.com/phoenix/dist/js/comment.min.js", BLOD.getResourceURL("comment")); return str; } const bofqiToView = BLOD.bofqiToView = () => { let bofqi = document.querySelector("#__bofqi") || document.querySelector(".bangumi_player") || document.querySelector("#bofqi") || ""; bofqi ? bofqi.scrollIntoView({ behavior: 'smooth', block: 'center' }) : ""; } const removePreview = async (node) => { try { if (!config.reset.preview) return; let hint = document.getElementsByClassName("video-float-hint-text")[0]; // 倒计时长度,单位:秒 let i = 10; let sec = document.createElement("span"); sec.setAttribute("class", "video-float-hint-btn second-cut"); hint.parentNode.appendChild(sec); function cut() { sec.innerText = i - 1 + "s"; if (i == 0) { node.remove(); return; } i = i - 1; window.setTimeout(cut, 1000); } new cut(); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("付费预览", ...e) } } const resetSction = async () => { if (!config.reset.grobalboard) return; if (!unsafeWindow.$) { let jq = document.createElement("script"); jq.setAttribute("type", "text/javascript"); jq.setAttribute("src", "//static.hdslb.com/js/jquery.min.js"); document.body.insertBefore(jq, document.body.firstChild); } document.getElementById("internationalHeader").setAttribute("style", "visibility:hidden;"); let newh = document.createElement("div"); let script = document.createElement("script"); let foot = document.getElementsByClassName("international-footer"); script.setAttribute("type", "text/javascript"); script.setAttribute("src", "//s1.hdslb.com/bfs/seed/jinkela/header/header.js"); if (document.getElementsByClassName("mini-type")[0]) { if (location.href.includes("blackboard/topic_list") || location.href.includes("blackboard/x/act_list")) newh.setAttribute("class", "z-top-container has-menu"); else newh.setAttribute("class", "z-top-container"); } else newh.setAttribute("class", "z-top-container has-menu"); document.body.insertBefore(newh, document.body.firstChild); document.body.insertBefore(script, document.body.firstChild); if (foot[0]) { let div = document.createElement("div"); div.setAttribute("class", "footer bili-footer report-wrap-module"); div.setAttribute("id", "home_footer"); foot[0].replaceWith(div); let script = document.createElement("script"); script.setAttribute("type", "text/javascript"); script.setAttribute("src", "//static.hdslb.com/common/js/footer.js"); document.body.appendChild(script); } window.setTimeout(() => { resetNodes() }, 3000); } const switchVideo = async () => { let title = document.getElementsByTagName("h1")[0] ? document.getElementsByTagName("h1")[0].title : ""; if (config.reset.download) { BLOD.url = ""; BLOD.mdf = ""; BLOD.hash = []; }; if (config.reset.selectdanmu && document.getElementsByClassName("bilibili-player-filter-btn")[1]) document.getElementsByClassName("bilibili-player-filter-btn")[1].click(); if (config.reset.midcrc && !config.reset.danmuku && !BLOD.hash[0]) { let data = await xhr.true(objUrl("https://api.bilibili.com/x/v1/dm/list.so", { oid: cid })); data.match(/d p=".+?"/g).forEach((v) => { BLOD.hash.push(v.split(",")[6]) }); } setTimeout(() => { if (config.reset.viewbofqi) bofqiToView(); if (config.reset.widescreen && document.querySelector(".bilibili-player-iconfont.bilibili-player-iconfont-widescreen.icon-24wideoff")) { document.querySelector(".bilibili-player-video-btn.bilibili-player-video-btn-widescreen").click(); } if (config.reset.danmakuoff && !document.querySelector(".bilibili-player-video-btn.bilibili-player-video-btn-danmaku.video-state-danmaku-off")) { if (document.querySelector(".bilibili-player-video-btn.bilibili-player-video-btn-danmaku")) { document.querySelector(".bilibili-player-video-btn.bilibili-player-video-btn-danmaku").click(); } } }); } const fixVideoLost = { // 收藏里的失效视频 favlist: async (msg, data) => { debug.log(1); // src判定是否为频道并取消重复处理 if (!config.reset.lostvideo || BLOD.src) return; // 获取av号或者将bv转为av let title, cover, aid = msg.target.getAttribute("data-aid"); if (!(1 * aid)) aid = abv(aid); if (BLOD.ids.indexOf(aid) != -1) return; // 记录已经处理过的视频aid BLOD.ids.push(aid); try { // 尝试读取来自jijidown的数据 data = await xhr.GM("https://www.jijidown.com/video/av" + aid); data.match('window._INIT')[0]; title = data.match(/\<title\>.+?\-哔哩哔哩唧唧/)[0].replace(/<title>/, "").replace(/-哔哩哔哩唧唧/, ""); cover = data.match(/\"img\":\ \".+?\",/)[0].match(/http.+?\",/)[0].replace(/",/, ""); // 判断封面是否有效 cover.match('hdslb')[0]; } catch (e) { try { // 尝试读取来自biliplus数据 data = await xhr.GM("https://www.biliplus.com/video/av" + aid); data.match(/\<title\>.+?\ \-\ AV/)[0]; title = data.match(/\<title\>.+?\ \-\ AV/)[0].replace(/<title>/, "").replace(/ - AV/, ""); cover = data.match(/\<img style=\"display:none\"\ src=\".+?\"\ alt/)[0].replace(/<img style="display:none" src="/, "").replace(/" alt/, ""); } catch (e) { // 无有效数据只能把标题改为av号 title = "av" + aid; } } debug.log("失效视频", "av" + aid); if (cover) msg.target.children[0].children[0].setAttribute("src", cover + "@380w_240h_100Q_1c.webp"); msg.target.children[0].children[0].setAttribute("alt", title); msg.target.children[1].setAttribute("href", "//www.bilibili.com/video/av" + aid); msg.target.children[1].setAttribute("title", title); msg.target.children[1].setAttribute("style", "text-decoration : line-through;color : #ff0000;"); msg.target.children[1].text = title; msg.target.setAttribute("class", "small-item"); msg.target.children[0].setAttribute("href", "//www.bilibili.com/video/av" + aid); msg.target.children[0].setAttribute("target", "_blank"); msg.target.children[0].setAttribute("class", "cover cover-normal"); }, // 频道里的失效视频 channel: async (link) => { if (!config.reset.lostvideo || BLOD.src == window.src) return; window.src = BLOD.src; try { let data, obj = urlObj(link), cid = obj.cid, mid = obj.mid, pn = obj.pn; let small_item = document.getElementsByClassName("small-item"); if (small_item[0]) for (let i = 0; i < small_item.length; i++) if (small_item[i].getElementsByClassName("title")[0].title == "已失效视频") break; data = await xhr.true(objUrl("https://api.bilibili.com/x/space/channel/video", { "mid": mid, "cid": cid, "pn": pn, "ps": 30, "order": 0 })); data = jsonCheck(data).data; for (let i = 0; i < small_item.length; i++) { let aid = small_item[i].getAttribute("data-aid") * 1; let title = data.list.archives[i].title || "av" + aid; if (small_item[i].children[1].title == "已失效视频") { small_item[i].setAttribute("class", "small-item fakeDanmu-item"); if (aid) { // 修复失效视频av号 debug.log("失效视频", "av" + aid); small_item[i].children[1].setAttribute("href", "//www.bilibili.com/video/av" + aid); small_item[i].children[0].setAttribute("href", "//www.bilibili.com/video/av" + aid); } else { // 修复失效视频bv号 aid = small_item[i].getAttribute("data-aid"); debug.log("失效视频", aid); small_item[i].children[1].setAttribute("href", "//www.bilibili.com/video/" + aid); small_item[i].children[0].setAttribute("href", "//www.bilibili.com/video/" + aid); } small_item[i].children[0].setAttribute("target", "_blank"); small_item[i].children[0].setAttribute("class", "cover cover-normal"); small_item[i].children[0].children[0].setAttribute("alt", title); small_item[i].children[0].children[0].setAttribute("src", data.list.archives[i].pic.replace("http", "https") + "@380w_240h_100Q_1c.webp"); small_item[i].children[1].setAttribute("target", "_blank"); small_item[i].children[1].setAttribute("title", title); small_item[i].children[1].setAttribute("style", "text-decoration : line-through;color : #ff0000;"); small_item[i].children[1].text = title; } } } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("失效视频·频道", ...e) } }, // 空间首页展示的失效视频 home: async (msg) => { if (!config.reset.lostvideo) return; let channel_item = document.getElementsByClassName("channel-item"); if (channel_item[0]) { let small_item = document.getElementsByClassName("small-item"); if (small_item[0]) { for (let i = 0; i < small_item.length; i++) { if (small_item[i].getAttribute("class") == "small-item disabled") { small_item[i].setAttribute("class", "small-item fakeDanmu-item"); let aid = small_item[i].getAttribute("data-aid") * 1; if (aid) { // 修改失效视频av链接 debug.log("失效视频", "av" + aid); small_item[i].children[1].setAttribute("href", "//www.bilibili.com/video/av" + aid); small_item[i].children[0].setAttribute("href", "//www.bilibili.com/video/av" + aid); } else { // 修改失效视频bv链接 aid = small_item[i].getAttribute("data-aid"); debug.log("失效视频", aid); small_item[i].children[1].setAttribute("href", "//www.bilibili.com/video/" + aid); small_item[i].children[0].setAttribute("href", "//www.bilibili.com/video/" + aid); } small_item[i].children[0].setAttribute("target", "_blank"); small_item[i].children[0].setAttribute("class", "cover cover-normal"); small_item[i].children[1].setAttribute("target", "_blank"); small_item[i].children[1].setAttribute("title", small_item[i].children[0].children[0].alt); small_item[i].children[1].setAttribute("style", "text-decoration : line-through;color : #ff0000;"); small_item[i].children[1].text = small_item[i].children[0].children[0].alt; } } } } // 固定失效视频数据防止被页面改回去 if (msg.relatedNode.text == '已失效视频') msg.relatedNode.text = msg.relatedNode.getAttribute("title"); if (msg.target.className == "small-item disabled") msg.target.className = "small-item"; } } const setBangumi = { init: async (data) => { if (!config.reset.episodedata) return; // 判断是否有分集数据 if (data.epList[1] && (data.epList[0].aid != data.epList[1].aid)) { aid = data.epInfo.aid; let timer = window.setInterval(() => { if (document.getElementsByClassName("info-sec-av")[0]) { setBangumi.episodeData("first"); window.clearInterval(timer); } }, 1000); // 延时取消操作,10s还未载入完成将不再处理 window.setTimeout(() => window.clearInterval(timer), 10000); } }, // 分集数据处理 episodeData: async (data, msg) => { try { let views = document.getElementsByClassName("view-count")[0].getElementsByTagName("span")[0]; let danmakus = document.getElementsByClassName("danmu-count")[0].getElementsByTagName("span")[0]; if (data == "first") { // 判断是否是首次处理 if (views.innerText == "-" && danmakus.innerText == "-") { window.setTimeout(() => { setBangumi.episodeData("first") }, 100); return; } // 备份总播放数和弹幕数 views.setAttribute("title", "总播放数 " + views.innerText); danmakus.setAttribute("title", "总弹幕数 " + danmakus.innerText); debug.debug("总播放数", views.innerText, " 总弹幕数", danmakus.innerText); data = await xhr.true(objUrl("https://api.bilibili.com/x/web-interface/archive/stat", { "aid": aid })); } if (!data) { aid = msg.relatedNode.innerText.match(/[0-9]+/)[0]; data = await xhr.true(objUrl("https://api.bilibili.com/x/web-interface/archive/stat", { "aid": aid })); } data = jsonCheck(data).data; let view = data.view; let danmaku = data.danmaku; view = unitFormat(view); danmaku = unitFormat(danmaku); views.innerText = view; danmakus.innerText = danmaku; debug.debug("播放", view + " 弹幕", danmaku); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("分集数据", ...e) } }, } const fixnews = async (node, move) => { try { let rank = config.reset.grobalboard ? document.getElementsByClassName("rank-tab")[0] : ""; if (node.id == "bili_ad") { let sight = node.getElementsByTagName("a"); node = node.getElementsByClassName("name"); if (node[0]) node[0].text = "资讯"; for (let i = 0; i < sight.length; i++) if (sight[i].href.includes("www.bilibili.com/v/ad/ad/")) sight[i].href = "https://www.bilibili.com/v/information/"; let rcon = document.createElement("div"); rcon.setAttribute("class", "r-con"); rcon.innerHTML = '<div class="r-con"><header style="margin-bottom: 14px"><h3 style="font-size: 18px;font-weight: 400;">资讯分区正式上线啦!</h3></header><div class="carousel-module"><div class="panel"><a href="https://www.bilibili.com/v/information" target="_blank"><img src="//i0.hdslb.com/bfs/archive/0747d26dbbc3bbf087d47cff49e598a326b0030c.jpg@320w_330h_1c.webp" width="260" height="280"/></a></div></div></div>'; document.getElementById("ranking_ad").replaceWith(rcon); } if (node.className == "report-wrap-module elevator-module") for (let item of node.children[1].children) if (item.innerHTML == "广告") item.innerHTML = "资讯"; if (node.id == "bili-header-m") { node = node.getElementsByClassName('nav-name'); if (node[0]) { for (let i = 0; i < node.length; i++) { if (node[i].textContent == "科技") { move = node[i].parentNode.parentNode.children[1].lastChild.cloneNode(true); move.firstChild.href = move.firstChild.href.replace("technology", "life"); node[i].parentNode.parentNode.children[1].lastChild.remove(); } if (node[i].textContent == "广告") { node[i].textContent = "资讯"; node[i].parentNode.href = "//www.bilibili.com/v/information/"; } if (node[i].textContent == "生活") { let sight = node[i].parentNode.parentNode.children[1]; sight.insertBefore(move, sight.lastChild) } if (node[i].textContent == "娱乐") node[i].parentNode.parentNode.children[1].lastChild.remove(); } } } if (rank && rank.children[5]) { rank.children[5].innerText == "知识" ? rank.children[5].innerText = "科技" : ""; rank.children[6].innerText == "知识" ? rank.children[6].innerText = "科技" : ""; } } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("分区·版面", ...e) } } const setReplyFloor = { init: async (link) => { BLOD.src = ""; if (!config.reset.replyfloor) return; try { let mode, data, obj = urlObj(link), oid = obj.oid, sort = obj.sort, pn = obj.pn, root = obj.root, type = obj.type; // sort与mode对应转化,sort == 1时暂时处理不了直接退出 // 热门:sort=2 mode=3 时间:sort=0 mode=2 回复:sort=1 默认(热门+时间): mode=1 if (sort == 0) mode = 1; if (sort == 1) throw ["暂无法处理按回复排列的评论", obj]; 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.true(objUrl("https://api.bilibili.com/x/v2/reply/reply/cursor", { "oid": oid, "root": root, "type": type })); else { // 3页以上先获取当页首条评论rpid 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;}}} // 根据当页首条评论rpid获取min_id data = await xhr.true(objUrl("https://api.bilibili.com/x/v2/reply/dialog/cursor", { "oid": oid, "root": root, "type": type, "dialog": dialog, "size": 20 })); let min_id = jsonCheck(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 { debug.msg("当前页楼中楼层获取失败 ಥ_ಥ"); return; } // 根据min_id获取当页数据 data = await xhr.true(objUrl("https://api.bilibili.com/x/v2/reply/reply/cursor", { "oid": oid, "root": root, "type": type, "min_id": min_id })); } } else { if (sort == 2) data = await xhr.true(objUrl("https://api.bilibili.com/x/v2/reply/main", { "oid": oid, "next": pn, "type": type, "mode": mode })); else if (pn == 1) data = await xhr.true(objUrl("https://api.bilibili.com/x/v2/reply/main", { "oid": oid, "type": type, "mode": mode })); else { // 时间排序的楼层号需要相对前页判定 pn = pn - 1; data = await xhr.true(objUrl("https://api.bilibili.com/x/v2/reply", { "type": type, "sort": sort, "oid": oid, "pn": pn })); data = jsonCheck(data).data; let i = data.replies.length - 1; oid = data.replies[0].oid; let root = data.replies[i].rpid; data = await xhr.true(objUrl("https://api.bilibili.com/x/v2/reply/reply/cursor", { "oid": oid, "root": root, "type": type })); data = jsonCheck(data).data; oid = data.root.oid; let next = data.root.floor; data = await xhr.true(objUrl("https://api.bilibili.com/x/v2/reply/main", { "oid": oid, "next": next, "type": type, "mode": mode })); } } data = jsonCheck(data).data; setReplyFloor.fix(setReplyFloor.floor(data)); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("评论楼层", ...e) } }, // 纪录楼层对照表 floor: (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; return floor; }, // 修复楼层号 fix: (floor) => { let list_item = document.getElementsByClassName("reply-wrap"); let main_floor = document.getElementsByTagName("li"); // 旧版评论直接写入楼层号 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); } } } } } const fixVideoSeek = (node) => { if (document.querySelector("#bofqi")) { node.querySelectorAll("a.video-seek").forEach(function (v) { v.addEventListener("click", function (e) { bofqiToView(); unsafeWindow.player.seek(Number(e.target.attributes[2].nodeValue)); }) }) } } const electricPanelJump = async (node) => { try { if (!config.reset.electric) return; config.reset.electric = 0; setTimeout(() => { node.click() }, 1); setTimeout(() => { config.reset.electric = 1 }, 5000); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("充电鸣谢", ...e) } } const fixrank = async (node) => { // 这些分区排行榜已全部采用类似番剧排行的模式,故采用相似的节点覆盖 let sort = { bili_movie: ["ranking_movie", 2, "https://www.bilibili.com/ranking/cinema/23/0/3"], bili_teleplay: ["ranking_teleplay", 5, "https://www.bilibili.com/ranking/cinema/11/0/3"], bili_documentary: ["ranking_documentary", 3, "https://www.bilibili.com/ranking/cinema/177/0/3"] } sort = sort[node.id]; if (!sort) return; let section = node.getElementsByClassName("sec-rank report-wrap-module zone-rank")[0]; section.innerHTML = '<header class="rank-head"><h3>排行</h3></header><div class="rank-list-wrap"><ul class="bangumi-rank-list rank-list"></ul></div><a href="' + sort[2] + '" target="_blank" class="more-link">查看更多<i class="icon icon-arrow-r"></i></a>'; try { let data = await xhr.true(objUrl("https://api.bilibili.com/pgc/season/rank/web/list", { season_type: sort[1], day: 3 })); data = jsonCheck(data).data; node = node.getElementsByClassName("bangumi-rank-list rank-list")[0]; for (let i = 0; i < 8; i++) { let li = document.createElement("li"), cl = i < 3 ? "rank-item highlight" : "rank-item", fw; li.setAttribute("class", cl); li.innerHTML = '<i class="ri-num">' + (i + 1) + '</i><a href="' + data.list[i].url + '" target="_blank" title="' + data.list[i].title + ' 播放:' + data.list[i].stat.view + '" class="ri-info-wrap"><p class="ri-title">' + data.list[i].title + '</p><span class="ri-total">' + data.list[i].new_ep.index_show + '</span></a>'; li.onmouseover = () => { fw = document.createElement("div"); fw.setAttribute("class", "bangumi-info-module"); fw.setAttribute("style", 'left: ' + li.getBoundingClientRect().left + 'px; top: ' + (getTotalTop(li) - 150) + 'px;'); fw.innerHTML = '<div class="v-preview clearfix"><div class="lazy-img cover"><img alt="' + data.list[i].title + '" src="' + data.list[i].cover + '" /></div><div><p class="title">' + data.list[i].title + '</p><p class="desc">' + data.list[i].new_ep.index_show + '</p></div></div><div class="v-data"><span class="play"><i class="icon"></i>' + unitFormat(data.list[i].stat.view) + '</span><span class="danmu"><i class="icon"></i>' + unitFormat(data.list[i].stat.danmaku) + '</span><span class="fav"><i class="icon"></i>' + unitFormat(data.list[i].stat.follow) + '</span></div>'; document.body.appendChild(fw); } li.onmouseout = () => fw.remove(); node.appendChild(li); } } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("分区排行", ...e) } } const danmkuHashId = async (node) => { if (!config.reset.midcrc) return; BLOD.midcrc = BLOD.midcrc || (await import(await GM_getResourceURL("crc"))).default; let index = 1 * node.getAttribute("dmno"); node.addEventListener("contextmenu", () => { setTimeout(async (data) => { try { let descipline = document.createElement("li"); let onwer = document.createElement("li"); let mid = BLOD.midcrc(BLOD.hash[index]); node = document.getElementsByClassName("bili-old-hash"); if (node[0]) for (let i = 0; i < node.length; i++) node[i].remove(); if (document.getElementsByClassName("bilibili-player-icon bilibili-player-icon-arrow-down")[0]) return; if (document.getElementsByClassName("bilibili-player-icon bilibili-player-icon-arrow-up")[0]) return; descipline.setAttribute("class", "context-line context-menu-descipline bili-old-hash"); descipline.innerHTML = '<a class="context-menu-a" href="javascript:void(0);"></a>'; onwer.setAttribute("class", "context-line context-menu-function bili-old-hash"); onwer.innerHTML = '<a class="context-menu-a js-action" title="" href="//space.bilibili.com/' + mid + '">hash: ' + BLOD.hash[index] + " mid: " + mid + '</a>'; node = document.getElementsByClassName("bilibili-player-context-menu-container")[0]; node.firstChild.insertBefore(descipline, node.firstChild.firstChild); onwer = node.firstChild.insertBefore(onwer, node.firstChild.firstChild); data = jsonCheck(await xhr.true(objUrl("https://api.bilibili.com/x/web-interface/card", { mid: mid }))); onwer.innerHTML = '<div style="min-height:0px;z-index:-5;" class="bb-comment"><div style="padding-top:10px;" class="comment-list"><div class="list-item"><div class="reply-box"><div style="padding:0px" class="reply-item reply-wrap"><div style="margin-left: 15px;" class="reply-face"><img src="' + data.data.card.face + '@52w_52h.webp" alt=""></div><div class="reply-con"><div class="user"><a style="display:initial;padding: 0px;" data-usercard-mid="' + mid + '" href="//space.bilibili.com/' + mid + '" target="_blank" class="' + (data.data.card.vip.vipType > 1 ? "name vip-red-name" : "name") + '">' + data.data.card.name + '</a> ' + data.data.card.sex + '<a style="display:initial;padding: 0px;" href="//www.bilibili.com/blackboard/help.html#%E4%BC%9A%E5%91%98%E7%AD%89%E7%BA%A7%E7%9B%B8%E5%85%B3" target="_blank"><i class="level l' + data.data.card.level_info.current_level + '"></i></a></div></div></div></div></div></div></div>'; } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("弹幕反查", ...e) } }) }) } const resetNodes = async (ext) => { let remove = (node, type, hidden, index) => { index ? index : index = 0; switch (type) { case "id": node = document.getElementById(node); break; case "class": node = document.getElementsByClassName(node)[index] ? document.getElementsByClassName(node)[index] : ""; break; case "tag": node = document.getElementsByTagName(node)[index] ? document.getElementsByTagName(node)[index] : ""; break; } if (!node || node.getAttribute("hidden")) return; // 一般能移除的就移除,否则隐藏 debug.debug("移除节点", node); hidden ? node.setAttribute("hidden", "hidden") : node.remove(); } // 隐藏联系客服 remove("contact-help", "class", true); // 移除新版提示 remove("new-entry", "class"); if (BLOD.path.name == "index") remove("ver", "class"); remove("trynew-btn", "class"); if (config.reset.panel) remove("bilibili-player-ending-panel", "class"); // 移除app下载浮动框 remove("fixed_app_download", "id"); remove("app-download", "class"); // 移除直播水印 remove("bilibili-live-player-video-logo", "class"); // 移除失效顶栏 remove("bili-header-m report-wrap-module", "class", false, 1); // 移除主页昨日榜 if (BLOD.path.name == "index") remove("rec-btn prev", "class"); // 移除主页七日榜 if (BLOD.path.name == "index") remove("rec-btn next", "class"); // 移除双重视频下载右键菜单 if (document.getElementsByClassName("bili-old-download")[1]) document.getElementsByClassName("bili-old-download")[0].remove(); // 使顶栏透明 if (config.reset.headblur) { let blur = document.getElementsByClassName("blur-bg"); if (blur[0]) blur[0].removeAttribute("style"); } } const setMediaList = { init: async (data) => { if (!BLOD.ml) return; if (data) { // 以传参data决定处理类型 try { // 获取首个视频av并跳转 data = await xhr.true(objUrl("https://api.bilibili.com/x/v1/medialist/detail", { "media_id": BLOD.ml, "pn": 1, "ps": 1 })); data = jsonCheck(data).data; location.replace("https://www.bilibili.com/video/av" + data.medias[0].id); } catch (e) { // 跳转失败,清理残余 GM_setValue("medialist", 0); debug.error(e); } } else { try { let avs = [], value = [], promises = []; // 获取收藏列表,这里获取只能获取到aid data = await xhr.true(objUrl("https://api.bilibili.com/x/v1/medialist/resource/ids4Player", { "media_id": BLOD.ml })); data = jsonCheck(data).data; for (let i = 0; i < data.medias.length; i++) { BLOD.ids[i] = data.medias[i].id; avs[i] = "av" + data.medias[i].id; } // 同时获取所有aid对应的数据,使用Promise.all对齐,该api会直接忽略失效视频 while (avs.length) { let i = avs.length > 20 ? 20 : avs.length; value = avs.splice(0, i); promises.push(xhr.true(objUrl("https://api.bilibili.com/x/article/cards", { "ids": value.join("%2C") }))); } value = []; data = await Promise.all(promises); // 格式化数据并排序 for (let i = 0; i < data.length; i++) { data[i] = jsonCheck(data[i]); for (let key in data[i].data) avs.push(data[i].data[key]); } for (let i = 0; i < BLOD.ids.length; i++) { for (let j = 0; j < avs.length; j++) { if (avs[j].aid == BLOD.ids[i]) { value.push(avs[j]); break; } } } BLOD.ids = value; let timer = window.setInterval(() => { if (unsafeWindow.BilibiliPlayer) { clearInterval(timer); // 将视频列表重构为稍后再看列表 for (let i = 0; i < BLOD.ids.length; i++) { BLOD.ids[i].progress = 0; BLOD.ids[i].add_at = BLOD.ids[i].ctime; BLOD.ids[i].pages = []; BLOD.ids[i].pages[0] = {}; BLOD.ids[i].pages[0].cid = BLOD.ids[i].cid; BLOD.ids[i].pages[0].dimension = BLOD.ids[i].dimension; BLOD.ids[i].pages[0].duration = BLOD.ids[i].duration; BLOD.ids[i].pages[0].from = "vupload"; BLOD.ids[i].pages[0].page = 1; BLOD.ids[i].pages[0].part = BLOD.ids[i].title; BLOD.ids[i].pages[0].vid = ""; BLOD.ids[i].pages[0].weblink = ""; } let toview = { "code": 0, "message": "0", "ttl": 1, "data": { "count": BLOD.ids.length, "list": BLOD.ids } }; // 保存初始aid,以便判断是否切p BLOD.oid = BLOD.ids[0].aid; debug.debug("收藏列表", toview); // 构造初始化参数并重新初始化播放器 BLOD.obj = { "aid": BLOD.ids[0].aid, "cid": BLOD.ids[0].cid, "watchlater": encodeURIComponent(JSON.stringify(toview)) }; // 重构初始化播放器参数 unsafeWindow.BilibiliPlayer(BLOD.obj); let bpui = document.getElementsByClassName("bpui-button-text"); let t = setInterval(() => { // 更新列表名称 if (bpui[1]) { clearInterval(t); bpui[1].firstChild.innerText = "收藏列表"; } }, 100); } }, 100); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("收藏模拟", ...e) } } }, // aid变化监听 fixvar: async () => { if (!aid) aid = unsafeWindow.aid ? unsafeWindow.aid : aid; if (BLOD.oid) { if (BLOD.oid != unsafeWindow.aid) { // 收藏播放切p判断 aid = unsafeWindow.aid ? unsafeWindow.aid : aid; BLOD.oid = unsafeWindow.aid; setMediaList.restore(); } } }, // 收藏播放更新 restore: async () => { let data; history.replaceState(null, null, "https://www.bilibili.com/video/av" + aid + location.search + location.hash); for (let i = 0; i < BLOD.ids.length; i++) if (BLOD.ids[i].aid == aid) data = BLOD.ids[i]; let video_info = document.getElementById("viewbox_report"); let up_info = document.getElementById("v_upinfo") let arc_toolbar_report = document.getElementById("arc_toolbar_report"); document.title = data.title; video_info.innerHTML = '<h1 title="' + data.title + '"><!----><span>' + data.title + '</span></h1>' + '<div class="tm-info tminfo"><span class="crumb"><a href="//www.bilibili.com">主页</a> ></span> <span class="crumb"><a href="//www.bilibili.com/v/douga/">动画</a> ></span> <span class="crumb"><a href="//www.bilibili.com/v/douga/mad/">MAD·AMV</a></span><time>' + timeFormat(data.pubdate * 1000, true) + '</time><a class="btn-appeal">稿件投诉</a></div>' + '<div class="number"><span title="总播放数' + data.stat.view + '" class="v play">' + unitFormat(data.stat.view) + '</span><span title="总弹幕数' + data.stat.danmaku + '" class="v dm">' + unitFormat(data.stat.danmaku) + '</span><span title="本日日排行数据过期后,再纳入本稿件的历史排行数据进行对比得出" class="v rank">最高全站日排行' + data.stat.like + '名</span><span class="line"></span><span class="u like" style="margin-right : 5px;" title="点赞人数' + data.stat.his_rank + '"><i class="l-icon-move" style="width : 22px;height : 22px;background-position : -660px -2068px;"></i><b class="l-icon-moved" style="width : 22px;height : 22px;background-position : -725px -2068px;display : none;"></b> 点赞 ' + unitFormat(data.stat.like) + '</span><span report-id="coinbtn1" title="投硬币枚数' + data.stat.coin + '" class="u coin"><i class="c-icon-move"></i><b class="c-icon-moved" style="background-position: -2340px -60px; display: none;"></b> 硬币 ' + unitFormat(data.stat.coin) + '</span> <span report-id="collect1" title="收藏人数' + data.stat.favorite + '" class="u fav"><i class="f-icon-move" style="background-position: 0px 0px;"></i><b class="f-icon-moved" style="background-position: -1740px -60px; display: none;"></b> 收藏 ' + unitFormat(data.stat.favorite) + '</span></div>'; up_info.innerHTML = '<div class="u-face fl"><!----><a href="//space.bilibili.com/' + data.owner.mid + '" target="_blank" report-id="head" class="a"><img src="' + data.owner.face + '@68w_68h.webp" width="68" height="68" class="up-face" /><!----><!----><i title="企业/团体认证" class="auth o-auth"></i></a></div>' + '<div class="info"><div class="user clearfix"><a href="//space.bilibili.com/' + data.owner.mid + '" target="_blank" report-id="name" class="name is-vip">' + data.owner.name + '</a><a href="//message.bilibili.com/#whisper/mid' + data.owner.mid + '" target="_blank" report-id="message" class="message icon">发消息</a></div><div class="sign static"><span>up主简介</span><!----></div><div class="number clearfix"><span title="投稿数--">投稿:--</span><span title="粉丝数--">粉丝:--</span></div><div class="btn followe"><a report-id="follow1" class="bi-btn b-gz"><span class="gz">+ 关注</span><span class="ygz">已关注</span><span class="qxgz">取消关注</span></a><a report-id="charge" class="bi-btn b-cd elecrank-btn"><span class="cd">充电</span><span class="wtcd">为TA充电</span></a></div></div>'; arc_toolbar_report.children[0].children[0].title = "分享人数" + data.stat.share; arc_toolbar_report.children[0].children[0].innerHTML = '<span class="t">分享</span><span class="num">' + unitFormat(data.stat.share) + '</span><i class="icon"></i>'; arc_toolbar_report.children[2].title = "收藏人数" + data.stat.favorite; arc_toolbar_report.children[2].innerHTML = '<div class="btn-item"><i class="icon-move f-icon-moved" style="display: none;"></i><b class="icon-move f-icon-move"></b><span class="t">收藏</span><span class="num">' + unitFormat(data.stat.favorite) + '</span></div>'; arc_toolbar_report.children[3].title = "投硬币枚数" + data.stat.coin; arc_toolbar_report.children[3].innerHTML = '<div class="btn-item"><i class="icon-move c-icon-moved" style="display: none;"></i><b class="icon-move c-icon-move"></b><span class="t">硬币</span><span class="num">' + unitFormat(data.stat.coin) + '</span></div>'; document.getElementById("v_tag").children[0].setAttribute("hidden", "hidden"); document.getElementById("v_desc").children[1].innerText = data.desc; new unsafeWindow.bbComment(".comment", unsafeWindow.aid, 1, unsafeWindow.UserStatus.userInfo, ""); data.stat.like ? video_info.children[2].children[2].setAttribute("style", "display: inline-block;") : video_info.children[2].children[2].setAttribute("style", "display: none;"); let bpui = document.getElementsByClassName("bpui-button-text"); let t = setInterval(() => { // 更新列表名称 if (bpui[1]) { clearInterval(t); bpui[1].firstChild.innerText = "收藏列表"; } }, 100); }, } const avdesc = async () => { if (!config.rewrite.av || !aid || BLOD.path[3] != 'video') return; let desc = document.getElementsByClassName("info"); if (desc[1] && desc[1].outerHTML.match(/BV[A-Za-z0-9]+/i)) { let paster = desc[1].outerHTML.match(/BV[A-Za-z0-9]+/i); for (let i = 0; i < paster.length; i++) { let newer = "av" + abv(paster[i]); newer = '<a target="_blank" href="//www.bilibili.com/video/' + newer + '">' + newer + '</a>'; desc[1].innerHTML = desc[1].outerHTML.replace(paster[i], newer); } } } const fixSort = { // av video: async () => { let sort = JSON.parse(GM_getResourceText("sort")); let timer = window.setInterval(() => { let tminfo = document.getElementsByClassName("tm-info"); if (tminfo[0]) { window.clearInterval(timer); if (!(BLOD.tid in sort)) return; let nodes = tminfo[0].childNodes; // 创建分区信息节点并写入tid对应的分区数据 nodes[1].replaceWith(nodes[0].cloneNode(true)); nodes[2].replaceWith(nodes[0].cloneNode(true)); nodes[2].childNodes[1].remove(); nodes[1].childNodes[0].href = sort[sort[BLOD.tid][0]][2]; nodes[1].childNodes[0].innerText = sort[sort[BLOD.tid][0]][1]; nodes[2].childNodes[0].href = sort[BLOD.tid][2]; nodes[2].childNodes[0].innerText = sort[BLOD.tid][1]; } }, 1000); }, // 稍后再看 watchlater: async (data) => { let sort = JSON.parse(GM_getResourceText("sort")); let timer = window.setInterval(async () => { let tminfo = document.getElementsByClassName("tm-info"); // 判断是否是少后再看页面 if (tminfo[0] && aid) { window.clearInterval(timer); let child = tminfo[0].childNodes; if (child[2].nodeType === 8) { try { // 通过链接获取tid data = await xhr.true(objUrl("https://api.bilibili.com/x/web-interface/view", { "aid": aid })); BLOD.tid = jsonCheck(data).data.tid; if (!(BLOD.tid in sort)) return; // 创建分区信息节点并写入tid对应的分区数据 child[2].replaceWith(child[0].cloneNode(true)); child[4].replaceWith(child[0].cloneNode(true)); child[4].childNodes[1].remove(); child[2].childNodes[0].href = sort[sort[BLOD.tid][0]][2]; child[2].childNodes[0].innerText = sort[sort[BLOD.tid][0]][1]; child[4].childNodes[0].href = sort[BLOD.tid][2]; child[4].childNodes[0].innerText = sort[BLOD.tid][1]; } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("分区·稍后再看", ...e) } } } }, 1000); }, } const setLike = async (data) => { if (!config.reset.like) return; let timer = window.setInterval(async () => { let coin = document.getElementsByClassName("bilibili-player-video-subtitle")[0]; let number = document.getElementsByClassName("number")[0]; let node = document.getElementsByClassName("coin")[0]; // 判断页面渲染进度 if (coin && node) { window.clearInterval(timer); let span = document.createElement("span"); let move = document.createElement("i"); let moved = document.createElement("b"); let text = document.createTextNode("点赞 --"); let arg = text; // 创建点赞数据相关节点并初始化 span.setAttribute("class", "u like"); span.setAttribute("style", "margin-right : 5px;"); span.appendChild(move); span.appendChild(moved); span.appendChild(text); move.setAttribute("class", "l-icon-move"); move.setAttribute("style", "width : 22px;height : 22px;background-position : -660px -2068px;"); moved.setAttribute("class", "l-icon-moved"); moved.setAttribute("style", "width : 22px;height : 22px;background-position : -725px -2068px;display : none;"); try { move.onclick = async () => { // 没有点赞过绑定点赞点击事件 if (!BLOD.uid) { // 没有登录(不可用)绑定快捷登录(不可用) document.getElementsByClassName("c-icon-move")[0].click(); return; } // 构造并请求点赞表单 let msg = "aid=" + aid + "&like=1&csrf=" + getCookies().bili_jct; data = await xhr.post("https://api.bilibili.com/x/web-interface/archive/like", "application/x-www-form-urlencoded", msg); data = jsonCheck(data).ttl; // 点亮点赞图标并修改显示数据 debug.msg("点赞成功!"); document.getElementsByClassName("l-icon-move")[0].setAttribute("style", "width : 22px;height : 22px;background-position : -660px -2068px;display : none;"); document.getElementsByClassName("l-icon-moved")[0].setAttribute("style", "width : 22px;height : 22px;background-position : -725px -2068px;"); if (arg.nodeValue.match("万")) return; let number = 1 * arg.nodeValue.match(/[0-9]+/) + 1; text = document.createTextNode(" 点赞 " + number) arg.replaceWith(text); arg = text; } moved.onclick = async () => { // 点赞过绑定取消点赞点击事件 // 构造并请求取消点赞表单 let msg = "aid=" + aid + "&like=2&csrf=" + getCookies().bili_jct; data = await xhr.post("https://api.bilibili.com/x/web-interface/archive/like", "application/x-www-form-urlencoded", msg); data = jsonCheck(data).ttl; // 熄灭点赞图标并修改显示数据 debug.msg("点赞撤回!"); document.getElementsByClassName("l-icon-move")[0].setAttribute("style", "width : 22px;height : 22px;background-position : -660px -2068px;"); document.getElementsByClassName("l-icon-moved")[0].setAttribute("style", "width : 22px;height : 22px;background-position : -725px -2068px;display : none;"); if (arg.nodeValue.match("万")) return; let number = 1 * arg.nodeValue.match(/[0-9]+/) - 1; text = document.createTextNode(" 点赞 " + number) arg.replaceWith(text); arg = text; } number.insertBefore(span, node); // 获取点赞数据 data = await xhr.true(objUrl("https://api.bilibili.com/x/web-interface/view", { "aid": aid })); data = jsonCheck(data).data.stat.like; document.getElementsByClassName("like")[0].setAttribute("title", "点赞人数" + data); text = document.createTextNode(" 点赞 " + unitFormat(data)); arg.replaceWith(text); arg = text; if (!BLOD.uid) return; data = jsonCheck(await xhr.true(objUrl("https://api.bilibili.com/x/web-interface/archive/has/like", { "aid": aid }))).data; if (data == 1) { // 点赞过点亮图标 move.setAttribute("style", "width : 22px;height : 22px;background-position : -660px -2068px;display : none;"); moved.setAttribute("style", "width : 22px;height : 22px;background-position : -725px -2068px;"); } } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("点赞功能", ...e) } } }, 100); } const setOnline = async () => { let timer = window.setInterval(async () => { let online = document.getElementsByClassName("online")[0]; if (online) { // 判断主页载入进程 window.clearInterval(timer); let loop = async () => { try { let data = await xhr.true("https://api.bilibili.com/x/web-interface/online"); data = jsonCheck(data).data; let all_count = data.all_count; let web_online = data.web_online; let play_online = data.play_online; let online = document.getElementsByClassName("online")[0]; if (online.tagName == "DIV") online = online.getElementsByTagName("a")[0]; else { // 旧版主页需额外创建节点 let parent = online.parentNode; online.remove(); let div = document.createElement("div"); let a = document.createElement("a"); div.setAttribute("class", "online"); parent.insertBefore(div, parent.firstChild); a.setAttribute("href", "//www.bilibili.com/video/online.html"); a.setAttribute("target", "_blank"); div.appendChild(a); online = a; } online.setAttribute("title", "在线观看:" + play_online); online.text = web_online ? "在线人数:" + web_online : "在线列表"; if (!online.parentNode.getElementsByTagName("em")[0]) { let em = document.createElement("em"); let count = document.createElement("a"); online.parentNode.insertBefore(em, online.nextSibling); count.setAttribute("href", "//www.bilibili.com/newlist.html"); count.setAttribute("target", "_blank"); online.parentNode.insertBefore(count, em.nextSibling); count.text = all_count ? "最新投稿:" + all_count : "最新投稿"; } else { let count = online.parentNode.getElementsByTagName("a")[1]; count.text = all_count ? "最新投稿:" + all_count : "最新投稿"; } if (!all_count || !web_online || !play_online) return; // 60s刷新一次 window.setTimeout(() => loop(), 60000); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("在线数据", ...e) } } loop(); } }, 1000); } const setJoinTime = async () => { if (!BLOD.mid && !config.reset.jointime) return; let data = await xhr.GM(objUrl("https://account.bilibili.com/api/member/getCardByMid", { "mid": BLOD.mid })); try { data = jsonCheck(data); // 格式化时间戳,不是13位,主动补位 let jointime = timeFormat(data.card.regtime * 1000, 1); let birthdate = data.card.birthday; debug.log("注册(不可用)时间", data.card.name, jointime); document.addEventListener("DOMNodeInserted", (msg) => { let birthday = document.getElementsByClassName("birthday"); if (birthday[0]) { if (document.getElementsByClassName("jointime")[0]) return; else { let div = document.createElement("div"); let icon = document.createElement("span"); let text = document.createElement("span"); let style = document.createElement("style"); div.setAttribute("class", "item jointime"); birthday[0].parentNode.appendChild(div); icon.setAttribute("class", "icon"); div.appendChild(icon); text.setAttribute("class", "text"); text.innerText = jointime; div.appendChild(text); style.setAttribute("type", "text/css"); document.head.appendChild(style); style.appendChild(document.createTextNode(".user .info .meta .row {height : 88px;white-space : normal;}.user .info .jointime .icon {background-position : -209px -84px;}.user .info .jointime .text {color : #00a1d6;}}")); } } }); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("注册(不可用)时间", ...e) } } // 播放器通知 const message = (...msg) => { let node = document.getElementsByClassName("bilibili-player-video-toast-bottom")[0]; if (!node) { debug.log(...msg); return; } let item = document.createElement("div"); node.children[0] ? node.children[0].replaceWith(item) : node.appendChild(item); item.setAttribute("class", "bilibili-player-video-toast-item bilibili-player-video-toast-msg"); item.innerHTML = '<div class="bilibili-player-video-toast-item-text"><span class="video-float-hint-text"></span><span class="video-float-hint-btn hint-red"></span></div>'; item.children[0].children[0].innerHTML = msg[0] || ""; item.children[0].children[1].innerHTML = msg[1] || ""; setTimeout(() => item.remove(), 3000); } // 通知封装 const debug = BLOD.debug = { log: (...msg) => console.log("[" + timeFormat(new Date()) + "]", ...msg), error: (...msg) => console.error("[" + timeFormat(new Date()) + "]", ...msg), warn: (...msg) => console.warn("[" + timeFormat(new Date()) + "]", ...msg), debug: (...msg) => console.debug("[" + timeFormat(new Date()) + "]", ...msg), msg: (...msg) => message(...msg) } const xhr = BLOD.xhr = { // 同步方法 'false': (url) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.withCredentials = true; xhr.send(null); return xhr.responseText; }, // 异步方法 'true': (url) => { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open('get', url, true); xhr.withCredentials = true; xhr.onload = () => resolve(xhr.responseText); xhr.onerror = () => reject(xhr.statusText || url + " net::ERR_CONNECTION_TIMED_OUT"); xhr.send(); }); }, // 跨域方法 GM: (url) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: url, onload: (xhr) => resolve(xhr.responseText), onerror: (xhr) => reject(xhr.statusText || url + " net::ERR_CONNECTION_TIMED_OUT"), }); }) }, // 表单方法 post: (url, header, data) => { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); header = header ? header : "application/x-www-form-urlencoded"; xhr.open('post', url, true); xhr.setRequestHeader("Content-type", header); xhr.withCredentials = true; xhr.onload = () => resolve(xhr.responseText); xhr.onerror = () => reject(xhr.statusText || url + " net::ERR_CONNECTION_TIMED_OUT"); xhr.send(data); }); } } const iniState = BLOD.iniState = { av: (data) => { data = BLOD.jsonCheck(data).data; aid = aid || data.View.aid; cid = cid || data.View.cid; let dat = { aid: -1, comment: { count: 0, list: [] }, error: {}, isClient: false, p: "", player: "", playurl: {}, related: [], tags: [], upData: {}, videoData: {} }; dat.aid = data.View.aid; dat.related = data.Related; dat.tags = data.Tags; dat.upData = data.Card.card; dat.upData.archiveCount = data.Card.archive_count; dat.videoData = data.View; dat.videoData.embedPlayer = 'EmbedPlayer("player", "//static.hdslb.com/play.swf", "cid=' + cid + '&aid=' + aid + '&pre_ad=")'; return dat; }, bangumi: (data, epId) => { let ep = 0, ini = {}, pug = {}, mode; let dat = { "ver": {}, "loginInfo": {}, "canReview": false, "userShortReview": {}, "userLongReview": {}, "userScore": 0, "userCoined": false, "isPlayerTrigger": false, "area": 0, "app": false, "mediaRating": {}, "recomList": [], "playerRecomList": [], "paster": {}, "payPack": {}, "payMent": {}, "activity": {}, "spending": 0, "sponsorTotal": { "code": 0, "result": { "ep_bp": 0, "users": 0, "mine": {}, "list": [] } }, "sponsorWeek": { "code": 0, "result": { "ep_bp": 0, "users": 0, "mine": {}, "list": [] } }, "sponsorTotalCount": 0, "miniOn": true, "seasonFollowed": false, "epStat": {}, "ssStat": {} }; if (data.startsWith("{")) { // DOCUMENT被404的备用数据源,无法获取播放进度信息,以ss进入默认选择第一p data = BLOD.jsonCheck(data).result; dat.special = data.bkg_cover ? true : false; if (epId) { dat.epId = 1 * epId; ep = 1; } else dat.epId = "" dat.ssId = data.season_id; dat.mdId = data.media_id; dat.mediaInfo = {}; dat.mediaInfo.actors = data.actors || ""; dat.mediaInfo.alias = data.alias; dat.mediaInfo.areas = data.areas || []; dat.mediaInfo.bkg_cover = data.bkg_cover; dat.mediaInfo.cover = data.cover; dat.mediaInfo.evaluate = data.evaluate; dat.mediaInfo.is_paster_ads = data.is_paster_ads || 0; dat.mediaInfo.jp_title = data.jp_title; dat.mediaInfo.link = data.link; dat.mediaInfo.media_id = data.media_id; dat.mediaInfo.mode = data.mode; dat.mediaInfo.paster_text = ""; dat.mediaInfo.season_id = data.season_id; dat.mediaInfo.season_status = data.status; dat.mediaInfo.season_title = data.season_title; dat.mediaInfo.season_type = data.type; dat.mediaInfo.square_cover = data.square_cover; dat.mediaInfo.staff = data.staff || ""; dat.mediaInfo.stat = data.state; dat.mediaInfo.style = data.style || []; dat.mediaInfo.title = data.title; dat.mediaInfo.total_ep = data.total; dat.mediaRating = data.rating; dat.epList = data.episodes; if (ep == 0) dat.epId = (data.episodes[0] && data.episodes[0].id) || ""; for (let i = 0; i < dat.epList.length; i++) { dat.epList[i].ep_id = dat.epList[i].id; dat.epList[i].episode_status = dat.epList[i].status; dat.epList[i].index = dat.epList[i].title; dat.epList[i].index_title = dat.epList[i].long_title; if (dat.epList[i].ep_id == dat.epId) dat.epInfo = dat.epList[i]; if (dat.epList[i].badge == "会员" || dat.epList[i].badge_type) BLOD.ids.push(dat.epList[i].cid); } dat.newestEp = data.new_ep; dat.seasonList = data.seasons; dat.rightsInfo = data.rights; dat.pubInfo = data.publish; dat.upInfo = data.up_info || {}; } else { // 正常DOCUMENT数据源,up组主数据可能无效,将指向uid=2(站长) ini = JSON.parse(data.match(/INITIAL_STATE__=.+?\;\(function/)[0].replace(/INITIAL_STATE__=/, "").replace(/;\(function/, "")); pug = JSON.parse(data.match(/PGC_USERSTATE__=.+?<\/script>/)[0].replace(/PGC_USERSTATE__=/, "").replace(/<\/script>/, "")); dat.special = ini.mediaInfo.specialCover ? true : false; mode = dat.special ? 1 : 2; if (epId) { dat.epId = 1 * epId; ep = 1; } else { dat.epId = ""; if (pug.hasOwnProperty("progress")) { dat.epId = pug.progress.last_ep_id; ep = 1; } } dat.ssId = ini.mediaInfo.ssId; dat.mdId = ini.mediaInfo.id; dat.mediaInfo = {}; dat.mediaInfo.actors = ""; dat.mediaInfo.alias = ini.mediaInfo.alias; dat.mediaInfo.areas = []; dat.mediaInfo.bkg_cover = ini.mediaInfo.specialCover; dat.mediaInfo.cover = ini.mediaInfo.cover; dat.mediaInfo.evaluate = ini.mediaInfo.evaluate; dat.mediaInfo.is_paster_ads = 0; dat.mediaInfo.jp_title = ini.mediaInfo.jpTitle; dat.mediaInfo.link = "https://www.bilibili.com/bangumi/media/md" + dat.mdId; dat.mediaInfo.media_id = dat.mdId; dat.mediaInfo.mode = mode; dat.mediaInfo.paster_text = ""; dat.mediaInfo.season_id = ini.mediaInfo.ssId; dat.mediaInfo.season_status = ini.mediaInfo.status; dat.mediaInfo.season_title = ini.mediaInfo.title; dat.mediaInfo.season_type = ini.mediaInfo.ssType; dat.mediaInfo.square_cover = ini.mediaInfo.squareCover; dat.mediaInfo.staff = ""; dat.mediaInfo.stat = ini.mediaInfo.stat; dat.mediaInfo.style = []; dat.mediaInfo.title = ini.mediaInfo.title; dat.mediaInfo.total_ep = ini.epList.length; dat.mediaRating = ini.mediaInfo.rating; dat.epList = []; for (let i = 0; i < ini.sections.length; i++) ini.epList.push(...ini.sections[i].epList); if (ep == 0) dat.epId = (ini.epList[0] && ini.epList[0].id) || ""; for (let i = 0; i < ini.epList.length; i++) { dat.epList[i] = {}; dat.epList[i].aid = ini.epList[i].aid; dat.epList[i].cid = ini.epList[i].cid; dat.epList[i].badge = ini.epList[i].badge; dat.epList[i].badge_type = ini.epList[i].badgeType; dat.epList[i].cover = ini.epList[i].cover; dat.epList[i].duration = -1; dat.epList[i].ep_id = ini.epList[i].id; dat.epList[i].episode_status = ini.epList[i].epStatus; dat.epList[i].from = ini.epList[i].from; dat.epList[i].index = ini.epList[i].title; dat.epList[i].index_title = ini.epList[i].longTitle; dat.epList[i].mid = ini.mediaInfo.upInfo.mid; dat.epList[i].page = 1; dat.epList[i].pub_real_time = ini.epList[i].releaseDate || ini.mediaInfo.pub.time; dat.epList[i].section_id = -1; dat.epList[i].section_type = 0; dat.epList[i].vid = ini.epList[i].vid; if (dat.epList[i].ep_id == dat.epId) dat.epInfo = dat.epList[i]; if (dat.epList[i].badge == "会员" || dat.epList[i].badge_type) BLOD.ids.push(dat.epList[i].cid); } dat.newestEp = ini.mediaInfo.newestEp; dat.seasonList = []; for (let i = 0; i < ini.ssList.length; i++) { dat.seasonList[i] = {}; dat.seasonList[i].badge = ini.ssList[i].badge; dat.seasonList[i].badge_type = ini.ssList[i].badgeType; dat.seasonList[i].cover = ini.ssList[i].cover; dat.seasonList[i].media_id = -1; dat.seasonList[i].new_ep = { cover: ini.ssList[i].epCover, id: -1, index_show: ini.ssList[i].desc }; dat.seasonList[i].season_id = ini.ssList[i].id; dat.seasonList[i].season_title = ini.ssList[i].title; dat.seasonList[i].season_type = ini.ssList[i].type; dat.seasonList[i].stat = { danmaku: 0, follow: 0, view: 0 }; dat.seasonList[i].title = ini.ssList[i].title; } dat.newestEp.isNew = dat.newestEp.isNew ? 1 : 0; dat.rightsInfo = {}; dat.rightsInfo.allow_bp = ini.mediaInfo.rights.allowBp ? 1 : 0; dat.rightsInfo.allow_download = 1; dat.rightsInfo.allow_review = ini.mediaInfo.rights.allowReview ? 1 : 0; dat.rightsInfo.copyright = "bilibili"; dat.rightsInfo.is_preview = ini.mediaInfo.rights.isPreview ? 1 : 0; dat.rightsInfo.watch_platform = ini.mediaInfo.rights.appOnly ? 1 : 0; dat.pubInfo = {}; dat.pubInfo.is_finish = ini.mediaInfo.pub.isFinish ? 1 : 0; dat.pubInfo.is_started = ini.mediaInfo.pub.isStart ? 1 : 0; dat.pubInfo.pub_time = ini.mediaInfo.pub.time; dat.pubInfo.pub_time_show = ini.mediaInfo.pub.timeShow; dat.pubInfo.weekday = -1; dat.upInfo = {}; dat.upInfo.avatar = ini.mediaInfo.upInfo.avatar; dat.upInfo.follower = "--"; dat.upInfo.is_vip = ini.mediaInfo.upInfo.isAnnualVip ? 1 : 0; dat.upInfo.mid = ini.mediaInfo.upInfo.mid; dat.upInfo.pendant = { image: ini.mediaInfo.upInfo.pendantImage, name: ini.mediaInfo.upInfo.pendantName, pid: ini.mediaInfo.upInfo.pendantId }; dat.upInfo.uname = ini.mediaInfo.upInfo.name; dat.upInfo.verify_type = 6; if (dat.upInfo.mid < 1) dat.upInfo = { avatar: "//i0.hdslb.com/bfs/face/ef0457addb24141e15dfac6fbf45293ccf1e32ab.jpg", follower: 897603, is_vip: 1, mid: 2, pendant: { image: "", name: "", pid: 0 }, uname: "碧诗", verify_type: 2 } } dat.seasonStat = { "views": 0, "danmakus": 0, "coins": 0, "favorites": 0 }; dat.userStat = { "loaded": true, "error": false, "follow": 0, "pay": 0, "payPackPaid": 0, "sponsor": 0 }; dat.userStat.watchProgress = pug.progress; dat.userStat.vipInfo = pug.vip_info; if (pug.dialog || pug.pay == 1) { dat.payMent = { "price": "0.0", "promotion": "", "tip": "大会员专享观看特权哦~" }; if (pug.dialog) { dat.payMent.vip_promotion = pug.dialog.title; if (pug.dialog.btn_left) dat.payMent.price = pug.dialog.btn_left.title.match(/[0-9]+/)[0]; } } if (dat.epInfo.index >= 0) { dat.special = false; dat.mediaInfo.bkg_cover = ""; } return dat; }, index: (data) => { let dat = {}; let ini = JSON.parse(data); dat.recommendData = []; for (let i = 0; i < ini.recommendList.length; i++) { dat.recommendData[i] = {}; dat.recommendData[i].aid = ini.recommendList[i].aid; dat.recommendData[i].typename = ini.recommendList[i].tname; dat.recommendData[i].title = ini.recommendList[i].title; dat.recommendData[i].subtitle = ""; dat.recommendData[i].play = ini.recommendList[i].stat.view; dat.recommendData[i].review = ini.recommendList[i].stat.reply; dat.recommendData[i].video_review = ""; dat.recommendData[i].favorites = ini.recommendList[i].stat.favorite; dat.recommendData[i].mid = ini.recommendList[i].owner.mid; dat.recommendData[i].author = ini.recommendList[i].owner.name; dat.recommendData[i].create = ini.recommendList[i].pubdate; dat.recommendData[i].pic = ini.recommendList[i].pic; dat.recommendData[i].coins = ini.recommendList[i].stat.coin; dat.recommendData[i].duration = ini.recommendList[i].duration; dat.recommendData[i].badgepay = false; dat.recommendData[i].rights = ini.recommendList[i].rights; } dat.locsData = ini.locsData; dat.locsData[23] = ini.locsData[3197]; if (config.reset.adloc) for (let key in dat.locsData) if (dat.locsData[key]) for (let i = dat.locsData[key].length - 1; i >= 0; i--) if (dat.locsData[key][i].is_ad) { debug.debug("移除广告", key, dat.locsData[key][i]); dat.locsData[key].splice(i, 1); } if (dat.locsData[31][0] && dat.locsData[31][0].id == 0) dat.locsData[31] = [{ "id": 36585, "contract_id": "", "pos_num": 1, "name": "小黑屋弹幕举报", "pic": "https://i0.hdslb.com/bfs/archive/0aa2f32c56cb65b6d453192a3015b65e62537b9a.jpg", "litpic": "", "url": "https://www.bilibili.com/blackboard/activity-dmjbfj.html", "style": 0, "agency": "", "label": "", "intro": "", "creative_type": 0, "request_id": "1546354354629q172a23a61a62q626", "src_id": 32, "area": 0, "is_ad_loc": true, "ad_cb": "", "title": "", "server_type": 0, "cm_mark": 0, "stime": 1520478000, "mid": "14629218" }]; return dat; } } const ui = { init: async (timer) => { if (window.self != window.top) return; let face = document.createElement("div"); let attribute = { "class": "bili-old ui-face", "id": "ui-face", "style": "right : -54px;" } for (let key in attribute) face.setAttribute(key, attribute[key]); face.onmouseover = () => face.setAttribute("style", "right : 0px;box-shadow : rgba(0, 85, 255, 0.098) 0px 0px 20px 0px;border : 1px solid rgb(233, 234, 236);"); face.onmouseout = () => face.setAttribute("style", "right : -54px;"); face.onclick = () => { let check = document.getElementsByClassName("ui-table")[0]; if (!check) ui.table(); else if (check.getAttribute("hidden")) check.removeAttribute("hidden"); } face.innerHTML = "<i></i><span>设置</span>"; (timer = () => { setTimeout(()=> {document.body ? document.body.appendChild(face) : timer()}, 100); })(); }, table: async (timer) => { let table = document.createElement("div"); table.setAttribute("class", "bili-old ui-table"); table.setAttribute("id", "ui-table"); table.innerHTML = '<span style="color : rgb(0,0,0);font-size : 14px;">BilibiliOld 设置</span><span style="color : blue;float : right;font-size : 12px;">恢复默认</span>'; document.body.appendChild(table); table.children[1].onclick = () => { for (let key in BLOD.defaultConfig.rewrite) if (key in config.rewrite) config.rewrite[key] = BLOD.defaultConfig.rewrite[key][0]; for (let key in BLOD.defaultConfig.reset) if (key in config.reset) config.reset[key] = BLOD.defaultConfig.reset[key][0]; BLOD.setValue("config", config); table.remove(); } for (let key in config.rewrite) ui.setTable(table, BLOD.defaultConfig.rewrite[key], config.rewrite[key], key); for (let key in config.reset) ui.setTable(table, BLOD.defaultConfig.reset[key], config.reset[key], key); table.onmouseover = () => window.clearTimeout(timer); table.onmouseout = () => { timer = window.setTimeout(() => { table.setAttribute("hidden", "hidden"); BLOD.setValue("config", config); }, 500); } }, setTable: async (table, name, check, key) => { let setTable = document.createElement("div"); setTable.setAttribute("style", "padding : 4px 4px 0px 4px;clear : both;"); setTable.innerHTML = '<span style="float : left;display : inline-block;color : rgb(0,0,0);font-size : 14px;"></span><input type="checkbox" class="checke">'; setTable.onmouseover = () => { let toast = document.createElement("div"); toast.setAttribute("class", "bili-old ui-state"); toast.setAttribute("id", "ui-state"); toast.innerHTML = name[2]; document.body.appendChild(toast); } setTable.onmouseout = () => document.getElementById("ui-state") ? document.getElementById("ui-state").remove() : ""; setTable.children[0].innerText = name[1]; setTable.children[1].onclick = () => { if (setTable.children[1].checked) { if (key in config.rewrite) config.rewrite[key] = 1; else config.reset[key] = 1; if (!config.reset.xhrhook && key != "xhrhook" && BLOD.defaultConfig.reset[key][1].includes("xhrhook")) { debug.msg("启用失败!xhrhook已关闭!", BLOD.defaultConfig.reset[key][0]); } } else { if (key in config.rewrite) config.rewrite[key] = 0; else config.reset[key] = 0; if (key == "xhrhook") debug.msg("xhrhook已关闭,部分功能无法生效!"); } } if (check) setTable.children[1].checked = true; table.appendChild(setTable); } } const rewrite = BLOD.rewrite = { av: () => { BLOD.path.name = "av"; BLOD.ml = BLOD.getValue("medialist"); BLOD.deleteValue("medialist"); if (config.reset.bvid2av && BLOD.path[4].toLowerCase().startsWith('bv')) { aid = BLOD.abv(BLOD.path[4]); history.replaceState(null, null, "https://www.bilibili.com/video/av" + aid + location.search + location.hash); } try { if (!config.rewrite.av) throw ["未启用旧版av页", location.href]; aid = aid || BLOD.path[4].match(/[0-9]+/)[0]; let page = xhr.false(BLOD.objUrl("https://api.bilibili.com/x/web-interface/view/detail", { aid: aid })); BLOD.__INITIAL_STATE__ = BLOD.iniState.av(page); if (!BLOD.__INITIAL_STATE__) throw "av/BV号可能无效!"; if (BLOD.__INITIAL_STATE__.videoData.redirect_url) throw ["番剧重定向:", BLOD.__INITIAL_STATE__.videoData.redirect_url]; if (BLOD.__INITIAL_STATE__.videoData.stein_guide_cid) throw ["忽略互动视频:", "av" + aid]; aid = BLOD.__INITIAL_STATE__.aid ? BLOD.__INITIAL_STATE__.aid : aid; BLOD.tid = BLOD.__INITIAL_STATE__.videoData.tid ? BLOD.__INITIAL_STATE__.videoData.tid : BLOD.tid; unsafeWindow.__INITIAL_STATE__ = BLOD.__INITIAL_STATE__; BLOD.write(oldScript(BLOD.getResourceText("av"))); document.title = BLOD.__INITIAL_STATE__.videoData.title + "_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili"; fixSort.video(); setLike(); setMediaList.init(); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("框架·av/BV", ...e) } }, watchlater: () => { try { if (!config.rewrite.watchlater) throw ["未启用旧版稍后再看", location.href]; if (!BLOD.uid) throw ["未登录(不可用)", "无法启用旧版稍后再看"]; BLOD.path.name = "watchlater"; BLOD.write(oldScript(BLOD.getResourceText("watchlater"))); setLike(); fixSort.watchlater(); if (BLOD.path[5]) { aid = BLOD.path[5].match(/[0-9]+/) ? BLOD.path[5].match(/[0-9]+/)[0] : aid; if (BLOD.path[5].toLowerCase().startsWith('bv')) { aid = BLOD.abv(BLOD.path[5]); BLOD.path[5] = "av" + aid; history.replaceState(null, null, BLOD.path.join("/")); } } } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("框架·稍后再看", ...e) } }, bangumi: () => { try { if (!config.rewrite.bangumi) throw ["未启用旧版Bangumi", location.href]; BLOD.path.name = "bangumi"; BLOD.pgc = true; let page = xhr.false(location.href); BLOD.__INITIAL_STATE__ = page.includes("__INITIAL_STATE__=") ? JSON.parse(page.match(/INITIAL_STATE__=.+?\;\(function/)[0].replace(/INITIAL_STATE__=/, "").replace(/;\(function/, "")) : ""; if (!BLOD.__INITIAL_STATE__) { if (BLOD.path[5].startsWith('ss')) { page = xhr.false(BLOD.objUrl("https://api.bilibili.com/pgc/view/web/season", { season_id: location.href.match(/[0-9]+/)[0] })); } else if (BLOD.path[5].startsWith('ep')) { page = xhr.false(BLOD.objUrl("https://api.bilibili.com/pgc/view/web/season", { ep_id: location.href.match(/[0-9]+/)[0] })); } } let id = BLOD.path[5].startsWith('ep') ? location.href.match(/[0-9]+/)[0] : ""; BLOD.__INITIAL_STATE__ = BLOD.iniState.bangumi(page, id); if (BLOD.__INITIAL_STATE__ && BLOD.__INITIAL_STATE__.epInfo && BLOD.__INITIAL_STATE__.epInfo.badge === "互动") throw ["忽略互动视频:", location.href]; unsafeWindow.__INITIAL_STATE__ = BLOD.__INITIAL_STATE__; if (page.match('"specialCover":""') || !BLOD.__INITIAL_STATE__.special) BLOD.write(oldScript(BLOD.getResourceText("bangumi"))); else BLOD.write(oldScript(BLOD.getResourceText("cinema"))); document.title = page.match(/<title.*?>.+?<\/title>/) ? page.match(/<title.*?>.+?<\/title>/)[0].replace(/<title.*?>/, "").replace(/<\/title>/, "") : BLOD.__INITIAL_STATE__.mediaInfo.title; if (BLOD.__INITIAL_STATE__) setBangumi.init(BLOD.__INITIAL_STATE__); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("框架·Bangumi", ...e) } }, blackboard: () => { if (BLOD.path[4].startsWith('html5player')) { if (BLOD.path[4].includes("3521416") && BLOD.path[4].includes("6041635")) { location.replace(BLOD.objUrl("https://www.bilibili.com/blackboard/html5player.html", { "aid": 3521416, "cid": 192446449 })); } } try { if (!config.rewrite.frame) throw ["未启用旧版嵌入播放器", location.href]; BLOD.path.name = "blackboard"; if (BLOD.path[4].startsWith('newplayer')) { let obj = BLOD.urlObj(location.href), season_type = obj.season_type || "", player_type = obj.player_type || ""; aid = 1 * obj.aid || (obj.aid ? BLOD.abv(obj.aid) : undefined) || (obj.bvid ? BLOD.abv(obj.bvid) : undefined); cid = obj.cid || ""; try { cid = cid || BLOD.jsonCheck(xhr.false( BLOD.objUrl("https://api.bilibili.com/x/player/pagelist", { "aid": aid }))).data[0].cid } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("框架·嵌入", ...e) } location.replace(BLOD.objUrl("https://www.bilibili.com/blackboard/html5player.html", { "aid": aid, "cid": cid, "season_type": season_type, "player_type": player_type, "as_wide": 1, })); debug.log("嵌入播放器", "aid=", aid, " cid=", cid); } } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("框架·嵌入", ...e) } }, playlist: () => { BLOD.path.name = "playlist"; if (BLOD.path[4] == "video") { BLOD.write(oldScript(BLOD.getResourceText("playlist"))); } if (BLOD.path[4] == "detail") { BLOD.__INITIAL_STATE__ = { mid: "", pid: "", plinfoData: {}, pllistData: {} } try { let page = BLOD.jsonCheck( xhr.false(BLOD.objUrl("https://api.bilibili.com/x/playlist/video/toview", { pid: BLOD.path[5].match(/[0-9]+/)[0] }))).data; BLOD.__INITIAL_STATE__.mid = page.mid; BLOD.__INITIAL_STATE__.pid = page.pid; BLOD.__INITIAL_STATE__.plinfoData = { attr: page.attr, count: page.count, cover: page.cover, ctime: page.ctime, description: page.description, favored: page.favored, id: page.id, is_favorite: page.is_favorite, mid: page.mid, mtime: page.mtime, owner: page.owner, pid: page.pid, stat: page.stat, state: page.state, type: page.type, }; BLOD.__INITIAL_STATE__.pllistData = page.list; } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("播单", ...e); BLOD.__INITIAL_STATE__ = JSON.parse(BLOD.getResourceText("playlistjson")); } unsafeWindow.__INITIAL_STATE__ = BLOD.__INITIAL_STATE__; BLOD.write(oldScript(BLOD.getResourceText("playlistdetail"))); } }, medialist: () => { if (BLOD.path[5].startsWith("ml")) { BLOD.ml = 1 * BLOD.path[5].match(/[0-9]+/)[0]; // 保存收藏号并调用av跳转 if (!config.rewrite.medialist) return; BLOD.path.name = "medialist"; GM_setValue("medialist", BLOD.ml); setMediaList.init(BLOD.ml); } // 新版稍后再看跳转到旧版稍后再看 if (BLOD.path[5].startsWith("watchlater") && config.rewrite.watchlater) location.replace("https://www.bilibili.com/watchlater/#/"); }, s: () => { if (!config.reset.static) return; BLOD.path.name = "s"; location.replace(location.href.replace("s/video", "video")); }, space: () => { BLOD.mid = BLOD.path[3] ? 1 * BLOD.path[3] : BLOD.mid; setJoinTime(); }, index: () => { try { if (!config.rewrite.home) throw ["未启用旧版主页", location.href]; BLOD.path.name = "index"; if (!window.__INITIAL_STATE__) { let page = xhr.false(location.href); BLOD.__INITIAL_STATE__ = page.includes("__INITIAL_STATE__=") ? page.match(/INITIAL_STATE__=.+?\;\(function/)[0].replace(/INITIAL_STATE__=/, "").replace(/;\(function/, "") : ""; } else BLOD.__INITIAL_STATE__ = JSON.stringify(window.__INITIAL_STATE__); unsafeWindow.__INITIAL_STATE__ = BLOD.__INITIAL_STATE__ = BLOD.iniState.index(BLOD.__INITIAL_STATE__); BLOD.write(BLOD.getResourceText("index")); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("框架·主页", ...e) } setOnline(); }, rank: (page) => { try { if (!config.rewrite.rank) throw ["未启用排行", location.href]; BLOD.path.name = "rank"; let refer = document.referrer.split("/"); if (refer && refer[4] && refer[4] == "all") page = jsonCheck(xhr.false(objUrl("https://api.bilibili.com/x/web-interface/ranking", { rid: refer[5], day: 3, type: 1, arc_type: 0 }))); else page = jsonCheck(xhr.false(objUrl("https://api.bilibili.com/x/web-interface/ranking", { rid: 0, day: 3, type: 1, arc_type: 0 }))); BLOD.__INITIAL_STATE__ = { loading: false, rankRouteParams: { arc_type: 0, day: 3, rankTab: "all", rid: 1 * refer[5] || 0, season_type: 1 }, showTypes: true, times: [{ name: "日排行", value: 1 }, { name: "三日排行", value: 3 }, { name: "周排行", value: 7 }, { name: "月排行", value: 30 }], typeList: [{ name: "全部投稿", value: 0 }, { name: "近期投稿", value: 1 }] }; BLOD.__INITIAL_STATE__.channels = [{ name: "全站", tid: 0 }, { name: "动画", tid: 1 }, { name: "国创相关", tid: 168 }, { name: "音乐", tid: 3 }, { name: "舞蹈", tid: 129 }, { name: "游戏", tid: 4 }, { name: "科技", tid: 36 }, { name: "数码", tid: 188 }, { name: "生活", tid: 160 }, { name: "美食", tid: 211 }, { name: "鬼畜", tid: 119 }, { name: "时尚", tid: 155 }, { name: "娱乐", tid: 5 }, { name: "影视", tid: 181 }]; BLOD.__INITIAL_STATE__.rankList = page.data.list; BLOD.__INITIAL_STATE__.note = page.data.note; unsafeWindow.__INITIAL_STATE__ = BLOD.__INITIAL_STATE__; write(BLOD.getResourceText("ranking")); } catch (e) { e = Array.isArray(e) ? e : [e]; debug.error("框架·排行", ...e) } } } // 初始化配置数据 let localConfig = BLOD.getValue("config"); let configSort = ["rewrite", "reset"]; BLOD.defaultConfig = JSON.parse(JSON.stringify(config)); for (let key in config) if (configSort.indexOf(key) < 0) delete config[key]; if (localConfig) { configSort.forEach(x => { for (let key in localConfig[x]) if (key in config[x]) config[x][key] = localConfig[x][key]; }) } else { configSort.forEach(x => { for (let key in config[x]) config[x][key] = config[x][key][0]; }) BLOD.setValue("config", config); } BLOD.uid = BLOD.getCookies().DedeUserID; BLOD.path = document.location.href.split('/'); getVariable(); if (BLOD.uid) { let offset = BLOD.getCookies()["bp_video_offset_" + BLOD.uid]; if (offset) document.cookie = "bp_t_offset_" + BLOD.uid + "=" + offset + "; domain=bilibili.com; expires=Aug, 18 Dec 2038 18:00:00 GMT; BLOD.path=/"; } let bilibili_player_settings = localStorage.getItem("bilibili_player_settings"); if (bilibili_player_settings) { bilibili_player_settings = JSON.parse(bilibili_player_settings); if (bilibili_player_settings.video_status.autopart !== "") BLOD.setValue("bilibili_player_settings", bilibili_player_settings); else if (GM_getValue("bilibili_player_settings")) localStorage.setItem("bilibili_player_settings", JSON.stringify(BLOD.getValue("bilibili_player_settings"))); } else if (BLOD.path[2] == 'www.bilibili.com' && BLOD.getValue("bilibili_player_settings")) { localStorage.setItem("bilibili_player_settings", JSON.stringify(BLOD.getValue("bilibili_player_settings"))); } // 页面分离 if (BLOD.path[3]) { if (BLOD.path[3] == 'video' && (BLOD.path[4].toLowerCase().startsWith('av') || BLOD.path[4].toLowerCase().startsWith('bv'))) BLOD.rewrite.av(); if (BLOD.path[3] == 'watchlater') BLOD.rewrite.watchlater(); if (BLOD.path[3] == 'bangumi' && BLOD.path[4] == 'play') BLOD.rewrite.bangumi(); if (BLOD.path[3] == 'blackboard' && BLOD.path[4]) BLOD.rewrite.blackboard(); if (BLOD.path[3] == 'playlist' && BLOD.path[5].startsWith('pl')) BLOD.rewrite.playlist(); if (BLOD.path[3] == 'medialist' && BLOD.path[4] == 'play') BLOD.rewrite.medialist(); if (BLOD.path[3] == 's' && (BLOD.path[5].toLowerCase().startsWith('av') || BLOD.path[5].toLowerCase().startsWith('bv'))) BLOD.rewrite.s(); if (BLOD.path[2] == 'space.bilibili.com') BLOD.rewrite.space(); if (BLOD.path[2] == 'www.bilibili.com' && (BLOD.path[3].startsWith('\?') || BLOD.path[3].startsWith('\#') || BLOD.path[3].startsWith('index.'))) BLOD.rewrite.index(); if (BLOD.path[3] == 'v' && BLOD.path[4] == "popular") BLOD.rewrite.rank(); } else { if (BLOD.path[2] == 'www.bilibili.com') BLOD.rewrite.index(); if (BLOD.path[2] == 'live.bilibili.com') BLOD.path.name = "live"; } addCss(BLOD.getResourceText("css")); xhrHook.init(); ui.init(); document.addEventListener("DOMNodeInserted", (msg) => { // 去除预览提示框 if (/bilibili-player-video-toast-pay/.test(msg.target.className)) removePreview(msg.target); // 版面替换 if (msg.target.id == "internationalHeader") resetSction(); if (msg.target.id == "bili-header-m") if (document.getElementById("internationalHeader")) document.getElementById("internationalHeader").remove(); // 切p监听 if (/bilibili-player-video-btn-start/.test(msg.target.className)) switchVideo(); // 创建播放器右键下载菜单 if (/bilibili-player-context-menu-container/.test(msg.target.className)) download.init(msg.target); // 捕获评论链接 if (msg.target.src && msg.target.src.startsWith('https://api.bilibili.com/x/v2/reply') && msg.target.src.includes("oid")) BLOD.src = msg.target.src; // 捕获频道视频链接 if (msg.target.src && msg.target.src.includes("//api.bilibili.com/x/space/channel/video?")) BLOD.src = msg.target.src; // 修复失效频道视频 if (msg.relatedNode.getAttribute("class") == "row video-list clearfix") fixVideoLost.channel(BLOD.src); // 修复失效收藏视频 if (msg.target.className == "small-item disabled") fixVideoLost.favlist(msg); // 刷新番剧分集数据 if (msg.relatedNode.className == "info-sec-av") setBangumi.episodeData("", msg); // 失效分区转换 if (msg.target.id == "bili_ad" || msg.target.className == "report-wrap-module elevator-module" || msg.target.id == "bili-header-m" || msg.target.className == "no-data loading") fixnews(msg.target); // 修复评论楼层&修复评论空降坐标 if (BLOD.src && (/l_id/.test(msg.target.id) || /reply-wrap/.test(msg.target.className))) { setReplyFloor.init(BLOD.src); fixVideoSeek(msg.target.parentNode); } // 跳过充电鸣谢 if (/bilibili-player-electric-panel-jump/.test(msg.relatedNode.className)) electricPanelJump(msg.relatedNode); // 修复分区排行 if (msg.target.id == "bili_movie" || msg.target.id == "bili_teleplay" || msg.target.id == "bili_documentary") fixrank(msg.target); // 弹幕哈希反查 if (/danmaku-info-row/.test(msg.target.className)) danmkuHashId(msg.target); // 其他节点监听 resetNodes(); // 收藏页切p监听 setMediaList.fixvar(); // 修复空间主页失效视频 fixVideoLost.home(msg); // bv号转超链接 avdesc(); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址