// ==UserScript== // @name Bangumi 隐藏NSFW条目 // @version 3.1 // @description 隐藏NSFW条目 // @author 墨云 // @match https://bangumi.tv/* // @match https://chii.in/* // @match https://bgm.tv/* // @grant none // @namespace https://greasyfork.org/users/1354622 // @downloadURL none // ==/UserScript==   (function() {     'use strict';   if (window.location.pathname.startsWith('/subject/')) { return; }     const SETTING_KEY = 'bangumi_hide_nsfw_mode';   const CACHE_EXPIRY = 7 * 24 * 60 * 60 * 1000;     async function isSubjectChecked(subjectId) { const cacheKey = `nsfw_cache_${subjectId}`; const cachedData = localStorage.getItem(cacheKey); if (cachedData) { try { const data = JSON.parse(cachedData); if (Date.now() < data.timestamp + CACHE_EXPIRY) { return data.value; } } catch (e) { localStorage.removeItem(cacheKey); } }         try {             const response = await fetch(`/subject/${subjectId}/edit_detail`);             const text = await response.text(); const isChecked = text.includes('checked="checked"'); const dataToCache = { value: isChecked, timestamp: Date.now() }; localStorage.setItem(cacheKey, JSON.stringify(dataToCache));             return isChecked;         } catch (error) {             console.error('Failed to fetch subject detail:', error); const dataToCache = { value: false, timestamp: Date.now() }; localStorage.setItem(cacheKey, JSON.stringify(dataToCache));             return false;         }     } function getSubjectIdFromLink(link) { const url = link.href; if (url.startsWith('https://bangumi.tv/subject/')) { return url.split('/')[4]; } else if (url.startsWith('/subject/')) { return url.split('/')[2]; } return null; }     async function applyMode(mode) {         const listItems = document.querySelectorAll('li > a[href^="/subject/"], li.tml_item a[href^="https://bangumi.tv/subject/"], #user_home li a[href^="https://bangumi.tv/subject/"], #columnSubjectBrowserA .mainItem a[href^="https://bangumi.tv/subject/"], #columnSubjectBrowserA .mainItem a[href^="/subject/"]'); const promises = []; for (const item of listItems) { const subjectId = getSubjectIdFromLink(item); if (subjectId) { promises.push(isSubjectChecked(subjectId).then(isChecked => { if (isChecked) { let containerToHide; if (item.closest('#columnSubjectBrowserA')) { containerToHide = item.closest('.mainItem'); } else { containerToHide = item.closest('li'); } if (containerToHide) { containerToHide.style.display = (mode === 'hide') ? 'none' : ''; } } })); } } await Promise.all(promises);     }       function addNSFWSetting() {         chiiLib.ukagaka.addGeneralConfig({             title: 'NSFW',             name: 'nsfwMode',             type: 'radio',             defaultValue: 'off',             getCurrentValue: function() { return $.cookie(SETTING_KEY) || 'off'; },             onChange: function(value) {                 $.cookie(SETTING_KEY, value, {expires: 30, path: '/'});                 applyMode(value);             },             options: [                 { value: 'off', label: '关闭' },                 { value: 'hide', label: '隐藏' }             ]         });     }       async function init() {         if (document.readyState === 'loading') {             document.addEventListener('DOMContentLoaded', addNSFWSetting);         } else {             addNSFWSetting();         }           const currentMode = $.cookie(SETTING_KEY) || 'off';         await applyMode(currentMode);           const observer = new MutationObserver((mutationsList) => {             for (const mutation of mutationsList) {                 if (mutation.type === 'childList') {                     mutation.addedNodes.forEach((node) => {                         if (node.nodeType === 1 && node.querySelector('a[href^="/subject/"], a[href^="https://bangumi.tv/subject/"]')) { console.log('检测到新条目加载,正在应用设置...');                             applyMode($.cookie(SETTING_KEY) || 'off');                         }                     });                 }             }         });         observer.observe(document.body, { childList: true, subtree: true });     }       init(); })();