Greasy Fork is available in English.
修复 哔哩哔哩(www.bilibili.com) 失效的收藏。(可查看av号、简介、标题、封面)
Fra og med
// ==UserScript==// @name 哔哩哔哩(B站|Bilibili)收藏夹Fix// @namespace http://tampermonkey.net/// @version 1.2// @description 修复 哔哩哔哩(www.bilibili.com) 失效的收藏。(可查看av号、简介、标题、封面)// @author Mr.Po// @match https://space.bilibili.com/*// @require http://code.jquery.com/jquery-1.11.0.min.js// @resource iconError https://raw.githubusercontent.com/Mr-Po/bilibili-favorites-fix/master/media/error.png// @resource iconSuccess https://raw.githubusercontent.com/Mr-Po/bilibili-favorites-fix/master/media/success.png// @resource iconInfo https://raw.githubusercontent.com/Mr-Po/bilibili-favorites-fix/master/media/info.png// @connect biliplus.com// @grant GM_xmlhttpRequest// @grant GM_notification// @grant GM_setClipboard// @grant GM_getResourceURL// ==/UserScript==/*jshint esversion: 8 */(function() {'use strict';/*** 失效收藏标题颜色(默认为灰色)。* @type {String}*/const invalTitleColor = "#999";/*** 是否启用调试模式。* 启用后,浏览器控制台会显示此脚本运行时的调试数据。* @type {Boolean}*/const isDebug = false;/*** 重试延迟[秒]。* @type {Number}*/const retryDelay = 5;/*** 每隔 space [毫秒]检查一次,是否有新的收藏被加载出来。* 此值越小,检查越快;过小会造成浏览器卡顿。* @type {Number}*/const space = 2000;/******************************************************//*** 收藏夹地址正则* @type {RegExp}*/const favlistRegex = /https:\/\/space\.bilibili\.com\/\d+\/favlist.*/;/*** 处理收藏*/function handleFavorites() {const flag = favlistRegex.test(window.location.href);if (flag) { // 当前页面是收藏地址// 失效收藏节点集const $lis = $("ul.fav-video-list.content li.small-item.disabled");if ($lis.size() > 0) {console.info(`${$lis.size()}个收藏待修复...`);$lis.each(function(i, it) {const bv = $(it).attr("data-aid");const aid = bv2aid(bv);// 多个超链接const $as = $(it).find("a");$as.attr("href", `https://www.biliplus.com/video/av${aid}/`);$as.attr("target", "_blank");addCopyAVCodeButton($(it), aid);fixTitleAndPic($(it), $($as[1]), aid);// 移除禁用样式$(it).removeClass("disabled");$as.removeClass("disabled");});showDetail($lis);}}}function addOperation($item, name, fun) {const $ul = $item.find(".be-dropdown-menu").first();const lastChild = $ul.children().last();// 未添加过扩展if (!lastChild.hasClass('be-dropdown-item-extend')) {lastChild.addClass("be-dropdown-item-delimiter");}const $li = $(`<li class="be-dropdown-item be-dropdown-item-extend">${name}</li>`);$li.click(fun);$ul.append($li);}function addCopyAVCodeButton($item, aid) {addOperation($item, "复制av号", function() {GM_setClipboard(`av${aid}`, "text");tipSuccess("av号复制成功!");});}function addCopyInfoButton($item, content) {addOperation($item, "复制简介", function() {GM_setClipboard(content, "text");tipSuccess("简介复制成功!");});}/*** 标记失效的收藏* @param {$节点} $it 当前收藏Item* @param {$节点} $a 标题链接*/function signInval($it, $a) {// 收藏时间const $pubdate = $it.find("div.meta.pubdate");// 增加 删除线$pubdate.attr("style", "text-decoration:line-through");// 增加 删除线 + 置(灰)$a.attr("style", `text-decoration:line-through;color:${invalTitleColor};`);}/*** 绑定重新加载* @param {$节点} $a 标题链接* @param {函数} fun 重试方法*/function bindReload($a, fun) {$a.text("->手动加载<-");$a.click(function() {$(this).unbind("click");$a.text("Loading...");fun();});}/*** 再次尝试加载* @param {$节点} $a 标题链接* @param {数字} aid AV号* @param {布尔} delayRetry 延迟重试* @param {函数} fun 重试方法*/function retryLoad($a, aid, delayRetry, fun) {console.warn(`查询:av${aid},请求过快!`);if (delayRetry) { // 延迟绑定$a.text(`请求过快,${retryDelay}秒后再试!`);setTimeout(bindReload, retryDelay * 1000, $a, fun);countdown($a, retryDelay);} else { // 首次,立即绑定$a.attr("href", "javascript:void(0);");bindReload($a, fun);}}/*** 重新绑定倒计时* @param {$节点} $a 标题链接* @param {数字} second 秒*/function countdown($a, second) {if ($a.text().indexOf("请求过快") === 0) {$a.text(`请求过快,${second}秒后再试!`);if (second > 1) {setTimeout(countdown, 1000, $a, second - 1);}}}/*** 修复收藏* @param {$节点} $it 当前收藏Item* @param {$节点} $a 标题链接* @param {数字} aid av号* @param {字符串} title 标题* @param {字符串} pic 海报* @param {字符串} history 历史归档,若无时,使用空字符串*/function fixFavorites($it, $a, aid, title, pic, history) {// 设置标题$a.text(title);$a.attr("title", $a.text());// 多个超链接const $as = $it.find("a");$as.attr("href", `https://www.biliplus.com/${history}video/av${aid}/`);signInval($it, $a);// 判断海报链接是否有效,有效时进行替换isLoad(pic, function() {const $img = $it.find("img");$img.attr("src", pic);});}/*** 修复标题和海报* @param {$节点} $it 当前收藏Item* @param {$节点} $a 标题链接* @param {数字} aid av号*/function fixTitleAndPic($it, $a, aid) {$a.text("Loading...");fixTitleAndPicEnhance3($it, $a, aid);}/*** 修复标题和海报 增强 - 0* 使用公开的API* @param {$节点} $it 当前收藏Item* @param {$节点} $a 标题链接* @param {数字} aid av号* @param {布尔} delayRetry 延迟重试*/function fixTitleAndPicEnhance0($it, $a, aid, delayRetry) {GM_xmlhttpRequest({method: 'GET',url: `https://www.biliplus.com/api/view?id=${aid}`,responseType: "json",onload: function(response) {const res = response.response;if (isDebug) {console.log("0---->:");console.log(res);}// 找到了if (res.title) {fixFavorites($it, $a, aid, res.title, res.pic, "");} else if (res.code == -503) { // 请求过快retryLoad($a, aid, delayRetry, function() {fixTitleAndPicEnhance0($it, $a, aid, true);});} else { // 未找到fixTitleAndPicEnhance1($it, $a, aid);}},onerror: function(e) {console.log("出错啦");console.log(e);}});}/*** 修复标题和海报 增强 - 1* 使用cache库* @param {$节点} $it 当前收藏Item* @param {$节点} $a 标题链接* @param {数字} aid av号*/function fixTitleAndPicEnhance1($it, $a, aid) {GM_xmlhttpRequest({method: 'GET',url: `https://www.biliplus.com/all/video/av${aid}/`,onload: function(response) {if (isDebug) {console.log("1---->:");console.log(response.response);}const params = response.responseText.match(/getjson\('(\/api\/view_all.+)'/);fixTitleAndPicEnhance2($it, $a, aid, params[1]);}});}/*** 修复标题和海报 增强 - 2* 使用cache库,第一段,需与fixTitleAndPicEnhance1连用* @param {$节点} $it 当前收藏Item* @param {$节点} $a 标题链接* @param {数字} aid av号* @param {字符串} param 待拼接参数* @param {布尔} delayRetry 延迟重试*/function fixTitleAndPicEnhance2($it, $a, aid, param, delayRetry) {GM_xmlhttpRequest({method: 'GET',url: `https://www.biliplus.com${param}`,responseType: "json",onload: function(response) {const res = response.response;if (isDebug) {console.log("2---->:");console.log(res);}// 找到了if (res.code === 0) {fixFavorites($it, $a, aid, res.data.info.title, res.data.info.pic, "all/");} else if (res.code == -503) { // 请求过快retryLoad($a, aid, delayRetry, function() {fixTitleAndPicEnhance2($it, $a, aid, param, true);});} else { // 未找到$a.text(`已失效(${aid})`);$a.attr("title", $a.text());}}});}/*** 修复标题和海报 增强 - 3* 模拟常规查询* @param {$节点} $it 当前收藏Item* @param {$节点} $a 标题链接* @param {数字} aid av号*/function fixTitleAndPicEnhance3($it, $a, aid) {let jsonRegex;GM_xmlhttpRequest({method: 'GET',url: `https://www.biliplus.com/video/av${aid}/`,onload: function(response) {try {if (isDebug) {console.log("3---->:");console.log(response.response);}jsonRegex = response.responseText.match(/window\.addEventListener\('DOMContentLoaded',function\(\){view\((.+)\);}\);/);if (isDebug) {console.log(jsonRegex);}const jsonStr = jsonRegex[1];if (isDebug) {console.log(jsonStr);}const res = $.parseJSON(jsonStr);if (res.title) { // 存在fixFavorites($it, $a, aid, res.title, res.pic, "");} else if (res.code == -503) { // 请求过快retryLoad($a, aid, null, function() {fixTitleAndPicEnhance0($it, $a, aid, true);});} else { // 不存在fixTitleAndPicEnhance1($it, $a, aid);}} catch (err) {console.error(err);console.log(jsonRegex);// 当出现错误时,出现手动加载retryLoad($a, aid, null, function() {fixTitleAndPicEnhance0($it, $a, aid, true);});}}});}/*** 判断一个url是否可以访问* @param {字符串} url http地址* @param {函数} fun 有效时的回调*/function isLoad(url, fun) {$.ajax({url: url,type: 'GET',success: function(response) {fun();},error: function(e) {}});}/*** 显示详细* @param {$节点} $lis 失效收藏节点集*/function showDetail($lis) {const fidRegex = window.location.href.match(/fid=(\d+)/);let fid;if (fidRegex) {fid = fidRegex[1];} else {fid = $("div.fav-item.cur").attr("fid");}const pn = $("ul.be-pager li.be-pager-item.be-pager-item-active").text();$.ajax({url: `https://api.bilibili.com/medialist/gateway/base/spaceDetail?media_id=${fid}&pn=${pn}&ps=20&keyword=&order=mtime&type=0&tid=0&jsonp=jsonp`,success: function(json) {const $medias = json.data.medias;$lis.each(function(i, it) {const bv = $(it).attr("data-aid");const $mediaF = $medias.filter(function(it) {if (it.bvid == bv) {return it;}});const $media = $mediaF[0];const $a = $(it).find("a");let titles = "";if ($media.pages) {const $titlesM = $media.pages.map(function(it, i, arry) {return it.title;});titles = $titlesM.join("、");}const aid = bv2aid(bv);const content = `av:${aid}\nP数:${$media.page}\n子P:${titles}\n简介:${$media.intro}`;$($a[0]).attr("title", content);addCopyInfoButton($(it), content);});}});}const bvTable = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF";const bvArray = [{ bvIndex: 11, bvTimes: 1 },{ bvIndex: 10, bvTimes: 58 },{ bvIndex: 3, bvTimes: 3364 },{ bvIndex: 8, bvTimes: 195112 },{ bvIndex: 4, bvTimes: 11316496 },{ bvIndex: 6, bvTimes: 656356768 },];const bvXor = 177451812;const bvAdd = 8728348608;/*** BV号转aid* @param {字符串} bv BV号* @return {数字} av号*/function bv2aid(bv) {const value = bvArray.map((it, i) => {return bvTable.indexOf(bv[it.bvIndex]) * it.bvTimes;}).reduce((total, num) => {return total + num;});return (value - bvAdd) ^ bvXor;}function tip(text, iconName) {GM_notification({text: text,image: GM_getResourceURL(iconName)});}function tipInfo(text) {tip(text, "iconInfo");}function tipError(text) {tip(text, "iconError");}function tipSuccess(text) {tip(text, "iconSuccess");}setInterval(handleFavorites, space);})();