// ==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 // @downloadURL none // ==/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); })();