哔哩哔哩(B站|Bilibili)收藏夹Fix(多网站跳转)

手动备份视频信息至biliplus, xbeibeix, jijidown, 以便失效后查看

Installer dette script?
Skaberens foreslåede script

Du vil måske også kunne lide bilibili favlist backup

Installer dette script
// ==UserScript==
// @name              哔哩哔哩(B站|Bilibili)收藏夹Fix(多网站跳转)
// @name:zh-CN        哔哩哔哩(B站|Bilibili)收藏夹Fix(多网站跳转)
// @name:zh-TW        嗶哩嗶哩(B站|Bilibili)收藏夾Fix(多網站跳轉)
// @namespace         http://tampermonkey.net/
// @version           1.2.3
// @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 = {
        'DROPDOWN_JUMP': {
            'zh-CN': '跳转至BXJ',
            'zh-TW': '跳轉至BXJ'
        },
        'DROPDOWN_COVER': {
            'zh-CN': '封面原图',
            'zh-TW': '封面原圖'
        },
        'DROPDOWN_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': '請求逾時'
        },
        'REQUEST_FAILED_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 newFreshSpace;

    const favlistObserver = new MutationObserver((mutations, observer) => {
        if (document.querySelector('div.favlist-main')) {
            observer.disconnect();
            newFreshSpace = true;
            biliCardDropdownPopperObserver.observe(document.body, { childList: true, attributes: false, characterData: false });
            return;
        }
        if (document.querySelector('div.fav-content.section')) {
            observer.disconnect();
            newFreshSpace = false;
            favContentSectionObserver.observe(document.querySelector('div.fav-content.section'), { characterData: false, attributeFilter: ['class'] });
            return;
        }
    });

    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;
                }
            }
        }
    });

    const favContentSectionObserver = new MutationObserver(mutations => {
        for (const mutation of mutations) {
            if (!mutation.target.classList.contains('loading')) {
                main();
                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, attributes: false, characterData: false });
            }
        } else {
            if (onFavlistPage) {
                onFavlistPage = false;
                favlistObserver.disconnect();
                biliCardDropdownPopperObserver.disconnect();
                favContentSectionObserver.disconnect();
            }
        }
    }

    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];
    }

    function mainNewFreshSpace(divDropdownPopper) {
        try {
            const divDropdowns = document.querySelectorAll('.bili-card-dropdown--visible');
            if (divDropdowns.length !== 1) {
                addMessage(getLocalizedText('MULTIPLE_DROPDOWN_FOUND_ERROR'));
                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'));
                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 dropdownJump = document.createElement('div');
            dropdownJump.classList.add('bili-card-dropdown-popper__item');
            dropdownJump.textContent = getLocalizedText('DROPDOWN_JUMP');
            dropdownJump.addEventListener('click', () => {
                try {
                    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 });
                } catch (error) {
                    catchUnknownError(error);
                }
            });
            divDropdownPopper.appendChild(dropdownJump);

            if (!disabled) {
                const dropdownCover = document.createElement('div');
                dropdownCover.classList.add('bili-card-dropdown-popper__item');
                dropdownCover.textContent = getLocalizedText('DROPDOWN_COVER');
                dropdownCover.addEventListener('click', () => {
                    try {
                        divDropdownPopper.classList.remove('visible');
                        GM_openInTab(`https://${divTargetVideo.querySelector('img').getAttribute('src').match(coverFromURLRegex)[1]}`, { active: true, insert: true, setParent: true });
                    } catch (error) {
                        catchUnknownError(error);
                    }
                });
                divDropdownPopper.appendChild(dropdownCover);
            }

        } catch (error) {
            catchUnknownError(error);
        }
    }

    async function main() {
        try {
            let medias;
            if (document.querySelectorAll('li.small-item.disabled').length) {
                const fid = document.querySelector('.fav-item.cur').getAttribute('fid');
                const pn = parseInt(document.querySelector('li.be-pager-item-active').innerText, 10);
                const response = await new Promise((resolve, reject) => {
                    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: 5000,
                        responseType: 'json',
                        onload: (res) => resolve(res),
                        onerror: () => reject(Error(getLocalizedText('REQUEST_FAILED_ERROR'))),
                        ontimeout: () => reject(Error(getLocalizedText('REQUEST_TIMEOUT_ERROR')))
                    });
                });
                medias = response.response.data.medias;
            }

            document.querySelectorAll('li.small-item').forEach(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');
                        const as = li.querySelectorAll('a');

                        let disabled = false;
                        if (li.classList.contains('disabled')) {
                            li.classList.remove('disabled');
                            as[0].classList.remove('disabled');
                            disabled = true;
                        }

                        const dropdownJump = document.createElement('li');
                        dropdownJump.className = 'be-dropdown-item added';
                        dropdownJump.textContent = getLocalizedText('DROPDOWN_JUMP');
                        dropdownJump.addEventListener('click', () => {
                            try {
                                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 });
                            } catch (error) {
                                catchUnknownError(error);
                            }
                        });
                        ul.appendChild(dropdownJump);

                        if (!disabled) {
                            const dropdownCover = document.createElement('li');
                            dropdownCover.className = 'be-dropdown-item added';
                            dropdownCover.textContent = getLocalizedText('DROPDOWN_COVER');
                            dropdownCover.addEventListener('click', () => {
                                try {
                                    GM_openInTab(`https://${li.querySelector('img').getAttribute('src').match(coverFromURLRegex)[1]}`, { active: true, insert: true, setParent: true });
                                } catch (error) {
                                    catchUnknownError(error);
                                }
                            });
                            ul.appendChild(dropdownCover);
                        }

                        if (disabled) {
                            try {
                                const media = medias.find(m => m.bvid === BV);
                                as[1].textContent = media.intro;
                                as[1].setAttribute('title', media.intro);

                                const dropdownSpace = document.createElement('li');
                                dropdownSpace.className = 'be-dropdown-item added';
                                dropdownSpace.textContent = getLocalizedText('DROPDOWN_SPACE');
                                dropdownSpace.addEventListener('click', () => {
                                    try {
                                        GM_openInTab(`https://space.bilibili.com/${media.upper.mid}`, { active: true, insert: true, setParent: true });
                                    } catch (error) {
                                        catchUnknownError(error);
                                    }
                                });
                                ul.appendChild(dropdownSpace);

                            } catch (error) {
                                addMessage(getLocalizedText('INTRO_AND_UPPER_UID_NOT_FOUND_ERROR'));
                                addMessage(error.stack, true);
                                console.error(error);
                            }
                        }
                    }

                } catch (error) {
                    catchUnknownError(error);
                }
            });

        } catch (error) {
            catchUnknownError(error);
        }
    }

    function addMessage(msg, smallFontSize) {
        let px;
        if (smallFontSize) {
            px = newFreshSpace ? 11 : 10;
        } else {
            px = newFreshSpace ? 13 : 12;
        }
        const p = document.createElement('p');
        p.innerHTML = msg;
        p.style.padding = newFreshSpace ? '2px 0' : '2px';
        p.style.fontSize = `${px}px`;
        p.style.lineHeight = '1.5';
        document.querySelector(newFreshSpace ? 'div.favlist-aside' : 'div.fav-sidenav').appendChild(p);
        p.scrollIntoView({ behavior: 'instant', block: 'nearest' });
    }

    function catchUnknownError(error) {
        addMessage(getLocalizedText('UNKNOWN_ERROR'));
        addMessage(error.stack, true);
        console.error(error);
    }
})();