哔哩哔哩(B站|Bilibili)收藏夹Fix(隐藏视频检测)

检测收藏夹中被up主设置为仅自己可见的视频

目前为 2024-12-13 提交的版本。查看 最新版本

// ==UserScript==
// @name              bilibili favlist hidden video detection
// @name:zh-CN        哔哩哔哩(B站|Bilibili)收藏夹Fix(隐藏视频检测)
// @name:zh-TW        嗶哩嗶哩(B站|Bilibili)收藏夾Fix(隱藏影片檢測)
// @namespace         http://tampermonkey.net/
// @version           5
// @description       detect videos in favlist that only visiable to upper
// @description:zh-CN 检测收藏夹中被up主设置为仅自己可见的视频
// @description:zh-TW 檢測收藏夾中被上傳者設定為僅自己可見的影片
// @author            YTB0710
// @match             https://space.bilibili.com/*
// @connect           api.bilibili.com
// @grant             GM_xmlhttpRequest
// @grant             GM_cookie
// @grant             GM_openInTab
// @grant             GM_setValue
// @grant             GM_getValue
// ==/UserScript==

(function () {
    'use strict';

    const localizedText = {
        'UPDATES': {
            'zh-CN': '更新内容:<br>新增: 适配新版个人空间<br>其他: 优化各个控件的展示效果',
            'zh-TW': '更新內容:<br>新增: 適配新版個人主頁<br>其他: 優化各個控件的顯示效果'
        },
        'INPUT_AV_OR_BV_HERE': {
            'zh-CN': '在此输入av号或bv号',
            'zh-TW': '在此輸入av號或bv號'
        },
        'DETECT_HIDDEN_VIDEO_WITH_PROMPT': {
            'zh-CN': '检测隐藏视频(先刷新页面)',
            'zh-TW': '檢測隱藏影片(先重新載入頁面)'
        },
        'DETECT_HIDDEN_VIDEO': {
            'zh-CN': '检测隐藏视频',
            'zh-TW': '檢測隱藏影片'
        },
        'GET_VIDEO_INFO_WITH_PROMPT': {
            'zh-CN': '查询视频信息(输入bv号)',
            'zh-TW': '查詢影片資訊(輸入bv號)'
        },
        'GET_VIDEO_INFO': {
            'zh-CN': '查询视频信息',
            'zh-TW': '查詢影片資訊'
        },
        'REMOVE_VIDEO_WITH_PROMPT': {
            'zh-CN': '取消收藏(输入av号)',
            'zh-TW': '取消收藏(輸入av號)'
        },
        'REMOVE_VIDEO': {
            'zh-CN': '取消收藏',
            'zh-TW': '取消收藏'
        },
        'ADD_VIDEO_WITH_PROMPT': {
            'zh-CN': '添加收藏(输入av号)',
            'zh-TW': '新增收藏(輸入av號)'
        },
        'ADD_VIDEO': {
            'zh-CN': '添加收藏',
            'zh-TW': '新增收藏'
        },
        'AV': {
            'zh-CN': 'av号',
            'zh-TW': 'av號'
        },
        'BV': {
            'zh-CN': 'bv号',
            'zh-TW': 'bv號'
        },
        'INPUT_AV': {
            'zh-CN': '请输入av号',
            'zh-TW': '請輸入av號'
        },
        'INPUT_BV': {
            'zh-CN': '请输入bv号',
            'zh-TW': '請輸入bv號'
        },
        'NO_HIDDEN_VIDEO_ON_THIS_PAGE': {
            'zh-CN': '本页没有隐藏的视频',
            'zh-TW': '本頁沒有隱藏的影片'
        },
        'POSITION_ON_THIS_PAGE_WITH_PROMPT': {
            'zh-CN': '在本页的位置(从1开始)',
            'zh-TW': '在本頁的位置(從1開始)'
        },
        'POSITION_ON_THIS_PAGE': {
            'zh-CN': '在本页的位置',
            'zh-TW': '在本頁的位置'
        },
        'API_RESPONSE_CONTENT': {
            'zh-CN': 'b站接口响应内容',
            'zh-TW': 'b站介面回應內容'
        },
        'REFRESH_PAGE_IF_ERROR': {
            'zh-CN': '如果出现问题, 请刷新页面后重试',
            'zh-TW': '如果出現問題, 請重新載入頁面後再試'
        },
        'COOKIE_READ_ERROR': {
            'zh-CN': '无法读取cookie, 请更新tampermonkey, 前往控制台查看错误信息',
            'zh-TW': '無法讀取cookie, 請更新tampermonkey, 前往控制台查看錯誤資訊'
        },
        'VIDEOS_PER_PAGE': {
            'zh-CN': '每页展示视频数量',
            'zh-TW': '每頁顯示影片數量'
        },
        'VIDEOS_PER_PAGE_WITH_PROMPT': {
            'zh-CN': '每页最多可以展示的视频数量(一般为36或40)',
            'zh-TW': '每頁最多可以顯示的影片數量(一般為36或40)'
        },
        'INVALID_VIDEOS_PER_PAGE_ERROR': {
            'zh-CN': '每页展示视频数量不正确, 请重新输入',
            'zh-TW': '每頁顯示影片數量不正確, 請重新輸入'
        },
        'CHECK_VIDEOS_PER_PAGE_AND_REFRESH_PAGE_IF_ERROR': {
            'zh-CN': '如果出现问题, 请检查每页展示视频数量是否正确, 如果无误, 请刷新页面后重试',
            'zh-TW': '如果出現問題, 請檢查每頁顯示影片數量是否正確, 如果無誤, 請重新載入頁面後再試'
        },
        'CHECK_VIDEOS_PER_PAGE_AND_RETRY': {
            'zh-CN': '请检查预设的每页展示视频数量是否正确, 然后再次点击按钮',
            'zh-TW': '請檢查預設的每頁顯示影片數量是否正確, 然後再次點擊按鈕'
        },
        'INPUT_VIDEOS_PER_PAGE_AND_RETRY': {
            'zh-CN': '请手动设置每页展示视频数量, 然后再次点击按钮',
            'zh-TW': '請手動設定每頁顯示影片數量, 然後再次點擊按鈕'
        },
        'FID_NOT_FOUND_ERROR': {
            'zh-CN': '无法获取当前收藏夹的fid, 请刷新页面后重试',
            'zh-TW': '無法獲取當前收藏夾的fid, 請重新載入頁面後再試'
        },
    };

    const preferredLanguage = getPreferredLanguage();
    const currentVersion = 5;

    const avRegex = /^[1-9]\d*$/;
    const bvRegex = /^BV[A-Za-z0-9]{10}$/;
    const startsWithAVRegex = /^av/i;
    const favlistURLRegex = /https:\/\/space\.bilibili\.com\/\d+\/favlist.*/;
    const pagenationCountRegex = /共 (\d+) 页 \/ (\d+) 个/;
    const fidFromUrlRegex = /fid=(\d+)/;
    const bvFromUrlRegex = /video\/(\w{12})/;

    let onFavlistPage = 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 sideObserver = new MutationObserver(function (mutations, observer) {
        for (const mutation of mutations) {
            if (mutation.type === 'childList') {
                if (document.querySelector('div.favlist-aside')) {
                    observer.disconnect();
                    mainNewFreshSpace();
                    return;
                }
                if (document.querySelector('div.fav-sidenav')) {
                    observer.disconnect();
                    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;
                sideObserver.observe(document.body, { subtree: true, childList: true });
            }
        } else {
            if (onFavlistPage) {
                onFavlistPage = false;
                sideObserver.disconnect();
            }
        }
    }

    function mainNewFreshSpace() {

        let videosPerPage;

        const storedVersion = GM_getValue('version', 0);
        let displayUpdate = false;
        if (storedVersion !== currentVersion) {
            GM_setValue('version', currentVersion);
            if (storedVersion) {
                displayUpdate = true;
            }
        }

        const usageCountNewFreshSpace = GM_getValue('usageCountNewFreshSpace', 0);
        const displayPrompt = usageCountNewFreshSpace < 10 ? '_WITH_PROMPT' : '';
        if (displayPrompt) {
            GM_setValue('usageCountNewFreshSpace', usageCountNewFreshSpace + 1);
        }

        const favlistAside = document.querySelector('div.favlist-aside');

        const divControls = document.createElement('div');
        divControls.style.padding = '2px 0';
        favlistAside.appendChild(divControls);

        const divVideosPerPage = document.createElement('div');
        divVideosPerPage.style.padding = '2px 0';
        divControls.appendChild(divVideosPerPage);

        const labelA = document.createElement('label');
        labelA.innerText = getLocalizedText('VIDEOS_PER_PAGE' + displayPrompt);
        labelA.style.lineHeight = '1';
        divVideosPerPage.appendChild(labelA);

        const inputTextA = document.createElement('input');
        inputTextA.type = 'text';
        inputTextA.style.boxSizing = 'content-box';
        inputTextA.style.height = '16px';
        inputTextA.style.width = '20px';
        inputTextA.style.padding = '4px';
        inputTextA.style.border = '1px solid #ccc';
        inputTextA.style.borderRadius = '3px';
        inputTextA.style.fontSize = '16px';
        inputTextA.style.lineHeight = '1';
        labelA.insertAdjacentElement('beforeend', inputTextA);

        const divInputTextB = document.createElement('div');
        divInputTextB.style.padding = '2px 0';
        divControls.appendChild(divInputTextB);

        const inputTextB = document.createElement('input');
        inputTextB.type = 'text';
        inputTextB.style.boxSizing = 'content-box';
        inputTextB.style.height = '16px';
        inputTextB.style.width = '170px';
        inputTextB.style.padding = '4px';
        inputTextB.style.border = '1px solid #ccc';
        inputTextB.style.borderRadius = '3px';
        inputTextB.style.fontSize = '16px';
        inputTextB.style.lineHeight = '1';
        if (displayPrompt) {
            inputTextB.placeholder = getLocalizedText('INPUT_AV_OR_BV_HERE');
        }
        divInputTextB.appendChild(inputTextB);

        const divButtonA = document.createElement('div');
        divButtonA.style.padding = '2px 0';
        divControls.appendChild(divButtonA);

        const buttonA = document.createElement('button');
        buttonA.type = 'button';
        buttonA.innerText = getLocalizedText('DETECT_HIDDEN_VIDEO' + displayPrompt);
        buttonA.style.padding = '4px';
        buttonA.style.border = '1px solid #ccc';
        buttonA.style.borderRadius = '3px';
        buttonA.style.fontSize = '16px';
        buttonA.style.lineHeight = '1';
        buttonA.style.cursor = 'pointer';
        buttonA.addEventListener('click', function () {
            clearMessage();

            const fidFromUrlMatch = location.href.match(fidFromUrlRegex);
            let fid;
            if (fidFromUrlMatch) {
                fid = fidFromUrlMatch[1];
            } else {
                addMessage(getLocalizedText('FID_NOT_FOUND_ERROR'));
                return;
            }

            const divVideos = document.querySelectorAll('div.bili-video-card__wrap');
            const spanPagenationGoCount = document.querySelector('span.vui_pagenation-go__count');
            if (spanPagenationGoCount) {
                videosPerPage = parseInt(inputTextA.value, 10);
                if (isNaN(videosPerPage)) {
                    if (divVideos.length <= 36) {
                        inputTextA.value = 36;
                    } else if (divVideos.length <= 40) {
                        inputTextA.value = 40;
                    } else {
                        addMessage(getLocalizedText('INPUT_VIDEOS_PER_PAGE_AND_RETRY'));
                        return;
                    }
                    addMessage(getLocalizedText('CHECK_VIDEOS_PER_PAGE_AND_RETRY'));
                    return;
                }

                const pagenationCountMatch = spanPagenationGoCount.innerText.match(pagenationCountRegex);
                const totalPages = parseInt(pagenationCountMatch[1], 10);
                const totalVideos = parseInt(pagenationCountMatch[2], 10);

                if (videosPerPage < Math.ceil(totalVideos / totalPages) || (videosPerPage > Math.floor((totalVideos - 1) / (totalPages - 1)))) {
                    addMessage(getLocalizedText('INVALID_VIDEOS_PER_PAGE_ERROR'));
                    return;
                }
            }

            addMessage(getLocalizedText('CHECK_VIDEOS_PER_PAGE_AND_REFRESH_PAGE_IF_ERROR'), 11);

            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://api.bilibili.com/x/v3/fav/resource/ids?media_id=${fid}`,
                responseType: 'json',
                onload: function (response) {
                    const datas = response.response.data;

                    let currentPage;
                    const pagenation = document.querySelector('button.vui_pagenation--btn-num.vui_button--active');
                    if (!pagenation) {
                        currentPage = 1;
                    } else {
                        currentPage = parseInt(pagenation.innerText, 10);
                    }

                    const startIndex = (currentPage - 1) * videosPerPage;
                    const currentPageExpectedDatas = datas.slice(startIndex, startIndex + videosPerPage);
                    const currentPageActualBVs = Array.from(divVideos).map(divVideo => divVideo.querySelector('a.bili-cover-card').getAttribute('href').match(bvFromUrlRegex)[1]);
                    const hiddenDatas = currentPageExpectedDatas.filter(data => !currentPageActualBVs.includes(data.bvid));
                    if (!hiddenDatas.length) {
                        addMessage(getLocalizedText('NO_HIDDEN_VIDEO_ON_THIS_PAGE'));
                        return;
                    }
                    hiddenDatas.forEach(hiddenData => {
                        addMessage(`${getLocalizedText('POSITION_ON_THIS_PAGE' + displayPrompt)}: ${currentPageExpectedDatas.findIndex(data => data.bvid === hiddenData.bvid) + 1}`);
                        addMessage(`${getLocalizedText('AV')}: ${hiddenData.id}`);
                        addMessage(`${getLocalizedText('BV')}: ${hiddenData.bvid}`);
                    });
                }
            });
        });
        divButtonA.appendChild(buttonA);

        const divButtonB = document.createElement('div');
        divButtonB.style.padding = '2px 0';
        divControls.appendChild(divButtonB);

        const buttonB = document.createElement('button');
        buttonB.type = 'button';
        buttonB.innerText = getLocalizedText('GET_VIDEO_INFO' + displayPrompt);
        buttonB.style.padding = '4px';
        buttonB.style.border = '1px solid #ccc';
        buttonB.style.borderRadius = '3px';
        buttonB.style.fontSize = '16px';
        buttonB.style.lineHeight = '1';
        buttonB.style.cursor = 'pointer';
        buttonB.addEventListener('click', function () {
            clearMessage();
            const bv = inputTextB.value;
            if (!bvRegex.test(bv)) {
                addMessage(getLocalizedText('INPUT_BV'));
                return;
            }
            GM_openInTab(`https://www.biliplus.com/video/${bv}`, { active: true, insert: false, setParent: true });
            GM_openInTab(`https://www.jijidown.com/video/${bv}`, { insert: false, setParent: true });
            GM_openInTab(`https://xbeibeix.com/video/${bv}`, { insert: false, setParent: true });
        });
        divButtonB.appendChild(buttonB);

        const divButtonC = document.createElement('div');
        divButtonC.style.padding = '2px 0';
        divControls.appendChild(divButtonC);

        const buttonC = document.createElement('button');
        buttonC.type = 'button';
        buttonC.innerText = getLocalizedText('REMOVE_VIDEO' + displayPrompt);
        buttonC.style.padding = '4px';
        buttonC.style.border = '1px solid #ccc';
        buttonC.style.borderRadius = '3px';
        buttonC.style.fontSize = '16px';
        buttonC.style.lineHeight = '1';
        buttonC.style.cursor = 'pointer';
        buttonC.addEventListener('click', function () {
            clearMessage();
            GM_cookie.list({ name: 'bili_jct' }, function (cookies, error) {
                if (!error) {
                    let av = inputTextB.value;
                    if (startsWithAVRegex.test(av)) {
                        av = av.slice(2);
                    }
                    if (!avRegex.test(av)) {
                        addMessage(getLocalizedText('INPUT_AV'));
                        return;
                    }
                    const fidFromUrlMatch = location.href.match(fidFromUrlRegex);
                    let fid;
                    if (fidFromUrlMatch) {
                        fid = fidFromUrlMatch[1];
                    } else {
                        addMessage(getLocalizedText('FID_NOT_FOUND_ERROR'));
                        return;
                    }
                    const csrf = cookies[0].value;
                    const data = `resources=${av}%3A2&media_id=${fid}&platform=web&csrf=${csrf}`;
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: 'https://api.bilibili.com/x/v3/fav/resource/batch-del',
                        data: data,
                        headers: {
                            'Content-Length': data.length,
                            'Content-Type': 'application/x-www-form-urlencoded'
                        },
                        onload: function (response) {
                            const json = response.response;
                            addMessage(`${getLocalizedText('API_RESPONSE_CONTENT')}:`);
                            addMessage(json, 11);
                        }
                    });
                } else {
                    console.error(error);
                    addMessage(getLocalizedText('COOKIE_READ_ERROR'));
                }
            });
        });
        divButtonC.appendChild(buttonC);

        const divButtonD = document.createElement('div');
        divButtonD.style.padding = '2px 0';
        divControls.appendChild(divButtonD);

        const buttonD = document.createElement('button');
        buttonD.type = 'button';
        buttonD.innerText = getLocalizedText('ADD_VIDEO' + displayPrompt);
        buttonD.style.padding = '4px';
        buttonD.style.border = '1px solid #ccc';
        buttonD.style.borderRadius = '3px';
        buttonD.style.fontSize = '16px';
        buttonD.style.lineHeight = '1';
        buttonD.style.cursor = 'pointer';
        buttonD.addEventListener('click', function () {
            clearMessage();
            GM_cookie.list({ name: 'bili_jct' }, function (cookies, error) {
                if (!error) {
                    let av = inputTextB.value;
                    if (startsWithAVRegex.test(av)) {
                        av = av.slice(2);
                    }
                    if (!avRegex.test(av)) {
                        addMessage(getLocalizedText('INPUT_AV'));
                        return;
                    }
                    const fidFromUrlMatch = location.href.match(fidFromUrlRegex);
                    let fid;
                    if (fidFromUrlMatch) {
                        fid = fidFromUrlMatch[1];
                    } else {
                        addMessage(getLocalizedText('FID_NOT_FOUND_ERROR'));
                        return;
                    }
                    const csrf = cookies[0].value;
                    const data = `rid=${av}&type=2&add_media_ids=${fid}&csrf=${csrf}`;
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: 'https://api.bilibili.com/x/v3/fav/resource/deal',
                        data: data,
                        headers: {
                            'Content-Length': data.length,
                            'Content-Type': 'application/x-www-form-urlencoded'
                        },
                        onload: function (response) {
                            const json = response.response;
                            addMessage(`${getLocalizedText('API_RESPONSE_CONTENT')}:`);
                            addMessage(json, 11);
                        }
                    });
                } else {
                    console.error(error);
                    addMessage(getLocalizedText('COOKIE_READ_ERROR'));
                }
            });
        });
        divButtonD.appendChild(buttonD);

        const divMessage = document.createElement('div');
        divMessage.style.padding = '2px 0';
        divMessage.style.lineHeight = '1.5';
        divControls.appendChild(divMessage);

        if (displayUpdate) {
            setTimeout(() => {
                addMessage(getLocalizedText('UPDATES'));
            }, 300);
        }

        function addMessage(msg, px = 13) {
            const p = document.createElement('p');
            p.innerHTML = msg;
            p.style.fontSize = `${px}px`;
            divMessage.appendChild(p);
            p.scrollIntoView({ behavior: 'instant', block: 'nearest' });
        }

        function clearMessage() {
            while (divMessage.firstChild) {
                divMessage.removeChild(divMessage.firstChild);
            }
        }
    }

    function main() {

        const videosPerPage = 20;

        const storedVersion = GM_getValue('version', 0);
        let displayUpdate = false;
        if (storedVersion !== currentVersion) {
            GM_setValue('version', currentVersion);
            if (storedVersion) {
                displayUpdate = true;
            }
        }

        const usageCount = GM_getValue('usageCount', 0);
        const displayPrompt = usageCount < 10 ? '_WITH_PROMPT' : '';
        if (displayPrompt) {
            GM_setValue('usageCount', usageCount + 1);
        }

        const favSidenav = document.querySelector('div.fav-sidenav');
        favSidenav.querySelector('a.watch-later').style.borderBottom = '1px solid #eee';

        const divControls = document.createElement('div');
        divControls.style.borderTop = '1px solid #e4e9f0';
        divControls.style.padding = '2px';
        favSidenav.appendChild(divControls);

        const divInputText = document.createElement('div');
        divInputText.style.padding = '2px';
        divControls.appendChild(divInputText);

        const inputText = document.createElement('input');
        inputText.type = 'text';
        inputText.style.boxSizing = 'content-box';
        inputText.style.height = '14px';
        inputText.style.width = '150px';
        inputText.style.padding = '3px';
        inputText.style.border = '1px solid #ccc';
        inputText.style.borderRadius = '3px';
        inputText.style.fontSize = '14px';
        inputText.style.lineHeight = '1';
        if (displayPrompt) {
            inputText.placeholder = getLocalizedText('INPUT_AV_OR_BV_HERE');
        }
        divInputText.appendChild(inputText);

        const divButtonA = document.createElement('div');
        divButtonA.style.padding = '2px';
        divControls.appendChild(divButtonA);

        const buttonA = document.createElement('button');
        buttonA.type = 'button';
        buttonA.innerText = getLocalizedText('DETECT_HIDDEN_VIDEO' + displayPrompt);
        buttonA.style.padding = '3px';
        buttonA.style.border = '1px solid #ccc';
        buttonA.style.borderRadius = '3px';
        buttonA.style.fontSize = '14px';
        buttonA.style.lineHeight = '1';
        buttonA.style.cursor = 'pointer';
        buttonA.addEventListener('click', function () {
            clearMessage();
            addMessage(getLocalizedText('REFRESH_PAGE_IF_ERROR'), 10);
            const favNum = parseInt(document.querySelector('.fav-item.cur > span.num').innerText, 10);
            const pager = document.querySelector('li.be-pager-next');
            let currentPageExpectedNum;
            if (pager && !pager.classList.contains('be-pager-disabled')) {
                currentPageExpectedNum = videosPerPage;
            } else {
                currentPageExpectedNum = favNum % videosPerPage;
            }
            const lis = document.querySelectorAll('li.small-item');
            if (lis.length === currentPageExpectedNum) {
                addMessage(getLocalizedText('NO_HIDDEN_VIDEO_ON_THIS_PAGE'));
                return;
            }
            const fid = document.querySelector('.fav-item.cur').getAttribute('fid');
            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://api.bilibili.com/x/v3/fav/resource/ids?media_id=${fid}`,
                responseType: 'json',
                onload: function (response) {
                    const datas = response.response.data;
                    let currentPage = 1;
                    if (favNum > videosPerPage) {
                        currentPage = parseInt(document.querySelector('li.be-pager-item-active').innerText, 10);
                    }
                    const startIndex = (currentPage - 1) * videosPerPage;
                    const currentPageExpectedDatas = datas.slice(startIndex, startIndex + videosPerPage);
                    const currentPageActualBVs = Array.from(lis).map(li => li.getAttribute('data-aid'));
                    const hiddenDatas = currentPageExpectedDatas.filter(data => !currentPageActualBVs.includes(data.bvid));
                    hiddenDatas.forEach(hiddenData => {
                        addMessage(`${getLocalizedText('POSITION_ON_THIS_PAGE' + displayPrompt)}: ${currentPageExpectedDatas.findIndex(data => data.bvid === hiddenData.bvid) + 1}`);
                        addMessage(`${getLocalizedText('AV')}: ${hiddenData.id}`);
                        addMessage(`${getLocalizedText('BV')}: ${hiddenData.bvid}`);
                    });
                }
            });
        });
        divButtonA.appendChild(buttonA);

        const divButtonB = document.createElement('div');
        divButtonB.style.padding = '2px';
        divControls.appendChild(divButtonB);

        const buttonB = document.createElement('button');
        buttonB.type = 'button';
        buttonB.innerText = getLocalizedText('GET_VIDEO_INFO' + displayPrompt);
        buttonB.style.padding = '3px';
        buttonB.style.border = '1px solid #ccc';
        buttonB.style.borderRadius = '3px';
        buttonB.style.fontSize = '14px';
        buttonB.style.lineHeight = '1';
        buttonB.style.cursor = 'pointer';
        buttonB.addEventListener('click', function () {
            clearMessage();
            const bv = inputText.value;
            if (!bvRegex.test(bv)) {
                addMessage(getLocalizedText('INPUT_BV'));
                return;
            }
            GM_openInTab(`https://www.biliplus.com/video/${bv}`, { active: true, insert: false, setParent: true });
            GM_openInTab(`https://www.jijidown.com/video/${bv}`, { insert: false, setParent: true });
            GM_openInTab(`https://xbeibeix.com/video/${bv}`, { insert: false, setParent: true });
        });
        divButtonB.appendChild(buttonB);

        const divButtonC = document.createElement('div');
        divButtonC.style.padding = '2px';
        divControls.appendChild(divButtonC);

        const buttonC = document.createElement('button');
        buttonC.type = 'button';
        buttonC.innerText = getLocalizedText('REMOVE_VIDEO' + displayPrompt);
        buttonC.style.padding = '3px';
        buttonC.style.border = '1px solid #ccc';
        buttonC.style.borderRadius = '3px';
        buttonC.style.fontSize = '14px';
        buttonC.style.lineHeight = '1';
        buttonC.style.cursor = 'pointer';
        buttonC.addEventListener('click', function () {
            clearMessage();
            GM_cookie.list({ name: 'bili_jct' }, function (cookies, error) {
                if (!error) {
                    let av = inputText.value;
                    if (startsWithAVRegex.test(av)) {
                        av = av.slice(2);
                    }
                    if (!avRegex.test(av)) {
                        addMessage(getLocalizedText('INPUT_AV'));
                        return;
                    }
                    const fid = document.querySelector('.fav-item.cur').getAttribute('fid');
                    const csrf = cookies[0].value;
                    const data = `resources=${av}%3A2&media_id=${fid}&platform=web&csrf=${csrf}`;
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: 'https://api.bilibili.com/x/v3/fav/resource/batch-del',
                        data: data,
                        headers: {
                            'Content-Length': data.length,
                            'Content-Type': 'application/x-www-form-urlencoded'
                        },
                        onload: function (response) {
                            const json = response.response;
                            addMessage(`${getLocalizedText('API_RESPONSE_CONTENT')}:`);
                            addMessage(json, 10);
                        }
                    });
                } else {
                    console.error(error);
                    addMessage(getLocalizedText('COOKIE_READ_ERROR'));
                }
            });
        });
        divButtonC.appendChild(buttonC);

        const divButtonD = document.createElement('div');
        divButtonD.style.padding = '2px';
        divControls.appendChild(divButtonD);

        const buttonD = document.createElement('button');
        buttonD.type = 'button';
        buttonD.innerText = getLocalizedText('ADD_VIDEO' + displayPrompt);
        buttonD.style.padding = '3px';
        buttonD.style.border = '1px solid #ccc';
        buttonD.style.borderRadius = '3px';
        buttonD.style.fontSize = '14px';
        buttonD.style.lineHeight = '1';
        buttonD.style.cursor = 'pointer';
        buttonD.addEventListener('click', function () {
            clearMessage();
            GM_cookie.list({ name: 'bili_jct' }, function (cookies, error) {
                if (!error) {
                    let av = inputText.value;
                    if (startsWithAVRegex.test(av)) {
                        av = av.slice(2);
                    }
                    if (!avRegex.test(av)) {
                        addMessage(getLocalizedText('INPUT_AV'));
                        return;
                    }
                    const fid = document.querySelector('.fav-item.cur').getAttribute('fid');
                    const csrf = cookies[0].value;
                    const data = `rid=${av}&type=2&add_media_ids=${fid}&csrf=${csrf}`;
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: 'https://api.bilibili.com/x/v3/fav/resource/deal',
                        data: data,
                        headers: {
                            'Content-Length': data.length,
                            'Content-Type': 'application/x-www-form-urlencoded'
                        },
                        onload: function (response) {
                            const json = response.response;
                            addMessage(`${getLocalizedText('API_RESPONSE_CONTENT')}:`);
                            addMessage(json, 10);
                        }
                    });
                } else {
                    console.error(error);
                    addMessage(getLocalizedText('COOKIE_READ_ERROR'));
                }
            });
        });
        divButtonD.appendChild(buttonD);

        const divMessage = document.createElement('div');
        divMessage.style.padding = '2px';
        divMessage.style.lineHeight = '1.5';
        divControls.appendChild(divMessage);

        if (displayUpdate) {
            setTimeout(() => {
                addMessage(getLocalizedText('UPDATES'));
            }, 300);
        }

        function addMessage(msg, px = 12) {
            const p = document.createElement('p');
            p.innerHTML = msg;
            p.style.fontSize = `${px}px`;
            divMessage.appendChild(p);
            p.scrollIntoView({ behavior: 'instant', block: 'nearest' });
        }

        function clearMessage() {
            while (divMessage.firstChild) {
                divMessage.removeChild(divMessage.firstChild);
            }
        }
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址