Greasy Fork

Greasy Fork is available in English.

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

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

当前为 2024-12-15 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name              bilibili favlist hidden video detection
// @name:zh-CN        哔哩哔哩(B站|Bilibili)收藏夹Fix(隐藏视频检测)
// @name:zh-TW        嗶哩嗶哩(B站|Bilibili)收藏夾Fix(隱藏影片檢測)
// @namespace         http://tampermonkey.net/
// @version           6
// @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           bilibili.com
// @grant             GM_openInTab
// @grant             GM_setValue
// @grant             GM_getValue
// @grant             GM_xmlhttpRequest
// @grant             GM_cookie
// ==/UserScript==

(function () {
    'use strict';

    const localizedText = {
        'UPDATES': {
            'zh-CN': '更新内容:<br>其他: 优化脚本部分逻辑',
            'zh-TW': '更新內容:<br>其他: 優化腳本部分邏輯'
        },
        'VIDEOS_PER_PAGE': {
            'zh-CN': '每页展示视频数量',
            'zh-TW': '每頁顯示影片數量'
        },
        'VIDEOS_PER_PAGE_WITH_PROMPT': {
            'zh-CN': '每页最多可以展示的视频数量(一般为36或40)',
            'zh-TW': '每頁最多可以顯示的影片數量(一般為36或40)'
        },
        'INPUT_AV_OR_BV_HERE': {
            'zh-CN': '在此输入av号或bv号',
            'zh-TW': '在此輸入av號或bv號'
        },
        'DETECT_HIDDEN_VIDEO': {
            'zh-CN': '检测隐藏视频',
            'zh-TW': '檢測隱藏影片'
        },
        'GET_VIDEO_INFO': {
            'zh-CN': '查询视频信息',
            'zh-TW': '查詢影片資訊'
        },
        'REMOVE_VIDEO': {
            'zh-CN': '取消收藏',
            'zh-TW': '取消收藏'
        },
        'ADD_VIDEO': {
            'zh-CN': '添加收藏',
            'zh-TW': '新增收藏'
        },
        'INPUT_VIDEOS_PER_PAGE_AND_RETRY': {
            'zh-CN': '请手动设置每页展示视频数量, 然后再次点击按钮',
            'zh-TW': '請手動設定每頁顯示影片數量, 然後再次點擊按鈕'
        },
        'CHECK_VIDEOS_PER_PAGE_AND_RETRY': {
            'zh-CN': '请检查预设的每页展示视频数量是否正确, 然后再次点击按钮',
            'zh-TW': '請檢查預設的每頁顯示影片數量是否正確, 然後再次點擊按鈕'
        },
        'INPUT_AV': {
            'zh-CN': '请输入av号',
            'zh-TW': '請輸入av號'
        },
        'INPUT_BV': {
            'zh-CN': '请输入bv号',
            'zh-TW': '請輸入bv號'
        },
        'WHAT_TO_DO_IF_ERROR': {
            'zh-CN': '如果出现问题, 以下操作或许能解决: 检查每页展示视频数量是否正确, 切换至按最近收藏排序, 刷新页面',
            'zh-TW': '如果出現問題, 以下操作或許能解決: 檢查每頁顯示影片數量是否正確, 切換至按最近收藏排序, 重新載入頁面'
        },
        'REFRESH_PAGE_IF_ERROR': {
            'zh-CN': '如果出现问题, 刷新页面或许能解决',
            'zh-TW': '如果出現問題, 重新載入頁面或許能解決'
        },
        'NO_HIDDEN_VIDEO_ON_THIS_PAGE': {
            'zh-CN': '本页没有隐藏的视频',
            'zh-TW': '本頁沒有隱藏的影片'
        },
        'POSITION_ON_THIS_PAGE': {
            'zh-CN': '在本页的位置',
            'zh-TW': '在本頁的位置'
        },
        'POSITION_ON_THIS_PAGE_WITH_PROMPT': {
            'zh-CN': '在本页的位置(从1开始)',
            'zh-TW': '在本頁的位置(從1開始)'
        },
        'AV': {
            'zh-CN': 'av号',
            'zh-TW': 'av號'
        },
        'BV': {
            'zh-CN': 'bv号',
            'zh-TW': 'bv號'
        },
        'API_RESPONSE_CONTENT': {
            'zh-CN': 'b站接口响应内容:',
            'zh-TW': 'b站介面回應內容:'
        },
        'FID_NOT_FOUND_ERROR': {
            'zh-CN': '无法获取当前收藏夹的fid, 刷新页面或许能解决',
            'zh-TW': '無法獲取當前收藏夾的fid, 重新載入頁面或許能解決'
        },
        'INVALID_VIDEOS_PER_PAGE_ERROR': {
            'zh-CN': '每页展示视频数量不正确, 请重新输入',
            'zh-TW': '每頁顯示影片數量不正確, 請重新輸入'
        },
        'REQUEST_TIMEOUT_ERROR': {
            'zh-CN': '请求超时, 请重试',
            'zh-TW': '請求逾時, 請重試'
        },
        'COOKIE_READ_ERROR': {
            'zh-CN': '无法读取cookie, 更新tampermonkey或许能解决',
            'zh-TW': '無法讀取cookie, 更新tampermonkey或許能解決'
        },
        'UNKNOWN_ERROR': {
            'zh-CN': '发生未知错误, 请反馈该问题',
            'zh-TW': '發生未知錯誤, 請反饋該問題'
        },
    };

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

    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();
                    main(true);
                    return;
                }
                if (document.querySelector('div.fav-sidenav')) {
                    observer.disconnect();
                    main(false);
                    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 main(newFreshSpace) {

        let videosPerPage = newFreshSpace ? 50000 : 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(newFreshSpace ? 'usageCountNewFreshSpace' : 'usageCount', 0);
        const displayPrompt = usageCount < 10 ? '_WITH_PROMPT' : '';
        if (displayPrompt) {
            GM_setValue(newFreshSpace ? 'usageCountNewFreshSpace' : 'usageCount', usageCount + 1);
        }

        const divSide = document.querySelector(newFreshSpace ? 'div.favlist-aside' : 'div.fav-sidenav');
        if (!newFreshSpace) {
            divSide.querySelector('a.watch-later').style.borderBottom = '1px solid #eee';
        }

        const divControls = document.createElement('div');
        divControls.style.padding = newFreshSpace ? '2px 0' : '2px';
        if (!newFreshSpace) {
            divControls.style.borderTop = '1px solid #e4e9f0';
        }
        divSide.appendChild(divControls);

        let inputTextA;
        if (newFreshSpace) {
            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);

            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 = newFreshSpace ? '2px 0' : '2px';
        divControls.appendChild(divInputTextB);

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

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

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

                let fid;
                if (newFreshSpace) {
                    const fidFromURLMatch = location.href.match(fidFromURLRegex);
                    if (fidFromURLMatch) {
                        fid = fidFromURLMatch[1];
                    } else {
                        addMessage(getLocalizedText('FID_NOT_FOUND_ERROR'));
                        return;
                    }
                } else {
                    fid = document.querySelector('.fav-item.cur').getAttribute('fid');
                }

                let currentPageActualVideos;
                if (newFreshSpace) {
                    currentPageActualVideos = document.querySelectorAll('div.bili-video-card__wrap');
                } else {
                    currentPageActualVideos = document.querySelectorAll('li.small-item');
                }

                if (newFreshSpace) {
                    // const pagenationBtnNums = document.querySelectorAll('button.vui_pagenation--btn-num');
                    // const totalPages = parseInt(pagenationBtnNums[pagenationBtnNums.length - 1].innerText, 10);
                    // const totalVideos = parseInt(document.querySelector('.vui_sidebar-item--active > div.vui_sidebar-item-right').innerText, 10);

                    const spanPagenationGoCount = document.querySelector('span.vui_pagenation-go__count');
                    if (spanPagenationGoCount) {
                        const pagenationCountMatch = spanPagenationGoCount.innerText.match(pagenationCountRegex);
                        const totalPages = parseInt(pagenationCountMatch[1], 10);
                        const totalVideos = parseInt(pagenationCountMatch[2], 10);

                        if (totalPages !== 1) {
                            videosPerPage = parseInt(inputTextA.value, 10);
                            if (isNaN(videosPerPage)) {
                                if (currentPageActualVideos.length <= 36) {
                                    inputTextA.value = 36;
                                } else if (currentPageActualVideos.length <= 40) {
                                    inputTextA.value = 40;
                                } else {
                                    addMessage(getLocalizedText('INPUT_VIDEOS_PER_PAGE_AND_RETRY'));
                                    return;
                                }
                                addMessage(getLocalizedText('CHECK_VIDEOS_PER_PAGE_AND_RETRY'));
                                return;
                            }

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

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

                if (newFreshSpace) {
                    addMessage(getLocalizedText('WHAT_TO_DO_IF_ERROR'), 11);
                } else {
                    addMessage(getLocalizedText('REFRESH_PAGE_IF_ERROR'), 10);
                }

                GM_xmlhttpRequest({
                    method: 'GET',
                    url: `https://api.bilibili.com/x/v3/fav/resource/ids?media_id=${fid}`,
                    timeout: 3000,
                    responseType: 'json',
                    onload: function (response) {
                        try {
                            const currentFavlistAVBVs = response.response.data;
                            const startIndex = (currentPage - 1) * videosPerPage;
                            const currentPageExpectedAVBVs = currentFavlistAVBVs.slice(startIndex, startIndex + videosPerPage);
                            let currentPageActualBVs;
                            if (newFreshSpace) {
                                currentPageActualBVs = Array.from(currentPageActualVideos).map(video => video.querySelector('a.bili-cover-card').getAttribute('href').match(bvFromURLRegex)[1]);
                            } else {
                                currentPageActualBVs = Array.from(currentPageActualVideos).map(video => video.getAttribute('data-aid'));
                            }
                            const hiddenAVBVs = currentPageExpectedAVBVs.filter(currentPageExpectedAVBV => !currentPageActualBVs.includes(currentPageExpectedAVBV.bvid));
                            if (!hiddenAVBVs.length) {
                                addMessage(getLocalizedText('NO_HIDDEN_VIDEO_ON_THIS_PAGE'));
                                return;
                            }
                            hiddenAVBVs.forEach(hiddenAVBV => {
                                addMessage(`${getLocalizedText('POSITION_ON_THIS_PAGE' + displayPrompt)}: ${currentPageExpectedAVBVs.findIndex(currentPageExpectedAVBV => currentPageExpectedAVBV.bvid === hiddenAVBV.bvid) + 1}`);
                                addMessage(`${getLocalizedText('AV')}: ${hiddenAVBV.id}`);
                                addMessage(`${getLocalizedText('BV')}: ${hiddenAVBV.bvid}`);
                            });

                        } catch (error) {
                            addMessage(getLocalizedText('UNKNOWN_ERROR'));
                            addMessage(error, newFreshSpace ? 11 : 10);
                            console.error(error);
                        }
                    },
                    onerror: function (response) {
                        addMessage(getLocalizedText('UNKNOWN_ERROR'));
                        addMessage(response, newFreshSpace ? 11 : 10);
                        console.error(response);
                    },
                    ontimeout: function (response) {
                        addMessage(getLocalizedText('REQUEST_TIMEOUT_ERROR'));
                        addMessage(response, newFreshSpace ? 11 : 10);
                        console.error(response);
                    }
                });

            } catch (error) {
                addMessage(getLocalizedText('UNKNOWN_ERROR'));
                addMessage(error, newFreshSpace ? 11 : 10);
                console.error(error);
            }
        });
        divButtonA.appendChild(buttonA);

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

        const buttonB = document.createElement('button');
        buttonB.type = 'button';
        buttonB.innerText = getLocalizedText('GET_VIDEO_INFO');
        buttonB.style.padding = newFreshSpace ? '4px' : '3px';
        buttonB.style.border = '1px solid #ccc';
        buttonB.style.borderRadius = '3px';
        buttonB.style.fontSize = newFreshSpace ? '16px' : '14px';
        buttonB.style.lineHeight = '1';
        buttonB.style.cursor = 'pointer';
        buttonB.addEventListener('click', function () {
            try {
                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://xbeibeix.com/video/${bv}`, { insert: false, setParent: true });
                GM_openInTab(`https://www.jijidown.com/video/${bv}`, { insert: false, setParent: true });

            } catch (error) {
                addMessage(getLocalizedText('UNKNOWN_ERROR'));
                addMessage(error, newFreshSpace ? 11 : 10);
                console.error(error);
            }
        });
        divButtonB.appendChild(buttonB);

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

        const buttonC = document.createElement('button');
        buttonC.type = 'button';
        buttonC.innerText = getLocalizedText('REMOVE_VIDEO');
        buttonC.style.padding = newFreshSpace ? '4px' : '3px';
        buttonC.style.border = '1px solid #ccc';
        buttonC.style.borderRadius = '3px';
        buttonC.style.fontSize = newFreshSpace ? '16px' : '14px';
        buttonC.style.lineHeight = '1';
        buttonC.style.cursor = 'pointer';
        buttonC.addEventListener('click', function () {
            try {
                GM_cookie.list({ name: 'bili_jct' }, function (cookies, error) {
                    if (!error) {
                        try {
                            let av = inputTextB.value;
                            if (startsWithAVRegex.test(av)) {
                                av = av.slice(2);
                            }
                            if (!avRegex.test(av)) {
                                addMessage(getLocalizedText('INPUT_AV'));
                                return;
                            }
                            let fid;
                            if (newFreshSpace) {
                                const fidFromURLMatch = location.href.match(fidFromURLRegex);
                                if (fidFromURLMatch) {
                                    fid = fidFromURLMatch[1];
                                } else {
                                    addMessage(getLocalizedText('FID_NOT_FOUND_ERROR'));
                                    return;
                                }
                            } else {
                                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,
                                timeout: 3000,
                                headers: {
                                    'Content-Length': data.length,
                                    'Content-Type': 'application/x-www-form-urlencoded'
                                },
                                onload: function (response) {
                                    try {
                                        const json = response.response;
                                        addMessage(getLocalizedText('API_RESPONSE_CONTENT'));
                                        addMessage(json, newFreshSpace ? 11 : 10);

                                    } catch (error) {
                                        addMessage(getLocalizedText('UNKNOWN_ERROR'));
                                        addMessage(error, newFreshSpace ? 11 : 10);
                                        console.error(error);
                                    }
                                },
                                onerror: function (response) {
                                    addMessage(getLocalizedText('UNKNOWN_ERROR'));
                                    addMessage(response, newFreshSpace ? 11 : 10);
                                    console.error(response);
                                },
                                ontimeout: function (response) {
                                    addMessage(getLocalizedText('REQUEST_TIMEOUT_ERROR'));
                                    addMessage(response, newFreshSpace ? 11 : 10);
                                    console.error(response);
                                }
                            });

                        } catch (error) {
                            addMessage(getLocalizedText('UNKNOWN_ERROR'));
                            addMessage(error, newFreshSpace ? 11 : 10);
                            console.error(error);
                        }

                    } else {
                        addMessage(getLocalizedText('COOKIE_READ_ERROR'));
                        addMessage(error, newFreshSpace ? 11 : 10);
                        console.error(error);
                    }
                });

            } catch (error) {
                addMessage(getLocalizedText('UNKNOWN_ERROR'));
                addMessage(error, newFreshSpace ? 11 : 10);
                console.error(error);
            }
        });
        divButtonC.appendChild(buttonC);

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

        const buttonD = document.createElement('button');
        buttonD.type = 'button';
        buttonD.innerText = getLocalizedText('ADD_VIDEO');
        buttonD.style.padding = newFreshSpace ? '4px' : '3px';
        buttonD.style.border = '1px solid #ccc';
        buttonD.style.borderRadius = '3px';
        buttonD.style.fontSize = newFreshSpace ? '16px' : '14px';
        buttonD.style.lineHeight = '1';
        buttonD.style.cursor = 'pointer';
        buttonD.addEventListener('click', function () {
            try {
                GM_cookie.list({ name: 'bili_jct' }, function (cookies, error) {
                    if (!error) {
                        try {
                            let av = inputTextB.value;
                            if (startsWithAVRegex.test(av)) {
                                av = av.slice(2);
                            }
                            if (!avRegex.test(av)) {
                                addMessage(getLocalizedText('INPUT_AV'));
                                return;
                            }
                            let fid;
                            if (newFreshSpace) {
                                const fidFromURLMatch = location.href.match(fidFromURLRegex);
                                if (fidFromURLMatch) {
                                    fid = fidFromURLMatch[1];
                                } else {
                                    addMessage(getLocalizedText('FID_NOT_FOUND_ERROR'));
                                    return;
                                }
                            } else {
                                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,
                                timeout: 3000,
                                headers: {
                                    'Content-Length': data.length,
                                    'Content-Type': 'application/x-www-form-urlencoded'
                                },
                                onload: function (response) {
                                    try {
                                        const json = response.response;
                                        addMessage(getLocalizedText('API_RESPONSE_CONTENT'));
                                        addMessage(json, newFreshSpace ? 11 : 10);

                                    } catch (error) {
                                        addMessage(getLocalizedText('UNKNOWN_ERROR'));
                                        addMessage(error, newFreshSpace ? 11 : 10);
                                        console.error(error);
                                    }
                                },
                                onerror: function (response) {
                                    addMessage(getLocalizedText('UNKNOWN_ERROR'));
                                    addMessage(response, newFreshSpace ? 11 : 10);
                                    console.error(response);
                                },
                                ontimeout: function (response) {
                                    addMessage(getLocalizedText('REQUEST_TIMEOUT_ERROR'));
                                    addMessage(response, newFreshSpace ? 11 : 10);
                                    console.error(response);
                                }
                            });

                        } catch (error) {
                            addMessage(getLocalizedText('UNKNOWN_ERROR'));
                            addMessage(error, newFreshSpace ? 11 : 10);
                            console.error(error);
                        }

                    } else {
                        addMessage(getLocalizedText('COOKIE_READ_ERROR'));
                        addMessage(error, newFreshSpace ? 11 : 10);
                        console.error(error);
                    }
                });

            } catch (error) {
                addMessage(getLocalizedText('UNKNOWN_ERROR'));
                addMessage(error, newFreshSpace ? 11 : 10);
                console.error(error);
            }
        });
        divButtonD.appendChild(buttonD);

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

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

        function addMessage(msg, px = newFreshSpace ? 13 : 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);
            }
        }
    }
})();