// ==UserScript==
// @name 哔哩哔哩(B站|Bilibili)收藏夹Fix(多网站跳转)
// @name:zh-CN 哔哩哔哩(B站|Bilibili)收藏夹Fix(多网站跳转)
// @name:zh-TW 嗶哩嗶哩(B站|Bilibili)收藏夾Fix(多網站跳轉)
// @namespace http://tampermonkey.net/
// @version 1.2.2
// @description 手动备份视频信息至biliplus, xbeibeix, jijidown, 以便失效后查看
// @description:zh-CN 手动备份视频信息至biliplus, xbeibeix, jijidown, 以便失效后查看
// @description:zh-TW 手動備份影片資訊至biliplus, xbeibeix, jijidown, 以便失效後查看
// @author YTB0710
// @match https://space.bilibili.com/*
// @connect bilibili.com
// @grant GM_openInTab
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function () {
'use strict';
const localizedText = {
'JUMP': {
'zh-CN': '跳转至BXJ',
'zh-TW': '跳轉至BXJ'
},
'COVER': {
'zh-CN': '封面原图',
'zh-TW': '封面原圖'
},
'SPACE': {
'zh-CN': 'up主空间',
'zh-TW': 'up主主頁'
},
'MULTIPLE_DROPDOWN_FOUND_ERROR': {
'zh-CN': '同时发现了多个下拉列表开关, 无法确定下拉列表所对应的视频, 刷新页面或许能解决',
'zh-TW': '同時發現了多個下拉列表開關, 無法確定下拉列表所對應的影片, 重新載入頁面或許能解決'
},
'TARGET_VIDEO_NOT_FOUND_ERROR': {
'zh-CN': '无法确定下拉列表所对应的视频, 请反馈该问题',
'zh-TW': '無法確定下拉列表所對應的影片, 請反饋該問題'
},
'REQUEST_TIMEOUT_ERROR': {
'zh-CN': '请求超时, 请重试',
'zh-TW': '請求逾時, 請重試'
},
'INTRO_AND_UPPER_UID_NOT_FOUND_ERROR': {
'zh-CN': '无法获取某个失效视频的简介和up主uid, 切换至按最近收藏排序或许能解决',
'zh-TW': '無法獲取某個失效影片的簡介和up主uid, 切換至按最近收藏排序或許能解決'
},
'UNKNOWN_ERROR': {
'zh-CN': '发生未知错误, 请反馈该问题',
'zh-TW': '發生未知錯誤, 請反饋該問題'
},
};
const preferredLanguage = getPreferredLanguage();
const favlistURLRegex = /https:\/\/space\.bilibili\.com\/\d+\/favlist.*/;
const bvFromURLRegex = /video\/(\w{12})/;
const coverFromURLRegex = /\/\/([^@]*)@/;
let onFavlistPage = false;
let divMessageExist = false;
function getPreferredLanguage() {
const languages = navigator.languages || [navigator.language];
for (const lang of languages) {
if (lang === 'zh-CN') {
return 'zh-CN';
}
if (lang === 'zh-TW') {
return 'zh-TW';
}
if (lang === 'zh-HK') {
return 'zh-TW';
}
}
return 'zh-CN';
}
function getLocalizedText(key) {
return localizedText[key][preferredLanguage];
}
const favlistObserver = new MutationObserver(function (mutations, observer) {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
if (document.querySelector('div.favlist-main')) {
observer.disconnect();
biliCardDropdownPopperObserver.observe(document.body, { childList: true, attributes: false, characterData: false });
return;
}
if (document.querySelector('div.fav-content.section')) {
observer.disconnect();
favContentSectionObserver.observe(document.querySelector('div.fav-content.section'), { characterData: false, attributeFilter: ['class'] });
return;
}
}
}
});
const favContentSectionObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (!mutation.target.classList.contains('loading')) {
main();
}
});
});
const biliCardDropdownPopperObserver = new MutationObserver(mutations => {
for (const mutation of mutations) {
for (const addedNode of mutation.addedNodes) {
if (addedNode.nodeType === 1 && addedNode.classList.contains('bili-card-dropdown-popper')) {
mainNewFreshSpace(addedNode);
return;
}
}
}
});
checkURL();
const originalPushState = history.pushState;
history.pushState = function (...args) {
originalPushState.apply(this, args);
checkURL();
};
const originalReplaceState = history.replaceState;
history.replaceState = function (...args) {
originalReplaceState.apply(this, args);
checkURL();
};
window.addEventListener('popstate', checkURL);
function checkURL() {
if (favlistURLRegex.test(location.href)) {
if (!onFavlistPage) {
onFavlistPage = true;
favlistObserver.observe(document.body, { subtree: true, childList: true });
}
} else {
if (onFavlistPage) {
onFavlistPage = false;
favlistObserver.disconnect();
biliCardDropdownPopperObserver.disconnect();
favContentSectionObserver.disconnect();
}
}
}
function mainNewFreshSpace(divDropdownPopper) {
try {
if (!divMessageExist) {
const divMessage = document.createElement('div');
divMessage.classList.add('divMessage');
divMessage.style.padding = '2px 0';
divMessage.style.lineHeight = '1.5';
document.querySelector('div.favlist-aside').appendChild(divMessage);
}
const divDropdowns = document.querySelectorAll('.bili-card-dropdown--visible');
if (divDropdowns.length !== 1) {
addMessage(getLocalizedText('MULTIPLE_DROPDOWN_FOUND_ERROR'), 13);
return;
}
let divTargetVideo;
try {
divTargetVideo = document.querySelector('div.items__item:has(.bili-card-dropdown--visible)');
} catch {
const items = document.querySelectorAll('div.items__item');
for (const item of items) {
if (item.contains(divDropdowns[0])) {
divTargetVideo = item;
break;
}
}
}
if (!divTargetVideo) {
addMessage(getLocalizedText('TARGET_VIDEO_NOT_FOUND_ERROR'), 13);
return;
}
let disabled = false;
if (!divTargetVideo.querySelector('.bili-cover-card__stats')) {
disabled = true;
}
const bv = divDropdowns[0].parentNode.querySelector('a').getAttribute('href').match(bvFromURLRegex)[1];
const divJump = document.createElement('div');
divJump.classList.add('bili-card-dropdown-popper__item');
divJump.textContent = getLocalizedText('JUMP');
divJump.addEventListener('click', function () {
divDropdownPopper.classList.remove('visible');
GM_openInTab(`https://www.biliplus.com/video/${bv}`, { active: disabled, insert: false, setParent: true });
GM_openInTab(`https://xbeibeix.com/video/${bv}`, { insert: false, setParent: true });
GM_openInTab(`https://www.jijidown.com/video/${bv}`, { insert: false, setParent: true });
});
divDropdownPopper.appendChild(divJump);
if (!disabled) {
const divCover = document.createElement('div');
divCover.classList.add('bili-card-dropdown-popper__item');
divCover.textContent = getLocalizedText('COVER');
divCover.addEventListener('click', function () {
divDropdownPopper.classList.remove('visible');
GM_openInTab(`https://${divTargetVideo.querySelector('img').getAttribute('src').match(coverFromURLRegex)[1]}`, { active: true, insert: true, setParent: true });
});
divDropdownPopper.appendChild(divCover);
}
} catch (error) {
addMessage(getLocalizedText('UNKNOWN_ERROR'), 13);
addMessage(error, 11);
console.error(error);
}
}
async function main() {
try {
if (!divMessageExist) {
const divMessage = document.createElement('div');
divMessage.classList.add('divMessage');
divMessage.style.padding = '2px';
divMessage.style.lineHeight = '1.5';
document.querySelector('div.fav-sidenav').appendChild(divMessage);
}
const lisAll = document.querySelectorAll('li.small-item');
const lisDisabled = document.querySelectorAll('li.small-item.disabled');
let medias;
if (lisDisabled.length) {
const fid = document.querySelector('.fav-item.cur').getAttribute('fid');
const pn = parseInt(document.querySelector('li.be-pager-item-active').innerText, 10);
await GM.xmlHttpRequest({
method: 'GET',
url: `https://api.bilibili.com/x/v3/fav/resource/list?media_id=${fid}&pn=${pn}&ps=20&keyword=&order=mtime&type=0&tid=0&platform=web`,
timeout: 3000,
responseType: 'json',
onload: function (response) {
try {
medias = response.response.data.medias;
} catch (error) {
addMessage(getLocalizedText('UNKNOWN_ERROR'), 12);
addMessage(error, 10);
console.error(error);
}
},
onerror: function (response) {
addMessage(getLocalizedText('UNKNOWN_ERROR'), 12);
addMessage(response, 10);
console.error(response);
},
ontimeout: function (response) {
addMessage(getLocalizedText('REQUEST_TIMEOUT_ERROR'), 12);
addMessage(response, 10);
console.error(response);
}
});
}
lisAll.forEach(function (li) {
try {
const ul = li.querySelector('ul.be-dropdown-menu');
if (!ul.lastElementChild.classList.contains('added')) {
ul.lastElementChild.classList.add('be-dropdown-item-delimiter');
const bv = li.getAttribute('data-aid');
let disabled = false;
if (li.classList.contains('disabled')) {
li.classList.remove('disabled');
disabled = true;
}
const liJump = document.createElement('li');
liJump.className = 'be-dropdown-item added';
liJump.textContent = getLocalizedText('JUMP');
liJump.addEventListener('click', function () {
GM_openInTab(`https://www.biliplus.com/video/${bv}`, { active: disabled, insert: false, setParent: true });
GM_openInTab(`https://xbeibeix.com/video/${bv}`, { insert: false, setParent: true });
GM_openInTab(`https://www.jijidown.com/video/${bv}`, { insert: false, setParent: true });
});
ul.appendChild(liJump);
if (!disabled) {
const liCover = document.createElement('li');
liCover.className = 'be-dropdown-item added';
liCover.textContent = getLocalizedText('COVER');
liCover.addEventListener('click', function () {
GM_openInTab(`https://${li.querySelector('img').getAttribute('src').match(coverFromURLRegex)[1]}`, { active: true, insert: true, setParent: true });
});
ul.appendChild(liCover);
}
if (disabled) {
try {
const media = medias.find(m => m.bvid === bv);
const as = li.querySelectorAll('a');
const content = `${media.intro} `;
as[0].setAttribute('title', content);
as[1].textContent = content;
const liSpace = document.createElement('li');
liSpace.className = 'be-dropdown-item added';
liSpace.textContent = getLocalizedText('SPACE');
liSpace.addEventListener('click', function () {
GM_openInTab(`https://space.bilibili.com/${media.upper.mid}`, { active: true, insert: true, setParent: true });
});
ul.appendChild(liSpace);
} catch (error) {
addMessage(getLocalizedText('INTRO_AND_UPPER_UID_NOT_FOUND_ERROR'), 12);
addMessage(error, 10);
console.error(error);
}
}
}
} catch (error) {
addMessage(getLocalizedText('UNKNOWN_ERROR'), 12);
addMessage(error, 10);
console.error(error);
}
});
} catch (error) {
addMessage(getLocalizedText('UNKNOWN_ERROR'), 12);
addMessage(error, 10);
console.error(error);
}
}
function addMessage(msg, px) {
const p = document.createElement('p');
p.innerHTML = msg;
p.style.fontSize = `${px}px`;
document.querySelector('.divMessage').appendChild(p);
p.scrollIntoView({ behavior: 'instant', block: 'nearest' });
}
})();