// ==UserScript==
// @name DBD-RawsBanHelper
// @namespace http://tampermonkey.net/
// @version 2.3
// @description 过滤动漫花园、末日动漫、Nyaa和蜜柑计划中的DBD-Raws内容,并修复行颜色问题
// @description 2.3更新内容:发现蜜柑计划番剧详情页面(https://mikanani.kas.pub/Home/Bangumi/XXXX)中也有残留
// @author Fuck DBD-Raws
// @license MIT
// @match *://share.dmhy.org/*
// @match *://share.acgnx.se/*
// @match *://nyaa.land/*
// @match *://nyaa.si/*
// @match *://mikanani.me/*
// @match *://mikanani.kas.pub/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
console.log('DBD-RawsBanHelper: 脚本开始执行');
// 配置参数
const config = {
targetKeywords: ['DBD-Raws', 'DBD制作组', 'DBD製作組','DBD转发','DBD轉發','DBD-SUB','DBD字幕组','DBD字幕組','DBD代发','DBD代發','DBD代传','DBD代傳','DBD转载','DBD轉載','DBD自购','DBD自購','DBD&','&DBD','[DBD]'], // 要过滤的关键词列表
filterClass: 'dmhy-filtered', // 用于标记已过滤元素的类名
showNotification: true, // 是否显示过滤通知
notificationDuration: 3000 // 通知显示时间(毫秒)
};
// 添加通知相关的CSS样式
const style = document.createElement('style');
style.textContent = `
#dmhy-filter-notification {
position: fixed;
top: 20px;
right: 20px;
background: #ff4757;
color: white;
padding: 12px 18px;
border-radius: 4px;
z-index: 10000;
font-family: Arial, sans-serif;
font-size: 14px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
transition: opacity 0.5s ease;
cursor: pointer;
}
#dmhy-filter-notification:hover {
opacity: 1 !important;
}
/* 蜜柑计划专用样式 */
.mikanani-notification {
top: 70px !important;
z-index: 9999;
}
`;
document.head.appendChild(style);
// 全局变量用于存储通知的定时器
let notificationTimer = null;
let hasExecutedStaticFilter = false; // 标记静态页面是否已执行过滤
let frameObservers = new Map(); // 存储各个frame的Observer实例
// 检查文本是否包含任何目标关键词,并返回匹配到的关键词
function containsTargetText(text) {
const matchedKeywords = [];
config.targetKeywords.forEach(keyword => {
if (text.includes(keyword)) {
matchedKeywords.push(keyword);
}
});
return matchedKeywords.length > 0 ? matchedKeywords : false;
}
// 检查是否为蜜柑计划网站
function isMikananiSite() {
return window.location.hostname.includes('mikanani') ||
window.location.hostname.includes('mikanime');
}
// 检查是否为蜜柑计划列表模式
function isMikananiClassicMode() {
return window.location.href.includes('/Home/Classic');
}
// 检查是否为动态加载页面(仅mikanani.kas.pub)
function isDynamicPage() {
return window.location.hostname === 'mikanani.kas.pub' && !window.location.href.includes('/Home');
}
// 等待页面加载完成
window.addEventListener('load', function() {
console.log('DBD-RawsBanHelper: 页面加载完成,开始初始化');
// 延迟执行以确保所有内容都已加载
setTimeout(() => {
if (isDynamicPage()) {
console.log('DBD-RawsBanHelper: 检测到动态加载页面,设置观察器');
setupDynamicPageObserver();
} else {
console.log('DBD-RawsBanHelper: 检测到静态页面,执行一次过滤');
filterContent();
hasExecutedStaticFilter = true;
}
}, 1000);
});
// 设置动态页面观察器
function setupDynamicPageObserver() {
// 先执行一次初始过滤
filterContent();
// 设置点击事件监听器来监听新的frame加载
setupClickListeners();
// 观察已存在的frame
observeExistingFrames();
// 设置全局观察器来监听新frame的添加
setupGlobalObserver();
}
// 设置点击事件监听器
function setupClickListeners() {
// 监听an-box animated中的li标签点击
document.addEventListener('click', function(e) {
const liElement = e.target.closest('.an-box.animated li');
if (liElement) {
console.log('DBD-RawsBanHelper: 检测到an-box animated中的li标签点击');
// 设置一个延迟来等待可能的frame加载,然后开始观察
setTimeout(() => {
observeExistingFrames();
}, 100);
}
});
}
// 观察已存在的frame
function observeExistingFrames() {
const frames = document.querySelectorAll('.row.an-res-row-frame');
console.log(`DBD-RawsBanHelper: 找到 ${frames.length} 个frame元素`);
frames.forEach((frame, index) => {
if (!frameObservers.has(frame)) {
observeFrameContent(frame);
}
});
}
// 观察单个frame的内容变化
function observeFrameContent(frame) {
// 如果已经在这个frame上设置了观察器,则跳过
if (frameObservers.has(frame)) {
return;
}
// 先立即执行一次过滤
filterFrameContent(frame);
// 创建MutationObserver来监听frame内部的内容变化
const observer = new MutationObserver(function(mutations) {
let shouldFilter = false;
mutations.forEach(function(mutation) {
if (mutation.type === 'childList') {
// 检查是否有新的子元素添加
if (mutation.addedNodes.length > 0) {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1) { // Element node
// 检查是否添加了包含资源信息的元素
if (node.classList && (
node.classList.contains('sk-col') ||
node.classList.contains('tag-res-name') ||
node.classList.contains('anime-res-block') ||
node.tagName.toLowerCase() === 'li' ||
node.tagName.toLowerCase() === 'div'
)) {
shouldFilter = true;
} else if (node.querySelectorAll) {
// 检查子元素中是否包含资源相关元素
const resourceElements = node.querySelectorAll('.sk-col, .tag-res-name, .anime-res-block, li, div');
if (resourceElements.length > 0) {
shouldFilter = true;
}
}
}
});
}
}
});
if (shouldFilter) {
console.log('DBD-RawsBanHelper: 检测到frame内容变化,执行过滤');
// 使用防抖机制,避免频繁过滤
clearTimeout(frame.filterTimeout);
frame.filterTimeout = setTimeout(() => {
filterFrameContent(frame);
}, 100);
}
});
// 开始观察frame内部的变化
observer.observe(frame, {
childList: true,
subtree: true,
characterData: true
});
// 存储观察器实例
frameObservers.set(frame, observer);
}
// 设置全局观察器来监听新frame的添加
function setupGlobalObserver() {
const globalObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1) {
// 检查是否添加了新的frame
if (node.classList && node.classList.contains('row') && node.classList.contains('an-res-row-frame')) {
console.log('DBD-RawsBanHelper: 检测到新frame添加');
// 给新frame一点时间加载内容
setTimeout(() => {
observeFrameContent(node);
}, 200);
}
// 检查子元素中是否包含新的frame
const newFrames = node.querySelectorAll ? node.querySelectorAll('.row.an-res-row-frame') : [];
newFrames.forEach(newFrame => {
console.log('DBD-RawsBanHelper: 检测到子元素中的新frame');
setTimeout(() => {
observeFrameContent(newFrame);
}, 200);
});
}
});
}
});
});
globalObserver.observe(document.body, {
childList: true,
subtree: true
});
console.log('DBD-RawsBanHelper: 全局观察器已启动');
}
// 过滤单个frame的内容
function filterFrameContent(frame) {
let removedCount = 0;
let matchedKeywords = new Set();
// 查找frame中的所有资源元素
const resourceElements = frame.querySelectorAll('div.sk-col.tag-res-name, li, a, span, .anime-res-block, .resource-item');
resourceElements.forEach(element => {
// 检查元素是否已经被过滤
if (element.classList.contains(config.filterClass)) {
return;
}
const keywords = containsTargetText(element.textContent);
if (keywords) {
keywords.forEach(keyword => matchedKeywords.add(keyword));
// 尝试找到最外层的li元素进行移除
const liElement = element.closest('li');
if (liElement && !liElement.classList.contains(config.filterClass)) {
liElement.classList.add(config.filterClass);
liElement.style.display = 'none';
removedCount++;
console.log('DBD-RawsBanHelper: 过滤了一个资源元素', liElement);
} else if (!liElement) {
// 如果没有找到li元素,直接隐藏当前元素
element.classList.add(config.filterClass);
element.style.display = 'none';
removedCount++;
console.log('DBD-RawsBanHelper: 过滤了一个资源元素', element);
}
}
});
if (removedCount > 0) {
console.log(`DBD-RawsBanHelper: 在当前frame中过滤了 ${removedCount} 个资源`);
if (config.showNotification) {
const keywordsText = Array.from(matchedKeywords).join('、');
showNotification(`动态过滤: 移除 ${removedCount} 个DBD资源 (${keywordsText})`);
}
}
return { removedCount, matchedKeywords: Array.from(matchedKeywords) };
}
// 主过滤函数
function filterContent() {
// 对于静态页面,确保只执行一次
if (!isDynamicPage() && hasExecutedStaticFilter) {
console.log('DBD-RawsBanHelper: 静态页面已执行过过滤,跳过本次执行');
return { removedCount: 0, matchedKeywords: [] };
}
console.log('DBD-RawsBanHelper: 开始执行内容过滤');
let rowsRemoved = 0;
let matchedKeywords = new Set();
// 根据网站结构选择不同的处理方式
if (window.location.hostname.includes('dmhy.org')) {
console.log('DBD-RawsBanHelper: 处理动漫花园网站');
const result = filterDmhyContent();
rowsRemoved = result.removedCount;
result.matchedKeywords.forEach(keyword => matchedKeywords.add(keyword));
} else if (window.location.hostname.includes('acgnx.se')) {
console.log('DBD-RawsBanHelper: 处理末日动漫网站');
const result = filterAcgnxContent();
rowsRemoved = result.removedCount;
result.matchedKeywords.forEach(keyword => matchedKeywords.add(keyword));
} else if (window.location.hostname.includes('nyaa')) {
console.log('DBD-RawsBanHelper: 处理Nyaa网站');
const result = filterNyaaContent();
rowsRemoved = result.removedCount;
result.matchedKeywords.forEach(keyword => matchedKeywords.add(keyword));
} else if (isMikananiSite()) {
console.log('DBD-RawsBanHelper: 处理蜜柑计划网站');
const result = filterMikananiContent();
rowsRemoved = result.removedCount;
result.matchedKeywords.forEach(keyword => matchedKeywords.add(keyword));
}
// 显示过滤通知
if (rowsRemoved > 0 && config.showNotification) {
const keywordsText = Array.from(matchedKeywords).join('、');
console.log(`DBD-RawsBanHelper: 过滤完成,共移除 ${rowsRemoved} 个资源,匹配关键词: ${keywordsText}`);
showNotification(`已过滤 ${rowsRemoved} 个DBD-Raws资源,匹配过滤词条:${keywordsText}`);
} else if (rowsRemoved === 0) {
console.log('DBD-RawsBanHelper: 未找到需要过滤的内容');
}
// 标记静态页面已执行
if (!isDynamicPage()) {
hasExecutedStaticFilter = true;
}
return { removedCount: rowsRemoved, matchedKeywords: Array.from(matchedKeywords) };
}
// 过滤动漫花园内容
function filterDmhyContent() {
let removedCount = 0;
let matchedKeywords = new Set();
const rows = document.querySelectorAll('tr');
if (rows.length > 5) {
rows.forEach((row, index) => {
const keywords = containsTargetText(row.textContent);
if (keywords && !row.classList.contains(config.filterClass)) {
keywords.forEach(keyword => matchedKeywords.add(keyword));
row.parentNode.removeChild(row);
removedCount++;
}
});
}
console.log(`DBD-RawsBanHelper: 动漫花园 - 移除了 ${removedCount} 行`);
return { removedCount, matchedKeywords: Array.from(matchedKeywords) };
}
// 过滤末日动漫内容
function filterAcgnxContent() {
let removedCount = 0;
let matchedKeywords = new Set();
const rowSelectors = ['tr', '.row', '.item'];
let rowsFound = false;
for (let selector of rowSelectors) {
const elements = document.querySelectorAll(selector);
if (elements.length > 5) {
rowsFound = true;
elements.forEach((element, index) => {
const keywords = containsTargetText(element.textContent);
if (keywords && !element.classList.contains(config.filterClass)) {
keywords.forEach(keyword => matchedKeywords.add(keyword));
element.classList.add(config.filterClass);
removedCount++;
element.style.display = 'none';
}
});
break;
}
}
console.log(`DBD-RawsBanHelper: 末日动漫 - 移除了 ${removedCount} 行`);
return { removedCount, matchedKeywords: Array.from(matchedKeywords) };
}
// 过滤Nyaa内容
function filterNyaaContent() {
let removedCount = 0;
let matchedKeywords = new Set();
const rows = document.querySelectorAll('tr');
if (rows.length > 0) {
rows.forEach((row, index) => {
const keywords = containsTargetText(row.textContent);
if (keywords && !row.classList.contains(config.filterClass)) {
keywords.forEach(keyword => matchedKeywords.add(keyword));
row.parentNode.removeChild(row);
removedCount++;
}
});
}
console.log(`DBD-RawsBanHelper: Nyaa - 移除了 ${removedCount} 行`);
return { removedCount, matchedKeywords: Array.from(matchedKeywords) };
}
// 过滤蜜柑计划内容
function filterMikananiContent() {
let removedCount = 0;
let matchedKeywords = new Set();
// 处理列表模式
if (isMikananiClassicMode()) {
console.log('DBD-RawsBanHelper: 检测到蜜柑计划列表模式');
const result = filterMikananiClassicMode();
removedCount = result.removedCount;
result.matchedKeywords.forEach(keyword => matchedKeywords.add(keyword));
} else {
// 处理普通模式
const result = filterMikananiNormalMode();
removedCount = result.removedCount;
result.matchedKeywords.forEach(keyword => matchedKeywords.add(keyword));
}
console.log(`DBD-RawsBanHelper: 蜜柑计划 - 移除了 ${removedCount} 行`);
return { removedCount, matchedKeywords: Array.from(matchedKeywords) };
}
// 过滤蜜柑计划列表模式内容
function filterMikananiClassicMode() {
let removedCount = 0;
let matchedKeywords = new Set();
// 查找所有包含magnet-link-wrap的tr行
const rows = document.querySelectorAll('tr');
rows.forEach(row => {
const magnetLink = row.querySelector('a.magnet-link-wrap');
if (magnetLink) {
const keywords = containsTargetText(magnetLink.textContent);
if (keywords) {
keywords.forEach(keyword => matchedKeywords.add(keyword));
row.parentNode.removeChild(row);
removedCount++;
console.log('DBD-RawsBanHelper: 列表模式 - 移除了一行包含DBD内容的tr');
}
}
});
return { removedCount, matchedKeywords: Array.from(matchedKeywords) };
}
// 过滤蜜柑计划普通模式内容
function filterMikananiNormalMode() {
let removedCount = 0;
let matchedKeywords = new Set();
// 1. 过滤主内容表格行
const allRows = document.querySelectorAll('tr[data-itemindex]');
const rowsToRemove = [];
allRows.forEach(row => {
const keywords = containsTargetText(row.textContent);
if (keywords && !row.classList.contains(config.filterClass)) {
keywords.forEach(keyword => matchedKeywords.add(keyword));
rowsToRemove.push(row);
}
});
rowsToRemove.forEach(row => {
row.parentNode.removeChild(row);
removedCount++;
});
// 重新排序剩余的行的data-itemindex
if (removedCount > 0) {
reindexMikananiTable();
}
// 2. 过滤"相关字幕组"列表(搜索页面)
const subtitleGroups = document.querySelectorAll('.leftbar-nav');
subtitleGroups.forEach(container => {
const items = container.querySelectorAll('a, span, li, div');
items.forEach(item => {
const keywords = containsTargetText(item.textContent);
if (keywords && !item.classList.contains(config.filterClass)) {
keywords.forEach(keyword => matchedKeywords.add(keyword));
item.parentNode.removeChild(item);
removedCount++;
}
});
});
const targetDiv = document.getElementById('575');
if (targetDiv) {
const nextTable = targetDiv.nextElementSibling;
if (nextTable && nextTable.tagName === 'TABLE') {
nextTable.remove();
}
targetDiv.remove();
}
return { removedCount, matchedKeywords: Array.from(matchedKeywords) };
}
// 重新索引蜜柑计划表格的data-itemindex
function reindexMikananiTable() {
const allRows = document.querySelectorAll('tr[data-itemindex]');
const sortedRows = Array.from(allRows).sort((a, b) => {
const indexA = parseInt(a.getAttribute('data-itemindex'));
const indexB = parseInt(b.getAttribute('data-itemindex'));
return indexA - indexB;
});
sortedRows.forEach((row, index) => {
row.setAttribute('data-itemindex', index + 1);
});
}
// 显示过滤通知
function showNotification(message) {
// 清除现有的定时器
if (notificationTimer) {
clearTimeout(notificationTimer);
notificationTimer = null;
}
// 移除现有的通知
const existingNotification = document.getElementById('dmhy-filter-notification');
if (existingNotification) {
existingNotification.remove();
}
// 创建新通知
const notification = document.createElement('div');
notification.id = 'dmhy-filter-notification';
notification.textContent = message;
// 为蜜柑计划添加特殊样式
if (isMikananiSite()) {
notification.classList.add('mikanani-notification');
}
// 添加鼠标事件监听器
notification.addEventListener('mouseenter', function() {
if (notificationTimer) {
clearTimeout(notificationTimer);
notificationTimer = null;
}
});
notification.addEventListener('mouseleave', function() {
startNotificationTimer(notification);
});
// 添加点击事件,点击也可以立即关闭
notification.addEventListener('click', function() {
hideNotification(notification);
});
document.body.appendChild(notification);
// 启动定时器
startNotificationTimer(notification);
}
// 启动通知定时器
function startNotificationTimer(notification) {
if (notificationTimer) {
clearTimeout(notificationTimer);
}
notificationTimer = setTimeout(() => {
hideNotification(notification);
}, config.notificationDuration);
}
// 隐藏通知
function hideNotification(notification) {
if (notification && notification.parentNode) {
notification.style.opacity = '0';
setTimeout(() => {
if (notification && notification.parentNode) {
notification.remove();
}
}, 500);
}
}
console.log('DBD-RawsBanHelper: 脚本初始化完成');
})();