Jsoso

通用搜索框架 - 用于多个网站的自动外部资源搜索

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

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

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

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

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

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Jsoso
// @namespace    Jsoso
// @version      1.0.2
// @description  通用搜索框架 - 用于多个网站的自动外部资源搜索
// @author       sexjpg
// @grant        GM_xmlhttpRequest
// @grant        GM.setValue
// @grant        GM.getValue
// @grant        GM_registerMenuCommand
// @run-at       document-end
// @license MIT
// @connect      *
// @noframes
// @match        *://*.fc2ppvdb.com/*
// @match        *://*.javdb.com/*
// @match        *://*.xiaojiadianmovie.be/*
// @match        *://*.javbus.com/*
// ==/UserScript==


// 创建样式元素并添加到页面
(function () {
    'use strict';
    const customCSS = `
/* 浮动菜单样式 */
#floating-menu {
    position: fixed;
    top: 50%;
    left: 0;
    transform: translateY(-50%);
    width: 200px;
    background-color: rgba(0, 0, 0, 0.9);
    color: white;
    padding: 10px;
    border-radius: 0 10px 10px 0;
    z-index: 10000;
    cursor: grab;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
    transition: all 0.3s ease;
    backdrop-filter: blur(4px);
}

/* 按钮颜色类 */
.bg-blue-500 {
    background-color: rgb(59 130 246) !important;
}

.bg-green-500 {
    background-color: rgb(34 197 94) !important;
}

.bg-red-500 {
    background-color: rgb(239 68 68) !important;
}

.bg-purple-500 {
    background-color: rgb(168 85 247) !important;
}

.bg-yellow-500 {
    background-color: rgb(234 179 8) !important;
}

.bg-orange-500 {
    background-color: rgb(249 115 22) !important;
}

.bg-gray-500 {
    background-color: rgb(107 114 128) !important;
}

/* 加载动画样式 */
.loading-container {
    display: inline-flex;
    align-items: center;
    margin-left: 0.5rem;
}

.loading-spinner {
    width: 1.2rem;
    height: 1.2rem;
    border: 3px solid rgba(243, 243, 243, 0.2);
    border-top: 3px solid #3498db;
    border-radius: 50%;
    animation: spin 0.8s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite;
}

.loading-progress {
    width: 100px;
    height: 4px;
    background-color: #e0e0e0;
    border-radius: 2px;
    margin-left: 0.5rem;
    overflow: hidden;
}

.text-gray-500 {
    color: rgb(107 114 128);
}

.ml-2 {
    margin-left: 0.5rem;
}

/* 结果容器样式 */
.resultContainer {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

/* 通用样式 */
.inline-block {
    display: inline-block;
}

.text-white {
    color: white;
}

.px-2 {
    padding-left: 0.5rem;
    padding-right: 0.5rem;
}

.py-1 {
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
}

.rounded {
    border-radius: 0.25rem;
}

.mt-2 {
    margin-top: 0.5rem;
}

.ml-2 {
    margin-left: 0.5rem;
}

/* 充能按钮样式 */
.relative {
    position: relative;
}

.overflow-hidden {
    overflow: hidden;
}

.rounded-lg {
    border-radius: 0.5rem;
}

.absolute {
    position: absolute;
}

.inset-0 {
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}

.bg-green-500\/50 {
    background-color: rgba(34, 197, 94, 0.5);
}

.bg-transparent {
    background-color: transparent;
}

.px-3 {
    padding-left: 0.75rem;
    padding-right: 0.75rem;
}

.py-1\\.5 {
    padding-top: 0.375rem;
    padding-bottom: 0.375rem;
}

.transition-all {
    transition-property: all;
    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    transition-duration: 150ms;
}

.duration-200 {
    transition-duration: 200ms;
}

.hover\\:scale-105:hover {
    transform: scale(1.05);
}

.hover\\:shadow-lg:hover {
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}

.active\\:scale-95:active {
    transform: scale(0.95);
}

/* 动画关键帧 */
@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}
`;
    const styleElement = document.createElement('style');
    styleElement.textContent = customCSS;
    document.head.appendChild(styleElement);
})();



// (function () {
'use strict';

// ==================== CSS样式 ====================





// ==================== 全局变量 ====================
let Glob_enableCharging;
let cache = {};
GM_registerMenuCommand('打开设置', () => { JsosoConfig() });



// ==================== FloatingMenu类 ====================
/**
 * 悬浮菜单类
 * 负责创建悬浮菜单,并实现拖拽、展开/收起功能
 */
class FloatingMenu {
    constructor(titleText) {
        this.menu = null;
        this.title = null;
        this.content = null;
        this.isDragging = false;
        this.offsetX = 0;
        this.offsetY = 0;
        this.createMenu(titleText);
    }

    createMenu(titleText) {
        this.menu = document.createElement("div");
        this.menu.id = "floating-menu";
        this.menu.style.position = "fixed";
        this.menu.style.top = "50%";
        this.menu.style.left = "45%";
        this.menu.style.transform = "translateY(-50%)";
        this.menu.style.width = "200px";
        // this.menu.style.backgroundColor = "rgba(0, 0, 0, 0.9)";
        this.menu.style.color = "white";
        this.menu.style.padding = "10px";
        this.menu.style.borderRadius = "0 10px 10px 0";
        this.menu.style.zIndex = "10000";
        this.menu.style.cursor = "grab";
        this.menu.style.boxShadow = "0 4px 16px rgba(0, 0, 0, 0.3)";
        this.menu.style.transition = "all 0.3s ease";
        this.menu.style.backdropFilter = "blur(4px)";
        document.body.appendChild(this.menu);
        this.menu.style.display = "none";

        this.title = document.createElement("div");
        this.title.textContent = titleText;
        this.title.style.fontWeight = "bold";
        this.title.style.marginBottom = "10px";
        this.title.style.cursor = "pointer";
        this.title.style.textAlign = "center";
        this.menu.appendChild(this.title);

        this.content = document.createElement("div");
        this.content.style.display = "none";
        this.menu.appendChild(this.content);

        this.addEventListeners();
    }

    addEventListeners() {
        this.title.addEventListener("click", () => {
            const isHidden = this.content.style.display === "none";
            this.content.style.display = isHidden ? "block" : "none";
            const isHidden2 = this.menu.style.display === "none";
            this.menu.style.display = isHidden2 ? "block" : "none";
        });

        this.menu.addEventListener("mousedown", (e) => {
            this.isDragging = true;
            this.offsetX = e.clientX - this.menu.getBoundingClientRect().left;
            this.offsetY = e.clientY - this.menu.getBoundingClientRect().top;
            this.menu.style.cursor = "grabbing";
        });

        document.addEventListener("mousemove", (e) => {
            if (this.isDragging) {
                this.menu.style.left = `${e.clientX - this.offsetX}px`;
                this.menu.style.top = `${e.clientY - this.offsetY}px`;
                this.menu.style.transform = "none";
            }
        });

        document.addEventListener("mouseup", () => {
            this.isDragging = false;
            this.menu.style.cursor = "grab";
        });
    }

    addContent(element) {
        this.content.appendChild(element);
    }
}

// ==================== Site类 ====================
/**
 * 单个网址类
 */
class Site {
    constructor(name, searchUrlFunc, responseHandler, buttonColor, queryInterval = 1, maxConcurrentSearches = 3) {
        this.name = name;
        this.searchUrlFunc = searchUrlFunc;
        this.responseHandler = responseHandler;
        this.buttonColor = buttonColor;
        this.queryInterval = queryInterval;
        this.maxConcurrentSearches = maxConcurrentSearches;
        this.queue = [];
        this.isSearching = 0;
    }

    addToQueue(id, element) {
        this.queue.push({
            id,
            element
        });
        if (this.isSearching < this.maxConcurrentSearches) {
            this.processQueue();
        }
    }

    async processQueue() {
        while (this.queue.length > 0 && this.isSearching < this.maxConcurrentSearches) {
            const {
                id,
                element
            } = this.queue.shift();
            this.isSearching++;
            try {
                await this.search(id, element);
            } finally {
                this.isSearching--;
                await new Promise(resolve => setTimeout(resolve, this.queryInterval));
            }
        }
    }

    async search(id, element) {
        const cacheKey = `${this.name}_${id}`;
        const cachedResult = await Utils.getCachedResult(cacheKey);
        if (cachedResult) {
            if (cachedResult.url) {
                this.addButton(element, cachedResult.url);
            }
            return;
        }

        const searchUrl = this.searchUrlFunc(id);
        const loadingElement = Utils.showLoading(element, this.name);
        new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: searchUrl,
                onload: (response) => {
                    let previewData = {
                        url: ''
                    };
                    this.responseHandler(response, id, previewData);
                    if (previewData.url) {
                        this.addButton(element, previewData.url);
                    }
                    Utils.removeLoading(loadingElement);
                    Utils.cacheResult(cacheKey, previewData);
                },
                onerror: () => {
                    Utils.removeLoading(loadingElement);
                    console.error(`${this.name} ${id}搜索失败`);
                },
            });
        })
    }

    addButton(element, url) {
        const button = Utils.createButton(url, this.name, this.buttonColor);
        element.appendChild(button);
    }
}

// ==================== SearchManager类 ====================
/**
 * 管理所有网站的类
 */
class SearchManager {
    constructor() {
        this.sites = [];
        this.SearchItems = []
    }

    addSite(site) {
        this.sites.push(site);
    }

    // 初始化搜索 - 需要根据具体网站实现
    async initializeSearch() {

        this.SearchItems = this.SearchItems.filter(item => item.id !== "");
        const uniqueItems = [];
        const seenIds = new Set();
        for (const item of this.SearchItems) {
            if (!seenIds.has(item.id)) {
                seenIds.add(item.id);
                uniqueItems.push(item);
            }
        }

        this.SearchItems = uniqueItems;
        this.SearchSomething()
    }

    // 单个页面搜索 - 需要根据具体网站实现
    async fc2ppvdb_singlePage() {
        const items = document.querySelectorAll('div#article-info')
        const activeSites = await this.getActiveSites();
        Utils.removeDivs()
        items.forEach(item => {
            const id = item.getAttribute("data-videoid")
            const container = Utils.createDiv();
            const element = item.querySelector('h2').parentElement
            element.appendChild(container);
            this.sites.forEach(site => {
                if (activeSites[site.name]) {
                    site.addToQueue(id, container);
                }
            });

        })
    }

    // 墙面搜索 - 需要根据具体网站实现
    async fc2ppvdb_wall() {
        const items = document.querySelectorAll("span.absolute.top-0.left-0")
        const activeSites = await this.getActiveSites();
        if (!items) return
        Utils.removeDivs()

        items.forEach(item => {
            if (!item) return
            let id = item.textContent.trim();
            id = Utils.formatID(id)
            const container = Utils.createDiv();
            const element = item.parentElement.parentElement
            element.appendChild(container)
            this.sites.forEach(site => {
                if (activeSites[site.name]) {
                    site.addToQueue(id, container);
                }
            });
        });
    }


    // 获取ID和作用容器的模板函数,以FC2PPVDB为例,需返回{id,element},id是要搜索的关键字,element是存放结果容器的父容器
    getitems() {
        const items = []
        const items_ = document.querySelectorAll("span.absolute.top-0.left-0")
        items_.forEach(item => {
            const id = Utils.formatID(item.textContent.trim());
            const element = item.parentElement.parentElement
            items.push({
                id,
                element
            })
        })
        return items
    }


    async SearchSomething() {
        const items = this.SearchItems
        const activeSites = await this.getActiveSites();
        Utils.removeDivs()
        items.forEach(item => {
            const id = item.id;
            const element = item.element
            const container = Utils.createDiv();
            element.appendChild(container)
            this.sites.forEach(site => {
                if (activeSites[site.name]) {
                    site.addToQueue(id, container);
                }
            });
        });

    }

    async getActiveSites() {
        const activeSites = {};
        for (const site of this.sites) {
            activeSites[site.name] = await GM.getValue(`site_${site.name}`, true);
        }
        return activeSites;
    }
}

// ==================== Utils类 ====================
/**
 * 通用工具类
 */
class Utils {

    static async setCache(key, value) {
        cache = await GM.getValue('cache', {});
        cache[key] = value;
        await GM.setValue('cache', cache);
    }

    static async getCache(key, value = null) {
        cache = await GM.getValue('cache', {});
        if (!cache[key]) { return value }
        return cache[key];
    }
    static showLoading(element, siteName) {
        const loadingContainer = document.createElement("div");
        loadingContainer.className = "loading-container";
        loadingContainer.style.display = "inline-flex";
        loadingContainer.style.alignItems = "center";
        loadingContainer.style.marginLeft = "0.5rem";

        const spinner = document.createElement("div");
        spinner.className = "loading-spinner";
        spinner.style.width = "1.2rem";
        spinner.style.height = "1.2rem";
        spinner.style.border = "3px solid rgba(243, 243, 243, 0.2)";
        spinner.style.borderTop = "3px solid #3498db";
        spinner.style.borderRadius = "50%";
        spinner.style.animation = "spin 0.8s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite";

        const progressBar = document.createElement("div");
        progressBar.className = "loading-progress";
        progressBar.style.width = "100px";
        progressBar.style.height = "4px";
        progressBar.style.backgroundColor = "#e0e0e0";
        progressBar.style.borderRadius = "2px";
        progressBar.style.marginLeft = "0.5rem";
        progressBar.style.overflow = "hidden";

        const progressInner = document.createElement("div");
        progressInner.style.width = "0%";
        progressInner.style.height = "100%";
        progressInner.style.backgroundColor = "#3498db";
        progressInner.style.transition = "width 0.3s ease";
        progressBar.appendChild(progressInner);

        const loadingText = document.createElement("span");
        loadingText.textContent = `搜索 ${siteName}...`;
        loadingText.className = "text-gray-500 ml-2";

        loadingContainer.appendChild(spinner);
        loadingContainer.appendChild(progressBar);
        loadingContainer.appendChild(loadingText);
        element.appendChild(loadingContainer);

        let progress = 0;
        const interval = setInterval(() => {
            progress += 10;
            progressInner.style.width = `${progress}%`;
            if (progress >= 100) progress = 0;
        }, 300);

        return {
            container: loadingContainer,
            interval: interval
        };
    }

    static removeLoading(loadingElement) {
        if (loadingElement) {
            clearInterval(loadingElement.interval);
            loadingElement.container.remove();
        }
    }

    static createChargingButton(url, text, bgColorClass) {
        const buttonContainer = document.createElement("div");
        buttonContainer.className = `relative inline-block mt-2 ml-2 overflow-hidden rounded-lg ${bgColorClass}`;
        buttonContainer.style.cursor = "pointer";

        const chargeBg = document.createElement("div");
        chargeBg.className = "absolute inset-0 bg-green-500/50";
        chargeBg.style = "width: 0%; transition: width 0.2s; background-color: rgba(0, 255, 0, 0.5); opacity: 1;"
        chargeBg.style.transition = "width 1s cubic-bezier(0.4, 0, 0.2, 1)";

        const linkButton = document.createElement("a");
        linkButton.textContent = text;
        linkButton.className = "relative block bg-transparent text-white px-3 py-1.5 rounded-lg transition-all duration-200 hover:scale-105 hover:shadow-lg active:scale-95";
        linkButton.style.pointerEvents = "none";

        let isCharging = false;
        let isCharged = false;

        buttonContainer.addEventListener("mousedown", () => {
            isCharging = true;
            isCharged = false;
            chargeBg.style.transition = "width 0.2s cubic-bezier(0.4, 0, 0.2, 1)";
            chargeBg.style.width = "100%";
        });

        const resetCharge = () => {
            if (!isCharged) {
                chargeBg.style.transition = "width 0.2s ease";
                chargeBg.style.width = "0%";
                isCharging = false;
            }
        };

        buttonContainer.addEventListener("mouseup", resetCharge);
        buttonContainer.addEventListener("mouseleave", resetCharge);

        chargeBg.addEventListener("transitionend", (e) => {
            if (e.propertyName === 'width' && chargeBg.style.width === "100%" && isCharging) {
                isCharged = true;
                isCharging = false;

                chargeBg.style.transition = "opacity 0.3s ease";
                chargeBg.style.backgroundColor = "rgba(255, 255, 255, 0.5)";
                chargeBg.style.opacity = "0";

                setTimeout(() => {
                    if (isCharged) {
                        window.open(url, "_blank");
                    }
                    setTimeout(() => {
                        chargeBg.style.transition = "width 1s cubic-bezier(0.4, 0, 0.2, 1)";
                        chargeBg.style.width = "0%";
                        chargeBg.style.backgroundColor = "rgba(0, 255, 0, 0.5)";
                        chargeBg.style.opacity = "1";
                        isCharged = false;
                    }, 300);
                }, 100);
            }
        });

        buttonContainer.appendChild(chargeBg);
        buttonContainer.appendChild(linkButton);
        return buttonContainer;
    }

    static createButton(url, text, bgColorClass) {
        if (Glob_enableCharging) {
            return this.createChargingButton(url, text, bgColorClass);
        } else {
            const linkButton = document.createElement("a");
            linkButton.href = url;
            linkButton.textContent = text;
            linkButton.className = `inline-block ${bgColorClass} text-white px-2 py-1 rounded mt-2 ml-2`;
            linkButton.target = "_blank";
            return linkButton;
        }
    }

    static async cacheResult(key, data, expiryDay = 10) {
        const timestamp = new Date().getTime();
        const expiry = timestamp + expiryDay * 24 * 60 * 60 * 1000;
        const cacheData = {
            data,
            expiry
        };
        await Utils.setCache(key, cacheData);
    }

    static async getCachedResult(key) {
        const cacheData = await Utils.getCache(key);
        if (!cacheData) return null;

        const timestamp = new Date().getTime();
        if (cacheData.expiry < timestamp) {
            await Utils.setCache(key, null);
            return null;
        }
        return cacheData.data;
    }

    static createDiv(className = `resultContainer`) {
        const div = document.createElement('div');
        div.className = className;
        div.style.flexWrap = "wrap";
        div.style.gap = "0.5rem";
        return div;
    }

    static removeDivs(className = `resultContainer`) {
        const divs = document.querySelectorAll(`.${className}`);
        divs.forEach(div => div.remove());
    }

    static formatID(id) {
        if (id.toLowerCase().startsWith('fc2-') && /^\d+$/.test(id.substring(4))) {
            id = id.substring(4);
        }
        return id;
    }

}



const manager = new SearchManager();
// 添加网站和解析结果
manager.addSite(new Site(
    '123av',
    id => `https://123av.com/zh/search?keyword=${id}`,
    (response, id, previewData) => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(response.responseText, "text/html");
        const itemlinks = doc.querySelectorAll("#page-list .box-item a")
        for (let i = 0; i < itemlinks.length; i++) {
            const innertext = itemlinks[i].textContent.trim();
            if (innertext.includes(id)) {
                previewData.url = `https://123av.com/zh/search?keyword=${id}`;
                // previewData.url = itemlinks[i].href;
                break;
            }
        }
    },
    'bg-blue-500'
));

manager.addSite(new Site(
    'MissAV',
    id => `https://missav.ws/search/${id}`,
    (response, id, previewData) => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(response.responseText, "text/html");
        const matchingLink = Array.from(doc.querySelectorAll('a[alt]')).find(a => {
            const altText = a.getAttribute('alt').toLowerCase();
            const linkText = a.textContent.toLowerCase();
            return altText.includes(id.toLowerCase()) && linkText.includes(id.toLowerCase());
        });
        if (matchingLink) {
            previewData.url = `https://missav.ws/search/${id}`;
        }
    },
    'bg-green-500'
));

manager.addSite(new Site(
    '7MMTV',
    id => `https://7mmtv.sx/zh/searchform_search/all/index.html?search_keyword=${encodeURIComponent(id)}&search_type=searchall&op=search`,
    (response, id, previewData) => {
        const responseText = response.responseText;
        const matchResult = responseText.match(new RegExp(`搜索 "${id}" \\((\\d+)\\)`));
        if (matchResult && parseInt(matchResult[1]) > 0) {
            previewData.url = `https://7mmtv.sx/zh/searchform_search/all/index.html?search_keyword=${encodeURIComponent(id)}`;
        }
    },
    'bg-red-500'
));

manager.addSite(new Site(
    'JAVFC2',
    id => `https://javfc2.xyz/search?q=${id}`,
    (response, id, previewData) => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(response.responseText, "text/html");
        const somethingFound = doc.querySelector("div.movie-container")
        if (somethingFound) {
            previewData.url = `https://javfc2.xyz/search?q=${id}`;
        }
    },
    'bg-purple-500'
));

manager.addSite(new Site(
    'Sukebei',
    id => `https://sukebei.nyaa.si/?page=rss&q=${encodeURIComponent(id)}&c=0_0&f=0`,
    (response, id, previewData) => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(response.responseText, "text/xml");
        const items = doc.querySelectorAll("item");
        if (items.length > 0) {
            previewData.url = `https://sukebei.nyaa.si/?f=0&c=0_0&q=${encodeURIComponent(id)}&s=seeders&o=desc`;
        }
    },
    'bg-yellow-500',
    1000
));

manager.addSite(new Site(
    'SupJav',
    id => `https://supjav.com/?s=${id}`,
    (response, id, previewData) => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(response.responseText, "text/html");
        const h1Element = doc.querySelector("h1");
        if (h1Element && h1Element.textContent.includes(`Search Result For: ${id}`)) {
            const resultCount = h1Element.textContent.match(/\((\d+)\)/);
            if (resultCount && parseInt(resultCount[1]) > 0) {
                previewData.url = `https://supjav.com/?s=${id}`;
            }
        }
    },
    'bg-orange-500'
));

manager.addSite(new Site(
    '3xplanet',
    id => `https://3xplanet.com/?s=${id}`,
    (response, id, previewData) => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(response.responseText, "text/html");
        const itemlinks = doc.querySelectorAll("h3 a")
        for (let i = 0; i < itemlinks.length; i++) {
            const innertext = itemlinks[i].title.trim();
            if (innertext.includes(id)) {
                previewData.url = `https://3xplanet.com/?s=${id}`;
                break;
            }
        }
    },
    'bg-gray-500'
));

manager.addSite(new Site(
    'bt4gprx',
    id => `https://bt4gprx.com/search?q=${id}&category=movie`,
    (response, id, previewData) => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(response.responseText, "text/html");
        const items = doc.querySelectorAll("div.list-group div.list-group-item.result-item");
        if (items.length > 0) {
            previewData.url = `https://bt4gprx.com/search?q=${id}&category=movie`;
        }
    },
    'bg-gray-500',
    100
));

manager.addSite(new Site(
    '字幕猫',
    id => `https://subtitlecat.com/index.php?search=${id}`,
    (response, id, previewData) => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(response.responseText, "text/html");
        const itemlinks = doc.querySelectorAll('table.table.sub-table a')
        for (let i = 0; i < itemlinks.length; i++) {
            const innertext = itemlinks[i].textContent.trim();
            if (innertext.includes(id)) {
                previewData.url = `https://subtitlecat.com/index.php?search=${id}`;
                // previewData.url = itemlinks[i].href;
                break;
            }
        }
    },
    'bg-purple-500'
));



// 创建悬浮菜单
const floatingMenu = new FloatingMenu("Jsoso");
// 添加充能按钮
(async () => {
    const enableCharging = await GM.getValue("enableCharging", false);
    Glob_enableCharging = await GM.getValue("enableCharging", false);
    const chargingSwitch = document.createElement("div");
    const checkbox = document.createElement("input");
    checkbox.type = "checkbox";
    checkbox.checked = enableCharging;
    checkbox.id = "charging-switch";
    checkbox.style.marginRight = "5px";

    checkbox.addEventListener("change", async () => {
        await GM.setValue("enableCharging", checkbox.checked);
        Glob_enableCharging = checkbox.checked;
        location.reload();
    });

    const label = document.createElement("label");
    label.textContent = "启动充能";
    label.htmlFor = "charging-switch";

    chargingSwitch.appendChild(checkbox);
    chargingSwitch.appendChild(label);
    floatingMenu.addContent(chargingSwitch);
})();

// 添加网站开关
(async () => {
    const activeSites = await manager.getActiveSites();
    for (const site of manager.sites) {
        const checkbox = document.createElement("input");
        checkbox.type = "checkbox";
        checkbox.id = `checkbox_${site.name}`;
        checkbox.checked = activeSites[site.name];
        checkbox.style.marginRight = "5px";

        checkbox.addEventListener("change", async () => {
            await GM.setValue(`site_${site.name}`, checkbox.checked);
            location.reload();
        });

        const label = document.createElement("label");
        label.textContent = site.name;
        label.htmlFor = `checkbox_${site.name}`;
        label.style.cursor = "pointer";

        const container = document.createElement("div");
        container.appendChild(checkbox);
        container.appendChild(label);
        floatingMenu.addContent(container);
    }
})();


function JsosoConfig(){
    floatingMenu.menu.style.display = "block";
    floatingMenu.content.style.display = "block";
}

// 替换掉FC2PPVDB中无图片的图片
function FC2DB_removeNoImage() {
    if(!window.location.href.includes("fc2ppvdb.com"))return
    const ArticleImage = document.querySelectorAll("#ArticleImage");
    for (let i = 0; i < ArticleImage.length; i++) {
        ArticleImage[i].classList.remove("hidden");
    }
    const NoImage = document.querySelectorAll("img#NoImage");
    for (let i = 0; i < NoImage.length; i++) {
        NoImage[i].display = "none";
    }
}

// 获取ID和作用容器的模板函数
function FC2PPVDB_wall() {
    const items = []
    const items_ = document.querySelectorAll("span.absolute.top-0.left-0")
    items_.forEach(item => {
        const id = Utils.formatID(item.textContent.trim());
        const element = item.parentElement.parentElement
        items.push({
            id,
            element
        })
    })
    return items
}

function FC2PPVDB_singlepage() {
    const items = []
    const items_ = document.querySelectorAll('div#article-info')
    items_.forEach(item => {
        const id = item.getAttribute("data-videoid")
        const element = item.querySelector('h2').parentElement
        items.push({
            id,
            element
        })
    })
    return items
}

function javdb_wall() {
    const items = []
    const items_ = document.querySelectorAll('div.container div.item')
    items_.forEach(item => {
        const id = Utils.formatID(item.querySelector("div.video-title strong")?.textContent.trim() ?? "");
        const element = item
        items.push({
            id,
            element
        })
    })
    return items
}

function javdb_singlepage() {
    const items = []
    const items_ = document.querySelectorAll('div.panel-block.first-block')
    items_.forEach(item => {
        const id = Utils.formatID(item.querySelector("a.button.is-white").getAttribute('data-clipboard-text'));
        const element = item.parentElement
        items.push({
            id,
            element
        })
    })
    return items
}

function FC2_wall() {
    const items = []
    const items_ = document.querySelectorAll('a[href*="/article/"][id*="mui"]')
    items_.forEach(item => {
        const href = item.getAttribute('href');
        const match = href.match(/\/article\/(\d+)/);
        const id = match ? match[1] : "";
        const element = item.parentElement.parentElement
        items.push({
            id,
            element
        })
    })
    return items
}

function FC2_singlepage() {
    const items = []
    const items_ = document.querySelectorAll('.items_article_headerInfo')
    items_.forEach(item => {
        const Ptag = item.querySelectorAll("p")[2]
        const match = Ptag.textContent.trim().match(/\d+$/);
        const id = match ? match[0] : "";
        const element = item
        items.push({
            id,
            element
        })
    })
    return items
}

function FC2_ranking() {
    const items = []
    const items_ = document.querySelectorAll(`h3 a[href*="article_search.php?id="]`);
    items_.forEach(item => {
        const href = item.getAttribute('href');
        const url = new URL(href, window.location.origin);
        const id = url.searchParams.get('id');
        const element = item.parentElement.parentElement
        items.push({
            id,
            element
        })
    })
    return items
}

function javbus_wall() {
    const items = []
    const items_ = document.querySelectorAll('div.container-fluid div.item')
    items_.forEach(item => {
        const id = Utils.formatID(item.querySelector("span date")?.textContent.trim() ?? "");
        const element = item.querySelector('a.movie-box')
        items.push({
            id,
            element
        })
    })
    return items
}



function startSearch() {
    manager.SearchItems = [];
    manager.SearchItems.push(...FC2PPVDB_wall());
    manager.SearchItems.push(...FC2PPVDB_singlepage());
    manager.SearchItems.push(...javdb_wall());
    manager.SearchItems.push(...javdb_singlepage());
    manager.SearchItems.push(...FC2_wall());
    manager.SearchItems.push(...FC2_singlepage());
    manager.SearchItems.push(...FC2_ranking());
    manager.SearchItems.push(...javbus_wall());
    console.log('准备开始搜索', manager.SearchItems);
    manager.initializeSearch();
    FC2DB_removeNoImage()
}


// 2秒后開始搜索
setTimeout(() => {
    startSearch()
}, 2000);

document.addEventListener('keydown', (event) => { // 建议使用 keydown 事件来监听组合键
    // 检查 Alt 键是否被按下以及按下的键是否是 'q' 或 'Q'
    if (event.altKey && (event.key === 'q' || event.key === 'Q')) {
        event.preventDefault(); // 可选:阻止浏览器的默认行为,例如某些浏览器可能有 Alt+Q 的快捷键
        setTimeout(() => {
            startSearch()
        }, 100);

    }
});





// })();