// ==UserScript== // @name UBits-影视评分过滤器 // @namespace http://tampermonkey.net/ // @version 1.2 // @description 可自定义过滤条件的电影过滤器,支持设置最低评分和N/A处理,可配置统计窗口 // @author Dost // @match https://ubits.club/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 默认配置 const DEFAULT_CONFIG = { minIMDbRating: 6.1, minDoubanRating: 6.5, removeNA: false, requireBothRatings: false, showDebugInfo: true, enabled: true, showNotification: true, // 新增:是否显示统计窗口 notificationDuration: 8 // 新增:统计窗口显示时间(秒) }; // 加载保存的配置 let FILTER_CONFIG; try { FILTER_CONFIG = Object.assign({}, DEFAULT_CONFIG, JSON.parse(GM_getValue('UBitsFilterConfig', '{}'))); } catch (e) { FILTER_CONFIG = Object.assign({}, DEFAULT_CONFIG); console.error('Failed to load saved config, using defaults', e); } // 注册菜单命令 GM_registerMenuCommand('配置UBits电影过滤器', showConfigUI); // 主函数 function main() { if (!FILTER_CONFIG.enabled) { console.log('UBits电影过滤器已禁用'); return; } addConfigButton(); applyFilters(); } // 应用过滤器 function applyFilters() { const stats = { totalChecked: 0, totalRemoved: 0, removedByLowIMDb: 0, removedByLowDouban: 0, removedByNA: 0, removedByMissingRating: 0 }; // 更精确的选择器:只选择包含评分容器的行 const rows = document.querySelectorAll('tr:has(> td > div[style*="display: flex; flex-direction: column"])'); rows.forEach(tr => { stats.totalChecked++; const ratingContainer = tr.querySelector('div[style*="display: flex; flex-direction: column"]'); if (!ratingContainer) return; const ratingSpans = ratingContainer.querySelectorAll('span'); const imdbRating = parseRating(ratingSpans[0]?.textContent); const doubanRating = ratingSpans[1] ? parseRating(ratingSpans[1]?.textContent) : null; let shouldRemove = false; const removeReasons = []; // 检查双评分要求 if (FILTER_CONFIG.requireBothRatings && (!imdbRating.valid || !doubanRating?.valid)) { shouldRemove = true; removeReasons.push('缺少有效评分'); stats.removedByMissingRating++; } // 检查N/A if (FILTER_CONFIG.removeNA && (imdbRating.isNA || (doubanRating && doubanRating.isNA))) { shouldRemove = true; removeReasons.push('N/A评分'); stats.removedByNA++; } // 检查IMDb评分 if (FILTER_CONFIG.minIMDbRating > 0 && imdbRating.valid && imdbRating.value < FILTER_CONFIG.minIMDbRating) { shouldRemove = true; removeReasons.push(`IMDb ${imdbRating.value} < ${FILTER_CONFIG.minIMDbRating}`); stats.removedByLowIMDb++; } // 检查豆瓣评分 if (doubanRating && FILTER_CONFIG.minDoubanRating > 0 && doubanRating.valid && doubanRating.value < FILTER_CONFIG.minDoubanRating) { shouldRemove = true; removeReasons.push(`豆瓣 ${doubanRating.value} < ${FILTER_CONFIG.minDoubanRating}`); stats.removedByLowDouban++; } if (shouldRemove) { if (FILTER_CONFIG.showDebugInfo) { console.log(`删除项目: ${removeReasons.join('; ')}`, tr); } tr.style.display = 'none'; // 改为隐藏而非删除 tr.dataset.filtered = 'true'; // 标记已过滤 stats.totalRemoved++; } }); showResults(stats); } // 解析评分 function parseRating(ratingText) { if (!ratingText) return { valid: false, isNA: true }; const text = ratingText.trim(); if (text === 'N/A' || text === '' || text === '-') { return { valid: false, isNA: true }; } const value = parseFloat(text); if (isNaN(value)) { return { valid: false, isNA: true }; } return { valid: true, isNA: false, value: value }; } // 显示结果 function showResults(stats) { const resultLines = [ `UBits电影过滤结果 (共检查 ${stats.totalChecked} 个项目)`, `-------------------------------------`, `隐藏总数: ${stats.totalRemoved}`, ...(stats.removedByLowIMDb > 0 ? [`- IMDb评分过低: ${stats.removedByLowIMDb}`] : []), ...(stats.removedByLowDouban > 0 ? [`- 豆瓣评分过低: ${stats.removedByLowDouban}`] : []), ...(stats.removedByNA > 0 ? [`- N/A或无效评分: ${stats.removedByNA}`] : []), ...(stats.removedByMissingRating > 0 ? [`- 缺少有效评分: ${stats.removedByMissingRating}`] : []), `-------------------------------------`, `当前过滤条件:`, `- 最低IMDb评分: ${FILTER_CONFIG.minIMDbRating > 0 ? FILTER_CONFIG.minIMDbRating : '不限制'}`, `- 最低豆瓣评分: ${FILTER_CONFIG.minDoubanRating > 0 ? FILTER_CONFIG.minDoubanRating : '不限制'}`, `- 删除N/A: ${FILTER_CONFIG.removeNA ? '是' : '否'}`, `- 要求双评分: ${FILTER_CONFIG.requireBothRatings ? '是' : '否'}`, `- 过滤器状态: ${FILTER_CONFIG.enabled ? '启用' : '禁用'}` ]; const resultMsg = resultLines.join('\n'); console.log(resultMsg); if (FILTER_CONFIG.showNotification) { showNotification(resultMsg, FILTER_CONFIG.notificationDuration * 1000); } } // 显示通知 function showNotification(message, duration = 8000) { const existing = document.getElementById('ubits-filter-notification'); if (existing) existing.remove(); const notification = document.createElement('div'); notification.id = 'ubits-filter-notification'; notification.style.cssText = ` position: fixed; top: 10px; right: 10px; background-color: #f8f9fa; color: #212529; padding: 12px; border-radius: 5px; z-index: 9999; box-shadow: 0 0 15px rgba(0,0,0,0.2); max-width: 320px; font-family: Arial, sans-serif; font-size: 14px; line-height: 1.5; white-space: pre-line; border-left: 4px solid #6c757d; transform: translateX(120%); transition: transform 0.3s ease-out; `; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.transform = 'translateX(0)'; }, 100); let hideTimer; const startHideTimer = () => { hideTimer = setTimeout(() => { notification.style.transition = 'opacity 1s'; notification.style.opacity = '0'; setTimeout(() => notification.remove(), 1000); }, duration); }; notification.addEventListener('mouseenter', () => { clearTimeout(hideTimer); notification.style.opacity = '1'; }); notification.addEventListener('mouseleave', startHideTimer); startHideTimer(); } // 添加配置按钮 function addConfigButton() { const existing = document.getElementById('ubits-filter-config-btn'); if (existing) existing.remove(); const btn = document.createElement('button'); btn.id = 'ubits-filter-config-btn'; btn.textContent = '⚙️ 过滤器配置'; btn.style.cssText = ` position: fixed; bottom: 20px; right: 20px; z-index: 9998; padding: 8px 15px; background: #6c757d; color: white; border: none; border-radius: 20px; cursor: pointer; box-shadow: 0 2px 10px rgba(0,0,0,0.2); font-family: Arial, sans-serif; `; btn.addEventListener('click', showConfigUI); document.body.appendChild(btn); } // 显示配置UI function showConfigUI() { const existing = document.getElementById('ubits-filter-config-dialog'); if (existing) existing.remove(); const dialog = document.createElement('div'); dialog.id = 'ubits-filter-config-dialog'; dialog.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; z-index: 10000; box-shadow: 0 0 20px rgba(0,0,0,0.3); border-radius: 8px; width: 350px; max-width: 90%; font-family: Arial, sans-serif; `; dialog.innerHTML = `

UBits电影过滤器配置

过滤条件

通知设置

`; document.body.appendChild(dialog); // 保存配置 document.getElementById('ubits-filter-save').addEventListener('click', () => { FILTER_CONFIG.enabled = document.getElementById('ubits-filter-enabled').checked; FILTER_CONFIG.minIMDbRating = parseFloat(document.getElementById('ubits-filter-imdb').value) || 0; FILTER_CONFIG.minDoubanRating = parseFloat(document.getElementById('ubits-filter-douban').value) || 0; FILTER_CONFIG.removeNA = document.getElementById('ubits-filter-remove-na').checked; FILTER_CONFIG.requireBothRatings = document.getElementById('ubits-filter-require-both').checked; FILTER_CONFIG.showNotification = document.getElementById('ubits-filter-show-notification').checked; FILTER_CONFIG.notificationDuration = parseInt(document.getElementById('ubits-filter-notification-duration').value) || 8; GM_setValue('UBitsFilterConfig', JSON.stringify(FILTER_CONFIG)); dialog.remove(); // 重新应用过滤器 resetFilters(); if (FILTER_CONFIG.enabled) { applyFilters(); } else { if (FILTER_CONFIG.showNotification) { showNotification('UBits电影过滤器已禁用'); } } }); // 取消 document.getElementById('ubits-filter-cancel').addEventListener('click', () => { dialog.remove(); }); } // 重置过滤器(显示所有隐藏的行) function resetFilters() { const hiddenRows = document.querySelectorAll('tr[data-filtered="true"]'); hiddenRows.forEach(row => { row.style.display = ''; row.removeAttribute('data-filtered'); }); } // 启动 window.addEventListener('load', main); })();