// ==UserScript== // @name:zh-CN 动漫花园种子屏蔽助手 // @name DMHY Torrent Block // @namespace https://github.com/xkbkx5904 // @version 1.1.0 // @author xkbkx5904 // @description Enhanced version of DMHY Block script with more features: UI management, regex filtering, context menu, and ad blocking // @description:zh-CN 增强版的动漫花园资源屏蔽工具,支持用户界面管理、正则表达式过滤、右键菜单和广告屏蔽等功能 // @homepage https://github.com/xkbkx5904/dmhy-torrent-block // @supportURL https://github.com/xkbkx5904/dmhy-torrent-block/issues // @match *://share.dmhy.org/* // @license MIT // @run-at document-end // @grant GM_setValue // @grant GM_getValue // @noframes // @copyright 2025, xkbkx5904 // @originalAuthor tautcony // @originalURL https://greasyfork.org/zh-CN/scripts/36871-dmhy-block // @icon https://share.dmhy.org/favicon.ico // @downloadURL none // ==/UserScript== /** * 全局配置对象 */ const CONFIG = { // 存储相关配置 storage: { blockListKey: 'dmhy_blocklist' }, // DOM选择器配置 selectors: { torrentList: "table#topic_list tbody tr", userLink: "td:last-child a[href*='/user_id/']", titleCell: "td.title", adSelectors: [ // 精确定位广告容器(修复 ID 选择器) '[id="1280_adv"]', '[id="pkpk"]', '.kiwi-ad-wrapper-1280x120', // 广告追踪相关 'a[onclick*="_trackEvent"][onclick*="ad"]', // PikPak 相关 'a[href*="mypikpak.com/drive/url-checker"]', // 特定广告图片 'div[align="center"] > a[href*="sng.link"] > img', 'div[align="center"] > a[href*="weidian.com"] > img[src*="/1280pik.png"]', 'img[src*="/VA"][src*=".gif"]' ] }, // UI相关样式配置 styles: { notification: ` position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 10px 20px; border-radius: 4px; z-index: 10001; font-size: 14px; transition: opacity 0.3s; `, blocklistUI: ` position: fixed; left: 10px; top: 10px; z-index: 9999; `, manager: ` position: fixed; left: 50%; top: 50%; transform: translate(-50%,-50%); background: white; padding: 20px; border: 1px solid #ccc; border-radius: 5px; z-index: 10000; width: 500px; max-height: 80vh; overflow-y: auto; ` } }; /** * 错误处理类 */ class ErrorHandler { /** * 处理错误 * @param {Error} error - 错误对象 * @param {string} context - 错误发生的上下文 */ static handle(error, context) { console.warn(`[DMHY Block] Error in ${context}:`, error); } } /** * 通知管理类 */ class NotificationManager { /** * 显示通知 * @param {string} message - 通知消息 */ static show(message) { const notification = document.createElement('div'); notification.style.cssText = CONFIG.styles.notification; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; setTimeout(() => notification.remove(), 300); }, 2000); } } /** * 黑名单管理类 */ class BlockListManager { constructor() { this.blockList = []; } /** * 初始化黑名单 */ async init() { await this.loadBlockList(); } /** * 从存储加载黑名单 */ async loadBlockList() { try { const saved = GM_getValue(CONFIG.storage.blockListKey, []); this.blockList = Array.isArray(saved) ? this.parseBlockList(saved) : []; } catch (error) { ErrorHandler.handle(error, 'BlockListManager.loadBlockList'); this.blockList = []; } } /** * 解析黑名单数据 * @param {Array} saved - 保存的黑名单数据 */ parseBlockList(saved) { return saved.map(item => { if (item.type === 'keywords') { return { type: 'keywords', values: item.values.map(this.parseKeyword) }; } return item; }); } /** * 解析关键词 * @param {string} keyword - 关键词 */ parseKeyword(keyword) { if (typeof keyword === 'string' && keyword.startsWith('/') && keyword.endsWith('/')) { try { return new RegExp(keyword.slice(1, -1)); } catch (e) { return keyword; } } return keyword; } /** * 保存黑名单到存储 */ saveBlockList() { try { const listToSave = this.blockList.map(item => ({ ...item, values: item.type === 'keywords' ? item.values.map(k => k instanceof RegExp ? `/${k.source}/` : k) : item.values })); GM_setValue(CONFIG.storage.blockListKey, listToSave); } catch (error) { ErrorHandler.handle(error, 'BlockListManager.saveBlockList'); } } /** * 添加用户到黑名单 * @param {number} userId - 用户ID */ addUser(userId) { if (!userId || isNaN(userId)) return false; const userIdList = this.getUserIds(); if (!userIdList.includes(userId)) { this.updateBlockList('userId', [...userIdList, userId]); return true; } return false; } /** * 获取黑名单用户ID列表 */ getUserIds() { return this.blockList.find(item => item.type === 'userId')?.values || []; } /** * 获取黑名单关键词列表 */ getKeywords() { return this.blockList.find(item => item.type === 'keywords')?.values || []; } /** * 更新黑名单 * @param {string} type - 黑名单类型 * @param {Array} values - 黑名单值 */ updateBlockList(type, values) { const index = this.blockList.findIndex(item => item.type === type); if (index >= 0) { this.blockList[index].values = values; } else { this.blockList.push({ type, values }); } this.saveBlockList(); } } /** * 过滤管理类 */ class FilterManager { constructor(blockListManager) { this.blockListManager = blockListManager; } /** * 初始化过滤器 */ init() { this.applyFilters(); } /** * 应用过滤规则 */ applyFilters() { try { this.resetHiddenItems(); if (!this.blockListManager.blockList.length) return; const blockedUserIds = this.blockListManager.getUserIds(); const blockedKeywords = this.blockListManager.getKeywords(); if (!blockedUserIds.length && !blockedKeywords.length) return; this.filterTorrentList(blockedUserIds, blockedKeywords); } catch (error) { ErrorHandler.handle(error, 'FilterManager.applyFilters'); } } /** * 重置隐藏的项目 */ resetHiddenItems() { document.querySelectorAll(`${CONFIG.selectors.torrentList}[style*='display: none']`) .forEach(elem => elem.style.display = ''); } /** * 过滤种子列表 * @param {Array} blockedUserIds - 被屏蔽的用户ID * @param {Array} blockedKeywords - 被屏蔽的关键词 */ filterTorrentList(blockedUserIds, blockedKeywords) { document.querySelectorAll(CONFIG.selectors.torrentList).forEach(elem => { try { const { title, userId } = this.extractItemInfo(elem); if (!title || !userId) return; if (this.shouldHideItem(userId, title, blockedUserIds, blockedKeywords)) { elem.style.display = 'none'; } } catch (error) { ErrorHandler.handle(error, 'FilterManager.filterTorrentList.item'); } }); } /** * 提取项目信息 * @param {Element} elem - DOM元素 */ extractItemInfo(elem) { const titleCell = elem.querySelector(CONFIG.selectors.titleCell); const title = titleCell ? Array.from(titleCell.childNodes) .map(node => node.textContent?.trim()) .filter(text => text) .join(' ') : ''; const idMatch = elem.querySelector(CONFIG.selectors.userLink)?.href?.match(/user_id\/(\d+)/); const userId = idMatch ? parseInt(idMatch[1]) : null; return { title, userId }; } /** * 判断是否应该隐藏项目 * @param {number} userId - 用户ID * @param {string} title - 标题 * @param {Array} blockedUserIds - 被屏蔽的用户ID * @param {Array} blockedKeywords - 被屏蔽的关键词 */ shouldHideItem(userId, title, blockedUserIds, blockedKeywords) { if (blockedUserIds.includes(userId)) return true; return blockedKeywords.some(keyword => { if (typeof keyword === 'string') { return title.toLowerCase().includes(keyword.toLowerCase()); } return keyword instanceof RegExp && title.match(keyword); }); } } /** * UI管理类 */ class UIManager { constructor(blockListManager, filterManager) { this.blockListManager = blockListManager; this.filterManager = filterManager; } /** * 初始化UI */ init() { this.addBlocklistUI(); this.addContextMenu(); } /** * 添加黑名单UI */ addBlocklistUI() { const uiHtml = `