Filtering "Incomplete" for PterClub

支持“非完结”种子主副标题过滤 + 精确识别制作组(仅匹配以 - 开头并出现在标题结尾的情况)

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

// ==UserScript==
// @name         Filtering "Incomplete" for PterClub
// @namespace    http://tampermonkey.net/
// @version      3.2
// @description  支持“非完结”种子主副标题过滤 + 精确识别制作组(仅匹配以 - 开头并出现在标题结尾的情况)
// @author       @Zuoans
// @match        https://pterclub.com/torrents.php* 
// @license      MIT
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';

    // ====================== 样式定义 ======================
    GM_addStyle(`
        :root {
            --blue-start: rgba(51, 160, 200, 0.6);
            --blue-mid: rgba(51, 160, 200, 0.1);
            --purple-start: rgba(147, 112, 219, 0.5);
            --purple-mid: rgba(147, 112, 219, 0.1);
        }

        /* incomplete样式 */
        table.torrentname > tbody > tr.tm-incomplete:not(.tm-group) {
            background: linear-gradient(to right, var(--blue-start) 0%, var(--blue-mid) 50%, rgba(255,255,255,0) 90%) !important;
        }

        /* 制作组样式 */
        table.torrentname > tbody > tr.tm-group:not(.tm-incomplete) {
            background: linear-gradient(to right, var(--purple-start) 0%, var(--purple-mid) 50%, rgba(255,255,255,0) 90%) !important;
        }

        /* incomplete&制作组双重标记样式 */
        table.torrentname > tbody > tr.tm-incomplete.tm-group {
            background: linear-gradient(to right, var(--purple-start) 0%, var(--purple-mid) 50%, rgba(255,255,255,0) 90%) !important;
        }

        /* 文字保护 */
        table.torrentname [class*="tm-"] td {
            text-shadow: 0 1px 1px rgba(255,255,255,0.1),
                         0 -1px 1px rgba(0,0,0,0.1) !important;
        }
    `);

    // 目标制作组列表(新增 PTerWEB)
    const targetGroups = [
        'AdBlue', 'AREY', 'BdC', 'BMDru', 'CatEDU', 'c0kE',
        'doraemon', 'JKCT', 'KMX', 'Lislander', 'RO',
        'Telesto', 'XPcl', 'ZTR', 'PTerWEB'
    ];

    // 辅助函数:转义正则特殊字符
    function escapeRegExp(string) {
        return string.replace(/[.*+?^${}()|[$$\$$]/g, '\\$&');
    }

    // ✅ 制作组正则:只匹配以 - 开头,并出现在标题结尾的组名(如 -ZTR)
    const groupRegex = new RegExp(
        `-(?:${targetGroups.map(g => escapeRegExp(g)).join('|')})(?=[\\s\\W]|$)`, 
        'i'
    );

    // 排除关键词正则(包含 Complete / Fin / FIN+ 的行不能被标记为 non-complete)
    const excludeKeywordRegex = /\b(?:Complete|Fin)(?!\w)|FIN\+/gi;

    // 新增:incomplete 正则(仅检测 SxxExx 或 第X集,不考虑 EP)
    const incompleteRegex = /(?:^|\s)(S\d+E\d+|第\d+集)(?=\s|$)/i;

    function safeHighlight() {
        const rows = document.querySelectorAll('table.torrentname > tbody > tr');

        rows.forEach(tr => {
            tr.classList.remove('tm-incomplete', 'tm-group');

            // 获取主标题
            const titleLink = tr.querySelector('.torrentname a');
            if (!titleLink) return;

            let mainTitle = titleLink.innerText.trim();

            // 获取副标题
            let subTitleEl = tr.querySelector('.torrentname div[style*="margin-top"] span');
            let subTitle = subTitleEl ? subTitleEl.innerText.trim() : '';

            let fullTitle = (mainTitle + ' ' + subTitle).trim();

            // 检查是否包含排除关键词
            const hasExclusion = excludeKeywordRegex.test(fullTitle);

            // 判断是否为非完结(主副标题中任意一个满足即可)
            const isIncomplete = !hasExclusion && incompleteRegex.test(fullTitle);

            // 判断是否为目标制作组(✅ 仅匹配以 - 开头并出现在结尾的组名)
            const isTargetGroup = groupRegex.test(fullTitle);

            if (isIncomplete) tr.classList.add('tm-incomplete');
            if (isTargetGroup) tr.classList.add('tm-group');
        });
    }

    // MutationObserver 设置
    const observer = new MutationObserver(mutations => {
        if (mutations.some(mut => mut.addedNodes.length > 0)) {
            safeHighlight();
        }
    });

    document.addEventListener('DOMContentLoaded', () => {
        safeHighlight();
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    });

    // 页面卸载时清理
    window.addEventListener('unload', () => {
        document.querySelectorAll('tr').forEach(el => {
            el.className = el.className.replace(/\btm-\w+\b/g, '');
        });
    });

})();