Greasy Fork is available in English.
隐藏 Bangumi 上的 NSFW 条目
// ==UserScript==
// @name Bangumi 隐藏NSFW条目
// @version 3.3
// @description 隐藏 Bangumi 上的 NSFW 条目
// @author 墨云
// @match https://bangumi.tv/*
// @match https://chii.in/*
// @match https://bgm.tv/*
// @grant none
// @namespace http://greasyfork.icu/users/1354622
// ==/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}`);
const text = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(text, 'text/html');
const isChecked = !!doc.querySelector('a[href*="/tag/R18"]');
if (!isChecked) {
console.warn(`R18 tag not found on page for subjectId: ${subjectId}`);
}
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, 'for subjectId:', subjectId);
const dataToCache = {
value: false,
timestamp: Date.now()
};
localStorage.setItem(cacheKey, JSON.stringify(dataToCache));
return false;
}
}
function getSubjectIdFromLink(link) {
const url = link.href;
const match = url.match(/\/subject\/(\d+)/);
return match ? match[1] : null;
}
async function applyMode(mode, rootElement = document) {
const links = rootElement.querySelectorAll('a[href*="/subject/"]');
const promises = [];
for (const link of links) {
const subjectId = getSubjectIdFromLink(link);
if (subjectId) {
promises.push(isSubjectChecked(subjectId).then(isChecked => {
if (isChecked) {
let containerToHide = link.closest('.mainItem, .subject_grid_list > li, .item, .subject-item, .clearit, .info_item, li.line_list, li.clearit, .home_subject_list > li, .list > li, .grid > li');
if (!containerToHide) {
containerToHide = link.closest('[id^="item_"]');
}
if (!containerToHide) {
containerToHide = link.closest('li');
}
if (!containerToHide) {
console.warn('Could not find a container to hide for the link:', link.href);
}
if (containerToHide) {
containerToHide.style.display = (mode === 'hide') ? 'none' : '';
}
}
}));
}
}
await Promise.all(promises);
}
function addNSFWSetting() {
if (typeof chiiLib === 'undefined' || typeof chiiLib.ukagaka === 'undefined') {
return;
}
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();
const currentMode = $.cookie(SETTING_KEY) || 'off';
applyMode(currentMode);
});
} else {
addNSFWSetting();
const currentMode = $.cookie(SETTING_KEY) || 'off';
applyMode(currentMode);
}
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE && node.querySelector('a[href*="/subject/"]')) {
console.log('Detected new entries being loaded. Applying settings...');
applyMode($.cookie(SETTING_KEY) || 'off', node);
}
});
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
init();
})();