Local Movie Search

在网页上添加输入框和按钮,搜索本地电影是否存在(需要配合Everything的HTTP服务器使用)。注:拖选要搜索的电影名再使用ALT+C快捷键可直接搜索。

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Local Movie Search
// @namespace    http://tampermonkey.net/
// @version      1.9
// @description  在网页上添加输入框和按钮,搜索本地电影是否存在(需要配合Everything的HTTP服务器使用)。注:拖选要搜索的电影名再使用ALT+C快捷键可直接搜索。
// @author       huangmmd
// @match        *://*/*
// @license      MIT
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    // 创建输入框和按钮
    const input = document.createElement('input');
    input.placeholder = '输入电影名字';
    input.style.padding = '7.744px 11.616px'; // 7.04px * 1.1 = 7.744px, 10.56px * 1.1 = 11.616px
    input.style.border = '2.2px solid #ccc'; // 2px * 1.1 = 2.2px
    input.style.borderRadius = '3.872px'; // 3.52px * 1.1 = 3.872px
    input.style.marginRight = '4.84px'; // 4.4px * 1.1 = 4.84px
    input.style.fontSize = '13.552px'; // 12.32px * 1.1 = 13.552px

    const button = document.createElement('button');
    button.textContent = '搜索'; // 修改按钮文本为“搜索”
    button.style.backgroundColor = '#007BFF';
    button.style.color = 'white';
    button.style.border = 'none';
    button.style.borderRadius = '4px'; // 调整圆角大小
    button.style.cursor = 'pointer';
    button.style.fontSize = '12px'; // 调整字体大小为12px
    button.style.padding = '6px 12px'; // 调整内边距
    button.addEventListener('mouseover', function() {
        this.style.backgroundColor = '#0056b3';
    });
    button.addEventListener('mouseout', function() {
        this.style.backgroundColor = '#007BFF';
    });

    // 创建用于显示结果的元素
    const resultDiv = document.createElement('div');
    resultDiv.style.color = '#333';
    resultDiv.style.marginBottom = '5px';
    resultDiv.style.fontSize = '13.552px'; // 修改字体大小为与输入框一致
    resultDiv.style.maxHeight = '200px'; // 设置最大高度
    resultDiv.style.overflowY = 'auto'; // 添加垂直滚动条

    // 创建用于触发搜索界面显示和隐藏的小方框按钮
    const toggleButton = document.createElement('button');
    toggleButton.textContent = '搜';
    toggleButton.style.padding = '7.744px 11.616px'; // 7.04px * 1.1 = 7.744px, 10.56px * 1.1 = 11.616px
    toggleButton.style.backgroundColor = '#007BFF'; // 与搜索按钮颜色一致
    toggleButton.style.color = 'white';
    toggleButton.style.border = 'none';
    toggleButton.style.borderRadius = '3.872px'; // 3.52px * 1.1 = 3.872px
    toggleButton.style.cursor = 'pointer';
    toggleButton.style.fontSize = '13.552px'; // 12.32px * 1.1 = 13.552px
    toggleButton.style.position = 'fixed';
    toggleButton.style.bottom = '19.36px'; // 17.6px * 1.1 = 19.36px
    toggleButton.style.left = '19.36px'; // 移动到左下角
    toggleButton.style.zIndex = 9999;
    toggleButton.addEventListener('click', function() {
        container.style.display = container.style.display === 'none' ? 'block' : 'none';
        toggleButton.style.display = container.style.display === 'none' ? 'block' : 'none'; // 隐藏或显示“搜”字按钮
    });
    document.body.appendChild(toggleButton);

    // 将输入框、按钮和结果显示元素添加到页面左下角,并默认隐藏
    const container = document.createElement('div');
    container.style.position = 'fixed';
    container.style.bottom = '19.36px'; // 17.6px * 1.1 = 19.36px
    container.style.left = '19.36px'; // 17.6px * 1.1 = 19.36px
    container.style.zIndex = 9999;
    container.style.backgroundColor = 'white';
    container.style.padding = '14.52px'; // 13.2px * 1.1 = 14.52px
    container.style.borderRadius = '7.744px'; // 7.04px * 1.1 = 7.744px
    container.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)';
    container.style.display = 'none'; // 默认隐藏搜索界面
    container.appendChild(resultDiv);
    container.appendChild(input);
    container.appendChild(button);
    document.body.appendChild(container);

    // 创建设置面板
    const settingsContainer = document.createElement('div');
    settingsContainer.style.position = 'fixed';
    settingsContainer.style.top = '50%';
    settingsContainer.style.left = '50%';
    settingsContainer.style.transform = 'translate(-50%, -50%)'; // 居中显示
    settingsContainer.style.zIndex = 9999;
    settingsContainer.style.backgroundColor = 'white';
    settingsContainer.style.padding = '15px';
    settingsContainer.style.borderRadius = '8px';
    settingsContainer.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)';
    settingsContainer.style.display = 'none'; // 默认隐藏设置面板

    // 添加关闭按钮
    const closeButton = document.createElement('button');
    closeButton.textContent = '关闭';
    closeButton.style.position = 'absolute';
    closeButton.style.top = '9.68px'; // 8.8px * 1.1 = 9.68px
    closeButton.style.right = '9.68px'; // 8.8px * 1.1 = 9.68px
    closeButton.style.padding = '4.84px 9.68px'; // 4.4px * 1.1 = 4.84px, 8.8px * 1.1 = 9.68px
    closeButton.style.backgroundColor = '#dc3545';
    closeButton.style.color = 'white';
    closeButton.style.border = 'none';
    closeButton.style.borderRadius = '3.872px'; // 3.52px * 1.1 = 3.872px
    closeButton.style.cursor = 'pointer';
    closeButton.style.fontSize = '11.616px'; // 10.56px * 1.1 = 11.616px
    closeButton.addEventListener('click', function() {
        settingsContainer.style.display = 'none';
    });
    settingsContainer.appendChild(closeButton);

    const settingsLabel = document.createElement('label');
    settingsLabel.textContent = '检索服务器地址: ';
    settingsLabel.style.display = 'block';
    settingsLabel.style.marginBottom = '4.84px'; // 4.4px * 1.1 = 4.84px

    const settingsInput = document.createElement('input');
    settingsInput.type = 'text';
    settingsInput.value = 'http://localhost:8080'; // 默认值
    settingsInput.style.width = '193.6px'; // 176px * 1.1 = 193.6px
    settingsInput.style.padding = '7.744px 11.616px'; // 7.04px * 1.1 = 7.744px, 10.56px * 1.1 = 11.616px
    settingsInput.style.border = '2px solid #ccc';
    settingsInput.style.borderRadius = '3.872px'; // 3.52px * 1.1 = 3.872px
    settingsInput.style.marginRight = '4.84px'; // 4.4px * 1.1 = 4.84px
    settingsInput.style.fontSize = '13.552px'; // 12.32px * 1.1 = 13.552px

    const settingsSaveButton = document.createElement('button');
    settingsSaveButton.textContent = '保存';
    settingsSaveButton.style.padding = '7.744px 11.616px'; // 7.04px * 1.1 = 7.744px, 10.56px * 1.1 = 11.616px
    settingsSaveButton.style.backgroundColor = '#007BFF';
    settingsSaveButton.style.color = 'white';
    settingsSaveButton.style.border = 'none';
    settingsSaveButton.style.borderRadius = '3.872px'; // 3.52px * 1.1 = 3.872px
    settingsSaveButton.style.cursor = 'pointer';
    settingsSaveButton.style.fontSize = '13.552px'; // 12.32px * 1.1 = 13.552px

    settingsContainer.appendChild(settingsLabel);
    settingsContainer.appendChild(settingsInput);
    settingsContainer.appendChild(settingsSaveButton);
    document.body.appendChild(settingsContainer);

    // 加载保存的服务器地址
    const savedUrl = localStorage.getItem('searchServerUrl');
    if (savedUrl) {
        settingsInput.value = savedUrl; // 恢复用户设置的地址
    }

    // 使用 GM_registerMenuCommand 创建设置菜单
    GM_registerMenuCommand('设置检索服务器地址', function() {
        settingsContainer.style.display = settingsContainer.style.display === 'none' ? 'block' : 'none';
    });

    // 修改保存按钮逻辑,确保地址持久化并全局生效
    settingsSaveButton.addEventListener('click', function() {
        const newUrl = settingsInput.value.trim();
        if (newUrl) {
            localStorage.setItem('searchServerUrl', newUrl); // 持久化到 localStorage
            settingsContainer.style.display = 'none';
        } else {
            alert('请输入有效的检索服务器地址');
        }
    });

    // 在页面加载时同步检索服务器地址到输入框
    window.addEventListener('load', function() {
        const globalSavedUrl = localStorage.getItem('searchServerUrl');
        if (globalSavedUrl) {
            settingsInput.value = globalSavedUrl; // 确保所有页面加载时使用相同的地址
        }
    });

    // 创建新的设置面板
    const websiteSettingsContainer = document.createElement('div');
    websiteSettingsContainer.style.position = 'fixed';
    websiteSettingsContainer.style.top = '50%';
    websiteSettingsContainer.style.left = '50%';
    websiteSettingsContainer.style.transform = 'translate(-50%, -50%)'; // 居中显示
    websiteSettingsContainer.style.zIndex = 9999;
    websiteSettingsContainer.style.backgroundColor = 'white';
    websiteSettingsContainer.style.padding = '15px';
    websiteSettingsContainer.style.borderRadius = '8px';
    websiteSettingsContainer.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)';
    websiteSettingsContainer.style.display = 'none'; // 默认隐藏设置面板

    // 添加关闭按钮
    const websiteCloseButton = document.createElement('button');
    websiteCloseButton.textContent = '关闭';
    websiteCloseButton.style.position = 'absolute';
    websiteCloseButton.style.top = '9.68px'; // 8.8px * 1.1 = 9.68px
    websiteCloseButton.style.right = '9.68px'; // 8.8px * 1.1 = 9.68px
    websiteCloseButton.style.padding = '4.84px 9.68px'; // 4.4px * 1.1 = 4.84px, 8.8px * 1.1 = 9.616px
    websiteCloseButton.style.backgroundColor = '#dc3545';
    websiteCloseButton.style.color = 'white';
    websiteCloseButton.style.border = 'none';
    websiteCloseButton.style.borderRadius = '3.872px'; // 3.52px * 1.1 = 3.872px
    websiteCloseButton.style.cursor = 'pointer';
    websiteCloseButton.style.fontSize = '11.616px'; // 10.56px * 1.1 = 11.616px
    websiteCloseButton.addEventListener('click', function() {
        websiteSettingsContainer.style.display = 'none';
    });
    websiteSettingsContainer.appendChild(websiteCloseButton);

    const websiteSettingsLabel = document.createElement('label');
    websiteSettingsLabel.textContent = '自动搜索网站列表 (每行一个URL): ';
    websiteSettingsLabel.style.display = 'block';
    websiteSettingsLabel.style.marginBottom = '4.84px'; // 4.4px * 1.1 = 4.84px

    const websiteSettingsTextarea = document.createElement('textarea');
    websiteSettingsTextarea.style.width = '300px'; // 设置宽度
    websiteSettingsTextarea.style.height = '100px'; // 设置高度
    websiteSettingsTextarea.style.padding = '7.744px 11.616px'; // 7.04px * 1.1 = 7.744px, 10.56px * 1.1 = 11.616px
    websiteSettingsTextarea.style.border = '2px solid #ccc';
    websiteSettingsTextarea.style.borderRadius = '3.872px'; // 3.52px * 1.1 = 3.872px
    websiteSettingsTextarea.style.marginRight = '4.84px'; // 4.4px * 1.1 = 4.84px
    websiteSettingsTextarea.style.fontSize = '13.552px'; // 12.32px * 1.1 = 13.552px

    // 加载保存的网站列表
    const savedWebsites = GM_getValue('autoSearchWebsites', '');
    websiteSettingsTextarea.value = savedWebsites;

    const websiteSettingsSaveButton = document.createElement('button');
    websiteSettingsSaveButton.textContent = '保存';
    websiteSettingsSaveButton.style.padding = '7.744px 11.616px'; // 7.04px * 1.1 = 7.744px, 10.56px * 1.1 = 11.616px
    websiteSettingsSaveButton.style.backgroundColor = '#007BFF';
    websiteSettingsSaveButton.style.color = 'white';
    websiteSettingsSaveButton.style.border = 'none';
    websiteSettingsSaveButton.style.borderRadius = '3.872px'; // 3.52px * 1.1 = 3.872px
    websiteSettingsSaveButton.style.cursor = 'pointer';
    websiteSettingsSaveButton.style.fontSize = '13.552px'; // 12.32px * 1.1 = 13.552px
    websiteSettingsSaveButton.addEventListener('click', function() {
        GM_setValue('autoSearchWebsites', websiteSettingsTextarea.value);
        websiteSettingsContainer.style.display = 'none';
    });

    websiteSettingsContainer.appendChild(websiteSettingsLabel);
    websiteSettingsContainer.appendChild(websiteSettingsTextarea);
    websiteSettingsContainer.appendChild(websiteSettingsSaveButton);
    document.body.appendChild(websiteSettingsContainer);

    // 使用 GM_registerMenuCommand 创建设置菜单
    GM_registerMenuCommand('设置自动搜索网站列表', function() {
        websiteSettingsContainer.style.display = websiteSettingsContainer.style.display === 'none' ? 'block' : 'none';
    });

    // 按钮点击事件处理函数
    function performSearch() {
        const movieName = input.value.trim();
        if (movieName) {
            // 检查电影名字是否包含“[]”或“《》”或“<>”符号
            const shouldSplit = !/[\[\]《》<>]/.test(movieName);

            // 根据检查结果决定是否将电影名字按空格拆分成多个部分
            const movieParts = shouldSplit ? movieName.split(' ') : [movieName];

            // 存储所有找到的电影和未找到的电影
            let allFoundMovies = [];
            let notFoundMovies = [];

            // 遍历每个部分进行搜索
            movieParts.forEach(part => {
                if (part) {
                    const searchUrl = `${settingsInput.value}/?s=${encodeURIComponent(part)}`;

                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: searchUrl,
                        onload: function(response) {
                            // 修改 commonFormats 数组,增加对 .zip 和 .epub 格式的支持
                            const commonFormats = ['.mp4', '.mkv', '.avi', '.mov', '.flv', '.wmv', '.zip', '.epub'];
                            let foundMovies = [];
                            commonFormats.forEach(format => {
                                const regex = new RegExp(`[^\\s"']+${format}`, 'g');
                                let matches = response.responseText.match(regex);
                                if (matches) {
                                    foundMovies = foundMovies.concat(matches);
                                }
                            });

                            // 过滤掉包含 /favi 的结果
                            foundMovies = foundMovies.filter(movie => !movie.includes('/favi'));

                            // 将找到的电影添加到所有找到的电影数组中
                            if (foundMovies.length > 0) {
                                allFoundMovies = allFoundMovies.concat(foundMovies);
                            } else {
                                notFoundMovies.push(part);
                            }

                            // 显示所有找到的电影和未找到的电影
                            let resultText = '';
                            if (allFoundMovies.length > 0) {
                                resultText += '本地存在以下电影、漫画:<br>';
                                allFoundMovies.forEach(movie => {
                                    let decodedMovie = decodeURIComponent(movie);
                                    // 提取文件路径和文件名
                                    let filePath = decodedMovie.substring(0, decodedMovie.lastIndexOf('/'));
                                    let fileName = decodedMovie.substring(decodedMovie.lastIndexOf('/') + 1);
                                    // 设置 title 属性为文件名
                                    resultText += `<span title="${fileName}">${filePath}</span><br>`;
                                });
                            }
                            if (notFoundMovies.length > 0) {
                                if (allFoundMovies.length > 0) {
                                    resultText += '<br>';
                                }
                                resultText += '<span style="color: red;">本地不存在以下电影、漫画:</span><br>'; // 修改:标红显示
                                notFoundMovies.forEach(movie => {
                                    resultText += `<span style="color: red;">${movie}</span><br>`; // 修改:标红显示
                                });
                            }
                            resultDiv.innerHTML = resultText;

                            // 添加点击事件监听器,点击结果显示页的其他位置时清除结果显示
                            document.addEventListener('click', function clearResult(event) {
                                if (!resultDiv.contains(event.target)) {
                                    resultDiv.textContent = '';
                                    document.removeEventListener('click', clearResult);
                                }
                            });

                            // 根据结果显示情况设置自动消失
                        },
                        onerror: function() {
                            resultDiv.textContent = '请求失败,请检查 Everything 服务器是否正常运行';
                        }
                    });
                }
            });
        } else {
            resultDiv.textContent = '请输入电影名字';
        }
    }

    button.addEventListener('click', performSearch);

    // 绑定快捷键 ALT + C
    document.addEventListener('keydown', function(event) {
        if (event.altKey && event.key === 'c') {
            const selectedText = window.getSelection().toString().trim();
            if (selectedText) {
                input.value = selectedText;
                // 显示搜索界面
                container.style.display = 'block';
                toggleButton.style.display = 'none'; // 隐藏“搜”字按钮
                // 模拟按钮点击事件进行搜索
                performSearch();
            }
        }
    });

    // 监听输入框内容变化
    input.addEventListener('input', function() {
        if (input.value.trim() === '') {
            container.style.display = 'none'; // 隐藏搜索页面
            toggleButton.style.display = 'block'; // 显示“搜”字按钮
        }
    });

    // 检查是否是豆瓣电影页面并提取电影名称
    function extractMovieNameFromDouban() {
        const movieNameElement = document.querySelector('span[property="v:itemreviewed"]');
        if (movieNameElement) {
            return movieNameElement.textContent.trim();
        }
        return null;
    }

    // 检查是否是漫画页面并提取漫画名称
    function extractMangaNameFromPage() {
        const mangaNameElement = document.querySelector('font.text_bglight_big');
        if (mangaNameElement) {
            return mangaNameElement.textContent.trim();
        }
        return null;
    }

    // 新增:检查页面是否存在 <h2>*****</h2> 元素并提取文件名
    function extractFileNameFromH2() {
        const h2Element = document.querySelector('h2');
        if (h2Element) {
            return h2Element.textContent.trim();
        }
        return null;
    }

    // 新增:检查页面是否存在 <span style="color:#CC0000;">*******</span> 元素并提取文件名
    function extractFileNameFromSpan() {
        const spanElement = document.querySelector('span[style="color:#CC0000;"]');
        if (spanElement) {
            return spanElement.textContent.trim();
        }
        return null;
    }

    // 自动填充并搜索电影或漫画名称
    function autoSearchDoubanMovie() {
        const currentUrl = window.location.href;
        const savedWebsites = GM_getValue('autoSearchWebsites', '');
        const websites = savedWebsites ? savedWebsites.split('\n').map(url => url.trim()) : [];

        // 新增:增加额外检查,确保 websites 列表有效且非空
        if (!websites || websites.length === 0 || websites.every(url => !url)) {
            console.log('自动搜索网站列表为空或无效,跳过自动搜索');
            return;
        }

        // 检查当前页面是否属于自动搜索网站列表
        if (websites.some(website => currentUrl.includes(website))) {
            if (currentUrl.includes('https://movie.douban.com/')) {
                const name = extractMovieNameFromDouban();
                if (name) {
                    input.value = name;
                    // 显示搜索界面
                    container.style.display = 'block';
                    toggleButton.style.display = 'none'; // 隐藏“搜”字按钮
                    performSearch();
                }
            } else {
                let name = extractFileNameFromSpan(); // 优先尝试提取 <span style="color:#CC0000;">*******</span> 元素中的文件名
                if (!name) {
                    name = extractMovieNameFromDouban();
                }
                if (!name) {
                    name = extractMangaNameFromPage();
                }
                if (!name) {
                    name = extractFileNameFromH2(); // 最后尝试提取 <h2> 元素中的文件名
                }
                if (name) {
                    input.value = name;
                    // 显示搜索界面
                    container.style.display = 'block';
                    toggleButton.style.display = 'none'; // 隐藏“搜”字按钮
                    performSearch();
                }
            }
        }
    }

    // 在页面加载完成后尝试自动搜索豆瓣电影或漫画
    window.addEventListener('load', function() {
        autoSearchDoubanMovie();
    });

    // 添加全局点击事件监听器
    document.addEventListener('click', function(event) {
        if (input.value.trim() === '' && !container.contains(event.target) && !toggleButton.contains(event.target)) {
            container.style.display = 'none'; // 隐藏插件页面
            toggleButton.style.display = 'block'; // 显示切换按钮
        }
    });

})();