Greasy Fork

Greasy Fork is available in English.

DBD-RawsBanHelper

过滤动漫花园、末日动漫、Nyaa和蜜柑计划中的DBD-Raws内容,并修复行颜色问题

// ==UserScript==
// @name         DBD-RawsBanHelper
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  过滤动漫花园、末日动漫、Nyaa和蜜柑计划中的DBD-Raws内容,并修复行颜色问题
// @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';

    // 配置参数
    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样式(不应用于蜜柑计划)
    if (!isMikananiSite()) {
        const style = document.createElement('style');
        style.textContent = `
            /* 行动画效果 */
            .resource-row {
                transition: background-color 0.3s ease;
            }

            .even {
                background-color: #f5f5f5;
            }

            .odd {
                background-color: #ffffff;
            }

            /* 为过滤区域添加视觉区分 */
            .resource-list:empty::after {
                content: "没有符合条件的内容或所有内容已被过滤";
                display: block;
                text-align: center;
                padding: 20px;
                color: #888;
                font-style: italic;
            }

            /* 自定义通知样式 */
            #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;
            }
        `;
        document.head.appendChild(style);
    } else {
        // 蜜柑计划专用的通知样式
        const style = document.createElement('style');
        style.textContent = `
            #dmhy-filter-notification {
                position: fixed;
                top: 70px !important;
                right: 20px;
                background: #ff4757;
                color: white;
                padding: 12px 18px;
                border-radius: 4px;
                z-index: 9999;
                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;
            }
        `;
        document.head.appendChild(style);
    }

    // 全局变量用于存储通知的定时器
    let notificationTimer = null;

    // 检查文本是否包含任何目标关键词,并返回匹配到的关键词
    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');
    }

    // 等待页面加载完成
    window.addEventListener('load', function() {
        // 延迟执行以确保所有内容都已加载
        setTimeout(() => {
            filterContent();
            // 监听蜜柑计划的动态内容
            if (isMikananiSite()) {
                observeMikananiDynamicContent();
            }
        }, 1000);
    });

    // 主过滤函数
    function filterContent() {
        let rowsRemoved = 0;
        let matchedKeywords = new Set();

        // 根据网站结构选择不同的处理方式
        if (window.location.hostname.includes('dmhy.org')) {
            // 动漫花园的处理方式 - 直接删除DOM元素
            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);
                        rowsRemoved++;
                    }
                });

                // 重新着色表格行
                recolorDmhyTableRows();
            }
        } else if (window.location.hostname.includes('acgnx.se')) {
            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);
                            rowsRemoved++;

                            // 添加渐隐效果
                            setTimeout(() => {
                                element.style.display = 'none';

                                // 重新着色表格行
                                recolorAcgnxTableRows(selector);
                            }, 500);
                        }
                    });
                    break;
                }
            }
        } else if (window.location.hostname.includes('nyaa')) {
            // Nyaa.*的处理方式
            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);
                        rowsRemoved++;
                    }
                });
            }
        } else if (isMikananiSite()) {
            // 蜜柑计划的处理方式
            const result = filterMikananiContent();
            rowsRemoved = result.removedCount;
            result.matchedKeywords.forEach(keyword => matchedKeywords.add(keyword));
        }

        // 显示过滤通知
        if (rowsRemoved > 0 && config.showNotification) {
            const keywordsText = Array.from(matchedKeywords).join('、');
            showNotification(`已过滤 ${rowsRemoved} 个DBD-Raws资源,匹配过滤词条:${keywordsText}`);
        }

        return { removedCount: rowsRemoved, matchedKeywords: Array.from(matchedKeywords) };
    }

    // 过滤蜜柑计划内容
    function filterMikananiContent() {
        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++;
                }
            });
        });

        // 3. 过滤已加载的可展开内容
        const expandableResult = filterMikananiExpandableContent();
        removedCount += expandableResult.removedCount;
        expandableResult.matchedKeywords.forEach(keyword => matchedKeywords.add(keyword));

        return { removedCount, matchedKeywords: Array.from(matchedKeywords) };
    }

    // 精确过滤蜜柑计划可展开内容中的li元素
    function filterMikananiExpandableContent() {
        let removedCount = 0;
        let matchedKeywords = new Set();

        // 查找所有row an-res-row-frame元素
        const expandableFrames = document.querySelectorAll('.row.an-res-row-frame');

        expandableFrames.forEach(frame => {
            // 查找所有包含目标关键词的元素
            const elements = frame.querySelectorAll('div.sk-col.tag-res-name, li, a, span');

            elements.forEach(element => {
                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.remove();
                        removedCount++;
                    }
                }
            });
        });

        return { removedCount, matchedKeywords: Array.from(matchedKeywords) };
    }

    // 监听蜜柑计划动态加载的内容
    function observeMikananiDynamicContent() {
        // 监听点击事件,当用户点击an-box animated中的元素时触发过滤
        document.addEventListener('click', function(e) {
            // 检查点击的是否在an-box animated元素内
            const clickedInAnBox = e.target.closest('.an-box.animated.fadeIn');
            if (clickedInAnBox) {
                // 延迟执行过滤,等待动态内容加载
                setTimeout(() => {
                    const result = filterMikananiExpandableContent();
                    if (result.removedCount > 0 && config.showNotification) {
                        const keywordsText = result.matchedKeywords.join('、');
                        showNotification(`已过滤 ${result.removedCount} 个动态加载的DBD-Raws资源,匹配过滤词条:${keywordsText}`);
                    }
                }, 300);
            }
        });

        // 监听.row.an-res-row-frame元素内部的内容变化
        const expandableFrames = document.querySelectorAll('.row.an-res-row-frame');

        expandableFrames.forEach(frame => {
            const observer = new MutationObserver(function(mutations) {
                let shouldRefilter = false;

                mutations.forEach(function(mutation) {
                    if (mutation.addedNodes && mutation.addedNodes.length > 0) {
                        for (let node of mutation.addedNodes) {
                            if (node.nodeType === 1) {
                                // 检查是否添加了包含目标内容的元素
                                if (node.querySelector && node.querySelector('div.sk-col.tag-res-name')) {
                                    shouldRefilter = true;
                                    break;
                                }
                                if (node.matches && node.matches('div.sk-col.tag-res-name')) {
                                    shouldRefilter = true;
                                    break;
                                }
                            }
                        }
                    }
                });

                if (shouldRefilter) {
                    setTimeout(() => {
                        const result = filterMikananiExpandableContent();
                        if (result.removedCount > 0 && config.showNotification) {
                            const keywordsText = result.matchedKeywords.join('、');
                            showNotification(`已过滤 ${result.removedCount} 个动态加载的DBD-Raws资源,匹配过滤词条:${keywordsText}`);
                        }
                    }, 100);
                }
            });

            // 观察每个frame的内容变化
            observer.observe(frame, {
                childList: true,
                subtree: true
            });
        });

        // 同时监听全局的DOM变化作为备用
        const globalObserver = new MutationObserver(function(mutations) {
            let shouldRefilter = false;

            mutations.forEach(function(mutation) {
                if (mutation.addedNodes && mutation.addedNodes.length > 0) {
                    for (let node of mutation.addedNodes) {
                        if (node.nodeType === 1) {
                            // 检查是否添加了包含目标内容的元素
                            if (node.querySelector && node.querySelector('div.sk-col.tag-res-name')) {
                                shouldRefilter = true;
                                break;
                            }
                            if (node.matches && node.matches('div.sk-col.tag-res-name')) {
                                shouldRefilter = true;
                                break;
                            }
                        }
                    }
                }
            });

            if (shouldRefilter) {
                setTimeout(() => {
                    const result = filterMikananiExpandableContent();
                    if (result.removedCount > 0 && config.showNotification) {
                        const keywordsText = result.matchedKeywords.join('、');
                        showNotification(`已过滤 ${result.removedCount} 个动态加载的DBD-Raws资源,匹配过滤词条:${keywordsText}`);
                    }
                }, 100);
            }
        });

        // 观察整个文档的变化
        globalObserver.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    // 重新索引蜜柑计划表格的data-itemindex
    function reindexMikananiTable() {
        const allRows = document.querySelectorAll('tr[data-itemindex]');

        // 按当前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;
        });

        // 重新编号,从1开始
        sortedRows.forEach((row, index) => {
            row.setAttribute('data-itemindex', index + 1);
        });
    }

    // 重新着色动漫花园表格行
    function recolorDmhyTableRows() {
        const rows = document.querySelectorAll('tbody tr:not(.' + config.filterClass + ')');

        rows.forEach((row, index) => {
            // 清空现有的颜色类
            row.classList.remove('even', 'odd', 'even-row', 'odd-row');

            // 添加新的颜色类
            if (index % 2 === 0) {
                row.classList.add('odd');
            } else {
                row.classList.add('even');
            }
        });
    }

    // 重新着色末日动漫表格行
    function recolorAcgnxTableRows(selector) {
        const rows = document.querySelectorAll(selector + ':not(.' + config.filterClass + ')');

        rows.forEach((row, index) => {
            // 移除现有的颜色类
            row.classList.remove('alt1', 'alt2');

            // 添加新的颜色类
            if (index % 2 === 0) {
                row.classList.add('alt1');
            } else {
                row.classList.add('alt2');
            }

            // 应用网站特定的样式
            row.style.backgroundColor = index % 2 === 0 ? '#f8f9fa' : '#ffffff';
        });
    }

    // 显示过滤通知
    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;

        // 添加鼠标事件监听器
        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);
        }
    }

    // 初始执行
    setTimeout(filterContent, 1500);
})();