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

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

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

您需要先安装一款用户脚本管理器扩展,例如 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           1
// @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
// ==/UserScript==

(function () {
    'use strict';

    const textDictionary = {
        'ENTER_AV_OR_BV_HERE': {
            'zh-CN': '在此输入av号或bv号',
            'zh-TW': '在此輸入av號或bv號'
        },
        'DETECT_HIDDEN_VIDEO': {
            'zh-CN': '检测隐藏视频(先刷新页面)',
            'zh-TW': '檢測隱藏影片(先重新載入頁面)'
        },
        'GET_VIDEO_INFO': {
            'zh-CN': '查询视频信息(输入bv号)',
            'zh-TW': '查詢影片資訊(輸入bv號)'
        },
        'REMOVE_VIDEO': {
            'zh-CN': '取消收藏(输入av号)',
            'zh-TW': '取消收藏(輸入av號)'
        },
        'ADD_VIDEO': {
            'zh-CN': '添加收藏(输入av号)',
            'zh-TW': '新增收藏(輸入av號)'
        },
        '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': {
            'zh-CN': '在本页的位置(从1开始)',
            'zh-TW': '在本頁的位置(從1開始)'
        },
        'RESPONSE_CONTENT': {
            'zh-CN': 'b站接口响应内容',
            'zh-TW': 'b站介面回應內容'
        },
        'ERROR_COOKIE': {
            'zh-CN': '无法读取cookie, 请更新tampermonkey, 前往控制台查看错误信息',
            'zh-TW': '無法讀取cookie, 請更新tampermonkey, 前往控制台查看錯誤資訊'
        }
    };

    const preferredLanguage = getPreferredLanguage();

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

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

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

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

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

        const buttonsContainer = document.createElement('div');
        buttonsContainer.classList.add('fix-buttons-container');
        buttonsContainer.style.padding = '5px 0';
        settingsContainer.appendChild(buttonsContainer);

        const buttonA = document.createElement('button');
        buttonA.type = 'button';
        buttonA.innerText = getText('DETECT_HIDDEN_VIDEO');
        buttonA.classList.add('fix-action-button');
        buttonA.addEventListener('click', function () {
            const fid = document.querySelector('.fav-item.cur').getAttribute('fid');
            const favAmount = document.querySelector('.fav-item.cur span.num').innerText;
            const lis = document.querySelectorAll('ul.fav-video-list li.small-item');
            if (favAmount == 0) {
                addInfo(getText('NO_HIDDEN_VIDEO_ON_THIS_PAGE'), 12);
                return;
            }
            const element = document.querySelector('.be-pager-next');
            let should;
            if (element && !element.classList.contains('be-pager-disabled')) {
                should = 20;
            } else {
                should = favAmount % 20;
            }
            if (lis.length === should) {
                addInfo(getText('NO_HIDDEN_VIDEO_ON_THIS_PAGE'), 12);
                return;
            }
            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 position = 0;
                    if (lis.length) {
                        const firstBV = lis[0].getAttribute('data-aid');
                        position = datas.findIndex(data => data.bvid === firstBV);
                    }
                    const head = position % 20;
                    for (let i = 0; i < head; i++) {
                        addInfo(`${getText('BV')}: ${datas[position - head + i].bvid}`, 12);
                        addInfo(`${getText('AV')}: ${datas[position - head + i].id}`, 12);
                        addInfo(`${getText('POSITION_ON_THIS_PAGE')}: ${i + 1}`, 12);
                    }
                    for (let i = 0, j = 0; j < should - head; i++, j++) {
                        if (i >= lis.length || lis[i].getAttribute('data-aid') !== datas[j + position].bvid) {
                            addInfo(`${getText('BV')}: ${datas[j + position].bvid}`, 12);
                            addInfo(`${getText('AV')}: ${datas[j + position].id}`, 12);
                            addInfo(`${getText('POSITION_ON_THIS_PAGE')}: ${j + head + 1}`, 12);
                            i--;
                        }
                    }
                }
            });
        });

        const buttonAContainer = document.createElement('div');
        buttonAContainer.classList.add('fix-button-container');
        buttonAContainer.appendChild(buttonA);
        buttonsContainer.appendChild(buttonAContainer);

        const buttonB = document.createElement('button');
        buttonB.type = 'button';
        buttonB.innerText = getText('GET_VIDEO_INFO');
        buttonB.classList.add('fix-action-button');
        buttonB.addEventListener('click', function () {
            const bv = document.querySelector('div.fix-inputText-container input').value;
            if (!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 });
        });

        const buttonBContainer = document.createElement('div');
        buttonBContainer.classList.add('fix-button-container');
        buttonBContainer.appendChild(buttonB);
        buttonsContainer.appendChild(buttonBContainer);

        const buttonC = document.createElement('button');
        buttonC.type = 'button';
        buttonC.innerText = getText('REMOVE_VIDEO');
        buttonC.classList.add('fix-action-button');
        buttonC.addEventListener('click', function () {
            GM_cookie.list({ name: 'bili_jct' }, function (cookies, error) {
                if (!error) {
                    const av = document.querySelector('div.fix-inputText-container input').value;
                    if (!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(json, 10);
                            addInfo(`${getText('RESPONSE_CONTENT')}:`, 12);
                        }
                    });
                } else {
                    console.error(error);
                    addInfo(getText('ERROR_COOKIE'), 12);
                }
            });
        });

        const buttonCContainer = document.createElement('div');
        buttonCContainer.classList.add('fix-button-container');
        buttonCContainer.appendChild(buttonC);
        buttonsContainer.appendChild(buttonCContainer);

        const buttonD = document.createElement('button');
        buttonD.type = 'button';
        buttonD.innerText = getText('ADD_VIDEO');
        buttonD.classList.add('fix-action-button');
        buttonD.addEventListener('click', function () {
            GM_cookie.list({ name: 'bili_jct' }, function (cookies, error) {
                if (!error) {
                    const av = document.querySelector('div.fix-inputText-container input').value;
                    if (!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(json, 10);
                            addInfo(`${getText('RESPONSE_CONTENT')}:`, 12);
                        }
                    });
                } else {
                    console.error(error);
                    addInfo(getText('ERROR_COOKIE'), 12);
                }
            });
        });

        const buttonDContainer = document.createElement('div');
        buttonDContainer.classList.add('fix-button-container');
        buttonDContainer.appendChild(buttonD);
        buttonsContainer.appendChild(buttonDContainer);

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

        function addInfo(info, px) {
            const p = document.createElement('p');
            p.innerText = info;
            p.style.fontSize = `${px}px`;
            textContainer.insertAdjacentElement('afterbegin', p);
        }

    }, 300);
})();