JAV本地视频匹配

匹配本地视频文件名并标记 JavBus 和 JavLibrary 网站上的电影。

// ==UserScript==
// @name         JAV本地视频匹配
// @version      1.6
// @author       Gemini
// @description  匹配本地视频文件名并标记 JavBus 和 JavLibrary 网站上的电影。
// @license      MIT
// @icon         https://www.javbus.com/favicon.ico
// @include      /^https:\/\/(\w*\.)?JavBus(\d)*\.com.*$/
// @match        *://*.javbus.com/*
// @match        *://*.javlibrary.com/*
// @namespace    https://greasyfork.org/users/439255
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 创建一个悬浮按钮,用于触发文件选择
    const button = document.createElement('button');
    button.innerHTML = '仓';
    button.style.position = 'fixed';
    button.style.bottom = '10px';
    button.style.left = '10px';
    button.style.zIndex = '9999';
    button.style.border = '1px solid #ccc';
    button.style.padding = '5px 10px';
    button.style.cursor = 'pointer';
    button.style.borderRadius = '5px';

    button.addEventListener('click', btnClick);
    document.body.appendChild(button);

    /**
     * 按钮点击事件处理函数
     * 触发文件选择对话框
     */
    async function btnClick() {
        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = '.txt';
        fileInput.onchange = async () => {
            const file = fileInput.files[0];
            if (file) {
                // 清空旧数据并读取新文件
                localStorage.removeItem("movieCodes");
                console.log("旧电影代码已清除");

                const movieCodes = await readFile(file);
                localStorage.setItem("movieCodes", JSON.stringify(movieCodes));
                console.log(`成功加载 ${movieCodes.length} 个新电影代码`);
                start(); // 重新执行匹配
            }
        };
        fileInput.click();
    }

    /**
     * 读取并解析txt文件
     * @param {File} file - 用户选择的txt文件
     * @returns {Promise<string[]>} - 解析后的电影番号数组
     */
    async function readFile(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = (event) => {
                const content = event.target.result;
                const movieCodes = content.split('\n')
                    .map(line => line.trim()) // 去除首尾空格
                    .filter(line => line)      // 过滤空行
                    .map(extractMovieCode)     // 提取番号
                    .filter(code => code);     // 过滤无效番号
                resolve(movieCodes);
            };
            reader.onerror = (error) => reject(error);
            reader.readAsText(file);
        });
    }

    /**
     * 从文件名中提取电影番号
     * @param {string} filename - 文件名
     * @returns {string} - 提取到的番号,如果匹配失败则返回空字符串
     */
    function extractMovieCode(filename) {
        // 移除常见的前缀和后缀
        let code = filename.replace(/^hhd800\.com@/i, '')
                           .replace(/\.(mp4|mkv|avi|wmv|mov)$/i, '')
                           .replace(/-C$/i, '');

        // 优先匹配 "字母-数字" 格式,例如 ABC-123, FSDSS-456, FC2-789012
        let match = code.match(/^([a-zA-Z]{2,}-\d+)/i);
        if (match) {
            return match[1].toUpperCase();
        }

        // 其次匹配无连字符的格式,例如 IPX123, MIDE999
        match = code.match(/^([a-zA-Z]+)(\d+)/i);
        if (match) {
            return `${match[1].toUpperCase()}-${match[2]}`;
        }

        return ''; // 未匹配到有效格式,返回空
    }



    /**
     * 主函数,在页面上标记已有的电影
     */
    async function start() {
        const movieCodesStr = localStorage.getItem('movieCodes');
        if (!movieCodesStr) {
            console.log("本地存储中没有电影代码,请先选择文件。");
            return;
        }
        const movieCodes = JSON.parse(movieCodesStr);
        if (movieCodes.length === 0) return; // 如果没有代码,则不执行

        const markStyle = '5px solid red'; // 统一标记样式

        if (location.href.includes('javbus.com')) {
            // 列表页
            document.querySelectorAll('#waterfall .movie-box').forEach(ele => {
                const url = ele.getAttribute('href');
                const code = url.substring(url.lastIndexOf('/') + 1);
                if (movieCodes.includes(code.toUpperCase())) {
                    ele.style.border = markStyle;
                }
            });

            // 详情页
            const url = location.href;
            const code = url.substring(url.lastIndexOf('/') + 1);
            if (movieCodes.includes(code.toUpperCase())) {
                const movieDetails = document.querySelector('div.row.movie');
                if (movieDetails) {
                    movieDetails.style.backgroundColor = 'gold';
                }
            }
        } else if (location.href.includes('javlibrary.com')) {
            // 列表页
            document.querySelectorAll('.video').forEach(ele => {
                const idElement = ele.querySelector('.id');
                if (idElement) {
                    const code = idElement.textContent.trim();
                    if (movieCodes.includes(code.toUpperCase())) {
                        ele.style.border = markStyle;
                    }
                }
            });
        }
    }

    // 脚本加载时立即执行一次匹配
    start();
})();