Greasy Fork

Greasy Fork is available in English.

UBits 智能电影过滤器

可自定义过滤条件的电影过滤器,支持设置最低评分和N/A处理,可配置统计窗口

当前为 2025-07-06 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         UBits 智能电影过滤器
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  可自定义过滤条件的电影过滤器,支持设置最低评分和N/A处理,可配置统计窗口
// @author       Dost
// @match        https://ubits.club/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 默认配置
    const DEFAULT_CONFIG = {
        minIMDbRating: 6.1,
        minDoubanRating: 6.5,
        removeNA: false,
        removeOnlyAllNA: true,
        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) {
                const imdbNA = imdbRating.isNA;
                const doubanNA = doubanRating ? doubanRating.isNA : true;

                if (FILTER_CONFIG.removeOnlyAllNA) {
                    // 仅当两个评分都是NA时才删除
                    if (imdbNA && doubanNA) {
                        shouldRemove = true;
                        removeReasons.push('双评分均为N/A');
                        stats.removedByNA++;
                    }
                } else {
                    // 任意一个评分为NA就删除
                    if (imdbNA || doubanNA) {
                        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.removeOnlyAllNA ? '仅双N/A' : '任意N/A') : '否'}`,
            `- 要求双评分: ${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);
    }

    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 = `
            <h3 style="margin-top:0;color:#495057">UBits电影过滤器配置</h3>
            <div style="margin-bottom:15px">
                <label style="display:flex;align-items:center">
                    <input type="checkbox" id="ubits-filter-enabled" ${FILTER_CONFIG.enabled ? 'checked' : ''} style="margin-right:8px">
                    启用过滤器
                </label>
            </div>

            <div style="margin:20px 0;border-top:1px solid #eee;padding-top:15px">
                <h4 style="margin:0 0 10px 0;color:#495057">过滤条件</h4>
                <div style="margin-bottom:15px">
                    <label style="display:block;margin-bottom:5px;color:#495057">最低IMDb评分:</label>
                    <input type="number" id="ubits-filter-imdb" step="0.1" min="0" max="10" value="${FILTER_CONFIG.minIMDbRating}" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:4px">
                </div>
                <div style="margin-bottom:15px">
                    <label style="display:block;margin-bottom:5px;color:#495057">最低豆瓣评分:</label>
                    <input type="number" id="ubits-filter-douban" step="0.1" min="0" max="10" value="${FILTER_CONFIG.minDoubanRating}" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:4px">
                </div>
                <div style="margin-bottom:15px">
                    <label style="display:flex;align-items:center">
                        <input type="checkbox" id="ubits-filter-remove-na" ${FILTER_CONFIG.removeNA ? 'checked' : ''} style="margin-right:8px">
                        删除N/A评分的项目
                    </label>
                </div>
                <div style="margin-bottom:15px">
                    <label style="display:flex;align-items:center">
                        <input type="checkbox" id="ubits-filter-remove-only-all-na" ${FILTER_CONFIG.removeOnlyAllNA ? 'checked' : ''} style="margin-right:8px" ${FILTER_CONFIG.removeNA ? '' : 'disabled'}>
                        仅当双评分均为N/A时删除
                    </label>
                </div>
                <div style="margin-bottom:15px">
                    <label style="display:flex;align-items:center">
                        <input type="checkbox" id="ubits-filter-require-both" ${FILTER_CONFIG.requireBothRatings ? 'checked' : ''} style="margin-right:8px">
                        要求同时有IMDb和豆瓣评分
                    </label>
                </div>
            </div>

            <div style="margin:20px 0;border-top:1px solid #eee;padding-top:15px">
                <h4 style="margin:0 0 10px 0;color:#495057">通知设置</h4>
                <div style="margin-bottom:15px">
                    <label style="display:flex;align-items:center">
                        <input type="checkbox" id="ubits-filter-show-notification" ${FILTER_CONFIG.showNotification ? 'checked' : ''} style="margin-right:8px">
                        显示统计窗口
                    </label>
                </div>
                <div style="margin-bottom:15px">
                    <label style="display:block;margin-bottom:5px;color:#495057">统计窗口显示时间(秒):</label>
                    <input type="number" id="ubits-filter-notification-duration" min="1" max="60" value="${FILTER_CONFIG.notificationDuration}" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:4px">
                </div>
            </div>

            <div style="display:flex;justify-content:space-between;margin-top:20px">
                <button id="ubits-filter-save" style="padding:8px 15px;background:#28a745;color:white;border:none;border-radius:4px;cursor:pointer">保存</button>
                <button id="ubits-filter-cancel" style="padding:8px 15px;background:#6c757d;color:white;border:none;border-radius:4px;cursor:pointer">取消</button>
            </div>
        `;

        document.body.appendChild(dialog);

        // 启用/禁用"仅当双评分均为N/A时删除"选项
        document.getElementById('ubits-filter-remove-na').addEventListener('change', function() {
            document.getElementById('ubits-filter-remove-only-all-na').disabled = !this.checked;
        });

        // 保存配置
        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.removeOnlyAllNA = document.getElementById('ubits-filter-remove-only-all-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);
})();