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

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

当前为 2024-11-22 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 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           3
// @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 textDictionary = {
        'UPDATES': {
            'zh-CN': '更新内容: 优化脚本部分逻辑',
            'zh-TW': '更新內容: 優化腳本部分邏輯'
        },
        'ENTER_AV_OR_BV_HERE': {
            'zh-CN': '在此输入av号或bv号',
            'zh-TW': '在此輸入av號或bv號'
        },
        'DETECT_HIDDEN_VIDEO_WITH_PROMPT': {
            'zh-CN': '检测隐藏视频(先刷新页面)',
            'zh-TW': '檢測隱藏影片(先重新載入頁面)'
        },
        'GET_VIDEO_INFO_WITH_PROMPT': {
            'zh-CN': '查询视频信息(输入bv号)',
            'zh-TW': '查詢影片資訊(輸入bv號)'
        },
        'REMOVE_VIDEO_WITH_PROMPT': {
            'zh-CN': '取消收藏(输入av号)',
            'zh-TW': '取消收藏(輸入av號)'
        },
        'ADD_VIDEO_WITH_PROMPT': {
            'zh-CN': '添加收藏(输入av号)',
            'zh-TW': '新增收藏(輸入av號)'
        },
        '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': '新增收藏'
        },
        'AV': {
            'zh-CN': 'av号',
            'zh-TW': 'av號'
        },
        'BV': {
            'zh-CN': 'bv号',
            'zh-TW': 'bv號'
        },
        'ENTER_AV': {
            'zh-CN': '请输入av号',
            'zh-TW': '請輸入av號'
        },
        'ENTER_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': '在本頁的位置'
        },
        'RESPONSE_CONTENT': {
            'zh-CN': 'b站接口响应内容',
            'zh-TW': 'b站介面回應內容'
        },
        'REFRESH_PROMPT': {
            'zh-CN': '如果出现问题, 请刷新页面后重试',
            'zh-TW': '如果出現問題, 請重新載入頁面後再試'
        },
        'ERROR_COOKIE': {
            'zh-CN': '无法读取cookie, 请更新tampermonkey, 前往控制台查看错误信息',
            'zh-TW': '無法讀取cookie, 請更新tampermonkey, 前往控制台查看錯誤資訊'
        }
    };

    const preferredLanguage = getPreferredLanguage();

    const currentVersion = 3;

    const avRegex = /^[1-9]\d*$/;
    const bvRegex = /^BV[A-Za-z0-9]{10}$/;

    const videosPerPage = 20;

    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 getText(key) {
        return textDictionary[key][preferredLanguage];
    }

    const checkInterval = setInterval(function () {
        const favSidenav = document.querySelector('.fav-sidenav');
        if (!favSidenav) {
            return;
        }
        clearInterval(checkInterval);

        let version = GM_getValue('version', currentVersion - 1);
        let usageCount = GM_getValue('usageCount', 0);
        const displayUpdate = version < currentVersion ? true : false;
        const displayPrompt = usageCount < 10 ? true : false;
        if (displayUpdate) {
            GM_setValue('version', version + 0.5);
        }
        if (displayPrompt) {
            GM_setValue('usageCount', usageCount + 1);
        }

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

        const controlsContainer = document.createElement('div');
        controlsContainer.classList.add('fix-settings-container');
        controlsContainer.style.borderTop = '1px solid #e4e9f0';
        controlsContainer.style.padding = '2px';
        favSidenav.appendChild(controlsContainer);

        const inputTextContainer = document.createElement('div');
        inputTextContainer.classList.add('fix-inputText-container');
        inputTextContainer.style.padding = '2px';
        controlsContainer.appendChild(inputTextContainer);

        const inputText = document.createElement('input');
        inputText.type = 'text';
        inputText.classList.add('fix-inputText');
        if (displayPrompt) {
            inputText.placeholder = getText('ENTER_AV_OR_BV_HERE');
        }
        inputTextContainer.appendChild(inputText);

        const buttonAContainer = document.createElement('div');
        buttonAContainer.classList.add('fix-button-container');
        buttonAContainer.style.padding = '2px';
        controlsContainer.appendChild(buttonAContainer);

        const buttonA = document.createElement('button');
        buttonA.type = 'button';
        if (displayPrompt) {
            buttonA.innerText = getText('DETECT_HIDDEN_VIDEO_WITH_PROMPT');
        } else {
            buttonA.innerText = getText('DETECT_HIDDEN_VIDEO');
        }
        buttonA.classList.add('fix-action-button');
        buttonA.addEventListener('click', function () {
            removeInfo();
            addInfo(getText('REFRESH_PROMPT'), 10);
            const favNum = parseInt(document.querySelector('.fav-item.cur span.num').innerText, 10);
            const pager = document.querySelector('.be-pager-next');
            let currentPageExpectedNum;
            if (pager && !pager.classList.contains('be-pager-disabled')) {
                currentPageExpectedNum = videosPerPage;
            } else {
                currentPageExpectedNum = favNum % videosPerPage;
            }
            const lis = document.querySelectorAll('ul.fav-video-list li.small-item');
            if (lis.length === currentPageExpectedNum) {
                addInfo(getText('NO_HIDDEN_VIDEO_ON_THIS_PAGE'), 12);
                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 > 20) {
                        currentPage = parseInt(document.querySelector('.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 => {
                        if (displayPrompt) {
                            addInfo(`${getText('POSITION_ON_THIS_PAGE_WITH_PROMPT')}: ${currentPageExpectedDatas.findIndex(data => data.bvid === hiddenData.bvid) + 1}`, 12);
                        } else {
                            addInfo(`${getText('POSITION_ON_THIS_PAGE')}: ${currentPageExpectedDatas.findIndex(data => data.bvid === hiddenData.bvid) + 1}`, 12);
                        }
                        addInfo(`${getText('AV')}: ${hiddenData.id}`, 12);
                        addInfo(`${getText('BV')}: ${hiddenData.bvid}`, 12);
                    });
                }
            });
        });
        buttonAContainer.appendChild(buttonA);

        const buttonBContainer = document.createElement('div');
        buttonBContainer.classList.add('fix-button-container');
        buttonBContainer.style.padding = '2px';
        controlsContainer.appendChild(buttonBContainer);

        const buttonB = document.createElement('button');
        buttonB.type = 'button';
        if (displayPrompt) {
            buttonB.innerText = getText('GET_VIDEO_INFO_WITH_PROMPT');
        } else {
            buttonB.innerText = getText('GET_VIDEO_INFO');
        }
        buttonB.classList.add('fix-action-button');
        buttonB.addEventListener('click', function () {
            removeInfo();
            const bv = document.querySelector('div.fix-inputText-container input').value;
            if (!bvRegex.test(bv)) {
                addInfo(getText('ENTER_BV'), 12);
                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 });
        });
        buttonBContainer.appendChild(buttonB);

        const buttonCContainer = document.createElement('div');
        buttonCContainer.classList.add('fix-button-container');
        buttonCContainer.style.padding = '2px';
        controlsContainer.appendChild(buttonCContainer);

        const buttonC = document.createElement('button');
        buttonC.type = 'button';
        if (displayPrompt) {
            buttonC.innerText = getText('REMOVE_VIDEO_WITH_PROMPT');
        } else {
            buttonC.innerText = getText('REMOVE_VIDEO');
        }
        buttonC.classList.add('fix-action-button');
        buttonC.addEventListener('click', function () {
            removeInfo();
            GM_cookie.list({ name: 'bili_jct' }, function (cookies, error) {
                if (!error) {
                    const av = document.querySelector('div.fix-inputText-container input').value;
                    if (!avRegex.test(av)) {
                        addInfo(getText('ENTER_AV'), 12);
                        return;
                    }
                    const id = document.querySelector('.fav-item.cur').getAttribute('fid');
                    const csrf = cookies[0].value;
                    const data = `resources=${av}%3A2&media_id=${id}&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;
                            addInfo(`${getText('RESPONSE_CONTENT')}:`, 12);
                            addInfo(json, 10);
                        }
                    });
                } else {
                    console.error(error);
                    addInfo(getText('ERROR_COOKIE'), 12);
                }
            });
        });
        buttonCContainer.appendChild(buttonC);

        const buttonDContainer = document.createElement('div');
        buttonDContainer.classList.add('fix-button-container');
        buttonDContainer.style.padding = '2px';
        controlsContainer.appendChild(buttonDContainer);

        const buttonD = document.createElement('button');
        buttonD.type = 'button';
        if (displayPrompt) {
            buttonD.innerText = getText('ADD_VIDEO_WITH_PROMPT');
        } else {
            buttonD.innerText = getText('ADD_VIDEO');
        }
        buttonD.classList.add('fix-action-button');
        buttonD.addEventListener('click', function () {
            removeInfo();
            GM_cookie.list({ name: 'bili_jct' }, function (cookies, error) {
                if (!error) {
                    const av = document.querySelector('div.fix-inputText-container input').value;
                    if (!avRegex.test(av)) {
                        addInfo(getText('ENTER_AV'), 12);
                        return;
                    }
                    const id = document.querySelector('.fav-item.cur').getAttribute('fid');
                    const csrf = cookies[0].value;
                    const data = `rid=${av}&type=2&add_media_ids=${id}&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;
                            addInfo(`${getText('RESPONSE_CONTENT')}:`, 12);
                            addInfo(json, 10);
                        }
                    });
                } else {
                    console.error(error);
                    addInfo(getText('ERROR_COOKIE'), 12);
                }
            });
        });
        buttonDContainer.appendChild(buttonD);

        const textContainer = document.createElement('div');
        textContainer.classList.add('fix-text-container');
        textContainer.style.padding = '2px';
        controlsContainer.appendChild(textContainer);

        if (displayUpdate) {
            addInfo(getText('UPDATES', 12));
        }

        function addInfo(info, px) {
            const p = document.createElement('p');
            p.innerText = info;
            p.style.fontSize = `${px}px`;
            textContainer.appendChild(p);
            p.scrollIntoView({ behavior: 'instant', block: 'nearest' });
        }

        function removeInfo() {
            while (textContainer.firstChild) {
                textContainer.removeChild(textContainer.firstChild);
            }
        }

    }, 300);
})();