MissAV Quick Copy & Javdb Search

为 MissAV 视频播放页面提供番号快速复制和一键跳转 JavDB 搜索功能。点击“复制番号”按钮即可将当前番号复制到剪贴板,点击“转到JavDB搜索”按钮即可在新标签页中搜索对应番号。

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         MissAV Quick Copy & Javdb Search
// @namespace    zerobiubiu.top
// @version      1.0
// @description  为 MissAV 视频播放页面提供番号快速复制和一键跳转 JavDB 搜索功能。点击“复制番号”按钮即可将当前番号复制到剪贴板,点击“转到JavDB搜索”按钮即可在新标签页中搜索对应番号。
// @author       zerobiubiu
// @match        https://missav.ws/*/*-*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=missav.ws
// @run-at       document-idle
// @grant        GM_openInTab
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    const selector = "body > div:nth-child(3) > div.sm\\:container.mx-auto.px-4.content-without-search.pb-12 > div > div.flex-1.order-first > div.mt-4 > div";

    // 番号提取
    function extractCode(pathname) {
        const filename = pathname.split("/").pop();
        if (/^FC2-PPV-\d+$/i.test(filename)) return filename;
        const parts = filename.split("-");
        return parts.length >= 2 ? parts[0] + "-" + parts[1] : filename;
    }

    // 复制文本到剪贴板
    function copyToClipboard(text) {
        return navigator.clipboard.writeText(text)
            .then(() => true)
            .catch(() => false);
    }

    // 页面内浮动提示
    function showToast(message, duration = 2000) {
        const toast = document.createElement("div");
        toast.textContent = message;
        Object.assign(toast.style, {
            position: "fixed",
            bottom: "20px",
            left: "50%",
            transform: "translateX(-50%)",
            background: "rgba(0,0,0,0.7)",
            color: "#fff",
            padding: "8px 16px",
            borderRadius: "4px",
            fontSize: "14px",
            zIndex: 9999,
            transition: "opacity 0.3s",
            opacity: "1"
        });
        document.body.appendChild(toast);
        setTimeout(() => {
            toast.style.opacity = "0";
            setTimeout(() => toast.remove(), 300);
        }, duration);
    }

    // 改进的打开新标签页函数
    function openInNewTab(url) {
        // 尝试多种方法打开新标签页
        try {
            // 方法1: 使用 GM_openInTab(如果可用)
            if (typeof GM_openInTab !== 'undefined') {
                GM_openInTab(url, { active: true, setParent: true });
                return true;
            }
        } catch (e) {
            console.log('GM_openInTab 不可用:', e);
        }

        try {
            // 方法2: 使用 window.open
            const newWindow = window.open(url, '_blank', 'noopener,noreferrer');
            if (newWindow) {
                return true;
            }
        } catch (e) {
            console.log('window.open 失败:', e);
        }

        try {
            // 方法3: 创建隐形链接并点击
            const link = document.createElement('a');
            link.href = url;
            link.target = '_blank';
            link.rel = 'noopener noreferrer';
            link.style.display = 'none';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            return true;
        } catch (e) {
            console.log('链接点击方法失败:', e);
        }

        // 方法4: 在当前页面打开(最后的备选方案)
        try {
            window.location.href = url;
            return true;
        } catch (e) {
            console.log('所有方法都失败了:', e);
            return false;
        }
    }

    // 创建通用按钮
    function createButton(label, svgElement, onClick) {
        const button = document.createElement("button");
        button.type = "button";
        button.className = "inline-flex items-center whitespace-nowrap shadow-sm text-sm text-nord4 leading-4 font-medium focus:outline-none hover:text-nord6 mr-2";
        button.appendChild(svgElement);
        button.appendChild(document.createTextNode(label));
        button.addEventListener("click", e => {
            e.preventDefault();
            e.stopPropagation();
            onClick();
        });
        return button;
    }

    // 创建 SVG 元素(原生 SVG 对象)
    function createSVGClipboard() {
        const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
        svg.setAttribute("width", "16");
        svg.setAttribute("height", "16");
        svg.setAttribute("fill", "currentColor");
        svg.setAttribute("class", "bi bi-clipboard-check mr-1");
        svg.setAttribute("viewBox", "0 0 16 16");

        const path1 = document.createElementNS(svg.namespaceURI, "path");
        path1.setAttribute("fill-rule", "evenodd");
        path1.setAttribute("d", "M10.854 7.146a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7.5 9.793l2.646-2.647a.5.5 0 0 1 .708 0");

        const path2 = document.createElementNS(svg.namespaceURI, "path");
        path2.setAttribute("d", "M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1z");

        const path3 = document.createElementNS(svg.namespaceURI, "path");
        path3.setAttribute("d", "M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0z");

        svg.appendChild(path1);
        svg.appendChild(path2);
        svg.appendChild(path3);
        return svg;
    }

    function createSVGJavDB() {
        const svgStr = `<svg  xmlns="http://www.w3.org/2000/svg"  width="24"  height="24"  viewBox="0 0 24 24"  fill="none"  stroke="currentColor"  stroke-width="2"  stroke-linecap="round"  stroke-linejoin="round"  class="icon icon-tabler icons-tabler-outline icon-tabler-letter-j-small"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 8h4v6a2 2 0 1 1 -4 0" /></svg>`;
        const template = document.createElement("template");
        template.innerHTML = svgStr.trim();
        return template.content.firstChild;
    }

    function insertButtons(menu_bar) {
        const fanhao = extractCode(window.location.pathname);

        // 复制番号按钮,插到最前
        const copyBtn = createButton("复制番号", createSVGClipboard(), () => {
            copyToClipboard(fanhao).then(success => {
                showToast(success ? `番号 ${fanhao} 已复制` : "复制失败");
            });
        });
        menu_bar.prepend(copyBtn);

        // 跳转 JavDB 按钮,放到最后 - 改进版
        const javdbBtn = createButton("转到JavDB搜索", createSVGJavDB(), () => {
            const javdbUrl = `https://javdb.com/search?q=${encodeURIComponent(fanhao)}&f=all`;
            console.log('尝试打开 JavDB 链接:', javdbUrl);

            const success = openInNewTab(javdbUrl);
            if (success) {
                showToast(`正在跳转到 JavDB 搜索: ${fanhao}`);
            } else {
                showToast("跳转失败,请检查弹窗阻止设置");
                // 作为备选方案,复制链接到剪贴板
                copyToClipboard(javdbUrl).then(copied => {
                    if (copied) {
                        showToast("链接已复制到剪贴板,请手动打开");
                    }
                });
            }
        });
        menu_bar.appendChild(javdbBtn);

        console.log("✅ Copy 和 JavDB 按钮已插入 menu_bar");
    }

    // 监听 DOM 变化,直到找到目标元素
    const observer = new MutationObserver(() => {
        const menu_bar = document.querySelector(selector);
        if (menu_bar) {
            observer.disconnect();
            insertButtons(menu_bar);
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });

})();