您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动备份视频信息至本地和第三方网站, 失效视频信息回显
// ==UserScript== // @name bilibili favlist backup // @name:zh-CN 哔哩哔哩(B站|Bilibili)收藏夹Fix (备份视频信息) // @name:zh-TW 哔哩哔哩(B站|Bilibili)收藏夹Fix (备份视频信息) // @namespace http://tampermonkey.net/ // @version 31 // @description automatically backup info of videos in favlist // @description:zh-CN 自动备份视频信息至本地和第三方网站, 失效视频信息回显 // @description:zh-TW 自动备份视频信息至本地和第三方网站, 失效视频信息回显 // @author YTB0710 // @match https://space.bilibili.com/* // @connect bbdownloader.com // @connect beibeigame.com // @connect bilibili.com // @connect biliplus.com // @connect jiji.moe // @connect jijidown.com // @connect xbeibeix.com // @grant GM_openInTab // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @grant GM_getValues // @grant GM_xmlhttpRequest // ==/UserScript== (function () { 'use strict'; const updates = '更新内容:<br>' + '新增: 设置从B站接口获取额外数据启用更新时, 只更新可能换源的视频还是总是更新所有视频的功能'; const version = 31; const favlistURLRegex = /https:\/\/space\.bilibili\.com\/\d+\/favlist.*/; const localeTimeStringRegex = /^\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2}$/; const getFidFromURLRegex = /fid=(\d+)/; const getUIDFromURLRegex = /https:\/\/space\.bilibili\.com\/(\d+)/; const getBVFromURLRegex = /video\/(BV\w{10})/; const getHttpsFromURLRegex = /^(https?:\/\/|\/\/)/; const getJsonFromBiliplusRegex = /window\.addEventListener\('DOMContentLoaded',function\(\){view\((.+)\);}\);/; const getParamsWithSignFromBiliplusRegex = /api\/view_all(.+)'/; const getFilenameFromURLRegex = /[^/]+(?:\.[a-zA-Z0-9]+)$/; const getAvifFromURLRegex = /@.*/; let onFavlistPage = false; let autoNextPage = false; let autoNextFavlist = false; let debug = false; let newFreshSpace; let classAppendNewFreshSpace; let pageSize; let firstTimeMain = true; let divMessage; let divMessageHeightFixed = false; let order = 'mtime'; const activeControllers = new Set(); let getFromApiExtraDelay = true; let getFromBiliplusExtraCounter = 0; setInterval(() => { if (getFromBiliplusExtraCounter > 0) { getFromBiliplusExtraCounter--; } }, 1000); const sortedKeys = [ 'BV', 'AV', 'title', 'intro', 'cover', 'upperUID', 'upperName', 'upperAvatar', 'timeUpload', 'timePublish', 'timeFavorite', 'dynamic', 'pages', 'api', 'apiExtra', 'biliplus', 'biliplusExtra', 'jijidown', 'jijidownExtra', 'xbeibeix', ]; const settings = GM_getValue('settings', { processNormal: true, processDisabled: true, getFromApi: true, getFromApiUpdate: true, getFromApiUpdateInterval: 5, getFromApiExtra: false, getFromApiExtraThumbnails: false, getFromApiExtraUpdate: true, getFromApiExtraUpdateAll: false, getFromApiExtraUpdateInterval: 5, getFromBiliplus: false, getFromBiliplusUpdate: false, getFromBiliplusUpdateInterval: 10, getFromBiliplusExtra: false, getFromBiliplusExtraUpdate: false, getFromBiliplusExtraUpdateInterval: 10, getFromJijidown: false, getFromJijidownUpdate: false, getFromJijidownUpdateInterval: 10, getFromJijidownExtra: false, getFromJijidownExtraUpdate: false, getFromJijidownExtraUpdateInterval: 10, getFromJijidownURL: 'www.jijidown.com', getFromXbeibeix: false, getFromXbeibeixUpdate: false, getFromXbeibeixUpdateInterval: 10, getFromXbeibeixURL: 'xbeibeix.com', autoNextPageInterval: 5, requestTimeout: 10, delayBeforeMain: 300, appendDropdownCover: true, appendDropdownLocal: true, appendDropdownJump: true, appendDropdownReset: true, TCABJXOpacity: 100, displayAdvancedControls: false, exportBackupWithoutTsFrom: false, version: 0, defaultUID: null, defaultFavlistFid: null, }); /////////////////////////////////////////////////////////////////////////////////// // debug = true; // getFromApiExtraDelay = false; // settings.delayBeforeMain = 0; // settings.processNormal = false; // settings.processDisabled = false; // settings.getFromApi = false; // v9 if (typeof settings.defaultFavlistFid === 'string') { settings.defaultFavlistFid = parseInt(settings.defaultFavlistFid, 10); GM_setValue('settings', settings); } // v10 if (settings.hasOwnProperty('enableGetFromJiji')) { settings.enableGetFromJijidown = settings.enableGetFromJiji; delete settings.enableGetFromJiji; GM_setValue('settings', settings); } // v10 if (settings.hasOwnProperty('enableGetFromBbdownloader')) { settings.enableGetFromXbeibeix = settings.enableGetFromBbdownloader; delete settings.enableGetFromBbdownloader; GM_setValue('settings', settings); } // v10 if (!settings.hasOwnProperty('getFromJijidownURL')) { settings.getFromJijidownURL = 'www.jijidown.com'; GM_setValue('settings', settings); } // v10 if (!settings.hasOwnProperty('getFromXbeibeixURL')) { settings.getFromXbeibeixURL = 'xbeibeix.com'; GM_setValue('settings', settings); } // v13 if (settings.hasOwnProperty('enableDebug')) { delete settings.enableDebug; GM_setValue('settings', settings); } // v14 if (!settings.hasOwnProperty('enableGetFromApiExtra')) { settings.enableGetFromApiExtra = false; GM_setValue('settings', settings); } // v14 if (!settings.hasOwnProperty('enableGetFromJijidownExtra')) { settings.enableGetFromJijidownExtra = false; GM_setValue('settings', settings); } // v16 if (!settings.enableGetFromApi && settings.enableGetFromApiExtra) { settings.enableGetFromApiExtra = false; GM_setValue('settings', settings); } // v19 if (!settings.hasOwnProperty('displayAdvancedControls')) { settings.displayAdvancedControls = false; settings.advancedEnableUpdateGetFromApi = true; settings.advancedEnableUpdateGetFromApiExtra = true; settings.advancedEnableUpdateGetFromBiliplus = false; settings.advancedEnableUpdateGetFromJijidown = false; settings.advancedEnableUpdateGetFromJijidownExtra = false; settings.advancedEnableUpdateGetFromXbeibeix = false; settings.advancedIntervalGetFromApi = 5; settings.advancedIntervalGetFromApiExtra = 5; settings.advancedIntervalGetFromBiliplus = 10; settings.advancedIntervalGetFromJijidown = 10; settings.advancedIntervalGetFromJijidownExtra = 10; settings.advancedIntervalGetFromXbeibeix = 10; settings.advancedIntervalAutoNextPage = 5; GM_setValue('settings', settings); } // v19 if (settings.hasOwnProperty('advancedDisableUpdateGetFromApi')) { settings.advancedEnableUpdateGetFromApi = !settings.advancedDisableUpdateGetFromApi; settings.advancedEnableUpdateGetFromApiExtra = !settings.advancedDisableUpdateGetFromApiExtra; settings.advancedEnableUpdateGetFromBiliplus = !settings.advancedDisableUpdateGetFromBiliplus; settings.advancedEnableUpdateGetFromJijidown = !settings.advancedDisableUpdateGetFromJijidown; settings.advancedEnableUpdateGetFromJijidownExtra = !settings.advancedDisableUpdateGetFromJijidownExtra; settings.advancedEnableUpdateGetFromXbeibeix = !settings.advancedDisableUpdateGetFromXbeibeix; delete settings.advancedDisableUpdateGetFromApi; delete settings.advancedDisableUpdateGetFromApiExtra; delete settings.advancedDisableUpdateGetFromBiliplus; delete settings.advancedDisableUpdateGetFromJijidown; delete settings.advancedDisableUpdateGetFromJijidownExtra; delete settings.advancedDisableUpdateGetFromXbeibeix; GM_setValue('settings', settings); } // v20 if (settings.hasOwnProperty('advancedIntervalAutoNextPage')) { settings.enableUpdateGetFromApi = settings.advancedEnableUpdateGetFromApi; settings.enableUpdateGetFromApiExtra = settings.advancedEnableUpdateGetFromApiExtra; settings.enableUpdateGetFromBiliplus = settings.advancedEnableUpdateGetFromBiliplus; settings.enableUpdateGetFromJijidown = settings.advancedEnableUpdateGetFromJijidown; settings.enableUpdateGetFromJijidownExtra = settings.advancedEnableUpdateGetFromJijidownExtra; settings.enableUpdateGetFromXbeibeix = settings.advancedEnableUpdateGetFromXbeibeix; settings.intervalGetFromApi = settings.advancedIntervalGetFromApi; settings.intervalGetFromApiExtra = settings.advancedIntervalGetFromApiExtra; settings.intervalGetFromBiliplus = settings.advancedIntervalGetFromBiliplus; settings.intervalGetFromJijidown = settings.advancedIntervalGetFromJijidown; settings.intervalGetFromJijidownExtra = settings.advancedIntervalGetFromJijidownExtra; settings.intervalGetFromXbeibeix = settings.advancedIntervalGetFromXbeibeix; settings.intervalAutoNextPage = settings.advancedIntervalAutoNextPage; delete settings.advancedEnableUpdateGetFromApi; delete settings.advancedEnableUpdateGetFromApiExtra; delete settings.advancedEnableUpdateGetFromBiliplus; delete settings.advancedEnableUpdateGetFromJijidown; delete settings.advancedEnableUpdateGetFromJijidownExtra; delete settings.advancedEnableUpdateGetFromXbeibeix; delete settings.advancedIntervalGetFromApi; delete settings.advancedIntervalGetFromApiExtra; delete settings.advancedIntervalGetFromBiliplus; delete settings.advancedIntervalGetFromJijidown; delete settings.advancedIntervalGetFromJijidownExtra; delete settings.advancedIntervalGetFromXbeibeix; delete settings.advancedIntervalAutoNextPage; GM_setValue('settings', settings); } // v20 if (settings.hasOwnProperty('advancedRequestTimeout')) { settings.requestTimeout = settings.advancedRequestTimeout; delete settings.advancedRequestTimeout; GM_setValue('settings', settings); } // v20 if (!settings.hasOwnProperty('requestTimeout')) { settings.requestTimeout = 10; GM_setValue('settings', settings); } // v20 if (settings.hasOwnProperty('advancedExportBackupWithoutTsFrom')) { settings.exportBackupWithoutTsFrom = settings.advancedExportBackupWithoutTsFrom; delete settings.advancedExportBackupWithoutTsFrom; GM_setValue('settings', settings); } // v20 if (!settings.hasOwnProperty('exportBackupWithoutTsFrom')) { settings.exportBackupWithoutTsFrom = false; GM_setValue('settings', settings); } // v20 if (!settings.hasOwnProperty('enableGetThumbnails')) { settings.enableGetThumbnails = false; GM_setValue('settings', settings); } // v21 if (!settings.hasOwnProperty('delayBeforeMain')) { settings.delayBeforeMain = 300; settings.appendDropdownCover = true; settings.appendDropdownLocal = true; settings.appendDropdownJump = true; settings.appendDropdownReset = true; GM_setValue('settings', settings); } // v22 if (!settings.hasOwnProperty('enableGetFromBiliplusExtra')) { settings.enableGetFromBiliplusExtra = false; settings.enableUpdateGetFromBiliplusExtra = false; settings.intervalGetFromBiliplusExtra = 10; GM_setValue('settings', settings); } // v28 if (!settings.hasOwnProperty('TCABJXOpacity')) { settings.TCABJXOpacity = 100; settings.enableDelayGetFromBiliplusExtra = true; GM_setValue('settings', settings); } // v31 if (!settings.hasOwnProperty('getFromApiExtraUpdateAll')) { settings.getFromApiExtraUpdateAll = false; settings.getFromApi = settings.enableGetFromApi; settings.getFromApiUpdate = settings.enableUpdateGetFromApi; settings.getFromApiUpdateInterval = settings.intervalGetFromApi; settings.getFromApiExtra = settings.enableGetFromApiExtra; settings.getFromApiExtraThumbnails = settings.enableGetThumbnails; settings.getFromApiExtraUpdate = settings.enableUpdateGetFromApiExtra; settings.getFromApiExtraUpdateInterval = settings.intervalGetFromApiExtra; settings.getFromBiliplus = settings.enableGetFromBiliplus; settings.getFromBiliplusUpdate = settings.enableUpdateGetFromBiliplus; settings.getFromBiliplusUpdateInterval = settings.intervalGetFromBiliplus; settings.getFromBiliplusExtra = settings.enableGetFromBiliplusExtra; settings.getFromBiliplusExtraUpdate = settings.enableUpdateGetFromBiliplusExtra; settings.getFromBiliplusExtraUpdateInterval = settings.intervalGetFromBiliplusExtra; settings.getFromJijidown = settings.enableGetFromJijidown; settings.getFromJijidownUpdate = settings.enableUpdateGetFromJijidown; settings.getFromJijidownUpdateInterval = settings.intervalGetFromJijidown; settings.getFromJijidownExtra = settings.enableGetFromJijidownExtra; settings.getFromJijidownExtraUpdate = settings.enableUpdateGetFromJijidownExtra; settings.getFromJijidownExtraUpdateInterval = settings.intervalGetFromJijidownExtra; settings.getFromXbeibeix = settings.enableGetFromXbeibeix; settings.getFromXbeibeixUpdate = settings.enableUpdateGetFromXbeibeix; settings.getFromXbeibeixUpdateInterval = settings.intervalGetFromXbeibeix; settings.autoNextPageInterval = settings.intervalAutoNextPage; delete settings.enableGetFromApi; delete settings.enableUpdateGetFromApi; delete settings.intervalGetFromApi; delete settings.enableGetFromApiExtra; delete settings.enableGetThumbnails; delete settings.enableUpdateGetFromApiExtra; delete settings.intervalGetFromApiExtra; delete settings.enableGetFromBiliplus; delete settings.enableUpdateGetFromBiliplus; delete settings.intervalGetFromBiliplus; delete settings.enableGetFromBiliplusExtra; delete settings.enableDelayGetFromBiliplusExtra; delete settings.enableUpdateGetFromBiliplusExtra; delete settings.intervalGetFromBiliplusExtra; delete settings.enableGetFromJijidown; delete settings.enableUpdateGetFromJijidown; delete settings.intervalGetFromJijidown; delete settings.enableGetFromJijidownExtra; delete settings.enableUpdateGetFromJijidownExtra; delete settings.intervalGetFromJijidownExtra; delete settings.enableGetFromXbeibeix; delete settings.enableUpdateGetFromXbeibeix; delete settings.intervalGetFromXbeibeix; delete settings.intervalAutoNextPage; GM_setValue('settings', settings); } /////////////////////////////////////////////////////////////////////////////////// const favlistObserver = new MutationObserver(async (_mutations, observer) => { if (debug) console.debug('callback favlistObserver'); if (document.querySelector('div.items')) { if (debug) console.debug('disconnect favlistObserver'); observer.disconnect(); newFreshSpace = true; classAppendNewFreshSpace = '-newFreshSpace'; pageSize = window.innerWidth < 1760 ? 40 : 36; initControls(); if (!firstTimeMain) { await delay(settings.delayBeforeMain + 300); main(); } if (debug) console.debug('observe itemsObserver'); itemsObserver.observe(document.querySelector('div.items'), { childList: true, attributes: false, characterData: false }); if (debug) console.debug('observe bodyChildListObserver'); bodyChildListObserver.observe(document.body, { childList: true, attributes: false, characterData: false }); if (debug) console.debug('observe radioFilterObserver'); radioFilterObserver.observe(document.querySelector('div.fav-list-header-filter__left > div'), { subtree: true, characterData: false, attributeFilter: ['class'] }); if (debug) console.debug('observe headerFilterLeftChildListObserver'); headerFilterLeftChildListObserver.observe(document.querySelector('div.fav-list-header-filter__left'), { childList: true, attributes: false, characterData: false }); return; } if (document.querySelector('div.fav-content.section')) { if (debug) console.debug('disconnect favlistObserver'); observer.disconnect(); newFreshSpace = false; classAppendNewFreshSpace = ''; pageSize = 20; initControls(); if (debug) console.debug('observe favContentSectionObserver'); favContentSectionObserver.observe(document.querySelector('div.fav-content.section'), { characterData: false, attributeFilter: ['class'] }); return; } }); const itemsObserver = new MutationObserver(async () => { abortActiveControllers(); if (debug) console.debug('callback itemsObserver'); if (settings.delayBeforeMain) { await delay(settings.delayBeforeMain); } main(); }); const bodyChildListObserver = new MutationObserver(mutations => { if (debug) console.debug('callback bodyChildListObserver'); if (debug) console.debug(mutations); for (const mutation of mutations) { for (const addedNode of mutation.addedNodes) { if (addedNode.nodeType === 1 && addedNode.classList.contains('bili-card-dropdown-popper')) { appendDropdowns(addedNode); // return; } // if (addedNode.nodeType === 1 && addedNode.classList.contains('vui_toast--wrapper')) { // abortActiveControllers(); // if (debug) console.debug('disconncet itemsObserver'); // itemsObserver.disconnect(); // // return; // } } // for (const removedNode of mutation.removedNodes) { // if (removedNode.nodeType === 1 && removedNode.classList.contains('vui_toast--wrapper')) { // abortActiveControllers(); // // mainNewFreshSpace(); // main(); // if (debug) console.debug('observe itemsObserver'); // itemsObserver.observe(document.querySelector('div.items'), { childList: true, attributes: false, characterData: false }); // // return; // } // } } }); const radioFilterObserver = new MutationObserver(mutations => { if (debug) console.debug('callback radioFilterObserver'); if (debug) console.debug(mutations); for (const mutation of mutations) { if (mutation.target.classList.contains('radio-filter__item--active')) { const orderText = mutation.target.innerText; if (orderText.includes('收藏')) { order = 'mtime'; } else if (orderText.includes('播放')) { order = 'view'; } else if (orderText.includes('投稿')) { order = 'pubtime'; } else { addMessage('无法确定各个视频的排序方式, 请反馈该问题', false, 'red'); } if (debug) console.log(`order: ${order}`); } } }); const headerFilterLeftChildListObserver = new MutationObserver(mutations => { if (debug) console.debug('callback headerFilterLeftChildListObserver'); if (debug) console.debug(mutations); for (const mutation of mutations) { for (const addedNode of mutation.addedNodes) { if (addedNode.nodeType === 1 && addedNode.classList.contains('radio-filter')) { order = 'mtime'; if (debug) console.log(`order: ${order}`); if (debug) console.debug('observe radioFilterObserver'); radioFilterObserver.observe(addedNode, { subtree: true, characterData: false, attributeFilter: ['class'] }); } } for (const removedNode of mutation.removedNodes) { if (removedNode.nodeType === 1 && removedNode.classList.contains('radio-filter')) { if (debug) console.debug('disconncet radioFilterObserver'); radioFilterObserver.disconnect(); } } } }); const favContentSectionObserver = new MutationObserver(mutations => { if (debug) console.debug('callback favContentSectionObserver'); for (const mutation of mutations) { if (!mutation.target.classList.contains('loading')) { abortActiveControllers(); 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 (debug) console.debug('checkURL'); if (favlistURLRegex.test(location.href)) { if (!onFavlistPage) { onFavlistPage = true; autoNextPage = false; autoNextFavlist = false; if (debug) console.debug('observe favlistObserver'); favlistObserver.observe(document.body, { subtree: true, childList: true, attributes: false, characterData: false }); } } else { if (onFavlistPage) { abortActiveControllers(); onFavlistPage = false; if (debug) console.debug('disconnect favlistObserver'); favlistObserver.disconnect(); if (debug) console.debug('disconncet itemsObserver'); itemsObserver.disconnect(); if (debug) console.debug('disconncet bodyChildListObserver'); bodyChildListObserver.disconnect(); if (debug) console.debug('disconncet radioFilterObserver'); radioFilterObserver.disconnect(); if (debug) console.debug('disconncet headerFilterLeftChildListObserver'); headerFilterLeftChildListObserver.disconnect(); if (debug) console.debug('disconncet favContentSectionObserver'); favContentSectionObserver.disconnect(); } } } async function main() { if (debug) console.log('============main============'); let controller; firstTimeMain = false; try { controller = new AbortController(); activeControllers.add(controller); let currentFavlist; if (newFreshSpace) { currentFavlist = document.querySelector('div.vui_sidebar-item--active'); if (!document.querySelector('div.fav-collapse').contains(currentFavlist)) { if (debug) console.log('不处理特殊收藏夹'); return; } } else { currentFavlist = document.querySelector('.fav-item.cur'); if (!document.querySelector('div.nav-container').contains(currentFavlist)) { if (debug) console.log('不处理特殊收藏夹'); return; } } let fid; if (newFreshSpace) { const getFidFromURLMatch = location.href.match(getFidFromURLRegex); if (getFidFromURLMatch) { fid = parseInt(getFidFromURLMatch[1], 10); if (!settings.defaultFavlistFid && !currentFavlist.parentNode.getAttribute('id')) { settings.defaultFavlistFid = fid; GM_setValue('settings', settings); } } else if (settings.defaultFavlistFid) { fid = settings.defaultFavlistFid; } else { throw ['无法获取当前收藏夹的fid, 刷新页面可能有帮助']; } } else { fid = parseInt(currentFavlist.getAttribute('fid'), 10); } let pageNumber; if (newFreshSpace) { const pagenation = document.querySelector('button.vui_pagenation--btn-num.vui_button--active'); if (!pagenation) { pageNumber = 1; } else { pageNumber = parseInt(pagenation.innerText, 10); } } else { pageNumber = parseInt(document.querySelector('li.be-pager-item-active > a').innerText, 10); } if (!settings.defaultUID) { settings.defaultUID = parseInt(location.href.match(getUIDFromURLRegex)[1], 10); GM_setValue('settings', settings); } let searchKeyword = ''; const inputKeyword = document.querySelector(newFreshSpace ? 'input.fav-list-header-filter__search' : 'input.search-fav-input'); if (inputKeyword) { searchKeyword = inputKeyword.value; } let searchType; const divType = document.querySelector(newFreshSpace ? 'div.vui_input__prepend' : 'div.search-types'); let typeText = '当前'; if (divType) { typeText = divType.innerText; } if (!searchKeyword) { typeText = '当前'; } if (typeText.includes('当前')) { searchType = 0; } else if (typeText.includes('全部')) { searchType = 1; } else { throw ['无法确定搜索的范围为当前收藏夹还是全部收藏夹, 请反馈该问题']; } const videos = document.querySelectorAll(newFreshSpace ? 'div.items__item' : 'li.small-item'); const apiDetails = {}; for (const [index, video] of videos.entries()) { let as; const AVBVTitle = { AV: null, BV: null, title: null }; try { if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (video.querySelector(newFreshSpace ? 'div.bili-cover-card__tags' : 'div.ogv-corner-tag')) { if (debug) console.log('不处理特殊视频'); continue; } let disabled = false; if (newFreshSpace) { const divStats = video.querySelector('div.bili-cover-card__stats'); if (!searchKeyword && !divStats) { disabled = true; } if (searchKeyword && video.querySelector('img').getAttribute('src').includes('be27fd62c99036dce67efface486fb0a88ffed06')) { disabled = true; if (divStats) { divStats.remove(); } else { if (debug) { addMessage('divStats已移除', false, 'red'); console.warn('divStats已移除'); } } } } else { if (video.classList.contains('disabled')) { disabled = true; } } if (!settings.processNormal && !disabled) { continue; } if (!settings.processDisabled && disabled) { continue; } as = video.querySelectorAll('a'); const divTitleNewFreshSpace = video.querySelector('div.bili-video-card__title'); if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (newFreshSpace) { const getBVFromURLMatch = as[0].getAttribute('href').match(getBVFromURLRegex); if (!getBVFromURLMatch) { throw ['无法获取该视频的BV号, 请检查是否有其他脚本或插件修改了该视频封面和标题的链接地址, 并将其关闭']; } AVBVTitle.BV = getBVFromURLMatch[1]; } else { AVBVTitle.BV = video.getAttribute('data-aid'); } AVBVTitle.title = as[1].innerText; if (debug) { console.log('========video========'); console.log(`收藏夹fid: ${fid}`); console.log(`位置: 第${pageNumber}页的第${index + 1}个`); consoleAVBVTitle('log', AVBVTitle); } let spanFavTime; let divTCABJX; if (newFreshSpace) { const divSubtitle = document.createElement('div'); divSubtitle.classList.add('bili-video-card__subtitle'); video.querySelector('div.bili-video-card__details').appendChild(divSubtitle); spanFavTime = document.createElement('span'); spanFavTime.textContent = '投稿于:'; divSubtitle.appendChild(spanFavTime); divTCABJX = document.createElement('div'); divTCABJX.style.marginLeft = 'auto'; divTCABJX.style.display = 'block'; divSubtitle.appendChild(divTCABJX); } else { const divMetaPubdate = video.querySelector('div.meta.pubdate'); const metaText = divMetaPubdate.innerText; divMetaPubdate.innerHTML = null; spanFavTime = document.createElement('span'); spanFavTime.textContent = metaText.replace(': ', ':'); spanFavTime.style.width = 'auto'; spanFavTime.style.lineHeight = '16px'; divMetaPubdate.appendChild(spanFavTime); divTCABJX = divMetaPubdate; } const spanX = document.createElement('span'); spanX.classList.add('backup-spanTCABJX' + classAppendNewFreshSpace); spanX.textContent = 'X'; spanX.style.opacity = settings.TCABJXOpacity / 100; if (!newFreshSpace) { // spanX.style.marginRight = '11px'; spanX.style.marginRight = '9px'; spanX.style.lineHeight = '16px'; } spanX.addEventListener('click', () => { try { GM_openInTab(`https://${settings.getFromXbeibeixURL}/video/${AVBVTitle.BV}`, { active: true, insert: false, setParent: true }); } catch (error) { catchUnknownError(error); } }); divTCABJX.appendChild(spanX); const spanJ = document.createElement('span'); spanJ.classList.add('backup-spanTCABJX' + classAppendNewFreshSpace); spanJ.textContent = 'J'; spanJ.style.opacity = settings.TCABJXOpacity / 100; spanJ.style.marginRight = '3px'; if (!newFreshSpace) { spanJ.style.lineHeight = '16px'; } spanJ.addEventListener('click', () => { try { GM_openInTab(`https://${settings.getFromJijidownURL}/video/${AVBVTitle.BV}`, { active: true, insert: false, setParent: true }); if (debug) { GM_openInTab(`https://${settings.getFromJijidownURL}/api/v1/video_bv/get_info?id=${AVBVTitle.BV.slice(2)}`, { insert: false, setParent: true }); GM_openInTab(`https://${settings.getFromJijidownURL}/api/v1/video_bv/get_download_info?id=${AVBVTitle.BV.slice(2)}`, { insert: false, setParent: true }); } } catch (error) { catchUnknownError(error); } }); divTCABJX.appendChild(spanJ); const spanB = document.createElement('span'); spanB.classList.add('backup-spanTCABJX' + classAppendNewFreshSpace); spanB.textContent = 'B'; spanB.style.opacity = settings.TCABJXOpacity / 100; spanB.style.marginRight = '3px'; if (!newFreshSpace) { spanB.style.lineHeight = '16px'; } spanB.addEventListener('click', () => { try { GM_openInTab(`https://www.biliplus.com/video/${AVBVTitle.BV}`, { active: true, insert: false, setParent: true }); if (debug) { if (AVBVTitle.AV) { GM_openInTab(`https://www.biliplus.com/api/view?id=${AVBVTitle.AV}`, { insert: false, setParent: true }); } else { GM_openInTab(`https://www.biliplus.com/api/view?id=${AVBVTitle.BV}`, { insert: false, setParent: true }); } } } catch (error) { catchUnknownError(error); } }); divTCABJX.appendChild(spanB); const spanA = document.createElement('span'); spanA.classList.add('backup-spanTCABJX' + classAppendNewFreshSpace); spanA.textContent = 'A'; spanA.style.opacity = settings.TCABJXOpacity / 100; spanA.style.marginRight = '3px'; if (!newFreshSpace) { spanA.style.lineHeight = '16px'; } spanA.addEventListener('click', async () => { try { GM_openInTab(`https://api.bilibili.com/x/web-interface/view?bvid=${AVBVTitle.BV}`, { active: true, insert: false, setParent: true }); if (debug) { GM_openInTab(`https://api.bilibili.com/x/web-interface/archive/desc?bvid=${AVBVTitle.BV}`, { insert: false, setParent: true }); if (AVBVTitle.AV) { GM_openInTab(`https://api.bilibili.com/x/v3/fav/resource/infos?resources=${AVBVTitle.AV}%3A2&platform=web&folder_id=${fid}`, { insert: false, setParent: true }); } GM_openInTab(await appendParamsForGetFromApi(fid, ((pageNumber - 1) * pageSize + index + 1), 1), { insert: false, setParent: true }); } } catch (error) { catchUnknownError(error); } }); divTCABJX.appendChild(spanA); const backup = GM_getValue(AVBVTitle.BV, { BV: null, AV: null, title: null, intro: null, cover: null, upperUID: null, upperName: null, upperAvatar: null, timeUpload: null, timePublish: null, timeFavorite: null, dynamic: null, pages: null, api: null, apiExtra: null, biliplus: null, biliplusExtra: null, jijidown: null, jijidownExtra: null, xbeibeix: null, }); if (!backup.BV) { backup.BV = AVBVTitle.BV; GM_setValue(AVBVTitle.BV, backup); } AVBVTitle.AV = backup.AV; formatBackup(backup, true, AVBVTitle.BV); if (!searchType && updateTimeFavoriteInBackup(backup, undefined, fid)) { GM_setValue(AVBVTitle.BV, backup); } const functions = []; try { if (newFreshSpace) { if (backup.timePublish) { spanFavTime.textContent = `投稿于:${formatTsTimePublish(1000 * backup.timePublish)}`; spanFavTime.setAttribute('title', new Date(1000 * backup.timePublish).toLocaleString()); } } else { if (backup.timeFavorite) { const target = backup.timeFavorite.find(el => el.fid === fid); if (target && target.value) { spanFavTime.textContent = `收藏于:${formatTsTimeFavorite(new Date(1000 * target.value))}`; spanFavTime.setAttribute('title', new Date(1000 * target.value).toLocaleString()); } } } if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } const getFromApiExtraNeeded = { value: false }; if (settings.getFromApi) { if (!backup.api || (settings.getFromApiUpdate && getCurrentTs() - backup.api.ts > 3600 * settings.getFromApiUpdateInterval)) { if (await getFromApi(AVBVTitle, backup, spanA, apiDetails, fid, pageNumber, disabled, spanFavTime, searchType, getFromApiExtraNeeded)) { continue; } } else { spanA.style.color = backup.api.value ? '#00ff00' : '#ff0000'; if (!searchType && apiDetails.value) { const apiDetail = apiDetails.value.find(el => el.bvid === AVBVTitle.BV); if (apiDetail) { if (updateTimeFavoriteInBackup(backup, apiDetail.fav_time, fid)) { GM_setValue(AVBVTitle.BV, backup); } } } } } if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (settings.getFromApiExtra) { if (!backup.apiExtra) { await getFromApiExtra(AVBVTitle, backup, spanA, disabled); } else if (settings.getFromApiExtraUpdate && settings.getFromApiExtraUpdateAll && getCurrentTs() - backup.apiExtra.ts > 3600 * 24 * settings.getFromApiExtraUpdateInterval) { await getFromApiExtra(AVBVTitle, backup, spanA, disabled); } else if (settings.getFromApiExtraUpdate && !settings.getFromApiExtraUpdateAll && getFromApiExtraNeeded.value) { await getFromApiExtra(AVBVTitle, backup, spanA, disabled); } else { spanA.style.color = backup.apiExtra.value ? '#008000' : '#800000'; } } if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (settings.getFromApiExtraThumbnails && !disabled && backup.AV && backup.pages) { await getFromApiExtraThumbnails(AVBVTitle, backup, as, controller); } if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (settings.getFromJijidown) { if (!backup.jijidown || (settings.getFromJijidownUpdate && getCurrentTs() - backup.jijidown.ts > 3600 * 24 * 7 * settings.getFromJijidownUpdateInterval)) { functions.push(getFromJijidown(AVBVTitle, backup, spanJ)); } else { if (settings.getFromJijidownExtra) { if (!backup.jijidownExtra || (settings.getFromJijidownExtraUpdate && getCurrentTs() - backup.jijidownExtra.ts > 3600 * 24 * 7 * settings.getFromJijidownExtraUpdateInterval)) { functions.push(getFromJijidown(AVBVTitle, backup, spanJ)); } else { spanJ.style.color = backup.jijidownExtra.value ? '#008000' : '#800000'; } } else { spanJ.style.color = backup.jijidown.value ? '#00ff00' : '#ff0000'; } } } if (settings.getFromXbeibeix) { if (!backup.xbeibeix || (settings.getFromXbeibeixUpdate && getCurrentTs() - backup.xbeibeix.ts > 3600 * 24 * 7 * settings.getFromXbeibeixUpdateInterval)) { functions.push(getFromXbeibeix(AVBVTitle, backup, spanX)); } else { spanX.style.color = backup.xbeibeix.value ? '#00ff00' : '#ff0000'; } } if (settings.getFromBiliplus) { if (!backup.biliplus || (settings.getFromBiliplusUpdate && getCurrentTs() - backup.biliplus.ts > 3600 * 24 * 7 * settings.getFromBiliplusUpdateInterval)) { functions.push(getFromBiliplus(AVBVTitle, backup, spanB)); } else { spanB.style.color = backup.biliplus.value ? '#00ff00' : '#ff0000'; } } if (functions.length) { if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } await Promise.all(functions); const sortedBackup = {}; for (const sortedKey of sortedKeys) { sortedBackup[sortedKey] = backup[sortedKey]; } GM_setValue(AVBVTitle.BV, sortedBackup); if (debug) { console.log('保存第三方网站的数据至本地'); consoleAVBVTitle('debug', AVBVTitle); console.debug(sortedBackup); } } if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (settings.getFromBiliplusExtra) { if (!backup.biliplusExtra || (settings.getFromBiliplusExtraUpdate && getCurrentTs() - backup.biliplusExtra.ts > 3600 * 24 * 7 * settings.getFromBiliplusExtraUpdateInterval)) { await getFromBiliplusExtra(AVBVTitle, backup, spanB, as, controller); } else { spanB.style.color = backup.biliplusExtra.value ? '#008000' : '#800000'; } } } catch (error) { if (error instanceof Error) { if (error.name === 'AbortError') { throw error; } addMessage('发生未知错误, 请反馈该问题', false, 'red'); addMessage(`收藏夹fid: ${fid}`, true); addMessage(`位置: 第${pageNumber}页的第${index + 1}个`, true); addMessageAVBVTitle(AVBVTitle); addMessage(error.stack, true); console.error(`收藏夹fid: ${fid}`); console.error(`位置: 第${pageNumber}页的第${index + 1}个`); consoleAVBVTitle('error', AVBVTitle); console.error(error); if (as[1]) { as[1].style.color = '#ff0000'; } } else { addMessage(error[0], false, 'red'); for (let i = 1; i < error.length; i++) { addMessage(error[i], true); } addMessage(`收藏夹fid: ${fid}`, true); addMessage(`位置: 第${pageNumber}页的第${index + 1}个`, true); addMessageAVBVTitle(AVBVTitle); if (as[1]) { as[1].style.color = '#ff0000'; } } } let picture; let sourceAvif; let sourceWebp; let img; if (newFreshSpace) { img = video.querySelector('img'); if (debug) { if (!img.getAttribute('src').endsWith('@672w_378h_1c.avif')) { addMessage('封面avif已更改', false, 'red'); console.warn('封面avif已更改'); } if (disabled && !searchKeyword && !img.getAttribute('src').includes('be27fd62c99036dce67efface486fb0a88ffed06')) { addMessage('失效视频封面已更改', false, 'red'); console.warn('失效视频封面已更改'); } } } else { picture = video.querySelector('picture'); sourceAvif = picture.querySelector('source[type="image/avif"]'); sourceWebp = picture.querySelector('source[type="image/webp"]'); img = picture.querySelector('img'); } if (disabled) { // video.style.opacity = '0.7'; if (newFreshSpace) { as[2].style.textDecoration = 'line-through'; as[2].style.opacity = '0.7'; if (backup.cover) { img.setAttribute('src', `//${backup.cover[backup.cover.length - 1].value.slice(8)}@672w_378h_1c.avif`); } } else { video.classList.remove('disabled'); as[0].classList.remove('disabled'); as[0].setAttribute('href', `//www.bilibili.com/video/${AVBVTitle.BV}/`); as[0].setAttribute('target', '_blank'); as[1].setAttribute('target', '_blank'); as[1].setAttribute('href', `//www.bilibili.com/video/${AVBVTitle.BV}/`); if (backup.cover) { sourceAvif.setAttribute('srcset', `//${backup.cover[backup.cover.length - 1].value.slice(8)}@320w_200h_1c_!web-space-favlist-video.avif`); sourceWebp.setAttribute('srcset', `//${backup.cover[backup.cover.length - 1].value.slice(8)}@320w_200h_1c_!web-space-favlist-video.webp`); img.setAttribute('src', `//${backup.cover[backup.cover.length - 1].value.slice(8)}@320w_200h_1c_!web-space-favlist-video.webp`); } } spanFavTime.style.textDecoration = 'line-through'; spanFavTime.style.opacity = '0.7'; as[1].style.textDecoration = 'line-through'; as[1].style.opacity = '0.5'; if (backup.title) { as[1].textContent = backup.title[backup.title.length - 1].value; img.setAttribute('alt', backup.title[backup.title.length - 1].value); if (newFreshSpace) { divTitleNewFreshSpace.setAttribute('title', backup.title[backup.title.length - 1].value); } else { as[1].setAttribute('title', backup.title[backup.title.length - 1].value); } } } if (backup.cover && backup.cover.length > 1) { const spanC = document.createElement('span'); spanC.classList.add('backup-spanTCABJX' + classAppendNewFreshSpace); spanC.textContent = 'C'; spanC.style.opacity = settings.TCABJXOpacity / 100; spanC.style.marginRight = '3px'; spanC.style.color = '#000000'; if (!newFreshSpace) { spanC.style.lineHeight = '16px'; } let i = backup.cover.length - 2; spanC.addEventListener('click', () => { try { if (i < 0) { i = backup.cover.length - 1; } if (newFreshSpace) { img.setAttribute('src', `//${backup.cover[i].value.slice(8)}@672w_378h_1c.avif`); } else { sourceAvif.setAttribute('srcset', `//${backup.cover[i].value.slice(8)}@320w_200h_1c_!web-space-favlist-video.avif`); sourceWebp.setAttribute('srcset', `//${backup.cover[i].value.slice(8)}@320w_200h_1c_!web-space-favlist-video.webp`); img.setAttribute('src', `//${backup.cover[i].value.slice(8)}@320w_200h_1c_!web-space-favlist-video.webp`); } if (i !== backup.cover.length - 1) { spanC.style.color = '#999999'; } else { spanC.style.color = '#000000'; } i--; } catch (error) { catchUnknownError(error); } }); divTCABJX.appendChild(spanC); } if (backup.title && backup.title.length > 1) { const spanT = document.createElement('span'); spanT.classList.add('backup-spanTCABJX' + classAppendNewFreshSpace); spanT.textContent = 'T'; spanT.style.opacity = settings.TCABJXOpacity / 100; spanT.style.marginRight = '3px'; spanT.style.color = '#000000'; if (!newFreshSpace) { spanT.style.lineHeight = '16px'; } let i = backup.title.length - 2; spanT.addEventListener('click', () => { try { if (i < 0) { i = backup.title.length - 1; } as[1].textContent = backup.title[i].value; if (newFreshSpace) { divTitleNewFreshSpace.setAttribute('title', backup.title[i].value); } else { as[1].setAttribute('title', backup.title[i].value); } if (i !== backup.title.length - 1) { spanT.style.color = '#999999'; } else { spanT.style.color = '#000000'; } i--; } catch (error) { catchUnknownError(error); } }); divTCABJX.appendChild(spanT); } if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (!newFreshSpace) { const ul = video.querySelector('ul.be-dropdown-menu'); if (ul) { appendDropdowns(ul, AVBVTitle.BV); } } } catch (error) { if (error instanceof Error) { if (error.name === 'AbortError') { throw error; } addMessage('发生未知错误, 请反馈该问题', false, 'red'); addMessage(`收藏夹fid: ${fid}`, true); addMessage(`位置: 第${pageNumber}页的第${index + 1}个`, true); addMessageAVBVTitle(AVBVTitle); addMessage(error.stack, true); console.error(`收藏夹fid: ${fid}`); console.error(`位置: 第${pageNumber}页的第${index + 1}个`); consoleAVBVTitle('error', AVBVTitle); console.error(error); if (as[1]) { as[1].style.color = '#ff0000'; } } else { addMessage(error[0], false, 'red'); for (let i = 1; i < error.length; i++) { addMessage(error[i], true); } addMessage(`收藏夹fid: ${fid}`, true); addMessage(`位置: 第${pageNumber}页的第${index + 1}个`, true); addMessageAVBVTitle(AVBVTitle); if (as[1]) { as[1].style.color = '#ff0000'; } } } } if (autoNextPage) { if (newFreshSpace) { const pager = Array.from(document.querySelectorAll('button.vui_pagenation--btn-side')).find(el => el.innerText === '下一页'); if (pager && !pager.classList.contains('vui_button--disabled')) { await delay(1000 * settings.autoNextPageInterval); if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (autoNextPage) { pager.click(); } } else if (autoNextFavlist) { if (!currentFavlist.parentNode.getAttribute('id')) { if (document.querySelector('div.fav-sortable-list').childElementCount) { await delay(1000 * settings.autoNextPageInterval); if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (autoNextFavlist) { document.querySelector('div.fav-sortable-list').firstElementChild.querySelector('div').click(); } } } else { const nextFavlist = currentFavlist.parentNode.nextElementSibling; if (nextFavlist) { await delay(1000 * settings.autoNextPageInterval); if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (autoNextFavlist) { nextFavlist.querySelector('div').click(); } } } } } else { const pager = document.querySelector('li.be-pager-next'); if (pager && !pager.classList.contains('be-pager-disabled')) { await delay(1000 * settings.autoNextPageInterval); if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (autoNextPage) { pager.click(); } } else if (autoNextFavlist) { if (currentFavlist.nodeName === 'DIV') { if (document.querySelector('ul.fav-list').childElementCount) { await delay(1000 * settings.autoNextPageInterval); if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (autoNextFavlist) { document.querySelector('ul.fav-list').firstElementChild.querySelector('a').click(); } } } else { const nextFavlist = currentFavlist.nextElementSibling; if (nextFavlist) { await delay(1000 * settings.autoNextPageInterval); if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (autoNextFavlist) { nextFavlist.querySelector('a').click(); } } } } } } } catch (error) { if (error instanceof Error) { if (error.name === 'AbortError') { return; } catchUnknownError(error); } else { addMessage(error[0], false, 'red'); for (let i = 1; i < error.length; i++) { addMessage(error[i], true); } } } finally { activeControllers.delete(controller); } } function initControls() { let displayUpdate = false; if (settings.version !== version) { if (settings.version) { displayUpdate = true; } settings.version = version; GM_setValue('settings', settings); } const style = document.createElement('style'); style.textContent = ` .backup-spanTCABJX, .backup-spanTCABJX-newFreshSpace { float: right; font-weight: bold; cursor: pointer; } .backup-div-first { padding: 2px; } .backup-div-first-newFreshSpace { padding: 2px 0; } .backup-div-second { padding: 2px 0 2px 16px; } .backup-div-second-newFreshSpace { padding: 2px 0 2px 16px; } .backup-label, .backup-label-newFreshSpace { line-height: 1; } .backup-inputCheckbox, .backup-inputCheckbox-newFreshSpace { margin-left: 0; } .backup-inputCheckbox { margin-right: 1px; } .backup-inputCheckbox-newFreshSpace { margin-right: 3px; } .backup-disabled, .backup-disabled-newFreshSpace { opacity: 0.5; pointer-events: none; } .backup-inputText, .backup-inputText-newFreshSpace { box-sizing: content-box; border: 1px solid #cccccc; padding: 1px 2px; line-height: 1; } .backup-inputText { width: 28px; height: 14px; border-radius: 2px; font-size: 14px; } .backup-inputText-newFreshSpace { width: 32px; height: 16px; border-radius: 3px; font-size: 16px; } .backup-inputRadio, .backup-inputRadio-newFreshSpace { margin-left: 0; } .backup-inputRadio { margin-right: 1px; } .backup-inputRadio-newFreshSpace { margin-right: 3px; } .backup-hidden, .backup-hidden-newFreshSpace { display: none; } .backup-button, .backup-button-newFreshSpace { border: 1px solid #cccccc; line-height: 1; cursor: pointer; } .backup-button { border-radius: 2px; padding: 2px; font-size: 14px; } .backup-button-newFreshSpace { border-radius: 3px; padding: 3px; font-size: 16px; } .backup-divMessage, .backup-divMessage-newFreshSpace { overflow-y: auto; background-color: #eeeeee; line-height: 1.5; scrollbar-width: none; } .backup-divMessage { margin: 2px; } .backup-divMessage-heightFixed { height: 280px; } .backup-divMessage::-webkit-scrollbar { display: none; } .backup-divMessage-newFreshSpace { margin: 2px 0; } .backup-divMessage-heightFixed-newFreshSpace { height: 320px; } .backup-divMessage-newFreshSpace::-webkit-scrollbar { display: none; } `; document.head.appendChild(style); const divSide = document.querySelector(newFreshSpace ? 'div.favlist-aside' : 'div.fav-sidenav'); if (!newFreshSpace && divSide.querySelector('a.watch-later')) { divSide.querySelector('a.watch-later').style.borderBottom = '1px solid #eeeeee'; } const divControls = document.createElement('div'); divControls.classList.add('backup-div-first' + classAppendNewFreshSpace); if (!newFreshSpace) { divControls.style.borderTop = '1px solid #e4e9f0'; } divSide.appendChild(divControls); const divLabelProcessNormal = document.createElement('div'); divLabelProcessNormal.classList.add('backup-div-first' + classAppendNewFreshSpace); divLabelProcessNormal.setAttribute('title', '默认: 开启\n' + '由于新视频的BV号随机生成, 各个第三方网站无法自动地爬取新视频的信息。\n' + '开启该选项的同时开启下面的从第三方网站获取数据, 脚本将代替您访问相应的第三方网站。\n' + '如果处理的视频第三方网站还没有备份, 这将使其备份该视频当前版本的信息。\n' + '如果处理的视频之前有人备份过了, 这将获取到该视频的信息备份到第三方网站时的版本。'); divControls.appendChild(divLabelProcessNormal); const labelProcessNormal = document.createElement('label'); labelProcessNormal.classList.add('backup-label' + classAppendNewFreshSpace); labelProcessNormal.textContent = '处理正常视频'; divLabelProcessNormal.appendChild(labelProcessNormal); const checkboxProcessNormal = document.createElement('input'); checkboxProcessNormal.type = 'checkbox'; checkboxProcessNormal.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxProcessNormal.checked = settings.processNormal; checkboxProcessNormal.addEventListener('change', () => { try { settings.processNormal = checkboxProcessNormal.checked; if (!settings.processNormal && !settings.processDisabled) { divLabelGetFromApi.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtra.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraThumbnails.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraDelay.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdatePart.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateAll.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplus.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtra.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidown.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtra.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownURL1.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownURL2.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeix.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL1.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL2.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL3.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromApi.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApi) { divLabelGetFromApiUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiUpdate) { divLabelGetFromApiUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromApiExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtra) { divLabelGetFromApiExtraThumbnails.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraDelay.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtraUpdate) { divLabelGetFromApiExtraUpdatePart.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateAll.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtraUpdateAll) { divLabelGetFromApiExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } } } divLabelGetFromBiliplus.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplus) { divLabelGetFromBiliplusUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusUpdate) { divLabelGetFromBiliplusUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } if (settings.getFromApi) { divLabelGetFromBiliplusExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusExtra) { divLabelGetFromBiliplusExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusExtraUpdate) { divLabelGetFromBiliplusExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } } } divLabelGetFromJijidown.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidown) { divLabelGetFromJijidownUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownUpdate) { divLabelGetFromJijidownUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromJijidownExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownExtra) { divLabelGetFromJijidownExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownExtraUpdate) { divLabelGetFromJijidownExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } divLabelGetFromJijidownURL1.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownURL2.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromXbeibeix.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromXbeibeix) { divLabelGetFromXbeibeixUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromXbeibeixUpdate) { divLabelGetFromXbeibeixUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromXbeibeixURL1.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL2.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL3.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelProcessNormal.insertAdjacentElement('afterbegin', checkboxProcessNormal); const divLabelProcessDisabled = document.createElement('div'); divLabelProcessDisabled.classList.add('backup-div-first' + classAppendNewFreshSpace); divLabelProcessDisabled.setAttribute('title', '默认: 开启\n' + '开启该选项的同时开启下面的从第三方网站获取数据, 脚本将尝试从相应的第三方网站获取失效视频的信息。'); divControls.appendChild(divLabelProcessDisabled); const labelProcessDisabled = document.createElement('label'); labelProcessDisabled.classList.add('backup-label' + classAppendNewFreshSpace); labelProcessDisabled.textContent = '处理失效视频'; divLabelProcessDisabled.appendChild(labelProcessDisabled); const checkboxProcessDisabled = document.createElement('input'); checkboxProcessDisabled.type = 'checkbox'; checkboxProcessDisabled.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxProcessDisabled.checked = settings.processDisabled; checkboxProcessDisabled.addEventListener('change', () => { try { settings.processDisabled = checkboxProcessDisabled.checked; if (!settings.processNormal && !settings.processDisabled) { divLabelGetFromApi.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtra.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraThumbnails.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraDelay.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdatePart.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateAll.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplus.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtra.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidown.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtra.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownURL1.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownURL2.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeix.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL1.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL2.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL3.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromApi.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApi) { divLabelGetFromApiUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiUpdate) { divLabelGetFromApiUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromApiExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtra) { divLabelGetFromApiExtraThumbnails.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraDelay.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtraUpdate) { divLabelGetFromApiExtraUpdatePart.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateAll.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtraUpdateAll) { divLabelGetFromApiExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } } } divLabelGetFromBiliplus.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplus) { divLabelGetFromBiliplusUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusUpdate) { divLabelGetFromBiliplusUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } if (settings.getFromApi) { divLabelGetFromBiliplusExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusExtra) { divLabelGetFromBiliplusExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusExtraUpdate) { divLabelGetFromBiliplusExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } } } divLabelGetFromJijidown.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidown) { divLabelGetFromJijidownUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownUpdate) { divLabelGetFromJijidownUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromJijidownExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownExtra) { divLabelGetFromJijidownExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownExtraUpdate) { divLabelGetFromJijidownExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } divLabelGetFromJijidownURL1.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownURL2.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromXbeibeix.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromXbeibeix) { divLabelGetFromXbeibeixUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromXbeibeixUpdate) { divLabelGetFromXbeibeixUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromXbeibeixURL1.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL2.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL3.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelProcessDisabled.insertAdjacentElement('afterbegin', checkboxProcessDisabled); const divLabelGetFromApi = document.createElement('div'); divLabelGetFromApi.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApi.setAttribute('title', '默认: 开启\n' + '地址: https://api.bilibili.com/x/v3/fav/resource/list?media_id={收藏夹fid}&pn={页码}&ps={每页展示视频数量}\n' + '数据: AV号, 标题 (失效视频无法获取), 简介 (仅能获取前255个字符), 封面地址 (失效视频无法获取), UP主UID, UP主昵称, UP主头像地址, 上传时间, 发布时间, 添加到当前收藏夹的时间, 第1个分集的cid (均为最新版本)\n' + '上述接口一次请求即可获取当前页所有视频的数据。\n' + '地址: https://api.bilibili.com/x/web-interface/archive/desc?bvid={BV号}\n' + '数据: 完整简介 (最新版本, 非必要不会调用该接口)'); divControls.appendChild(divLabelGetFromApi); const labelGetFromApi = document.createElement('label'); labelGetFromApi.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromApi.textContent = '从B站接口获取数据'; divLabelGetFromApi.appendChild(labelGetFromApi); const checkboxGetFromApi = document.createElement('input'); checkboxGetFromApi.type = 'checkbox'; checkboxGetFromApi.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromApi.checked = settings.getFromApi; checkboxGetFromApi.addEventListener('change', () => { try { settings.getFromApi = checkboxGetFromApi.checked; if (!settings.getFromApi) { settings.getFromApiExtra = false; checkboxGetFromApiExtra.checked = false; settings.getFromApiExtraThumbnails = false; checkboxGetFromApiExtraThumbnails.checked = false; settings.getFromBiliplusExtra = false; checkboxGetFromBiliplusExtra.checked = false; divLabelGetFromApiUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtra.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraThumbnails.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraDelay.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdatePart.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateAll.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtra.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromApiUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiUpdate) { divLabelGetFromApiUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromApiExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtra) { divLabelGetFromApiExtraThumbnails.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraDelay.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtraUpdate) { divLabelGetFromApiExtraUpdatePart.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateAll.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtraUpdateAll) { divLabelGetFromApiExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } } if (settings.getFromBiliplus) { divLabelGetFromBiliplusExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusExtra) { divLabelGetFromBiliplusExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusExtraUpdate) { divLabelGetFromBiliplusExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } } } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromApi.insertAdjacentElement('afterbegin', checkboxGetFromApi); const divLabelGetFromApiUpdate = document.createElement('div'); divLabelGetFromApiUpdate.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiUpdate.setAttribute('title', '默认: 开启\n' + '关闭后每个视频从B站接口只会获取一次数据。\n' + '不建议关闭, 因为某些视频的信息可能会经常更新。'); divControls.appendChild(divLabelGetFromApiUpdate); const labelGetFromApiUpdate = document.createElement('label'); labelGetFromApiUpdate.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromApiUpdate.textContent = '启用更新'; divLabelGetFromApiUpdate.appendChild(labelGetFromApiUpdate); const checkboxGetFromApiUpdate = document.createElement('input'); checkboxGetFromApiUpdate.type = 'checkbox'; checkboxGetFromApiUpdate.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromApiUpdate.checked = settings.getFromApiUpdate; checkboxGetFromApiUpdate.addEventListener('change', () => { try { settings.getFromApiUpdate = checkboxGetFromApiUpdate.checked; if (!settings.getFromApiUpdate) { divLabelGetFromApiUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromApiUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromApiUpdate.insertAdjacentElement('afterbegin', checkboxGetFromApiUpdate); const divLabelGetFromApiUpdateInterval = document.createElement('div'); divLabelGetFromApiUpdateInterval.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiUpdateInterval.setAttribute('title', '默认: 5小时\n' + '脚本处理某个视频时, 如果发现距离上次从B站接口获取到该视频的数据已经超过了设定的时间间隔, 则会再次从B站接口获取该视频的数据。'); divControls.appendChild(divLabelGetFromApiUpdateInterval); const labelGetFromApiUpdateInterval = document.createElement('label'); labelGetFromApiUpdateInterval.classList.add('backup-label' + classAppendNewFreshSpace); divLabelGetFromApiUpdateInterval.appendChild(labelGetFromApiUpdateInterval); const inputTextGetFromApiUpdateInterval = document.createElement('input'); inputTextGetFromApiUpdateInterval.type = 'text'; inputTextGetFromApiUpdateInterval.classList.add('backup-inputText' + classAppendNewFreshSpace); inputTextGetFromApiUpdateInterval.value = settings.getFromApiUpdateInterval; inputTextGetFromApiUpdateInterval.setAttribute('backup-def', 5); inputTextGetFromApiUpdateInterval.setAttribute('backup-min', 0); inputTextGetFromApiUpdateInterval.setAttribute('backup-max', 100); inputTextGetFromApiUpdateInterval.setAttribute('backup-setting', 'getFromApiUpdateInterval'); inputTextGetFromApiUpdateInterval.addEventListener('blur', validateInputText); labelGetFromApiUpdateInterval.appendChild(document.createTextNode('最小更新间隔')); labelGetFromApiUpdateInterval.appendChild(inputTextGetFromApiUpdateInterval); labelGetFromApiUpdateInterval.appendChild(document.createTextNode('小时')); const divLabelGetFromApiExtra = document.createElement('div'); divLabelGetFromApiExtra.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtra.setAttribute('title', '默认: 关闭\n' + '地址: https://api.bilibili.com/x/web-interface/view?bvid={BV号}\n' + '数据: 完整简介, 视频发布动态内容, 每个分集的标题, 第1帧截图地址 (旧视频无法获取), cid (均为最新版本, 失效视频均无法获取)\n' + '目前接口 "api.bilibili.com/x/v3/fav/resource/list" 已经设置调用频率限制, 每次请求之间需要至少间隔300毫秒左右。在未来接口 "api.bilibili.com/x/web-interface/view" 也可能会设置类似的限制, 届时频繁从B站接口获取额外数据将会出现问题。'); divControls.appendChild(divLabelGetFromApiExtra); const labelGetFromApiExtra = document.createElement('label'); labelGetFromApiExtra.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromApiExtra.textContent = '从B站接口获取额外数据(可能频率受限)'; divLabelGetFromApiExtra.appendChild(labelGetFromApiExtra); const checkboxGetFromApiExtra = document.createElement('input'); checkboxGetFromApiExtra.type = 'checkbox'; checkboxGetFromApiExtra.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromApiExtra.checked = settings.getFromApiExtra; checkboxGetFromApiExtra.addEventListener('change', () => { try { settings.getFromApiExtra = checkboxGetFromApiExtra.checked; if (!settings.getFromApiExtra) { settings.getFromApiExtraThumbnails = false; checkboxGetFromApiExtraThumbnails.checked = false; divLabelGetFromApiExtraThumbnails.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraDelay.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdatePart.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateAll.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromApiExtraThumbnails.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraDelay.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtraUpdate) { divLabelGetFromApiExtraUpdatePart.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateAll.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtraUpdateAll) { divLabelGetFromApiExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromApiExtra.insertAdjacentElement('afterbegin', checkboxGetFromApiExtra); const divLabelGetFromApiExtraThumbnails = document.createElement('div'); divLabelGetFromApiExtraThumbnails.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraThumbnails.setAttribute('title', '默认: 关闭\n' + '地址: https://api.bilibili.com/x/player/videoshot?aid={AV号}&cid={分集cid}\n' + '数据: 进度条缩略图地址 (失效视频以及部分旧视频无法获取)\n' + '如果UP主对某个视频进行了换源, 只要该视频的本地备份数据中保存了旧源的cid, 就可以尝试获取旧源的进度条缩略图地址。\n' + '一次请求只能获取一个分集的进度条缩略图地址, 如果某个视频的分集较多, 则需要等待一段时间。'); divControls.appendChild(divLabelGetFromApiExtraThumbnails); const labelGetFromApiExtraThumbnails = document.createElement('label'); labelGetFromApiExtraThumbnails.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromApiExtraThumbnails.textContent = '获取进度条缩略图地址'; divLabelGetFromApiExtraThumbnails.appendChild(labelGetFromApiExtraThumbnails); const checkboxGetFromApiExtraThumbnails = document.createElement('input'); checkboxGetFromApiExtraThumbnails.type = 'checkbox'; checkboxGetFromApiExtraThumbnails.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromApiExtraThumbnails.checked = settings.getFromApiExtraThumbnails; checkboxGetFromApiExtraThumbnails.addEventListener('change', () => { try { settings.getFromApiExtraThumbnails = checkboxGetFromApiExtraThumbnails.checked; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromApiExtraThumbnails.insertAdjacentElement('afterbegin', checkboxGetFromApiExtraThumbnails); const divLabelGetFromApiExtraDelay = document.createElement('div'); divLabelGetFromApiExtraDelay.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraDelay.setAttribute('title', '默认: 开启'); if (debug) divControls.appendChild(divLabelGetFromApiExtraDelay); const labelGetFromApiExtraDelay = document.createElement('label'); labelGetFromApiExtraDelay.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromApiExtraDelay.textContent = '停留500毫秒'; if (debug) divLabelGetFromApiExtraDelay.appendChild(labelGetFromApiExtraDelay); const checkboxGetFromApiExtraDelay = document.createElement('input'); checkboxGetFromApiExtraDelay.type = 'checkbox'; checkboxGetFromApiExtraDelay.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromApiExtraDelay.checked = getFromApiExtraDelay; checkboxGetFromApiExtraDelay.addEventListener('change', () => { try { getFromApiExtraDelay = checkboxGetFromApiExtraDelay.checked; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); if (debug) labelGetFromApiExtraDelay.insertAdjacentElement('afterbegin', checkboxGetFromApiExtraDelay); const divLabelGetFromApiExtraUpdate = document.createElement('div'); divLabelGetFromApiExtraUpdate.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdate.setAttribute('title', '默认: 开启'); divControls.appendChild(divLabelGetFromApiExtraUpdate); const labelGetFromApiExtraUpdate = document.createElement('label'); labelGetFromApiExtraUpdate.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromApiExtraUpdate.textContent = '启用更新'; divLabelGetFromApiExtraUpdate.appendChild(labelGetFromApiExtraUpdate); const checkboxGetFromApiExtraUpdate = document.createElement('input'); checkboxGetFromApiExtraUpdate.type = 'checkbox'; checkboxGetFromApiExtraUpdate.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromApiExtraUpdate.checked = settings.getFromApiExtraUpdate; checkboxGetFromApiExtraUpdate.addEventListener('change', () => { try { settings.getFromApiExtraUpdate = checkboxGetFromApiExtraUpdate.checked; if (!settings.getFromApiExtraUpdate) { divLabelGetFromApiExtraUpdatePart.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateAll.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromApiExtraUpdatePart.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateAll.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtraUpdateAll) { divLabelGetFromApiExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromApiExtraUpdate.insertAdjacentElement('afterbegin', checkboxGetFromApiExtraUpdate); const divLabelGetFromApiExtraUpdatePart = document.createElement('div'); divLabelGetFromApiExtraUpdatePart.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdatePart.setAttribute('title', '默认: 只更新可能换源的视频\n' + '只更新可能换源的视频: 脚本处理某个视频时, 从接口 "api.bilibili.com/x/v3/fav/resource/list" 中可以获取到该视频的分集数量以及第1个分集的cid, 如果发现该视频可能进行了换源 (获取的第1个分集的cid在本地备份数据中找不到, 或者获取的分集数量大于1), 才会再次从接口 "api.bilibili.com/x/web-interface/view" 获取该视频的数据。\n' + '总是更新所有视频: 脚本处理某个视频时, 如果发现距离上次从接口 "api.bilibili.com/x/web-interface/view" 获取到该视频的数据已经超过了设定的时间间隔, 则会再次从该接口获取该视频的数据。一次请求只能获取一个视频的数据, 频繁调用该接口不仅耗费时间, 如果该接口设置了调用频率限制还可能出现问题。'); divControls.appendChild(divLabelGetFromApiExtraUpdatePart); const labelGetFromApiExtraUpdatePart = document.createElement('label'); labelGetFromApiExtraUpdatePart.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromApiExtraUpdatePart.textContent = '只更新可能换源的视频'; divLabelGetFromApiExtraUpdatePart.appendChild(labelGetFromApiExtraUpdatePart); const radioGetFromApiExtraUpdatePart = document.createElement('input'); radioGetFromApiExtraUpdatePart.type = 'radio'; radioGetFromApiExtraUpdatePart.classList.add('backup-inputRadio' + classAppendNewFreshSpace); radioGetFromApiExtraUpdatePart.name = 'getFromApiExtraUpdateAll'; radioGetFromApiExtraUpdatePart.value = false; radioGetFromApiExtraUpdatePart.checked = !settings.getFromApiExtraUpdateAll; radioGetFromApiExtraUpdatePart.addEventListener('change', () => { try { settings.getFromApiExtraUpdateAll = false; divLabelGetFromApiExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromApiExtraUpdatePart.insertAdjacentElement('afterbegin', radioGetFromApiExtraUpdatePart); const divLabelGetFromApiExtraUpdateAll = document.createElement('div'); divLabelGetFromApiExtraUpdateAll.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateAll.setAttribute('title', '默认: 只更新可能换源的视频\n' + '只更新可能换源的视频: 脚本处理某个视频时, 从接口 "api.bilibili.com/x/v3/fav/resource/list" 中可以获取到该视频的分集数量以及第1个分集的cid, 如果发现该视频可能进行了换源 (获取的第1个分集的cid在本地备份数据中找不到, 或者获取的分集数量大于1), 才会再次从接口 "api.bilibili.com/x/web-interface/view" 获取该视频的数据。\n' + '总是更新所有视频: 脚本处理某个视频时, 如果发现距离上次从接口 "api.bilibili.com/x/web-interface/view" 获取到该视频的数据已经超过了设定的时间间隔, 则会再次从该接口获取该视频的数据。一次请求只能获取一个视频的数据, 频繁调用该接口不仅耗费时间, 如果该接口设置了调用频率限制还可能出现问题。'); divControls.appendChild(divLabelGetFromApiExtraUpdateAll); const labelGetFromApiExtraUpdateAll = document.createElement('label'); labelGetFromApiExtraUpdateAll.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromApiExtraUpdateAll.textContent = '总是更新所有视频'; divLabelGetFromApiExtraUpdateAll.appendChild(labelGetFromApiExtraUpdateAll); const radioGetFromApiExtraUpdateAll = document.createElement('input'); radioGetFromApiExtraUpdateAll.type = 'radio'; radioGetFromApiExtraUpdateAll.classList.add('backup-inputRadio' + classAppendNewFreshSpace); radioGetFromApiExtraUpdateAll.name = 'getFromApiExtraUpdateAll'; radioGetFromApiExtraUpdateAll.value = true; radioGetFromApiExtraUpdateAll.checked = settings.getFromApiExtraUpdateAll; radioGetFromApiExtraUpdateAll.addEventListener('change', () => { try { settings.getFromApiExtraUpdateAll = true; divLabelGetFromApiExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromApiExtraUpdateAll.insertAdjacentElement('afterbegin', radioGetFromApiExtraUpdateAll); const divLabelGetFromApiExtraUpdateInterval = document.createElement('div'); divLabelGetFromApiExtraUpdateInterval.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateInterval.setAttribute('title', '默认: 5天'); divControls.appendChild(divLabelGetFromApiExtraUpdateInterval); const labelGetFromApiExtraUpdateInterval = document.createElement('label'); labelGetFromApiExtraUpdateInterval.classList.add('backup-label' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateInterval.appendChild(labelGetFromApiExtraUpdateInterval); const inputTextGetFromApiExtraUpdateInterval = document.createElement('input'); inputTextGetFromApiExtraUpdateInterval.type = 'text'; inputTextGetFromApiExtraUpdateInterval.classList.add('backup-inputText' + classAppendNewFreshSpace); inputTextGetFromApiExtraUpdateInterval.value = settings.getFromApiExtraUpdateInterval; inputTextGetFromApiExtraUpdateInterval.setAttribute('backup-def', 5); inputTextGetFromApiExtraUpdateInterval.setAttribute('backup-min', 0); inputTextGetFromApiExtraUpdateInterval.setAttribute('backup-max', 100); inputTextGetFromApiExtraUpdateInterval.setAttribute('backup-setting', 'getFromApiExtraUpdateInterval'); inputTextGetFromApiExtraUpdateInterval.addEventListener('blur', validateInputText); labelGetFromApiExtraUpdateInterval.appendChild(document.createTextNode('最小更新间隔')); labelGetFromApiExtraUpdateInterval.appendChild(inputTextGetFromApiExtraUpdateInterval); labelGetFromApiExtraUpdateInterval.appendChild(document.createTextNode('天')); const divLabelGetFromBiliplus = document.createElement('div'); divLabelGetFromBiliplus.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplus.setAttribute('title', '默认: 关闭\n' + '从BiliPlus获取数据时可能会出现问题。\n' + '地址: https://www.biliplus.com/video/{BV号}\n' + '数据: 标题, 简介, 封面地址, UP主昵称, 每个分集的标题, cid (均为备份时的版本)'); divControls.appendChild(divLabelGetFromBiliplus); const labelGetFromBiliplus = document.createElement('label'); labelGetFromBiliplus.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromBiliplus.textContent = '从BiliPlus获取数据(不稳定)'; divLabelGetFromBiliplus.appendChild(labelGetFromBiliplus); const checkboxGetFromBiliplus = document.createElement('input'); checkboxGetFromBiliplus.type = 'checkbox'; checkboxGetFromBiliplus.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromBiliplus.checked = settings.getFromBiliplus; checkboxGetFromBiliplus.addEventListener('change', () => { try { settings.getFromBiliplus = checkboxGetFromBiliplus.checked; if (!settings.getFromBiliplus) { settings.getFromBiliplusExtra = false; checkboxGetFromBiliplusExtra.checked = false; divLabelGetFromBiliplusUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtra.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromBiliplusUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusUpdate) { divLabelGetFromBiliplusUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } if (settings.getFromApi) { divLabelGetFromBiliplusExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusExtra) { divLabelGetFromBiliplusExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusExtraUpdate) { divLabelGetFromBiliplusExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } } } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromBiliplus.insertAdjacentElement('afterbegin', checkboxGetFromBiliplus); const divLabelGetFromBiliplusUpdate = document.createElement('div'); divLabelGetFromBiliplusUpdate.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusUpdate.setAttribute('title', '默认: 关闭'); divControls.appendChild(divLabelGetFromBiliplusUpdate); const labelGetFromBiliplusUpdate = document.createElement('label'); labelGetFromBiliplusUpdate.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromBiliplusUpdate.textContent = '启用更新'; divLabelGetFromBiliplusUpdate.appendChild(labelGetFromBiliplusUpdate); const checkboxGetFromBiliplusUpdate = document.createElement('input'); checkboxGetFromBiliplusUpdate.type = 'checkbox'; checkboxGetFromBiliplusUpdate.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromBiliplusUpdate.checked = settings.getFromBiliplusUpdate; checkboxGetFromBiliplusUpdate.addEventListener('change', () => { try { settings.getFromBiliplusUpdate = checkboxGetFromBiliplusUpdate.checked; if (!settings.getFromBiliplusUpdate) { divLabelGetFromBiliplusUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromBiliplusUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromBiliplusUpdate.insertAdjacentElement('afterbegin', checkboxGetFromBiliplusUpdate); const divLabelGetFromBiliplusUpdateInterval = document.createElement('div'); divLabelGetFromBiliplusUpdateInterval.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusUpdateInterval.setAttribute('title', '默认: 10星期'); divControls.appendChild(divLabelGetFromBiliplusUpdateInterval); const labelGetFromBiliplusUpdateInterval = document.createElement('label'); labelGetFromBiliplusUpdateInterval.classList.add('backup-label' + classAppendNewFreshSpace); divLabelGetFromBiliplusUpdateInterval.appendChild(labelGetFromBiliplusUpdateInterval); const inputTextGetFromBiliplusUpdateInterval = document.createElement('input'); inputTextGetFromBiliplusUpdateInterval.type = 'text'; inputTextGetFromBiliplusUpdateInterval.classList.add('backup-inputText' + classAppendNewFreshSpace); inputTextGetFromBiliplusUpdateInterval.value = settings.getFromBiliplusUpdateInterval; inputTextGetFromBiliplusUpdateInterval.setAttribute('backup-def', 10); inputTextGetFromBiliplusUpdateInterval.setAttribute('backup-min', 0); inputTextGetFromBiliplusUpdateInterval.setAttribute('backup-max', 100); inputTextGetFromBiliplusUpdateInterval.setAttribute('backup-setting', 'getFromBiliplusUpdateInterval'); inputTextGetFromBiliplusUpdateInterval.addEventListener('blur', validateInputText); labelGetFromBiliplusUpdateInterval.appendChild(document.createTextNode('最小更新间隔')); labelGetFromBiliplusUpdateInterval.appendChild(inputTextGetFromBiliplusUpdateInterval); labelGetFromBiliplusUpdateInterval.appendChild(document.createTextNode('星期')); const divLabelGetFromBiliplusExtra = document.createElement('div'); divLabelGetFromBiliplusExtra.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtra.setAttribute('title', '默认: 关闭\n' + '地址: https://www.biliplus.com/all/video/av{AV号}/\n' + '数据: 调用下面的接口所需的验证参数\n' + '地址: https://www.biliplus.com/api/view_all?av={AV号}&ts={验证参数1}&sign={验证参数2}\n' + '数据: 标题, 简介, 封面地址, UP主昵称 (第一次备份到BiliPlus时的版本); 每个分集的标题, cid (所有曾经备份到BiliPlus版本)\n' + 'BiliPlus原始页面的底部有一个刷新数据的功能, 该功能会让BiliPlus再次从B站接口获取某个视频的最新信息并保存在其数据库中。\n' + 'BiliPlus原始页面显示的信息为最后一次备份到BiliPlus时的版本, 而此接口可以获取到之前备份到BiliPlus时的版本。\n' + '请注意: 此接口有调用频率限制, 短时间内频繁调用此接口可能会出现请求失败的情况, 之后每次调用此接口需要先等待10秒左右。'); divControls.appendChild(divLabelGetFromBiliplusExtra); const labelGetFromBiliplusExtra = document.createElement('label'); labelGetFromBiliplusExtra.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromBiliplusExtra.textContent = '从BiliPlus获取额外数据(频率受限)'; divLabelGetFromBiliplusExtra.appendChild(labelGetFromBiliplusExtra); const checkboxGetFromBiliplusExtra = document.createElement('input'); checkboxGetFromBiliplusExtra.type = 'checkbox'; checkboxGetFromBiliplusExtra.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromBiliplusExtra.checked = settings.getFromBiliplusExtra; checkboxGetFromBiliplusExtra.addEventListener('change', () => { try { settings.getFromBiliplusExtra = checkboxGetFromBiliplusExtra.checked; if (!settings.getFromBiliplusExtra) { divLabelGetFromBiliplusExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromBiliplusExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusExtraUpdate) { divLabelGetFromBiliplusExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromBiliplusExtra.insertAdjacentElement('afterbegin', checkboxGetFromBiliplusExtra); const divLabelGetFromBiliplusExtraUpdate = document.createElement('div'); divLabelGetFromBiliplusExtraUpdate.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtraUpdate.setAttribute('title', '默认: 关闭'); divControls.appendChild(divLabelGetFromBiliplusExtraUpdate); const labelGetFromBiliplusExtraUpdate = document.createElement('label'); labelGetFromBiliplusExtraUpdate.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromBiliplusExtraUpdate.textContent = '启用更新'; divLabelGetFromBiliplusExtraUpdate.appendChild(labelGetFromBiliplusExtraUpdate); const checkboxGetFromBiliplusExtraUpdate = document.createElement('input'); checkboxGetFromBiliplusExtraUpdate.type = 'checkbox'; checkboxGetFromBiliplusExtraUpdate.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromBiliplusExtraUpdate.checked = settings.getFromBiliplusExtraUpdate; checkboxGetFromBiliplusExtraUpdate.addEventListener('change', () => { try { settings.getFromBiliplusExtraUpdate = checkboxGetFromBiliplusExtraUpdate.checked; if (!settings.getFromBiliplusExtraUpdate) { divLabelGetFromBiliplusExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromBiliplusExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromBiliplusExtraUpdate.insertAdjacentElement('afterbegin', checkboxGetFromBiliplusExtraUpdate); const divLabelGetFromBiliplusExtraUpdateInterval = document.createElement('div'); divLabelGetFromBiliplusExtraUpdateInterval.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtraUpdateInterval.setAttribute('title', '默认: 10星期'); divControls.appendChild(divLabelGetFromBiliplusExtraUpdateInterval); const labelGetFromBiliplusExtraUpdateInterval = document.createElement('label'); labelGetFromBiliplusExtraUpdateInterval.classList.add('backup-label' + classAppendNewFreshSpace); divLabelGetFromBiliplusExtraUpdateInterval.appendChild(labelGetFromBiliplusExtraUpdateInterval); const inputTextGetFromBiliplusExtraUpdateInterval = document.createElement('input'); inputTextGetFromBiliplusExtraUpdateInterval.type = 'text'; inputTextGetFromBiliplusExtraUpdateInterval.classList.add('backup-inputText' + classAppendNewFreshSpace); inputTextGetFromBiliplusExtraUpdateInterval.value = settings.getFromBiliplusExtraUpdateInterval; inputTextGetFromBiliplusExtraUpdateInterval.setAttribute('backup-def', 10); inputTextGetFromBiliplusExtraUpdateInterval.setAttribute('backup-min', 0); inputTextGetFromBiliplusExtraUpdateInterval.setAttribute('backup-max', 100); inputTextGetFromBiliplusExtraUpdateInterval.setAttribute('backup-setting', 'getFromBiliplusExtraUpdateInterval'); inputTextGetFromBiliplusExtraUpdateInterval.addEventListener('blur', validateInputText); labelGetFromBiliplusExtraUpdateInterval.appendChild(document.createTextNode('最小更新间隔')); labelGetFromBiliplusExtraUpdateInterval.appendChild(inputTextGetFromBiliplusExtraUpdateInterval); labelGetFromBiliplusExtraUpdateInterval.appendChild(document.createTextNode('星期')); const divLabelGetFromJijidown = document.createElement('div'); divLabelGetFromJijidown.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidown.setAttribute('title', '默认: 关闭\n' + '从唧唧获取数据时可能会出现请求超时和请求失败的情况。\n' + '地址: https://www.jijidown.com/api/v1/video_bv/get_info?id={BV号后10位}\n' + '或 https://www.jiji.moe/api/v1/video_bv/get_info?id={BV号后10位} (取决于下面的单选项)\n' + '数据: 标题, 简介, 封面地址, UP主昵称, UP主头像地址 (均为备份时的版本)'); divControls.appendChild(divLabelGetFromJijidown); const labelGetFromJijidown = document.createElement('label'); labelGetFromJijidown.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromJijidown.textContent = '从唧唧获取数据(不稳定)'; divLabelGetFromJijidown.appendChild(labelGetFromJijidown); const checkboxGetFromJijidown = document.createElement('input'); checkboxGetFromJijidown.type = 'checkbox'; checkboxGetFromJijidown.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromJijidown.checked = settings.getFromJijidown; checkboxGetFromJijidown.addEventListener('change', () => { try { settings.getFromJijidown = checkboxGetFromJijidown.checked; if (!settings.getFromJijidown) { settings.getFromJijidownExtra = false; checkboxGetFromJijidownExtra.checked = false; divLabelGetFromJijidownUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtra.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownURL1.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownURL2.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromJijidownUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownUpdate) { divLabelGetFromJijidownUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromJijidownExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownExtra) { divLabelGetFromJijidownExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownExtraUpdate) { divLabelGetFromJijidownExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } divLabelGetFromJijidownURL1.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownURL2.classList.remove('backup-disabled' + classAppendNewFreshSpace); } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromJijidown.insertAdjacentElement('afterbegin', checkboxGetFromJijidown); const divLabelGetFromJijidownUpdate = document.createElement('div'); divLabelGetFromJijidownUpdate.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownUpdate.setAttribute('title', '默认: 关闭'); divControls.appendChild(divLabelGetFromJijidownUpdate); const labelGetFromJijidownUpdate = document.createElement('label'); labelGetFromJijidownUpdate.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromJijidownUpdate.textContent = '启用更新'; divLabelGetFromJijidownUpdate.appendChild(labelGetFromJijidownUpdate); const checkboxGetFromJijidownUpdate = document.createElement('input'); checkboxGetFromJijidownUpdate.type = 'checkbox'; checkboxGetFromJijidownUpdate.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromJijidownUpdate.checked = settings.getFromJijidownUpdate; checkboxGetFromJijidownUpdate.addEventListener('change', () => { try { settings.getFromJijidownUpdate = checkboxGetFromJijidownUpdate.checked; if (!settings.getFromJijidownUpdate) { divLabelGetFromJijidownUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromJijidownUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromJijidownUpdate.insertAdjacentElement('afterbegin', checkboxGetFromJijidownUpdate); const divLabelGetFromJijidownUpdateInterval = document.createElement('div'); divLabelGetFromJijidownUpdateInterval.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownUpdateInterval.setAttribute('title', '默认: 10星期'); divControls.appendChild(divLabelGetFromJijidownUpdateInterval); const labelGetFromJijidownUpdateInterval = document.createElement('label'); labelGetFromJijidownUpdateInterval.classList.add('backup-label' + classAppendNewFreshSpace); divLabelGetFromJijidownUpdateInterval.appendChild(labelGetFromJijidownUpdateInterval); const inputTextGetFromJijidownUpdateInterval = document.createElement('input'); inputTextGetFromJijidownUpdateInterval.type = 'text'; inputTextGetFromJijidownUpdateInterval.classList.add('backup-inputText' + classAppendNewFreshSpace); inputTextGetFromJijidownUpdateInterval.value = settings.getFromJijidownUpdateInterval; inputTextGetFromJijidownUpdateInterval.setAttribute('backup-def', 10); inputTextGetFromJijidownUpdateInterval.setAttribute('backup-min', 0); inputTextGetFromJijidownUpdateInterval.setAttribute('backup-max', 100); inputTextGetFromJijidownUpdateInterval.setAttribute('backup-setting', 'getFromJijidownUpdateInterval'); inputTextGetFromJijidownUpdateInterval.addEventListener('blur', validateInputText); labelGetFromJijidownUpdateInterval.appendChild(document.createTextNode('最小更新间隔')); labelGetFromJijidownUpdateInterval.appendChild(inputTextGetFromJijidownUpdateInterval); labelGetFromJijidownUpdateInterval.appendChild(document.createTextNode('星期')); const divLabelGetFromJijidownExtra = document.createElement('div'); divLabelGetFromJijidownExtra.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtra.setAttribute('title', '默认: 关闭\n' + '从唧唧获取数据时可能会出现请求超时和请求失败的情况。\n' + '地址: https://www.jijidown.com/api/v1/video_bv/get_download_info?id={BV号后10位}\n' + '或 https://www.jiji.moe/api/v1/video_bv/get_download_info?id={BV号后10位} (取决于下面的单选项)\n' + '数据: 每个分集的标题, cid (均为备份时的版本)'); divControls.appendChild(divLabelGetFromJijidownExtra); const labelGetFromJijidownExtra = document.createElement('label'); labelGetFromJijidownExtra.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromJijidownExtra.textContent = '从唧唧获取额外数据(不稳定)'; divLabelGetFromJijidownExtra.appendChild(labelGetFromJijidownExtra); const checkboxGetFromJijidownExtra = document.createElement('input'); checkboxGetFromJijidownExtra.type = 'checkbox'; checkboxGetFromJijidownExtra.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromJijidownExtra.checked = settings.getFromJijidownExtra; checkboxGetFromJijidownExtra.addEventListener('change', () => { try { settings.getFromJijidownExtra = checkboxGetFromJijidownExtra.checked; if (!settings.getFromJijidownExtra) { divLabelGetFromJijidownExtraUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromJijidownExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownExtraUpdate) { divLabelGetFromJijidownExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromJijidownExtra.insertAdjacentElement('afterbegin', checkboxGetFromJijidownExtra); const divLabelGetFromJijidownExtraUpdate = document.createElement('div'); divLabelGetFromJijidownExtraUpdate.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtraUpdate.setAttribute('title', '默认: 关闭'); divControls.appendChild(divLabelGetFromJijidownExtraUpdate); const labelGetFromJijidownExtraUpdate = document.createElement('label'); labelGetFromJijidownExtraUpdate.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromJijidownExtraUpdate.textContent = '启用更新'; divLabelGetFromJijidownExtraUpdate.appendChild(labelGetFromJijidownExtraUpdate); const checkboxGetFromJijidownExtraUpdate = document.createElement('input'); checkboxGetFromJijidownExtraUpdate.type = 'checkbox'; checkboxGetFromJijidownExtraUpdate.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromJijidownExtraUpdate.checked = settings.getFromJijidownExtraUpdate; checkboxGetFromJijidownExtraUpdate.addEventListener('change', () => { try { settings.getFromJijidownExtraUpdate = checkboxGetFromJijidownExtraUpdate.checked; if (!settings.getFromJijidownExtraUpdate) { divLabelGetFromJijidownExtraUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromJijidownExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromJijidownExtraUpdate.insertAdjacentElement('afterbegin', checkboxGetFromJijidownExtraUpdate); const divLabelGetFromJijidownExtraUpdateInterval = document.createElement('div'); divLabelGetFromJijidownExtraUpdateInterval.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownExtraUpdateInterval.setAttribute('title', '默认: 10星期'); divControls.appendChild(divLabelGetFromJijidownExtraUpdateInterval); const labelGetFromJijidownExtraUpdateInterval = document.createElement('label'); labelGetFromJijidownExtraUpdateInterval.classList.add('backup-label' + classAppendNewFreshSpace); divLabelGetFromJijidownExtraUpdateInterval.appendChild(labelGetFromJijidownExtraUpdateInterval); const inputTextGetFromJijidownExtraUpdateInterval = document.createElement('input'); inputTextGetFromJijidownExtraUpdateInterval.type = 'text'; inputTextGetFromJijidownExtraUpdateInterval.classList.add('backup-inputText' + classAppendNewFreshSpace); inputTextGetFromJijidownExtraUpdateInterval.value = settings.getFromJijidownExtraUpdateInterval; inputTextGetFromJijidownExtraUpdateInterval.setAttribute('backup-def', 10); inputTextGetFromJijidownExtraUpdateInterval.setAttribute('backup-min', 0); inputTextGetFromJijidownExtraUpdateInterval.setAttribute('backup-max', 100); inputTextGetFromJijidownExtraUpdateInterval.setAttribute('backup-setting', 'getFromJijidownExtraUpdateInterval'); inputTextGetFromJijidownExtraUpdateInterval.addEventListener('blur', validateInputText); labelGetFromJijidownExtraUpdateInterval.appendChild(document.createTextNode('最小更新间隔')); labelGetFromJijidownExtraUpdateInterval.appendChild(inputTextGetFromJijidownExtraUpdateInterval); labelGetFromJijidownExtraUpdateInterval.appendChild(document.createTextNode('星期')); const divLabelGetFromJijidownURL1 = document.createElement('div'); divLabelGetFromJijidownURL1.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownURL1.setAttribute('title', '默认: www.jijidown.com\n' + 'www.jiji.moe为唧唧的新域名。\n' + '如果脚本从唧唧获取数据时反复出现问题, 切换至另一域名可能有帮助。'); divControls.appendChild(divLabelGetFromJijidownURL1); const labelGetFromJijidownURL1 = document.createElement('label'); labelGetFromJijidownURL1.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromJijidownURL1.textContent = 'www.jijidown.com'; divLabelGetFromJijidownURL1.appendChild(labelGetFromJijidownURL1); const radioGetFromJijidownURL1 = document.createElement('input'); radioGetFromJijidownURL1.type = 'radio'; radioGetFromJijidownURL1.classList.add('backup-inputRadio' + classAppendNewFreshSpace); radioGetFromJijidownURL1.name = 'getFromJijidownURL'; radioGetFromJijidownURL1.value = 'www.jijidown.com'; radioGetFromJijidownURL1.checked = settings.getFromJijidownURL === 'www.jijidown.com'; radioGetFromJijidownURL1.addEventListener('change', () => { try { settings.getFromJijidownURL = 'www.jijidown.com'; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromJijidownURL1.insertAdjacentElement('afterbegin', radioGetFromJijidownURL1); const divLabelGetFromJijidownURL2 = document.createElement('div'); divLabelGetFromJijidownURL2.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownURL2.setAttribute('title', '默认: www.jijidown.com\n' + 'www.jiji.moe为唧唧的新域名。\n' + '如果脚本从唧唧获取数据时反复出现问题, 切换至另一域名可能有帮助。'); divControls.appendChild(divLabelGetFromJijidownURL2); const labelGetFromJijidownURL2 = document.createElement('label'); labelGetFromJijidownURL2.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromJijidownURL2.textContent = 'www.jiji.moe'; divLabelGetFromJijidownURL2.appendChild(labelGetFromJijidownURL2); const radioGetFromJijidownURL2 = document.createElement('input'); radioGetFromJijidownURL2.type = 'radio'; radioGetFromJijidownURL2.classList.add('backup-inputRadio' + classAppendNewFreshSpace); radioGetFromJijidownURL2.name = 'getFromJijidownURL'; radioGetFromJijidownURL2.value = 'www.jiji.moe'; radioGetFromJijidownURL2.checked = settings.getFromJijidownURL === 'www.jiji.moe'; radioGetFromJijidownURL2.addEventListener('change', () => { try { settings.getFromJijidownURL = 'www.jiji.moe'; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromJijidownURL2.insertAdjacentElement('afterbegin', radioGetFromJijidownURL2); const divLabelGetFromXbeibeix = document.createElement('div'); divLabelGetFromXbeibeix.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeix.setAttribute('title', '默认: 关闭\n' + '从贝贝工具站获取数据时可能会出现请求超时和请求失败的情况。\n' + '地址: https://xbeibeix.com/video/{BV号}\n' + '或 https://beibeigame.com/video/{BV号}\n' + '或 https://bbdownloader.com/video/{BV号} (取决于下面的单选项)\n' + '数据: 标题, 简介, 封面地址, UP主昵称 (均为备份时的版本)'); divControls.appendChild(divLabelGetFromXbeibeix); const labelGetFromXbeibeix = document.createElement('label'); labelGetFromXbeibeix.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromXbeibeix.textContent = '从贝贝工具站获取数据(不稳定)'; divLabelGetFromXbeibeix.appendChild(labelGetFromXbeibeix); const checkboxGetFromXbeibeix = document.createElement('input'); checkboxGetFromXbeibeix.type = 'checkbox'; checkboxGetFromXbeibeix.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromXbeibeix.checked = settings.getFromXbeibeix; checkboxGetFromXbeibeix.addEventListener('change', () => { try { settings.getFromXbeibeix = checkboxGetFromXbeibeix.checked; if (!settings.getFromXbeibeix) { divLabelGetFromXbeibeixUpdate.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL1.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL2.classList.add('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL3.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromXbeibeixUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromXbeibeixUpdate) { divLabelGetFromXbeibeixUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromXbeibeixURL1.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL2.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL3.classList.remove('backup-disabled' + classAppendNewFreshSpace); } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromXbeibeix.insertAdjacentElement('afterbegin', checkboxGetFromXbeibeix); const divLabelGetFromXbeibeixUpdate = document.createElement('div'); divLabelGetFromXbeibeixUpdate.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixUpdate.setAttribute('title', '默认: 关闭'); divControls.appendChild(divLabelGetFromXbeibeixUpdate); const labelGetFromXbeibeixUpdate = document.createElement('label'); labelGetFromXbeibeixUpdate.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromXbeibeixUpdate.textContent = '启用更新'; divLabelGetFromXbeibeixUpdate.appendChild(labelGetFromXbeibeixUpdate); const checkboxGetFromXbeibeixUpdate = document.createElement('input'); checkboxGetFromXbeibeixUpdate.type = 'checkbox'; checkboxGetFromXbeibeixUpdate.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxGetFromXbeibeixUpdate.checked = settings.getFromXbeibeixUpdate; checkboxGetFromXbeibeixUpdate.addEventListener('change', () => { try { settings.getFromXbeibeixUpdate = checkboxGetFromXbeibeixUpdate.checked; if (!settings.getFromXbeibeixUpdate) { divLabelGetFromXbeibeixUpdateInterval.classList.add('backup-disabled' + classAppendNewFreshSpace); } else { divLabelGetFromXbeibeixUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromXbeibeixUpdate.insertAdjacentElement('afterbegin', checkboxGetFromXbeibeixUpdate); const divLabelGetFromXbeibeixUpdateInterval = document.createElement('div'); divLabelGetFromXbeibeixUpdateInterval.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced', 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixUpdateInterval.setAttribute('title', '默认: 10星期'); divControls.appendChild(divLabelGetFromXbeibeixUpdateInterval); const labelGetFromXbeibeixUpdateInterval = document.createElement('label'); labelGetFromXbeibeixUpdateInterval.classList.add('backup-label' + classAppendNewFreshSpace); divLabelGetFromXbeibeixUpdateInterval.appendChild(labelGetFromXbeibeixUpdateInterval); const inputTextGetFromXbeibeixUpdateInterval = document.createElement('input'); inputTextGetFromXbeibeixUpdateInterval.type = 'text'; inputTextGetFromXbeibeixUpdateInterval.classList.add('backup-inputText' + classAppendNewFreshSpace); inputTextGetFromXbeibeixUpdateInterval.value = settings.getFromXbeibeixUpdateInterval; inputTextGetFromXbeibeixUpdateInterval.setAttribute('backup-def', 10); inputTextGetFromXbeibeixUpdateInterval.setAttribute('backup-min', 0); inputTextGetFromXbeibeixUpdateInterval.setAttribute('backup-max', 100); inputTextGetFromXbeibeixUpdateInterval.setAttribute('backup-setting', 'getFromXbeibeixUpdateInterval'); inputTextGetFromXbeibeixUpdateInterval.addEventListener('blur', validateInputText); labelGetFromXbeibeixUpdateInterval.appendChild(document.createTextNode('最小更新间隔')); labelGetFromXbeibeixUpdateInterval.appendChild(inputTextGetFromXbeibeixUpdateInterval); labelGetFromXbeibeixUpdateInterval.appendChild(document.createTextNode('星期')); const divLabelGetFromXbeibeixURL1 = document.createElement('div'); divLabelGetFromXbeibeixURL1.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL1.setAttribute('title', '默认: xbeibeix.com\n' + 'beibeigame.com和bbdownloader.com为贝贝工具站的新域名。\n' + '如果脚本从贝贝工具站获取数据时反复出现问题, 切换至另一域名可能有帮助。'); divControls.appendChild(divLabelGetFromXbeibeixURL1); const labelGetFromXbeibeixURL1 = document.createElement('label'); labelGetFromXbeibeixURL1.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromXbeibeixURL1.textContent = 'xbeibeix.com'; divLabelGetFromXbeibeixURL1.appendChild(labelGetFromXbeibeixURL1); const radioGetFromXbeibeixURL1 = document.createElement('input'); radioGetFromXbeibeixURL1.type = 'radio'; radioGetFromXbeibeixURL1.classList.add('backup-inputRadio' + classAppendNewFreshSpace); radioGetFromXbeibeixURL1.name = 'getFromXbeibeixURL'; radioGetFromXbeibeixURL1.value = 'xbeibeix.com'; radioGetFromXbeibeixURL1.checked = settings.getFromXbeibeixURL === 'xbeibeix.com'; radioGetFromXbeibeixURL1.addEventListener('change', () => { try { settings.getFromXbeibeixURL = 'xbeibeix.com'; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromXbeibeixURL1.insertAdjacentElement('afterbegin', radioGetFromXbeibeixURL1); const divLabelGetFromXbeibeixURL2 = document.createElement('div'); divLabelGetFromXbeibeixURL2.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL2.setAttribute('title', '默认: xbeibeix.com\n' + 'beibeigame.com和bbdownloader.com为贝贝工具站的新域名。\n' + '如果脚本从贝贝工具站获取数据时反复出现问题, 切换至另一域名可能有帮助。'); divControls.appendChild(divLabelGetFromXbeibeixURL2); const labelGetFromXbeibeixURL2 = document.createElement('label'); labelGetFromXbeibeixURL2.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromXbeibeixURL2.textContent = 'beibeigame.com'; divLabelGetFromXbeibeixURL2.appendChild(labelGetFromXbeibeixURL2); const radioGetFromXbeibeixURL2 = document.createElement('input'); radioGetFromXbeibeixURL2.type = 'radio'; radioGetFromXbeibeixURL2.classList.add('backup-inputRadio' + classAppendNewFreshSpace); radioGetFromXbeibeixURL2.name = 'getFromXbeibeixURL'; radioGetFromXbeibeixURL2.value = 'beibeigame.com'; radioGetFromXbeibeixURL2.checked = settings.getFromXbeibeixURL === 'beibeigame.com'; radioGetFromXbeibeixURL2.addEventListener('change', () => { try { settings.getFromXbeibeixURL = 'beibeigame.com'; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromXbeibeixURL2.insertAdjacentElement('afterbegin', radioGetFromXbeibeixURL2); const divLabelGetFromXbeibeixURL3 = document.createElement('div'); divLabelGetFromXbeibeixURL3.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL3.setAttribute('title', '默认: xbeibeix.com\n' + 'beibeigame.com和bbdownloader.com为贝贝工具站的新域名。\n' + '如果脚本从贝贝工具站获取数据时反复出现问题, 切换至另一域名可能有帮助。'); divControls.appendChild(divLabelGetFromXbeibeixURL3); const labelGetFromXbeibeixURL3 = document.createElement('label'); labelGetFromXbeibeixURL3.classList.add('backup-label' + classAppendNewFreshSpace); labelGetFromXbeibeixURL3.textContent = 'bbdownloader.com'; divLabelGetFromXbeibeixURL3.appendChild(labelGetFromXbeibeixURL3); const radioGetFromXbeibeixURL3 = document.createElement('input'); radioGetFromXbeibeixURL3.type = 'radio'; radioGetFromXbeibeixURL3.classList.add('backup-inputRadio' + classAppendNewFreshSpace); radioGetFromXbeibeixURL3.name = 'getFromXbeibeixURL'; radioGetFromXbeibeixURL3.value = 'bbdownloader.com'; radioGetFromXbeibeixURL3.checked = settings.getFromXbeibeixURL === 'bbdownloader.com'; radioGetFromXbeibeixURL3.addEventListener('change', () => { try { settings.getFromXbeibeixURL = 'bbdownloader.com'; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelGetFromXbeibeixURL3.insertAdjacentElement('afterbegin', radioGetFromXbeibeixURL3); const divLabelAutoNextPage = document.createElement('div'); divLabelAutoNextPage.classList.add('backup-div-first' + classAppendNewFreshSpace); divLabelAutoNextPage.setAttribute('title', '开启后脚本将在当前页的视频都处理完毕之后点击下一页, 继续处理下一页的视频。'); divControls.appendChild(divLabelAutoNextPage); const labelAutoNextPage = document.createElement('label'); labelAutoNextPage.classList.add('backup-label' + classAppendNewFreshSpace); labelAutoNextPage.textContent = '自动点击下一页'; divLabelAutoNextPage.appendChild(labelAutoNextPage); const checkboxAutoNextPage = document.createElement('input'); checkboxAutoNextPage.type = 'checkbox'; checkboxAutoNextPage.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxAutoNextPage.checked = autoNextPage; checkboxAutoNextPage.addEventListener('change', async () => { try { autoNextPage = checkboxAutoNextPage.checked; if (autoNextPage) { divLabelAutoNextFavlist.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (!activeControllers.size) { if (newFreshSpace) { if (!document.querySelector('div.fav-collapse').contains(document.querySelector('div.vui_sidebar-item--active'))) { return; } const pager = Array.from(document.querySelectorAll('button.vui_pagenation--btn-side')).find(el => el.innerText === '下一页'); if (pager && !pager.classList.contains('vui_button--disabled')) { await delay(500); if (autoNextPage) { pager.click(); } } } else { if (!document.querySelector('div.nav-container').contains(document.querySelector('.fav-item.cur'))) { return; } const pager = document.querySelector('li.be-pager-next'); if (pager && !pager.classList.contains('be-pager-disabled')) { await delay(500); if (autoNextPage) { pager.click(); } } } } } else { autoNextFavlist = false; checkboxAutoNextFavlist.checked = false; divLabelAutoNextFavlist.classList.add('backup-disabled' + classAppendNewFreshSpace); } } catch (error) { catchUnknownError(error); } }); labelAutoNextPage.insertAdjacentElement('afterbegin', checkboxAutoNextPage); const divLabelAutoNextFavlist = document.createElement('div'); divLabelAutoNextFavlist.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-disabled' + classAppendNewFreshSpace); divLabelAutoNextFavlist.setAttribute('title', '开启后脚本将在当前收藏夹的视频都处理完毕之后点击下一收藏夹, 继续处理下一收藏夹的视频。'); divControls.appendChild(divLabelAutoNextFavlist); const labelAutoNextFavlist = document.createElement('label'); labelAutoNextFavlist.classList.add('backup-label' + classAppendNewFreshSpace); labelAutoNextFavlist.textContent = '自动点击下一收藏夹'; divLabelAutoNextFavlist.appendChild(labelAutoNextFavlist); const checkboxAutoNextFavlist = document.createElement('input'); checkboxAutoNextFavlist.type = 'checkbox'; checkboxAutoNextFavlist.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxAutoNextFavlist.checked = autoNextFavlist; checkboxAutoNextFavlist.addEventListener('change', async () => { try { autoNextFavlist = checkboxAutoNextFavlist.checked; if (autoNextFavlist && !activeControllers.size) { let currentFavlist; if (newFreshSpace) { currentFavlist = document.querySelector('div.vui_sidebar-item--active'); if (!document.querySelector('div.fav-collapse').contains(currentFavlist)) { return; } const pager = Array.from(document.querySelectorAll('button.vui_pagenation--btn-side')).find(el => el.innerText === '下一页'); if (pager && !pager.classList.contains('vui_button--disabled')) { } else { if (!currentFavlist.parentNode.getAttribute('id')) { if (document.querySelector('div.fav-sortable-list').childElementCount) { await delay(500); if (autoNextFavlist) { document.querySelector('div.fav-sortable-list').firstElementChild.querySelector('div').click(); } } } else { const nextFavlist = currentFavlist.parentNode.nextElementSibling; if (nextFavlist) { await delay(500); if (autoNextFavlist) { nextFavlist.querySelector('div').click(); } } } } } else { currentFavlist = document.querySelector('.fav-item.cur'); if (!document.querySelector('div.nav-container').contains(currentFavlist)) { return; } const pager = document.querySelector('li.be-pager-next'); if (pager && !pager.classList.contains('be-pager-disabled')) { } else { if (currentFavlist.nodeName === 'DIV') { if (document.querySelector('ul.fav-list').childElementCount) { await delay(500); if (autoNextFavlist) { document.querySelector('ul.fav-list').firstElementChild.querySelector('a').click(); } } } else { const nextFavlist = currentFavlist.nextElementSibling; if (nextFavlist) { await delay(500); if (autoNextFavlist) { nextFavlist.querySelector('a').click(); } } } } } } } catch (error) { catchUnknownError(error); } }); labelAutoNextFavlist.insertAdjacentElement('afterbegin', checkboxAutoNextFavlist); const divLabelAutoNextPageInterval = document.createElement('div'); divLabelAutoNextPageInterval.classList.add('backup-div-second' + classAppendNewFreshSpace, 'backup-advanced'); divLabelAutoNextPageInterval.setAttribute('title', '默认: 5秒\n' + '每次点击下一页或下一收藏夹之前停留在当前页的时间。'); divControls.appendChild(divLabelAutoNextPageInterval); const labelAutoNextPageInterval = document.createElement('label'); labelAutoNextPageInterval.classList.add('backup-label' + classAppendNewFreshSpace); divLabelAutoNextPageInterval.appendChild(labelAutoNextPageInterval); const inputTextAutoNextPageInterval = document.createElement('input'); inputTextAutoNextPageInterval.type = 'text'; inputTextAutoNextPageInterval.classList.add('backup-inputText' + classAppendNewFreshSpace); inputTextAutoNextPageInterval.value = settings.autoNextPageInterval; inputTextAutoNextPageInterval.setAttribute('backup-def', 5); inputTextAutoNextPageInterval.setAttribute('backup-min', 1); inputTextAutoNextPageInterval.setAttribute('backup-max', 500); inputTextAutoNextPageInterval.setAttribute('backup-setting', 'autoNextPageInterval'); inputTextAutoNextPageInterval.addEventListener('blur', validateInputText); labelAutoNextPageInterval.appendChild(document.createTextNode('停留间隔')); labelAutoNextPageInterval.appendChild(inputTextAutoNextPageInterval); labelAutoNextPageInterval.appendChild(document.createTextNode('秒')); const divLabelRequestTimeout = document.createElement('div'); divLabelRequestTimeout.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divLabelRequestTimeout.setAttribute('title', '默认: 10秒\n' + '脚本获取数据时, 如果等待了设定的时长后仍未得到响应数据, 则暂时跳过处理当前视频。'); divControls.appendChild(divLabelRequestTimeout); const labelRequestTimeout = document.createElement('label'); labelRequestTimeout.classList.add('backup-label' + classAppendNewFreshSpace); divLabelRequestTimeout.appendChild(labelRequestTimeout); const inputTextRequestTimeout = document.createElement('input'); inputTextRequestTimeout.type = 'text'; inputTextRequestTimeout.classList.add('backup-inputText' + classAppendNewFreshSpace); inputTextRequestTimeout.value = settings.requestTimeout; inputTextRequestTimeout.setAttribute('backup-def', 10); inputTextRequestTimeout.setAttribute('backup-min', 1); inputTextRequestTimeout.setAttribute('backup-max', 100); inputTextRequestTimeout.setAttribute('backup-setting', 'requestTimeout'); inputTextRequestTimeout.addEventListener('blur', validateInputText); labelRequestTimeout.appendChild(document.createTextNode('请求超时时间')); labelRequestTimeout.appendChild(inputTextRequestTimeout); labelRequestTimeout.appendChild(document.createTextNode('秒')); const divLabelDelayBeforeMain = document.createElement('div'); divLabelDelayBeforeMain.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divLabelDelayBeforeMain.setAttribute('title', '默认: 300毫秒\n' + '如果您使用的是新版个人空间, 并且遇到了视频标题下方的信息展示出来后又突然消失的情况, 请将这个时长调大一些。\n' + '如果您想将其设置为0, 则需要避免取消收藏, 指定排序方式或筛选条件等操作, 否则可能会出现问题。'); divControls.appendChild(divLabelDelayBeforeMain); const labelDelayBeforeMain = document.createElement('label'); labelDelayBeforeMain.classList.add('backup-label' + classAppendNewFreshSpace); divLabelDelayBeforeMain.appendChild(labelDelayBeforeMain); const inputTextDelayBeforeMain = document.createElement('input'); inputTextDelayBeforeMain.type = 'text'; inputTextDelayBeforeMain.classList.add('backup-inputText' + classAppendNewFreshSpace); inputTextDelayBeforeMain.value = settings.delayBeforeMain; inputTextDelayBeforeMain.setAttribute('backup-def', 300); inputTextDelayBeforeMain.setAttribute('backup-min', 0); inputTextDelayBeforeMain.setAttribute('backup-max', 1000); inputTextDelayBeforeMain.setAttribute('backup-setting', 'delayBeforeMain'); inputTextDelayBeforeMain.addEventListener('blur', validateInputText); labelDelayBeforeMain.appendChild(document.createTextNode('开始处理视频前等待')); labelDelayBeforeMain.appendChild(inputTextDelayBeforeMain); labelDelayBeforeMain.appendChild(document.createTextNode('毫秒')); const divLabelAppendDropdownCover = document.createElement('div'); divLabelAppendDropdownCover.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divLabelAppendDropdownCover.setAttribute('title', '默认: 开启\n' + '控制是否在各个视频右下角的下拉列表中添加查看封面原图的功能。'); divControls.appendChild(divLabelAppendDropdownCover); const labelAppendDropdownCover = document.createElement('label'); labelAppendDropdownCover.classList.add('backup-label' + classAppendNewFreshSpace); labelAppendDropdownCover.textContent = '下拉列表: 封面原图'; divLabelAppendDropdownCover.appendChild(labelAppendDropdownCover); const checkboxAppendDropdownCover = document.createElement('input'); checkboxAppendDropdownCover.type = 'checkbox'; checkboxAppendDropdownCover.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxAppendDropdownCover.checked = settings.appendDropdownCover; checkboxAppendDropdownCover.addEventListener('change', () => { try { settings.appendDropdownCover = checkboxAppendDropdownCover.checked; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelAppendDropdownCover.insertAdjacentElement('afterbegin', checkboxAppendDropdownCover); const divLabelAppendDropdownLocal = document.createElement('div'); divLabelAppendDropdownLocal.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divLabelAppendDropdownLocal.setAttribute('title', '默认: 开启'); divControls.appendChild(divLabelAppendDropdownLocal); const labelAppendDropdownLocal = document.createElement('label'); labelAppendDropdownLocal.classList.add('backup-label' + classAppendNewFreshSpace); labelAppendDropdownLocal.textContent = '下拉列表: 本地备份数据'; divLabelAppendDropdownLocal.appendChild(labelAppendDropdownLocal); const checkboxAppendDropdownLocal = document.createElement('input'); checkboxAppendDropdownLocal.type = 'checkbox'; checkboxAppendDropdownLocal.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxAppendDropdownLocal.checked = settings.appendDropdownLocal; checkboxAppendDropdownLocal.addEventListener('change', () => { try { settings.appendDropdownLocal = checkboxAppendDropdownLocal.checked; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelAppendDropdownLocal.insertAdjacentElement('afterbegin', checkboxAppendDropdownLocal); const divLabelAppendDropdownJump = document.createElement('div'); divLabelAppendDropdownJump.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divLabelAppendDropdownJump.setAttribute('title', '默认: 开启'); divControls.appendChild(divLabelAppendDropdownJump); const labelAppendDropdownJump = document.createElement('label'); labelAppendDropdownJump.classList.add('backup-label' + classAppendNewFreshSpace); labelAppendDropdownJump.textContent = '下拉列表: 跳转至BJX'; divLabelAppendDropdownJump.appendChild(labelAppendDropdownJump); const checkboxAppendDropdownJump = document.createElement('input'); checkboxAppendDropdownJump.type = 'checkbox'; checkboxAppendDropdownJump.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxAppendDropdownJump.checked = settings.appendDropdownJump; checkboxAppendDropdownJump.addEventListener('change', () => { try { settings.appendDropdownJump = checkboxAppendDropdownJump.checked; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelAppendDropdownJump.insertAdjacentElement('afterbegin', checkboxAppendDropdownJump); const divLabelAppendDropdownReset = document.createElement('div'); divLabelAppendDropdownReset.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divLabelAppendDropdownReset.setAttribute('title', '默认: 开启'); divControls.appendChild(divLabelAppendDropdownReset); const labelAppendDropdownReset = document.createElement('label'); labelAppendDropdownReset.classList.add('backup-label' + classAppendNewFreshSpace); labelAppendDropdownReset.textContent = '下拉列表: 重置备份数据'; divLabelAppendDropdownReset.appendChild(labelAppendDropdownReset); const checkboxAppendDropdownReset = document.createElement('input'); checkboxAppendDropdownReset.type = 'checkbox'; checkboxAppendDropdownReset.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxAppendDropdownReset.checked = settings.appendDropdownReset; checkboxAppendDropdownReset.addEventListener('change', () => { try { settings.appendDropdownReset = checkboxAppendDropdownReset.checked; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelAppendDropdownReset.insertAdjacentElement('afterbegin', checkboxAppendDropdownReset); const divLabelTCABJXOpacity = document.createElement('div'); divLabelTCABJXOpacity.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divLabelTCABJXOpacity.setAttribute('title', '默认: 100%\n' + '如果您觉得视频标题下方的TCABJX太显眼, 可以将其不透明度调低一些。'); divControls.appendChild(divLabelTCABJXOpacity); const labelTCABJXOpacity = document.createElement('label'); labelTCABJXOpacity.classList.add('backup-label' + classAppendNewFreshSpace); divLabelTCABJXOpacity.appendChild(labelTCABJXOpacity); const inputTextTCABJXOpacity = document.createElement('input'); inputTextTCABJXOpacity.type = 'text'; inputTextTCABJXOpacity.classList.add('backup-inputText' + classAppendNewFreshSpace); inputTextTCABJXOpacity.value = settings.TCABJXOpacity; inputTextTCABJXOpacity.setAttribute('backup-def', 100); inputTextTCABJXOpacity.setAttribute('backup-min', 0); inputTextTCABJXOpacity.setAttribute('backup-max', 100); inputTextTCABJXOpacity.setAttribute('backup-setting', 'TCABJXOpacity'); inputTextTCABJXOpacity.addEventListener('blur', validateInputText); labelTCABJXOpacity.appendChild(document.createTextNode('TCABJX不透明度')); labelTCABJXOpacity.appendChild(inputTextTCABJXOpacity); labelTCABJXOpacity.appendChild(document.createTextNode('%')); const divLabelDisplayAdvancedControls = document.createElement('div'); divLabelDisplayAdvancedControls.classList.add('backup-div-first' + classAppendNewFreshSpace); divControls.appendChild(divLabelDisplayAdvancedControls); const labelDisplayAdvancedControls = document.createElement('label'); labelDisplayAdvancedControls.classList.add('backup-label' + classAppendNewFreshSpace); labelDisplayAdvancedControls.textContent = '显示高级选项和功能'; divLabelDisplayAdvancedControls.appendChild(labelDisplayAdvancedControls); const checkboxDisplayAdvancedControls = document.createElement('input'); checkboxDisplayAdvancedControls.type = 'checkbox'; checkboxDisplayAdvancedControls.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxDisplayAdvancedControls.checked = settings.displayAdvancedControls; checkboxDisplayAdvancedControls.addEventListener('change', () => { try { settings.displayAdvancedControls = checkboxDisplayAdvancedControls.checked; if (!settings.displayAdvancedControls) { divControls.querySelectorAll('.backup-advanced').forEach(el => { el.classList.add('backup-hidden' + classAppendNewFreshSpace) }); } else { divControls.querySelectorAll('.backup-advanced').forEach(el => { el.classList.remove('backup-hidden' + classAppendNewFreshSpace) }); } GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelDisplayAdvancedControls.insertAdjacentElement('afterbegin', checkboxDisplayAdvancedControls); const divButtonExportBackup = document.createElement('div'); divButtonExportBackup.classList.add('backup-div-first' + classAppendNewFreshSpace); divButtonExportBackup.setAttribute('title', '脚本内已有的备份数据会与导入的备份数据合并在一起。\n' + '请不要随意修改导出的备份数据文件中的内容, 否则导入时可能会出错。'); divControls.appendChild(divButtonExportBackup); const buttonExportBackup = document.createElement('button'); buttonExportBackup.type = 'button'; buttonExportBackup.classList.add('backup-button' + classAppendNewFreshSpace); buttonExportBackup.textContent = '导出本地备份数据'; buttonExportBackup.addEventListener('click', () => { try { // const BVs = GM_listValues(); // const backupsToExport = {}; // BVs.forEach(BV => { // backupsToExport[BV] = GM_getValue(BV, null); // }); const backupsToExport = GM_getValues(GM_listValues().sort()); delete backupsToExport.settings; const currentTs = getCurrentTs(); // const backupData = JSON.stringify(backupToExport); const backupsData = JSON.stringify(backupsToExport, null, 4); const blob = new Blob([backupsData], { type: 'application/json' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = `${formatTsYYMMDD_HHMMSS(currentTs)}.json`; link.click(); URL.revokeObjectURL(link.href); if (settings.exportBackupWithoutTsFrom) { removeTsFromInBackup(backupsToExport); // const backupData = JSON.stringify(backupToExport); const backupsData = JSON.stringify(backupsToExport, null, 4); const blob = new Blob([backupsData], { type: 'application/json' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = `${formatTsYYMMDD_HHMMSS(currentTs)}_without_ts_from.json`; link.click(); URL.revokeObjectURL(link.href); } } catch (error) { catchUnknownError(error); } }); divButtonExportBackup.appendChild(buttonExportBackup); const divLabelExportBackupWithoutTsFrom = document.createElement('div'); divLabelExportBackupWithoutTsFrom.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divLabelExportBackupWithoutTsFrom.setAttribute('title', '默认: 关闭\n' + '本地备份数据中各个信息的ts代表上一次从B站接口获取到该信息的时间戳或第三方网站备份该信息的时间戳, from代表该信息的获取来源。\n' + '如果您想要对比两个备份数据文件内容的差异, 对比两个文件去除ts和from的副本会更容易一些。\n' + '导入本地备份数据时请导入原始文件, 不要导入去除ts和from的副本。'); divControls.appendChild(divLabelExportBackupWithoutTsFrom); const labelExportBackupWithoutTsFrom = document.createElement('label'); labelExportBackupWithoutTsFrom.classList.add('backup-label' + classAppendNewFreshSpace); labelExportBackupWithoutTsFrom.textContent = '同时导出去除ts和from的副本'; divLabelExportBackupWithoutTsFrom.appendChild(labelExportBackupWithoutTsFrom); const checkboxExportBackupWithoutTsFrom = document.createElement('input'); checkboxExportBackupWithoutTsFrom.type = 'checkbox'; checkboxExportBackupWithoutTsFrom.classList.add('backup-inputCheckbox' + classAppendNewFreshSpace); checkboxExportBackupWithoutTsFrom.checked = settings.exportBackupWithoutTsFrom; checkboxExportBackupWithoutTsFrom.addEventListener('change', () => { try { settings.exportBackupWithoutTsFrom = checkboxExportBackupWithoutTsFrom.checked; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } }); labelExportBackupWithoutTsFrom.insertAdjacentElement('afterbegin', checkboxExportBackupWithoutTsFrom); const divButtonImportBackup = document.createElement('div'); divButtonImportBackup.classList.add('backup-div-first' + classAppendNewFreshSpace); divButtonImportBackup.setAttribute('title', '脚本内已有的备份数据会与导入的备份数据合并在一起。\n' + '请不要随意修改导出的备份数据文件中的内容, 否则导入时可能会出错。'); divControls.appendChild(divButtonImportBackup); const buttonImportBackup = document.createElement('button'); buttonImportBackup.type = 'button'; buttonImportBackup.classList.add('backup-button' + classAppendNewFreshSpace); buttonImportBackup.textContent = '导入本地备份数据'; divButtonImportBackup.appendChild(buttonImportBackup); const divInputFile = document.createElement('div'); divInputFile.style.display = 'none'; divControls.appendChild(divInputFile); const inputFile = document.createElement('input'); inputFile.type = 'file'; inputFile.accept = '.json'; divInputFile.appendChild(inputFile); buttonImportBackup.addEventListener('click', () => { try { inputFile.click(); } catch (error) { catchUnknownError(error); } }); inputFile.addEventListener('change', (event) => { try { const file = event.target.files[0]; if (!file) { return; } const reader = new FileReader(); reader.onload = (e) => { try { const backupsToImport = JSON.parse(e.target.result); if (typeof backupsToImport !== 'object') { addMessage('文件内容有误, 无法导入', false, 'red'); return; } for (const BVOfBackupToImport in backupsToImport) { const backupToImport = backupsToImport[BVOfBackupToImport]; try { formatBackup(backupToImport, false, undefined); const backup = GM_getValue(BVOfBackupToImport, { BV: null, AV: null, title: null, intro: null, cover: null, upperUID: null, upperName: null, upperAvatar: null, timeUpload: null, timePublish: null, timeFavorite: null, dynamic: null, pages: null, api: null, apiExtra: null, biliplus: null, biliplusExtra: null, jijidown: null, jijidownExtra: null, xbeibeix: null, }); formatBackup(backup, false, undefined); if (backupToImport.AV) { backup.AV = backupToImport.AV; } if (backupToImport.BV) { backup.BV = backupToImport.BV; } if (backupToImport.title) { backupToImport.title.forEach(el => { updateArrayDataInBackup(backup, 'title', el.value, el.ts, el.from); }); } if (backupToImport.intro) { backupToImport.intro.forEach(el => { updateArrayDataInBackup(backup, 'intro', el.value, el.ts, el.from); }); } if (backupToImport.cover) { backupToImport.cover.forEach(el => { updateArrayDataInBackup(backup, 'cover', el.value, el.ts, el.from); }); } if (backupToImport.upperUID) { backup.upperUID = backupToImport.upperUID; } if (backupToImport.upperName) { backupToImport.upperName.forEach(el => { updateArrayDataInBackup(backup, 'upperName', el.value, el.ts, el.from); }); } if (backupToImport.upperAvatar) { backupToImport.upperAvatar.forEach(el => { updateArrayDataInBackup(backup, 'upperAvatar', el.value, el.ts, el.from); }); } if (backupToImport.timeUpload) { backup.timeUpload = backupToImport.timeUpload; } if (backupToImport.timePublish) { backup.timePublish = backupToImport.timePublish; } if (backupToImport.timeFavorite) { backupToImport.timeFavorite.forEach(el => { updateTimeFavoriteInBackup(backup, el.value, el.fid); }); } if (backupToImport.dynamic) { backupToImport.dynamic.forEach(el => { updateArrayDataInBackup(backup, 'dynamic', el.value, el.ts, el.from); }); } if (backupToImport.pages) { backupToImport.pages.forEach(el => { updatePagesInBackup(backup, el.index, el.title, el.firstFrame, el.thumbnails, el.cid, el.ts, el.from); }); } ['api', 'apiExtra', 'biliplus', 'biliplusExtra', 'jijidown', 'jijidownExtra', 'xbeibeix'].forEach(key => { if (backupToImport[key]) { if (!backup[key]) { backup[key] = { value: backupToImport[key].value, ts: backupToImport[key].ts }; } else { if (backup[key].ts < backupToImport[key].ts) { backup[key].value = backupToImport[key].value; backup[key].ts = backupToImport[key].ts; } } } }); const sortedBackup = {}; for (const sortedKey of sortedKeys) { sortedBackup[sortedKey] = backup[sortedKey]; } GM_setValue(BVOfBackupToImport, sortedBackup); } catch (error) { addMessage('导入该视频失败', false, 'red'); addMessage(`BV号: ${BVOfBackupToImport}`, true); if (error instanceof Error) { addMessage(error.stack, true); console.error(error); } else { addMessage(error[0], true); for (let i = 1; i < error.length; i++) { addMessage(error[i], true); } } console.error('需要导入的数据'); console.error(backupToImport); console.error('本地已有的数据'); console.error(GM_getValue(BVOfBackupToImport, {})); } } addMessage('导入完成', false, 'green'); } catch (error) { catchUnknownError(error); } }; reader.readAsText(file); } catch (error) { catchUnknownError(error); } }); const divButtonGetFromApiLegacy = document.createElement('div'); divButtonGetFromApiLegacy.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divButtonGetFromApiLegacy.setAttribute('title', '地址: https://api.bilibili.com/medialist/gateway/base/spaceDetail?media_id={收藏夹fid}&pn={页码}&ps={每页展示视频数量}\n' + '数据: AV号, 标题 (失效视频无法获取), 简介 (仅能获取前255个字符), 封面地址 (失效视频无法获取), UP主UID, UP主昵称, UP主头像地址, 上传时间, 发布时间, 添加到当前收藏夹的时间, 每个分集的标题, cid (均为最新版本)\n' + '此功能将会从旧版B站接口获取当前收藏夹内所有视频的数据。与目前的B站接口相比, 旧版接口可以获取到每个分集的标题和cid, 无论视频是否失效。\n' + '您需要将当前收藏夹设置为公开后才能获取到数据。如果收藏夹内第一个视频不是失效视频, 修改可见性会导致收藏夹的封面被固定为该视频的封面, 建议修改可见性之前先复制一个失效视频到当前收藏夹的首位。'); divControls.appendChild(divButtonGetFromApiLegacy); const buttonGetFromApiLegacy = document.createElement('button'); buttonGetFromApiLegacy.type = 'button'; buttonGetFromApiLegacy.classList.add('backup-button' + classAppendNewFreshSpace); buttonGetFromApiLegacy.textContent = '从旧版B站接口获取数据'; buttonGetFromApiLegacy.addEventListener('click', async () => { try { clearMessage(); let currentFavlist; if (newFreshSpace) { currentFavlist = document.querySelector('div.vui_sidebar-item--active'); if (!document.querySelector('div.fav-collapse').contains(currentFavlist)) { throw ['不支持处理特殊收藏夹']; } } else { currentFavlist = document.querySelector('.fav-item.cur'); if (!document.querySelector('div.nav-container').contains(currentFavlist)) { throw ['不支持处理特殊收藏夹']; } } let fid; if (newFreshSpace) { const getFidFromURLMatch = location.href.match(getFidFromURLRegex); if (getFidFromURLMatch) { fid = parseInt(getFidFromURLMatch[1], 10); } else if (settings.defaultFavlistFid) { fid = settings.defaultFavlistFid; } else { throw ['无法获取当前收藏夹的fid, 刷新页面可能有帮助']; } } else { fid = parseInt(currentFavlist.getAttribute('fid'), 10); } let pageNumber = 1; let count = 0; while (true) { const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://api.bilibili.com/medialist/gateway/base/spaceDetail?media_id=${fid}&pn=${pageNumber}&ps=20&keyword=&order=mtime&type=0&tid=0&jsonp=jsonp`, timeout: 1000 * settings.requestTimeout, responseType: 'json', onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', 'api.bilibili.com/medialist/gateway/base/spaceDetail', res.error]), ontimeout: () => reject(['请求超时', 'api.bilibili.com/medialist/gateway/base/spaceDetail']) }); }); if (response.response.code === -403 || response.response.code === 7201004) { throw ['不支持处理私密收藏夹']; } else if (response.response.code) { throw ['发生未知错误, 请反馈该问题', JSON.stringify(response.response)]; } if (!response.response.data.medias) { addMessage('已完成', false, 'green'); return; } response.response.data.medias.forEach(media => { const backup = GM_getValue(media.bvid, { BV: null, AV: null, title: null, intro: null, cover: null, upperUID: null, upperName: null, upperAvatar: null, timeUpload: null, timePublish: null, timeFavorite: null, dynamic: null, pages: null, api: null, apiExtra: null, biliplus: null, biliplusExtra: null, jijidown: null, jijidownExtra: null, xbeibeix: null, }); formatBackup(backup, false, undefined); let disabled = false; if (media.cover.includes('be27fd62c99036dce67efface486fb0a88ffed06')) { disabled = true; } if (!backup.BV) { backup.BV = media.bvid; } if (!backup.AV) { backup.AV = media.id; } if (!disabled) { updateArrayDataInBackup(backup, 'title', media.title, getCurrentTs(), 'api.bilibili.com/medialist/gateway/base/spaceDetail'); updateArrayDataInBackup(backup, 'cover', media.cover, getCurrentTs(), 'api.bilibili.com/medialist/gateway/base/spaceDetail'); } updateArrayDataInBackup(backup, 'intro', media.intro, getCurrentTs(), 'api.bilibili.com/medialist/gateway/base/spaceDetail'); if (!backup.upperUID) { backup.upperUID = media.upper.mid; } updateArrayDataInBackup(backup, 'upperName', media.upper.name, getCurrentTs(), 'api.bilibili.com/medialist/gateway/base/spaceDetail'); updateArrayDataInBackup(backup, 'upperAvatar', media.upper.face, getCurrentTs(), 'api.bilibili.com/medialist/gateway/base/spaceDetail'); if (!backup.timeUpload) { backup.timeUpload = media.ctime; } if (!backup.timePublish) { backup.timePublish = media.pubtime; } updateTimeFavoriteInBackup(backup, media.fav_time, fid); if (Array.isArray(media.pages)) { media.pages.forEach(el => { updatePagesInBackup(backup, el.page, el.title, undefined, undefined, el.id, getCurrentTs(), 'api.bilibili.com/medialist/gateway/base/spaceDetail'); }); } else { if (debug) { addMessage('getFromApiLegacy 无法获取pages', false, 'red'); addMessage(media.bvid, true); console.warn('getFromApiLegacy 无法获取pages'); console.warn(media); } } const sortedBackup = {}; for (const sortedKey of sortedKeys) { sortedBackup[sortedKey] = backup[sortedKey]; } GM_setValue(media.bvid, sortedBackup); if (debug) { console.log('保存旧版B站接口的数据至本地'); console.debug(media.bvid); console.debug(sortedBackup); } }); count += response.response.data.medias.length; addMessage(`已处理视频个数: ${count}`, true); pageNumber++; } } catch (error) { if (error instanceof Error) { catchUnknownError(error); } else { addMessage(error[0], false, 'red'); for (let i = 1; i < error.length; i++) { addMessage(error[i], true); } } } }); divButtonGetFromApiLegacy.appendChild(buttonGetFromApiLegacy); if (debug) { const divButton = document.createElement('div'); divButton.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divControls.appendChild(divButton); const button = document.createElement('button'); button.type = 'button'; button.classList.add('backup-button' + classAppendNewFreshSpace); button.textContent = '清空提示信息'; button.addEventListener('click', () => { try { clearMessage(); } catch (error) { catchUnknownError(error); } }); divButton.appendChild(button); } if (debug) { const divButton = document.createElement('div'); divButton.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divControls.appendChild(divButton); const button = document.createElement('button'); button.type = 'button'; button.classList.add('backup-button' + classAppendNewFreshSpace); button.textContent = '重置timeFavorite'; button.addEventListener('click', () => { try { const backups = GM_getValues(GM_listValues()); delete backups.settings; for (const BVOfBackup in backups) { const backup = backups[BVOfBackup]; backup.timeFavorite = null; GM_setValue(BVOfBackup, backup); } addMessage('已完成', false, 'green'); } catch (error) { catchUnknownError(error); } }); divButton.appendChild(button); } if (debug) { const divButton = document.createElement('div'); divButton.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divControls.appendChild(divButton); const button = document.createElement('button'); button.type = 'button'; button.classList.add('backup-button' + classAppendNewFreshSpace); button.textContent = '删除timeFavorite为null'; button.addEventListener('click', () => { try { const backups = GM_getValues(GM_listValues()); delete backups.settings; for (const BVOfBackup in backups) { if (!backups[BVOfBackup].timeFavorite) { GM_deleteValue(BVOfBackup); } } addMessage('已完成', false, 'green'); } catch (error) { catchUnknownError(error); } }); divButton.appendChild(button); } if (debug) { const divButton = document.createElement('div'); divButton.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divControls.appendChild(divButton); const button = document.createElement('button'); button.type = 'button'; button.classList.add('backup-button' + classAppendNewFreshSpace); button.textContent = '保留timeFavorite为null'; button.addEventListener('click', () => { try { const backups = GM_getValues(GM_listValues()); delete backups.settings; for (const BVOfBackup in backups) { if (backups[BVOfBackup].timeFavorite) { GM_deleteValue(BVOfBackup); } } addMessage('已完成', false, 'green'); } catch (error) { catchUnknownError(error); } }); divButton.appendChild(button); } if (debug) { const divButton = document.createElement('div'); divButton.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divControls.appendChild(divButton); const button = document.createElement('button'); button.type = 'button'; button.classList.add('backup-button' + classAppendNewFreshSpace); button.textContent = '删除指定BV'; button.addEventListener('click', () => { try { const BVs = [ "BV17x411w7KC", ]; const backups = GM_getValues(GM_listValues()); delete backups.settings; for (const BVOfBackup in backups) { if (BVs.includes(BVOfBackup)) { GM_deleteValue(BVOfBackup); } } addMessage('已完成', false, 'green'); } catch (error) { catchUnknownError(error); } }); divButton.appendChild(button); } if (debug) { const divButton = document.createElement('div'); divButton.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divControls.appendChild(divButton); const button = document.createElement('button'); button.type = 'button'; button.classList.add('backup-button' + classAppendNewFreshSpace); button.textContent = '保留指定BV'; button.addEventListener('click', () => { try { const BVs = [ "BV17x411w7KC", ]; const backups = GM_getValues(GM_listValues()); delete backups.settings; for (const BVOfBackup in backups) { if (!BVs.includes(BVOfBackup)) { GM_deleteValue(BVOfBackup); } } addMessage('已完成', false, 'green'); } catch (error) { catchUnknownError(error); } }); divButton.appendChild(button); } // @grant GM_cookie if (debug) { const divButton = document.createElement('div'); divButton.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divControls.appendChild(divButton); const button = document.createElement('button'); button.type = 'button'; button.classList.add('backup-button' + classAppendNewFreshSpace); button.textContent = '批量移动视频'; button.addEventListener('click', () => { try { GM_cookie.list({ name: 'bili_jct' }, async (cookies, error) => { if (error) { throw ['无法读取cookie, 更新Tampermonkey可能有帮助']; } try { let currentFavlist; if (newFreshSpace) { currentFavlist = document.querySelector('div.vui_sidebar-item--active'); if (!document.querySelector('div.fav-collapse').contains(currentFavlist)) { throw ['不支持处理特殊收藏夹']; } } else { currentFavlist = document.querySelector('.fav-item.cur'); if (!document.querySelector('div.nav-container').contains(currentFavlist)) { throw ['不支持处理特殊收藏夹']; } } let fid; if (newFreshSpace) { const getFidFromURLMatch = location.href.match(getFidFromURLRegex); if (getFidFromURLMatch) { fid = parseInt(getFidFromURLMatch[1], 10); } else if (settings.defaultFavlistFid) { fid = settings.defaultFavlistFid; } else { throw ['无法获取当前收藏夹的fid, 刷新页面可能有帮助']; } } else { fid = parseInt(currentFavlist.getAttribute('fid'), 10); } let searchKeyword = ''; const inputKeyword = document.querySelector(newFreshSpace ? 'input.fav-list-header-filter__search' : 'input.search-fav-input'); if (inputKeyword) { searchKeyword = inputKeyword.value; } const fidSource = fid; const fidTarget = parseInt(searchKeyword, 10); const order = 'pubtime'; let pageNumber = 1; const BVsFavlistVisable = []; while (true) { const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://api.bilibili.com/x/v3/fav/resource/list?media_id=${fidSource}&pn=${pageNumber}&ps=40&keyword=&order=${order}&type=0&tid=0&platform=web&web_location=333.1387`, timeout: 1000 * settings.requestTimeout, responseType: 'json', onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', 'api.bilibili.com/x/v3/fav/resource/list', res.error]), ontimeout: () => reject(['请求超时', 'api.bilibili.com/x/v3/fav/resource/list']) }); }); if (response.response.data.medias) { BVsFavlistVisable.push(...response.response.data.medias.filter(el => el.type === 2).map(el => `${el.id}:2`)); } addMessage(`已获取可见视频个数: ${BVsFavlistVisable.length}`, true); if (!response.response.data.has_more) { break; } pageNumber++; } BVsFavlistVisable.reverse(); let start = 0; while (true) { const sl = BVsFavlistVisable.slice(start, start + 40); if (!sl.length) { addMessage('已完成', false, 'green'); break; } const csrf = cookies[0].value; const data = `resources=${sl.join()}&src_media_id=${fidSource}&tar_media_id=${fidTarget}&mid=${settings.defaultUID}&platform=web&csrf=${csrf}`; const response2 = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: 'https://api.bilibili.com/x/v3/fav/resource/move', data: data, timeout: 1000 * settings.requestTimeout, headers: { 'Content-Length': data.length, 'Content-Type': 'application/x-www-form-urlencoded' }, onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', 'api.bilibili.com/x/v3/fav/resource/move', res.error]), ontimeout: () => reject(['请求超时', 'api.bilibili.com/x/v3/fav/resource/move']) }); }); addMessage('B站接口响应内容:'); addMessage(response2.response, true); start += 40; await delay(1000); } } catch (error) { if (error instanceof Error) { catchUnknownError(error); } else { addMessage(error[0], false, 'red'); for (let i = 1; i < error.length; i++) { addMessage(error[i], true); } } } }); } catch (error) { catchUnknownError(error); } }); divButton.appendChild(button); } if (debug) { const divButton = document.createElement('div'); divButton.classList.add('backup-div-first' + classAppendNewFreshSpace, 'backup-advanced'); divControls.appendChild(divButton); const button = document.createElement('button'); button.type = 'button'; button.classList.add('backup-button' + classAppendNewFreshSpace); button.textContent = '手动获取数据'; button.addEventListener('click', async () => { try { const videos = [ { "AV": 170001, "BV": "BV17x411w7KC" }, ]; for (const video of videos) { const AVBVTitle = { AV: null, BV: null, title: null }; try { AVBVTitle.AV = video.AV; AVBVTitle.BV = video.BV; AVBVTitle.title = ''; const spanX = document.createElement('span'); const spanJ = document.createElement('span'); const spanB = document.createElement('span'); const spanA = document.createElement('span'); const backup = GM_getValue(AVBVTitle.BV, { BV: null, AV: null, title: null, intro: null, cover: null, upperUID: null, upperName: null, upperAvatar: null, timeUpload: null, timePublish: null, timeFavorite: null, dynamic: null, pages: null, api: null, apiExtra: null, biliplus: null, biliplusExtra: null, jijidown: null, jijidownExtra: null, xbeibeix: null, }); const functions = []; functions.push(getFromJijidown(AVBVTitle, backup, spanJ)); functions.push(getFromXbeibeix(AVBVTitle, backup, spanX)); functions.push(getFromBiliplus(AVBVTitle, backup, spanB)); await Promise.all(functions); } catch (error) { if (error instanceof Error) { if (error.name === 'AbortError') { throw error; } addMessage('发生未知错误, 请反馈该问题', false, 'red'); addMessageAVBVTitle(AVBVTitle); addMessage(error.stack, true); consoleAVBVTitle('error', AVBVTitle); console.error(error); } else { addMessage(error[0], false, 'red'); for (let i = 1; i < error.length; i++) { addMessage(error[i], true); } addMessageAVBVTitle(AVBVTitle); } } } addMessage('已完成', false, 'green'); } catch (error) { if (error instanceof Error) { if (error.name === 'AbortError') { return; } catchUnknownError(error); } else { addMessage(error[0], false, 'red'); for (let i = 1; i < error.length; i++) { addMessage(error[i], true); } } } }); divButton.appendChild(button); } const divButtonStopProcessing = document.createElement('div'); divButtonStopProcessing.classList.add('backup-div-first' + classAppendNewFreshSpace); divButtonStopProcessing.setAttribute('title', '此功能适用于脚本处理视频时反复出现问题的情况。'); divControls.appendChild(divButtonStopProcessing); const buttonStopProcessing = document.createElement('button'); buttonStopProcessing.type = 'button'; buttonStopProcessing.classList.add('backup-button' + classAppendNewFreshSpace); buttonStopProcessing.textContent = '停止处理当前页视频'; buttonStopProcessing.addEventListener('click', () => { try { abortActiveControllers(); } catch (error) { catchUnknownError(error); } }); divButtonStopProcessing.appendChild(buttonStopProcessing); divMessage = document.createElement('div'); divMessage.classList.add('backup-divMessage' + classAppendNewFreshSpace); divControls.appendChild(divMessage); // if (displayUpdate) { // setTimeout(() => { // addMessage(updates); // }, 300); // } if (settings.processNormal || settings.processDisabled) { divLabelGetFromApi.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApi) { divLabelGetFromApiUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiUpdate) { divLabelGetFromApiUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromApiExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtra) { divLabelGetFromApiExtraThumbnails.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraDelay.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtraUpdate) { divLabelGetFromApiExtraUpdatePart.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromApiExtraUpdateAll.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromApiExtraUpdateAll) { divLabelGetFromApiExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } } } divLabelGetFromBiliplus.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplus) { divLabelGetFromBiliplusUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusUpdate) { divLabelGetFromBiliplusUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } if (settings.getFromApi) { divLabelGetFromBiliplusExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusExtra) { divLabelGetFromBiliplusExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromBiliplusExtraUpdate) { divLabelGetFromBiliplusExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } } } divLabelGetFromJijidown.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidown) { divLabelGetFromJijidownUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownUpdate) { divLabelGetFromJijidownUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromJijidownExtra.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownExtra) { divLabelGetFromJijidownExtraUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromJijidownExtraUpdate) { divLabelGetFromJijidownExtraUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } divLabelGetFromJijidownURL1.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromJijidownURL2.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromXbeibeix.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromXbeibeix) { divLabelGetFromXbeibeixUpdate.classList.remove('backup-disabled' + classAppendNewFreshSpace); if (settings.getFromXbeibeixUpdate) { divLabelGetFromXbeibeixUpdateInterval.classList.remove('backup-disabled' + classAppendNewFreshSpace); } divLabelGetFromXbeibeixURL1.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL2.classList.remove('backup-disabled' + classAppendNewFreshSpace); divLabelGetFromXbeibeixURL3.classList.remove('backup-disabled' + classAppendNewFreshSpace); } } if (!settings.displayAdvancedControls) { divControls.querySelectorAll('.backup-advanced').forEach(el => { el.classList.add('backup-hidden' + classAppendNewFreshSpace); }); } } function appendDropdowns(dropdownContainer, BV) { try { if (newFreshSpace) { if (dropdownContainer.childElementCount === 1) { return; } const biliCardDropdownVisible = document.querySelectorAll('div.bili-card-dropdown--visible'); if (biliCardDropdownVisible.length !== 1) { addMessage('下拉列表开关不存在或不唯一, 无法确定下拉列表所对应的视频, 刷新页面可能有帮助', false, 'red'); return; } let divTargetVideo; try { divTargetVideo = document.querySelector('div.items__item:has(div.bili-card-dropdown--visible)'); } catch { const items = document.querySelectorAll('div.items__item'); for (const item of items) { if (item.contains(biliCardDropdownVisible[0])) { divTargetVideo = item; break; } } } if (!divTargetVideo) { addMessage('无法确定下拉列表所对应的视频, 请反馈该问题', false, 'red'); return; } if (divTargetVideo.querySelector('div.bili-cover-card__tags')) { if (debug) console.log('不处理特殊视频'); return; } if (!settings.processNormal && divTargetVideo.querySelector('div.bili-cover-card__stats')) { return; } if (!settings.processDisabled && !divTargetVideo.querySelector('div.bili-cover-card__stats')) { return; } const getBVFromURLMatch = biliCardDropdownVisible[0].parentNode.querySelector('a').getAttribute('href').match(getBVFromURLRegex); if (!getBVFromURLMatch) { addMessage('无法获取该视频的BV号, 请检查是否有其他脚本或插件修改了该视频封面和标题的链接地址, 并将其关闭', false, 'red'); return; } BV = getBVFromURLMatch[1]; } else { if (dropdownContainer.lastElementChild.classList.contains('backup')) { return; } if (settings.appendDropdownCover || settings.appendDropdownLocal || settings.appendDropdownJump || settings.appendDropdownReset) { dropdownContainer.lastElementChild.classList.add('be-dropdown-item-delimiter'); } } const backup = GM_getValue(BV, {}); if (settings.appendDropdownCover && backup.cover) { const dropdownCover = document.createElement(newFreshSpace ? 'div' : 'li'); dropdownCover.classList.add(newFreshSpace ? 'bili-card-dropdown-popper__item' : 'be-dropdown-item'); if (!newFreshSpace) { dropdownCover.classList.add('backup'); } dropdownCover.setAttribute('title', '查看该视频的本地备份数据中所有版本的封面原图, 从新到旧'); dropdownCover.textContent = '封面原图'; dropdownCover.addEventListener('click', () => { try { if (newFreshSpace) { dropdownContainer.classList.remove('visible'); } GM_openInTab(backup.cover[backup.cover.length - 1].value, { active: true, insert: true, setParent: true }); for (let i = backup.cover.length - 2; i >= 0; i--) { GM_openInTab(backup.cover[i].value, { insert: false, setParent: true }); } } catch (error) { catchUnknownError(error); } }); dropdownContainer.appendChild(dropdownCover); } if (settings.appendDropdownLocal) { const dropdownLocal = document.createElement(newFreshSpace ? 'div' : 'li'); dropdownLocal.classList.add(newFreshSpace ? 'bili-card-dropdown-popper__item' : 'be-dropdown-item'); if (!newFreshSpace) { dropdownLocal.classList.add('backup'); } dropdownLocal.setAttribute('title', '查看该视频的本地备份数据'); dropdownLocal.textContent = '本地备份数据'; dropdownLocal.addEventListener('click', () => { try { if (newFreshSpace) { dropdownContainer.classList.remove('visible'); } const data = GM_getValue(BV, {}); const json = JSON.stringify(data); // GM_openInTab('data:text/plain;charset=utf-8,' + encodeURIComponent(json), { active: true, insert: false, setParent: true }); GM_openInTab('data:application/json;charset=utf-8,' + encodeURIComponent(json), { active: true, insert: false, setParent: true }); } catch (error) { catchUnknownError(error); } }); dropdownContainer.appendChild(dropdownLocal); } if (settings.appendDropdownJump) { const dropdownJump = document.createElement(newFreshSpace ? 'div' : 'li'); dropdownJump.classList.add(newFreshSpace ? 'bili-card-dropdown-popper__item' : 'be-dropdown-item'); if (!newFreshSpace) { dropdownJump.classList.add('backup'); } dropdownJump.setAttribute('title', '跳转至该视频在各个第三方网站的原始页面'); dropdownJump.textContent = '跳转至BJX'; dropdownJump.addEventListener('click', () => { try { if (newFreshSpace) { dropdownContainer.classList.remove('visible'); } GM_openInTab(`https://www.biliplus.com/video/${BV}`, { active: true, insert: false, setParent: true }); GM_openInTab(`https://${settings.getFromJijidownURL}/video/${BV}`, { insert: false, setParent: true }); GM_openInTab(`https://${settings.getFromXbeibeixURL}/video/${BV}`, { insert: false, setParent: true }); } catch (error) { catchUnknownError(error); } }); dropdownContainer.appendChild(dropdownJump); } if (settings.appendDropdownReset) { const dropdownReset = document.createElement(newFreshSpace ? 'div' : 'li'); dropdownReset.classList.add(newFreshSpace ? 'bili-card-dropdown-popper__item' : 'be-dropdown-item'); if (!newFreshSpace) { dropdownReset.classList.add('backup'); } dropdownReset.setAttribute('title', '如果该视频的本地备份数据出现错乱, 请使用此功能将其删除以便重新备份该视频。\n' + '如果您想删除脚本内所有已保存的数据, 请依次点击: Tampermonkey > 管理面板 >\n' + '哔哩哔哩(B站|Bilibili)收藏夹Fix (备份视频信息) > 开发者 > 重置到出厂。'); dropdownReset.textContent = '重置备份数据'; dropdownReset.addEventListener('click', () => { try { if (newFreshSpace) { dropdownContainer.classList.remove('visible'); } GM_deleteValue(BV); } catch (error) { catchUnknownError(error); } }); dropdownContainer.appendChild(dropdownReset); } } catch (error) { catchUnknownError(error); } } async function getFromApi(AVBVTitle, backup, spanA, apiDetails, fid, pageNumber, disabled, spanFavTime, searchType, getFromApiExtraNeeded) { if (!apiDetails.value) { const urlWithParams = await appendParamsForGetFromApi(fid, pageNumber, pageSize); const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: urlWithParams, timeout: 1000 * settings.requestTimeout, responseType: 'json', onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', 'api.bilibili.com/x/v3/fav/resource/list', res.error]), ontimeout: () => reject(['请求超时', 'api.bilibili.com/x/v3/fav/resource/list']) }); }); if (response.status !== 200) { throw ['请求失败', 'api.bilibili.com/x/v3/fav/resource/list', `${response.status} ${response.statusText}`]; } apiDetails.value = response.response.data.medias; } const apiDetail = apiDetails.value.find(el => el.bvid === AVBVTitle.BV); if (!apiDetail) { if (debug) { addMessage('getFromApi 从B站接口获取的数据中没有该视频', false, 'red'); addMessage(AVBVTitle.BV, true); console.warn('getFromApi 从B站接口获取的数据中没有该视频'); console.warn(AVBVTitle.BV); } return true; } if (!AVBVTitle.AV) { AVBVTitle.AV = apiDetail.id; backup.AV = apiDetail.id; } if (debug) { console.log('从B站接口获取数据'); consoleAVBVTitle('debug', AVBVTitle); console.debug(apiDetail); } if (!disabled) { updateArrayDataInBackup(backup, 'title', apiDetail.title, getCurrentTs(), 'api.bilibili.com/x/v3/fav/resource/list'); updateArrayDataInBackup(backup, 'cover', apiDetail.cover, getCurrentTs(), 'api.bilibili.com/x/v3/fav/resource/list'); } else { if (debug) { if (apiDetail.title !== '已失效视频') { addMessage('失效视频标题已更改', false, 'red'); console.warn('失效视频标题已更改'); } if (!apiDetail.cover.includes('be27fd62c99036dce67efface486fb0a88ffed06')) { addMessage('失效视频封面已更改', false, 'red'); console.warn('失效视频封面已更改'); } } } updateArrayDataInBackup(backup, 'intro', apiDetail.intro, getCurrentTs(), 'api.bilibili.com/x/v3/fav/resource/list'); if (!backup.upperUID) { backup.upperUID = apiDetail.upper.mid; } updateArrayDataInBackup(backup, 'upperName', apiDetail.upper.name, getCurrentTs(), 'api.bilibili.com/x/v3/fav/resource/list'); updateArrayDataInBackup(backup, 'upperAvatar', apiDetail.upper.face, getCurrentTs(), 'api.bilibili.com/x/v3/fav/resource/list'); backup.timeUpload = apiDetail.ctime; backup.timePublish = apiDetail.pubtime; if (!searchType) { updateTimeFavoriteInBackup(backup, apiDetail.fav_time, fid); } if (apiDetail.ugc && apiDetail.ugc.first_cid) { if (updatePagesInBackup(backup, 1, undefined, undefined, undefined, apiDetail.ugc.first_cid, getCurrentTs(), 'api.bilibili.com/x/v3/fav/resource/list')) { getFromApiExtraNeeded.value = true; } } else if (!disabled) { if (debug) { addMessage('getFromApi 无first_cid', false, 'red'); addMessage(AVBVTitle.BV, true); console.warn('getFromApi 无first_cid'); console.warn(AVBVTitle.BV); } } if (apiDetail.page) { if (apiDetail.page > 1) { getFromApiExtraNeeded.value = true; } } else if (!disabled) { if (debug) { addMessage('getFromApi 无page', false, 'red'); addMessage(AVBVTitle.BV, true); console.warn('getFromApi 无page'); console.warn(AVBVTitle.BV); } } if (!disabled && apiDetail.intro.length >= 255) { let getIntroNeeded = true; if (settings.getFromApiExtra) { if (!backup.apiExtra) { getIntroNeeded = false; } else if (settings.getFromApiExtraUpdate && settings.getFromApiExtraUpdateAll && getCurrentTs() - backup.apiExtra.ts > 3600 * 24 * settings.getFromApiExtraUpdateInterval) { getIntroNeeded = false; } else if (settings.getFromApiExtraUpdate && !settings.getFromApiExtraUpdateAll && getFromApiExtraNeeded.value) { getIntroNeeded = false; } } if (getIntroNeeded) { const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://api.bilibili.com/x/web-interface/archive/desc?bvid=${AVBVTitle.BV}`, timeout: 1000 * settings.requestTimeout, responseType: 'json', onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', 'api.bilibili.com/x/web-interface/archive/desc', res.error]), ontimeout: () => reject(['请求超时', 'api.bilibili.com/x/web-interface/archive/desc']) }); }); if (response.status !== 200) { throw ['请求失败', 'api.bilibili.com/x/web-interface/archive/desc', `${response.status} ${response.statusText}`]; } updateArrayDataInBackup(backup, 'intro', response.response.data, getCurrentTs(), 'api.bilibili.com/x/web-interface/archive/desc'); } } if (newFreshSpace) { spanFavTime.textContent = `投稿于:${formatTsTimePublish(1000 * apiDetail.pubtime)}`; spanFavTime.setAttribute('title', new Date(1000 * apiDetail.pubtime).toLocaleString()); } else { spanFavTime.textContent = `收藏于:${formatTsTimeFavorite(new Date(1000 * apiDetail.fav_time))}`; spanFavTime.setAttribute('title', new Date(1000 * apiDetail.fav_time).toLocaleString()); } if (disabled) { backup.api = { value: false, ts: getCurrentTs() }; spanA.style.color = '#ff0000'; } else { backup.api = { value: true, ts: getCurrentTs() }; spanA.style.color = '#00ff00'; } const sortedBackup = {}; for (const sortedKey of sortedKeys) { sortedBackup[sortedKey] = backup[sortedKey]; } GM_setValue(AVBVTitle.BV, sortedBackup); if (debug) { console.log('保存B站接口的数据至本地'); consoleAVBVTitle('debug', AVBVTitle); console.debug(sortedBackup); } } async function getFromApiExtra(AVBVTitle, backup, spanA, disabled) { if (disabled) { backup.apiExtra = { value: false, ts: getCurrentTs() }; spanA.style.color = '#800000'; } else { if (getFromApiExtraDelay) { await delay(500); } const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://api.bilibili.com/x/web-interface/view?bvid=${AVBVTitle.BV}`, timeout: 1000 * settings.requestTimeout, responseType: 'json', onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', 'api.bilibili.com/x/web-interface/view', res.error]), ontimeout: () => reject(['请求超时', 'api.bilibili.com/x/web-interface/view']) }); }); if (response.status !== 200) { throw ['请求失败', 'api.bilibili.com/x/web-interface/view', `${response.status} ${response.statusText}`]; } const currentTs = getCurrentTs(); updateArrayDataInBackup(backup, 'intro', response.response.data.desc, currentTs, 'api.bilibili.com/x/web-interface/view'); if (response.response.data.desc_v2) { let introWithUID = ''; for (const [index, desc] of response.response.data.desc_v2.entries()) { if (desc.type === 1) { introWithUID = introWithUID + desc.raw_text; } else if (desc.type === 2) { introWithUID = introWithUID + `@${desc.raw_text}{{UID:${desc.biz_id}}}` + (index === response.response.data.desc_v2.length - 1 ? '' : ' '); } else { if (debug) { addMessage(`getFromApiExtra desc_v2未知type: ${desc.type}`, false, 'red'); addMessage(AVBVTitle.BV, true); console.warn(`getFromApiExtra desc_v2未知type: ${desc.type}`); console.warn(AVBVTitle.BV); } } } updateArrayDataInBackup(backup, 'intro', introWithUID, currentTs, 'api.bilibili.com/x/web-interface/view'); } updateArrayDataInBackup(backup, 'dynamic', response.response.data.dynamic, getCurrentTs(), 'api.bilibili.com/x/web-interface/view'); response.response.data.pages.forEach(el => { updatePagesInBackup(backup, el.page, el.part, el.first_frame, undefined, el.cid, getCurrentTs(), 'api.bilibili.com/x/web-interface/view'); }); backup.apiExtra = { value: true, ts: getCurrentTs() }; spanA.style.color = '#008000'; } const sortedBackup = {}; for (const sortedKey of sortedKeys) { sortedBackup[sortedKey] = backup[sortedKey]; } GM_setValue(AVBVTitle.BV, sortedBackup); if (debug) { console.log('保存B站接口的额外数据至本地'); consoleAVBVTitle('debug', AVBVTitle); console.debug(sortedBackup); } } async function getFromApiExtraThumbnails(AVBVTitle, backup, as, controller) { let setValueNeeded = false; for (const [index, page] of backup.pages.entries()) { if (controller.signal.aborted) { throw new DOMException('', 'AbortError'); } if (!page.cid) { continue; } let getThumbnailsNeeded = true; if (page.thumbnails) { // for (const thumbnail of page.thumbnails) { // if (thumbnail.includes('videoshotpvhdboss')) { // getThumbnailsNeeded = false; // break; // } // } getThumbnailsNeeded = false; } if (getThumbnailsNeeded) { if (backup.pages.length > 10) { as[1].textContent = `获取缩略图${index + 1}/${backup.pages.length} | ${AVBVTitle.title}`; } const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://api.bilibili.com/x/player/videoshot?aid=${backup.AV}&cid=${page.cid}`, timeout: 1000 * settings.requestTimeout, responseType: 'json', onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', 'api.bilibili.com/x/player/videoshot', res.error]), ontimeout: () => reject(['请求超时', 'api.bilibili.com/x/player/videoshot']) }); }); if (response.status !== 200) { throw ['请求失败', 'api.bilibili.com/x/player/videoshot', `${response.status} ${response.statusText}`]; } if (!response.response.data.image || !response.response.data.image.length) { if (debug) { addMessage('getFromApiExtra 无法获取thumbnails', false, 'red'); addMessage(JSON.stringify(response.response), true); addMessage(`https://api.bilibili.com/x/player/videoshot?aid=${backup.AV}&cid=${page.cid}`, true); console.warn('getFromApiExtra 无法获取thumbnails'); console.warn(response.response.message); console.warn(`https://api.bilibili.com/x/player/videoshot?aid=${backup.AV}&cid=${page.cid}`); } continue; } response.response.data.image.forEach(el => { updatePagesInBackup(backup, undefined, undefined, undefined, el, page.cid, undefined, undefined); }); setValueNeeded = true; } } if (backup.pages.length > 10) { as[1].textContent = AVBVTitle.title; } if (setValueNeeded) { const sortedBackup = {}; for (const sortedKey of sortedKeys) { sortedBackup[sortedKey] = backup[sortedKey]; } GM_setValue(AVBVTitle.BV, sortedBackup); if (debug) { console.log('保存进度条缩略图地址至本地'); consoleAVBVTitle('debug', AVBVTitle); console.debug(sortedBackup); } } } async function getFromBiliplus(AVBVTitle, backup, spanB) { const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://www.biliplus.com/video/${AVBVTitle.BV}`, timeout: 1000 * settings.requestTimeout, onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', 'www.biliplus.com/video', res.error]), ontimeout: () => reject(['请求超时', 'www.biliplus.com/video']) }); }); let json; const getJsonFromBiliplusMatch = response.response.match(getJsonFromBiliplusRegex); if (getJsonFromBiliplusMatch) { json = JSON.parse(getJsonFromBiliplusMatch[1]); } if (!json) { if (debug) { addMessage('getFromBiliplus 获取数据为空', false, 'red'); addMessage(AVBVTitle.BV, true); console.warn('getFromBiliplus 获取数据为空'); console.warn(AVBVTitle.BV); } } if (json && json.title) { if (debug) { console.log('从BiliPlus获取有效数据'); consoleAVBVTitle('debug', AVBVTitle); console.debug(json); } if (!json.lastupdatets) { if (!json.lastupdate) { throw ['从BiliPlus获取的数据中无备份时间, 请反馈该问题']; } if (!localeTimeStringRegex.test(json.lastupdate)) { throw ['从BiliPlus获取的数据中备份时间不符合规范, 请反馈该问题']; } if (isNaN(new Date(json.lastupdate).getTime())) { throw ['从BiliPlus获取的数据中备份时间不符合规范, 请反馈该问题']; } json.lastupdatets = new Date(json.lastupdate).getTime() / 1000; } updateArrayDataInBackup(backup, 'title', json.title, json.lastupdatets, 'www.biliplus.com/video'); updateArrayDataInBackup(backup, 'intro', json.description, json.lastupdatets, 'www.biliplus.com/video'); updateArrayDataInBackup(backup, 'cover', json.pic, json.lastupdatets, 'www.biliplus.com/video'); updateArrayDataInBackup(backup, 'upperName', json.author, json.lastupdatets, 'www.biliplus.com/video'); if (Array.isArray(json.list)) { json.list.forEach(el => { updatePagesInBackup(backup, el.page, el.part, undefined, undefined, el.cid, json.lastupdatets, 'www.biliplus.com/video'); }); } if (json.v2_app_api) { updateArrayDataInBackup(backup, 'title', json.v2_app_api.title, json.lastupdatets, 'www.biliplus.com/video'); updateArrayDataInBackup(backup, 'intro', json.v2_app_api.desc, json.lastupdatets, 'www.biliplus.com/video'); updateArrayDataInBackup(backup, 'cover', json.v2_app_api.pic, json.lastupdatets, 'www.biliplus.com/video'); updateArrayDataInBackup(backup, 'upperName', json.v2_app_api.owner?.name, json.lastupdatets, 'www.biliplus.com/video'); // updateArrayDataInBackup(backup, 'upperAvatar', json.v2_app_api.owner.face, json.lastupdatets, 'www.biliplus.com/video'); // if (debug) { // if (json.v2_app_api.cid && json.v2_app_api.cid !== json.list[0].cid) { // addMessage('getFromBiliplus cid 不一致1', false, 'red'); // addMessage(json.v2_app_api.cid, true); // addMessage(json.list[0].cid, true); // console.warn('getFromBiliplus cid 不一致1'); // console.warn(json.v2_app_api.cid); // console.warn(json.list[0].cid); // } // if (json.v2_app_api.cid && json.v2_app_api.cid !== json.v2_app_api.pages[0].cid) { // addMessage('getFromBiliplus cid 不一致2', false, 'red'); // addMessage(json.v2_app_api.cid, true); // addMessage(json.v2_app_api.pages[0].cid, true); // console.warn('getFromBiliplus cid 不一致2'); // console.warn(json.v2_app_api.cid); // console.warn(json.v2_app_api.pages[0].cid); // } // if (json.list[0].cid !== json.v2_app_api.pages[0].cid) { // addMessage('getFromBiliplus cid 不一致3', false, 'red'); // addMessage(json.list[0].cid, true); // addMessage(json.v2_app_api.pages[0].cid, true); // console.warn('getFromBiliplus cid 不一致3'); // console.warn(json.list[0].cid); // console.warn(json.v2_app_api.pages[0].cid); // } // } // if (json.v2_app_api.first_frame !== json.v2_app_api.pages[0].first_frame) { // if (debug) { // addMessage('getFromBiliplus firstFrame 不一致', false, 'red'); // addMessage(json.v2_app_api.first_frame, true); // addMessage(json.v2_app_api.pages[0].first_frame, true); // console.warn('getFromBiliplus firstFrame 不一致'); // console.warn(json.v2_app_api.first_frame); // console.warn(json.v2_app_api.pages[0].first_frame); // } // updatePagesInBackup(backup, 0, undefined, json.v2_app_api.first_frame, undefined, 0, json.lastupdatets, 'www.biliplus.com/video'); // } updateArrayDataInBackup(backup, 'dynamic', json.v2_app_api.dynamic, json.lastupdatets, 'www.biliplus.com/video'); // json.v2_app_api.pages.forEach(el => { // updatePagesInBackup(backup, el.page, el.part, el.first_frame, undefined, el.cid, json.lastupdatets, 'www.biliplus.com/video'); // }); } backup.biliplus = { value: true, ts: getCurrentTs() }; spanB.style.color = '#00ff00'; } else { if (debug) { console.log('从BiliPlus获取无效数据'); consoleAVBVTitle('debug', AVBVTitle); console.debug(json); } backup.biliplus = { value: false, ts: getCurrentTs() }; spanB.style.color = '#ff0000'; } } async function getFromBiliplusExtra(AVBVTitle, backup, spanB, as, controller) { if (getFromBiliplusExtraCounter > 350) { as[1].textContent = `等待${getFromBiliplusExtraCounter - 350}秒 | ${AVBVTitle.title}`; await new Promise((resolve, reject) => { const timer = setInterval(() => { if (controller.signal.aborted) { clearInterval(timer); reject(new DOMException('', 'AbortError')); return; } if (getFromBiliplusExtraCounter <= 350) { clearInterval(timer); resolve(); return; } as[1].textContent = `等待${getFromBiliplusExtraCounter - 350}秒 | ${AVBVTitle.title}`; }, 1000); }); as[1].textContent = AVBVTitle.title; } const response1 = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://www.biliplus.com/all/video/av${AVBVTitle.AV}/`, timeout: 1000 * settings.requestTimeout, onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', 'www.biliplus.com/all/video', res.error]), ontimeout: () => reject(['请求超时', 'www.biliplus.com/all/video']) }); }); const paramsWithSign = response1.response.match(getParamsWithSignFromBiliplusRegex)[1]; const response2 = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://www.biliplus.com/api/view_all${paramsWithSign}`, timeout: 1000 * settings.requestTimeout, responseType: 'json', onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', 'www.biliplus.com/api/view_all', res.error]), ontimeout: () => reject(['请求超时', 'www.biliplus.com/api/view_all']) }); }); if (response2.response.code === -503) { getFromBiliplusExtraCounter = 372; throw ['从BiliPlus获取额外数据的频率过快']; } getFromBiliplusExtraCounter += 12; if (response2.response.code === 0) { if (debug) { console.log('从BiliPlus获取有效额外数据'); consoleAVBVTitle('debug', AVBVTitle); console.debug(response2.response); if (!backup.biliplus.value) { addMessage('getFromBiliplusExtra 无法获取数据但可获取额外数据', false, 'red'); addMessage(AVBVTitle.BV, true); console.warn('getFromBiliplusExtra 无法获取数据但可获取额外数据'); console.warn(AVBVTitle.BV); } } updateArrayDataInBackup(backup, 'title', response2.response.data.info.title, undefined, 'www.biliplus.com/api/view_all'); updateArrayDataInBackup(backup, 'intro', response2.response.data.info.description, undefined, 'www.biliplus.com/api/view_all'); updateArrayDataInBackup(backup, 'cover', response2.response.data.info.pic, undefined, 'www.biliplus.com/api/view_all'); updateArrayDataInBackup(backup, 'upperName', response2.response.data.info.author, undefined, 'www.biliplus.com/api/view_all'); response2.response.data.parts.forEach(el => { updatePagesInBackup(backup, el.page, el.part, undefined, undefined, el.cid, undefined, 'www.biliplus.com/api/view_all'); }); backup.biliplusExtra = { value: true, ts: getCurrentTs() }; spanB.style.color = '#008000'; } else if (response2.response.code === -404) { // if (debug) { // addMessage('getFromBiliplusExtra 无法获取额外数据', false, 'red'); // addMessage(AVBVTitle.BV, true); // console.warn('getFromBiliplusExtra 无法获取额外数据'); // console.warn(AVBVTitle.BV); // } backup.biliplusExtra = { value: false, ts: getCurrentTs() }; spanB.style.color = '#800000'; } else { if (debug) { addMessage('getFromBiliplusExtra 未知错误', false, 'red'); addMessage(JSON.stringify(response2.response), true); addMessage(AVBVTitle.BV, true); console.warn('getFromBiliplusExtra 未知错误'); console.warn(response2.response); console.warn(AVBVTitle.BV); } backup.biliplusExtra = { value: false, ts: getCurrentTs() }; spanB.style.color = '#800000'; } const sortedBackup = {}; for (const sortedKey of sortedKeys) { sortedBackup[sortedKey] = backup[sortedKey]; } GM_setValue(AVBVTitle.BV, sortedBackup); if (debug) { console.log('保存BiliPlus的额外数据至本地'); consoleAVBVTitle('debug', AVBVTitle); console.debug(sortedBackup); } } async function getFromJijidown(AVBVTitle, backup, spanJ) { let retryCount = 0; while (true) { const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://${settings.getFromJijidownURL}/api/v1/video_bv/get_info?id=${AVBVTitle.BV.slice(2)}`, timeout: 1000 * settings.requestTimeout, responseType: 'json', onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', `${settings.getFromJijidownURL}/api/v1/video_bv/get_info`, res.error]), ontimeout: () => reject(['请求超时', `${settings.getFromJijidownURL}/api/v1/video_bv/get_info`]) }); }); if (debug) console.debug(response); if (response.status !== 200) { throw ['请求失败', `${settings.getFromJijidownURL}/api/v1/video_bv/get_info`, `${response.status} ${response.statusText}`]; } if (response.response.upid > 0) { if (debug) { console.log('从唧唧获取有效数据'); consoleAVBVTitle('debug', AVBVTitle); console.debug(response.response); } updateArrayDataInBackup(backup, 'title', response.response.title, response.response.ltime, `${settings.getFromJijidownURL}/api/v1/video_bv/get_info`); if (response.response.desc) { updateArrayDataInBackup(backup, 'intro', decodeHTMLEntities(response.response.desc.replaceAll('<br/>', '\n').replaceAll('\r', '\\r')).replaceAll('\\r', '\r'), response.response.ltime, `${settings.getFromJijidownURL}/api/v1/video_bv/get_info`); } updateArrayDataInBackup(backup, 'cover', response.response.img, response.response.ltime, `${settings.getFromJijidownURL}/api/v1/video_bv/get_info`); if (response.response.up.id > 0) { updateArrayDataInBackup(backup, 'upperName', response.response.up.author, response.response.ltime, `${settings.getFromJijidownURL}/api/v1/video_bv/get_info`); updateArrayDataInBackup(backup, 'upperAvatar', response.response.up.avatar, response.response.ltime, `${settings.getFromJijidownURL}/api/v1/video_bv/get_info`); } backup.jijidown = { value: true, ts: getCurrentTs() }; spanJ.style.color = '#00ff00'; if (settings.getFromJijidownExtra && (!backup.jijidownExtra || (settings.getFromJijidownExtraUpdate && getCurrentTs() - backup.jijidownExtra.ts > 3600 * 24 * 7 * settings.getFromJijidownExtraUpdateInterval))) { await getFromJijidownExtra(AVBVTitle, backup, spanJ, response.response.ltime); } return; } else if (response.response.msg === 'loading') { retryCount++; if (debug) { console.log('从唧唧获取无效数据'); consoleAVBVTitle('debug', AVBVTitle); console.log(`请求重试次数: ${retryCount}`); console.debug(response.response); } if (retryCount > 4) { throw ['请求重试次数过多', `${settings.getFromJijidownURL}/api/v1/video_bv/get_info`]; } } else { if (debug) { console.log('从唧唧获取无效数据'); consoleAVBVTitle('debug', AVBVTitle); console.debug(response.response); } backup.jijidown = { value: false, ts: getCurrentTs() }; if (settings.getFromJijidownExtra && (!backup.jijidownExtra || (settings.getFromJijidownExtraUpdate && getCurrentTs() - backup.jijidownExtra.ts > 3600 * 24 * 7 * settings.getFromJijidownExtraUpdateInterval))) { backup.jijidownExtra = { value: false, ts: getCurrentTs() }; spanJ.style.color = '#800000'; } else { spanJ.style.color = '#ff0000'; } return; } await delay(600); } } async function getFromJijidownExtra(AVBVTitle, backup, spanJ, ts) { const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://${settings.getFromJijidownURL}/api/v1/video_bv/get_download_info?id=${AVBVTitle.BV.slice(2)}`, timeout: 1000 * settings.requestTimeout, responseType: 'json', onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', `${settings.getFromJijidownURL}/api/v1/video_bv/get_download_info`, res.error]), ontimeout: () => reject(['请求超时', `${settings.getFromJijidownURL}/api/v1/video_bv/get_download_info`]) }); }); if (debug) console.debug(response); if (response.status !== 200) { throw ['请求失败', `${settings.getFromJijidownURL}/api/v1/video_bv/get_download_info`, `${response.status} ${response.statusText}`]; } if (response.response.res && response.response.res.length) { if (debug) { console.log('从唧唧获取有效额外数据'); consoleAVBVTitle('debug', AVBVTitle); console.debug(response.response); } response.response.res.forEach(el => { updatePagesInBackup(backup, 0, el.part, undefined, undefined, el.cid, ts, `${settings.getFromJijidownURL}/api/v1/video_bv/get_download_info`); }); backup.jijidownExtra = { value: true, ts: getCurrentTs() }; spanJ.style.color = '#008000'; } else { if (debug) { console.log('从唧唧获取无效额外数据'); consoleAVBVTitle('debug', AVBVTitle); console.debug(response.response); } backup.jijidownExtra = { value: false, ts: getCurrentTs() }; spanJ.style.color = '#800000'; } } async function getFromXbeibeix(AVBVTitle, backup, spanX) { const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://${settings.getFromXbeibeixURL}/video/${AVBVTitle.BV}`, timeout: 1000 * settings.requestTimeout, onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', `${settings.getFromXbeibeixURL}/video`, res.error]), ontimeout: () => reject(['请求超时', `${settings.getFromXbeibeixURL}/video`]) }); }); if (debug) console.debug(response); if (response.status !== 200) { throw ['请求失败', `${settings.getFromXbeibeixURL}/video`, `${response.status} ${response.statusText}`]; } if (response.finalUrl !== `https://${settings.getFromXbeibeixURL}/`) { if (debug) { console.log('从贝贝工具站获取有效数据'); consoleAVBVTitle('debug', AVBVTitle); console.debug(response); } if (!response.responseXML) { throw ['请您使用版本为 5.3.0 及以上的 <a href="https://www.tampermonkey.net/" target="_blank" style="text-decoration-line: underline;">Tampermonkey</a> 运行此脚本']; } updateArrayDataInBackup(backup, 'title', response.responseXML.querySelector('h5.fw-bold').innerText, undefined, `${settings.getFromXbeibeixURL}/video`); if (response.responseXML.querySelector('div.col-8 > textarea').value) { updateArrayDataInBackup(backup, 'intro', decodeHTMLEntities(response.responseXML.querySelector('div.col-8 > textarea').value), undefined, `${settings.getFromXbeibeixURL}/video`); } updateArrayDataInBackup(backup, 'cover', response.responseXML.querySelector('div.col-4 > img').getAttribute('src'), undefined, `${settings.getFromXbeibeixURL}/video`); updateArrayDataInBackup(backup, 'upperName', response.responseXML.querySelector('div.input-group.mb-2 > input').value, undefined, `${settings.getFromXbeibeixURL}/video`); backup.xbeibeix = { value: true, ts: getCurrentTs() }; spanX.style.color = '#00ff00'; } else { if (debug) { console.log('从贝贝工具站获取无效数据'); consoleAVBVTitle('debug', AVBVTitle); console.debug(response); } backup.xbeibeix = { value: false, ts: getCurrentTs() }; spanX.style.color = '#ff0000'; } } function addMessage(msg, smallFontSize, border) { let px; if (smallFontSize) { px = newFreshSpace ? 11 : 10; } else { px = newFreshSpace ? 13 : 12; } const p = document.createElement('p'); p.innerHTML = msg; p.style.fontSize = `${px}px`; if (border === 'red') { p.style.borderTop = '1px solid #800000'; } else if (border === 'green') { p.style.borderTop = '1px solid #008000'; } divMessage.appendChild(p); if (divMessageHeightFixed) { divMessage.scrollTop = divMessage.scrollHeight; } else { if (newFreshSpace) { if (divMessage.scrollHeight > 320) { divMessage.classList.add('backup-divMessage-heightFixed-newFreshSpace'); divMessageHeightFixed = true; divMessage.scrollTop = divMessage.scrollHeight; } } else { if (divMessage.scrollHeight > 280) { divMessage.classList.add('backup-divMessage-heightFixed'); divMessageHeightFixed = true; divMessage.scrollTop = divMessage.scrollHeight; } } } divMessage.scrollIntoView({ behavior: 'instant', block: 'nearest' }); } function clearMessage() { while (divMessage.firstChild) { divMessage.removeChild(divMessage.firstChild); } divMessage.classList.remove('backup-divMessage-heightFixed' + classAppendNewFreshSpace); divMessageHeightFixed = false; } function addMessageAVBVTitle(AVBVTitle) { addMessage(`AV号: ${AVBVTitle.AV}`, true); addMessage(`BV号: ${AVBVTitle.BV}`, true); if (AVBVTitle.title) { addMessage(`标题: ${AVBVTitle.title.slice(0, 13)}`, true); } } function consoleAVBVTitle(type, AVBVTitle) { switch (type) { case 'debug': console.debug(`AV号: ${AVBVTitle.AV}`); console.debug(`BV号: ${AVBVTitle.BV}`); console.debug(`标题: ${AVBVTitle.title.slice(0, 13)}`); break; case 'log': console.log(`AV号: ${AVBVTitle.AV}`); console.log(`BV号: ${AVBVTitle.BV}`); console.log(`标题: ${AVBVTitle.title.slice(0, 13)}`); break; case 'warn': console.warn(`AV号: ${AVBVTitle.AV}`); console.warn(`BV号: ${AVBVTitle.BV}`); console.warn(`标题: ${AVBVTitle.title.slice(0, 13)}`); break; case 'error': console.error(`AV号: ${AVBVTitle.AV}`); console.error(`BV号: ${AVBVTitle.BV}`); if (AVBVTitle.title) { console.error(`标题: ${AVBVTitle.title.slice(0, 13)}`); } break; default: throw Error('invalid type'); } } function catchUnknownError(error) { addMessage('发生未知错误, 请反馈该问题', false, 'red'); addMessage(error.stack, true); console.error(error); } function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function getCurrentTs() { return Math.floor(Date.now() / 1000); } function abortActiveControllers() { for (const controller of activeControllers) { controller.abort(); } } function formatTsTimeFavorite(t) { const e = new Date(); const n = e.getTime(); const r = t.getTime(); const o = n - r; return o < 6e4 ? '刚刚' : o < 36e5 ? Math.floor(o / 6e4) + '分钟前' : o < 864e5 ? Math.floor(o / 36e5) + '小时前' : r >= new Date(e.getFullYear(), e.getMonth(), e.getDate() - 1).getTime() ? '昨天' : r >= new Date(e.getFullYear(), 0, 1).getTime() ? (t.getMonth() + 1) + '-' + t.getDate() // : o < 63072e6 // ? t.getFullYear() + '-' + (t.getMonth() + 1) + '-' + t.getDate() // : '2年前'; : t.getFullYear() + '-' + (t.getMonth() + 1) + '-' + t.getDate(); } function formatTsYYMMDD_HHMMSS(ts) { const date = new Date(1000 * ts); const year = String(date.getFullYear()).slice(2); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); return `${year}${month}${day}_${hours}${minutes}${seconds}`; } function formatTsTimePublish(e) { const n = new Date(e); const u = Date.now(); if (u - e <= 6e4) { return '刚刚'; } if (u - e < 36e5) { return Math.floor((u - e) / 6e4) + '分钟前'; } if (u - e < 864e5) { return Math.floor((u - e) / 36e5) + '小时前'; } if (new Date().setHours(0, 0, 0, 0) - e < 864e5) { return '昨天'; } const l = n.getFullYear(); const c = '0'.concat(n.getMonth() + 1).slice(-2); const f = '0'.concat(n.getDate()).slice(-2); return l === new Date().getFullYear() ? ''.concat(c, '-').concat(f) : ''.concat(l, '-').concat(c, '-').concat(f); } function decodeHTMLEntities(str) { return new DOMParser().parseFromString(`<!doctype html><body>${str}`, 'text/html').body.textContent; } function updateArrayDataInBackup(backup, key, value, ts, from) { if (!value) { return; } if (!ts) { ts = 0; } if (key === 'cover' || key === 'upperAvatar') { value = 'https://' + value.replace(getHttpsFromURLRegex, '').replace(getAvifFromURLRegex, ''); } if (!backup[key]) { backup[key] = []; const data = { value, ts, from }; backup[key].push(data); return; } let target; target = backup[key].find(el => el.value === value); if (target) { if (target.ts <= ts) { target.ts = ts; target.from = from; backup[key].sort((a, b) => a.ts - b.ts); } return; } if (key === 'cover' || key === 'upperAvatar') { target = backup[key].find(el => el.value.match(getFilenameFromURLRegex)[0] === value.match(getFilenameFromURLRegex)[0]); if (target) { if (target.ts <= ts) { target.value = value; target.ts = ts; target.from = from; backup[key].sort((a, b) => a.ts - b.ts); } return; } } else if (key === 'intro') { target = backup.intro.find(el => el.value.replaceAll('\r', '') === value); if (target) { return; } target = backup.intro.find(el => el.value === value.replaceAll('\r', '')); if (target) { target.value = value; target.ts = ts; target.from = from; backup.intro.sort((a, b) => a.ts - b.ts); return; } if (value.length >= 255 || ((from.includes('xbeibeix') || from.includes('bbdownloader') || from.includes('beibeigame')) && value.length >= 200)) { target = backup.intro.find(el => el.value.replaceAll('\r', '').startsWith(value.replaceAll('\r', ''))); if (target) { return; } target = backup.intro.find(el => value.replaceAll('\r', '').startsWith(el.value.replaceAll('\r', ''))); if (target) { target.value = value; target.ts = ts; target.from = from; backup.intro.sort((a, b) => a.ts - b.ts); return; } } } const data = { value, ts, from }; backup[key].push(data); backup[key].sort((a, b) => a.ts - b.ts); } function updateTimeFavoriteInBackup(backup, value, fid) { if (!value) { value = 0; } if (!backup.timeFavorite) { backup.timeFavorite = []; const data = { value, fid }; backup.timeFavorite.push(data); return true; } else { const target = backup.timeFavorite.find(el => el.fid === fid); if (target) { if (target.value < value) { target.value = value; backup.timeFavorite.sort((a, b) => a.value - b.value); return true; } } else { const data = { value, fid }; backup.timeFavorite.push(data); backup.timeFavorite.sort((a, b) => a.value - b.value); return true; } } } function updatePagesInBackup(backup, index, title, firstFrame, thumbnails, cid, ts, from) { if (!index && index !== 0) { index = null; } if (title) { if (!Array.isArray(title)) { title = [title]; } } else { title = null; } if (firstFrame) { firstFrame = 'https://' + firstFrame.replace(getHttpsFromURLRegex, '').replace(getAvifFromURLRegex, ''); } else { firstFrame = null; } if (thumbnails) { if (!Array.isArray(thumbnails)) { thumbnails = ['https://' + thumbnails.replace(getHttpsFromURLRegex, '').replace(getAvifFromURLRegex, '')]; } else { thumbnails = thumbnails.map(el => 'https://' + el.replace(getHttpsFromURLRegex, '').replace(getAvifFromURLRegex, '')); } } else { thumbnails = null; } if (!cid && cid !== 0) { cid = null; } if (!ts) { ts = 0; } if (!from) { from = null; } if (!backup.pages) { backup.pages = []; const data = { index, title, firstFrame, thumbnails, cid, ts, from }; backup.pages.push(data); return true; } else { const target = backup.pages.find(el => el.cid === cid); if (target) { let modified = false; if (index) { if (!target.index) { target.index = index; modified = true; } else if (target.index !== index && target.ts <= ts) { target.index = index; modified = true; } } if (title) { if (!target.title) { target.title = title; modified = true; } else { title.forEach(el => { if (!target.title.find(ele => ele === el)) { target.title.push(el); modified = true; } }); } } if (firstFrame) { if (!target.firstFrame) { target.firstFrame = firstFrame; modified = true; } else if (target.firstFrame !== firstFrame) { if (debug) { addMessage('updatePagesInBackup firstFrame 不一致', false, 'red'); addMessage(`旧: ${target.firstFrame}`, true); addMessage(`新: ${firstFrame}`, true); console.warn('updatePagesInBackup firstFrame 不一致'); console.warn(`旧: ${target.firstFrame}`); console.warn(`新: ${firstFrame}`); } // target.firstFrame = firstFrame; // modified = true; } } if (thumbnails) { if (!target.thumbnails) { target.thumbnails = thumbnails; modified = true; } else { thumbnails.forEach(el => { if (!target.thumbnails.find(ele => ele === el)) { target.thumbnails.push(el); modified = true; } }); } } if (target.ts <= ts) { target.ts = ts; modified = true; } if (modified) { if (from) { target.from = from; } backup.pages.sort((a, b) => { if (a.index !== b.index) { return a.index - b.index; } else if (a.ts !== b.ts) { return a.ts - b.ts; } else { return a.cid - b.cid; } }); } } else { const data = { index, title, firstFrame, thumbnails, cid, ts, from }; backup.pages.push(data); backup.pages.sort((a, b) => { if (a.index !== b.index) { return a.index - b.index; } else if (a.ts !== b.ts) { return a.ts - b.ts; } else { return a.cid - b.cid; } }); return true; } } } async function appendParamsForGetFromApi(fid, pageNumber, pageSize) { const inputKeyword = document.querySelector(newFreshSpace ? 'input.fav-list-header-filter__search' : 'input.search-fav-input'); let keyword = ''; if (inputKeyword) { keyword = encodeURIComponent(inputKeyword.value); } if (debug) { console.log(`keyword: ${decodeURIComponent(keyword)}`); console.log(keyword); } let divFilterOrder; let divTid; if (!newFreshSpace) { const divDropdownFilterItems = document.querySelectorAll('div.fav-filters > div.be-dropdown.filter-item'); if (divDropdownFilterItems.length === 2) { divFilterOrder = divDropdownFilterItems[1].querySelector('span'); divTid = divDropdownFilterItems[0].querySelector('span'); } else if (divDropdownFilterItems.length === 1) { divFilterOrder = divDropdownFilterItems[0].querySelector('span'); divTid = null; } else { divFilterOrder = null; divTid = null; } } if (!newFreshSpace) { let orderText = '收藏'; if (divFilterOrder) { orderText = divFilterOrder.innerText; } if (orderText.includes('收藏')) { order = 'mtime'; } else if (orderText.includes('播放')) { order = 'view'; } else if (orderText.includes('投稿')) { order = 'pubtime'; } else { throw ['无法确定各个视频的排序方式, 请反馈该问题']; } } if (debug) console.log(`order: ${order}`); const divType = document.querySelector(newFreshSpace ? 'div.vui_input__prepend' : 'div.search-types'); let typeText = '当前'; if (divType) { typeText = divType.innerText; } if (!keyword) { typeText = '当前'; } if (debug) console.log(`typeText: ${typeText}`); let type; if (typeText.includes('当前')) { type = 0; } else if (typeText.includes('全部')) { type = 1; } else { throw ['无法确定搜索的范围为当前收藏夹还是全部收藏夹, 请反馈该问题']; } if (debug) console.log(`type: ${type}`); if (newFreshSpace) { divTid = document.querySelector('div.fav-list-header-collapse div.radio-filter__item--active'); } let tidText = '全部分区'; if (divTid) { tidText = divTid.innerText; } if (debug) console.log(`tidText: ${tidText}`); let tid; if (tidText.includes('全部')) { tid = 0; } else { const UID = parseInt(location.href.match(getUIDFromURLRegex)[1], 10); const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://api.bilibili.com/x/v3/fav/resource/partition?up_mid=${UID}&media_id=${fid}` + (newFreshSpace ? '&web_location=333.1387' : ''), timeout: 1000 * settings.requestTimeout, responseType: 'json', onload: (res) => resolve(res), onerror: (res) => reject(['请求失败', 'api.bilibili.com/x/v3/fav/resource/partition', res.error]), ontimeout: () => reject(['请求超时', 'api.bilibili.com/x/v3/fav/resource/partition']) }); }); if (response.status !== 200) { throw ['请求失败', 'api.bilibili.com/x/v3/fav/resource/partition', `${response.status} ${response.statusText}`]; } const target = response.response.data.find(el => tidText.includes(el.name)); if (target) { tid = target.tid; } else { throw ['无法确定选择的分区, 请反馈该问题']; } } if (debug) console.log(`tid: ${tid}`); if (debug) console.log(`https://api.bilibili.com/x/v3/fav/resource/list?media_id=${fid}&pn=${pageNumber}&ps=${pageSize}&keyword=${keyword}&order=${order}&type=${type}&tid=${tid}&platform=web` + (newFreshSpace ? '&web_location=333.1387' : '')); return (`https://api.bilibili.com/x/v3/fav/resource/list?media_id=${fid}&pn=${pageNumber}&ps=${pageSize}&keyword=${keyword}&order=${order}&type=${type}&tid=${tid}&platform=web` + (newFreshSpace ? '&web_location=333.1387' : '')); } function validateInputText(event) { try { const inputText = event.target; let value = inputText.value.trim(); const def = parseInt(inputText.getAttribute('backup-def'), 10); if (!value || isNaN(value)) { value = def; } else { value = parseInt(value, 10); const min = parseInt(inputText.getAttribute('backup-min'), 10); const max = parseInt(inputText.getAttribute('backup-max'), 10); if (value < min) { value = min; } else if (value > max) { value = max; } } inputText.value = value; const key = inputText.getAttribute('backup-setting'); settings[key] = value; GM_setValue('settings', settings); } catch (error) { catchUnknownError(error); } } function removeTsFromInBackup(obj) { if (Array.isArray(obj)) { obj.forEach(el => removeTsFromInBackup(el)); } else if (obj && typeof obj === 'object') { for (const key in obj) { if (key === 'ts' || key === 'from') { delete obj[key]; } else { removeTsFromInBackup(obj[key]); } } } } function formatBackup(backup, setValue, BV) { let modified = false; // v9 if (backup.timeFavorite) { backup.timeFavorite.forEach(el => { if (typeof el.fid === 'string') { el.fid = parseInt(el.fid, 10); modified = true; } }); } // v9 if (Array.isArray(backup.timeUpload)) { if (backup.timeUpload.length === 1) { backup.timeUpload = backup.timeUpload[0].value; } else { backup.timeUpload = null; } modified = true; } // v9 if (Array.isArray(backup.timePublish)) { if (backup.timePublish.length === 1) { backup.timePublish = backup.timePublish[0].value; } else { backup.timePublish = null; } modified = true; } // v10 if (backup.hasOwnProperty('jiji')) { backup.jijidown = backup.jiji; delete backup.jiji; modified = true; } // v10 if (backup.hasOwnProperty('bbdownloader')) { backup.xbeibeix = backup.bbdownloader; delete backup.bbdownloader; modified = true; } // v14 if (backup.hasOwnProperty('firstFrame')) { delete backup.firstFrame; modified = true; } // v16 if (backup.intro) { const tempBackup = { intro: null }; backup.intro.forEach(el => { updateArrayDataInBackup(tempBackup, 'intro', el.value, el.ts, el.from); }); backup.intro = tempBackup.intro; modified = true; } // v21 ['cover', 'upperAvatar'].forEach(key => { if (backup[key]) { const tempBackup = {}; backup[key].forEach(el => { updateArrayDataInBackup(tempBackup, key, el.value, el.ts, el.from); }); backup[key] = tempBackup[key]; modified = true; } }); // v21 if (backup.pages) { const tempBackup = {}; backup.pages.forEach(el => { updatePagesInBackup(tempBackup, el.index, el.title, el.firstFrame, el.thumbnails, el.cid, el.ts, el.from); }); backup.pages = tempBackup.pages; modified = true; } if (setValue && modified) { GM_setValue(BV, backup); } } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址