Jav Multi-Site Jumper

Jump between JavDB, MissAV, SupJav, OneJav, and U9A9

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

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.

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

(У мене вже є менеджер скриптів, дайте мені встановити його!)

Advertisement:

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!)

Advertisement:

// ==UserScript==
// @name         Jav Multi-Site Jumper
// @namespace    https://javdb.com/
// @version      1.1
// @description  Jump between JavDB, MissAV, SupJav, OneJav, and U9A9
// @license      MIT
// @author       YourName
// @match        https://javdb.com/*
// @match        https://missav.ws/*
// @match        https://missav.ai/*
// @match        https://supjav.com/*
// @match        *://onejav.com/*
// @match        https://u9a9.com/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_notification
// @grant        GM_setClipboard
// @grant        GM_getResourceURL
// @grant        GM_registerMenuCommand
// @grant        GM_info
// @grant        GM_openInTab
// @grant        GM_xmlhttpRequest
// @connect      *
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // ═══════════════════════════════════════════════════════════
    //  全局樣式 (以 d.js 風格為主)
    // ═══════════════════════════════════════════════════════════
    const styles = `
        #jav-jumper-container {
            position: fixed;
            left: 20px;
            bottom: 20px;
            z-index: 99999;
            display: flex;
            flex-direction: column;
            gap: 10px;
            font-family: Arial, sans-serif;
        }
        .jav-jumper-btn {
            display: inline-block;
            padding: 8px 12px;
            color: white;
            text-decoration: none;
            border: none;
            border-radius: 6px;
            font-weight: bold;
            font-size: 12px;
            cursor: pointer;
            transition: all 0.3s ease;
            text-align: center;
            min-width: 70px;
        }
        .jav-jumper-btn:hover {
            transform: translateY(-2px);
        }
        .jj-missav {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
        }
        .jj-missav:hover {
            box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
        }
        .jj-supjav {
            background: linear-gradient(135deg, #00bcd4 0%, #008ba3 100%);
            box-shadow: 0 4px 15px rgba(0, 188, 212, 0.4);
        }
        .jj-supjav:hover {
            box-shadow: 0 6px 20px rgba(0, 188, 212, 0.6);
        }
        .jj-javdb {
            background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
            box-shadow: 0 4px 15px rgba(17, 153, 142, 0.4);
        }
        .jj-javdb:hover {
            box-shadow: 0 6px 20px rgba(17, 153, 142, 0.6);
        }
        .jj-copy {
            background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
            box-shadow: 0 4px 15px rgba(240, 147, 251, 0.4);
        }
        .jj-copy:hover {
            box-shadow: 0 6px 20px rgba(240, 147, 251, 0.6);
        }
        .jj-copied {
            background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%) !important;
        }
        /* OneJav 按鈕容器 */
        .jj-onejav-container {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-bottom: 10px;
        }
    `;

    const styleSheet = document.createElement('style');
    styleSheet.textContent = styles;
    document.head.appendChild(styleSheet);

    // ═══════════════════════════════════════════════════════════
    //  番號格式化 (OneJav / U9A9 共用)
    // ═══════════════════════════════════════════════════════════
    function getAvCode(avid) {
        if (!avid || typeof avid !== 'string') return 'Unknown';

        // FC2PPV 格式統一處理
        const fc2Match = avid.match(/^fc2ppv[-_]?(\d+)$/i);
        if (fc2Match) return 'fc2-ppv-' + fc2Match[1];

        // 保留特殊格式
        if (/^fc2-ppv-\d+$/i.test(avid)) return avid.toLowerCase();
        if (/^[-a-z0-9]+$/i.test(avid) && avid.includes('-')) return avid;

        // OneJav 原有邏輯
        if (avid.match(/-[^0]/g)) return avid;
        if (avid.match(/^[0-9-_]+$/g)) return avid;
        if (avid.match(/^(crazyasia|sm|video_|BrazzersExxtra)+/gi)) return avid;
        if (avid.match(/^FC2PPV\d+$/i)) {
            let numMatch = avid.match(/\d+$/);
            let num = numMatch ? numMatch[0] : '000';
            return "FC2-PPV-" + num;
        }

        // 移除 FC2 前綴並標準化
        avid = avid.replace(/\b(FC2+)/gi, "");
        const letter = (avid.match(/^[a-z]+/i)?.[0] || 'ID').toUpperCase();
        const num = (avid.match(/-?(\d+)$/)?.[1] || '000').replace(/^0+/, '').padStart(3, '0');
        return `${letter}-${num}`;
    }

    // ═══════════════════════════════════════════════════════════
    //  取得番號 (javdb / missav / supjav)
    // ═══════════════════════════════════════════════════════════
    function getCode() {
        if (window.location.href.includes('javdb.com')) {
            var link = document.querySelector('.panel-block.first-block a.button.is-white.copy-to-clipboard');
            return link ? link.getAttribute('data-clipboard-text') : '';
        }
        if (window.location.href.includes('missav.ws') || window.location.href.includes('missav.ai')) {
            var el = document.querySelector('.text-secondary span.font-medium');
            if (!el) return '';
            return el.innerText.replace(/-UNCENSORED-LEAK|-CHINESE-SUBTITLE/g, '');
        }
        if (window.location.href.includes('supjav.com')) {
            var m = window.location.search.match(/[?&]s=([^&]+)/);
            return m ? decodeURIComponent(m[1]) : '';
        }
        return '';
    }

    // ═══════════════════════════════════════════════════════════
    //  U9A9 專用:從標題提取 AV code
    // ═══════════════════════════════════════════════════════════
    function extractU9a9Code() {
        const panelTitle = document.querySelector('.panel-heading .panel-title')?.textContent.trim();
        if (!panelTitle) return null;

        // 優先匹配 FC2PPV- 格式
        const fc2Match = panelTitle.match(/FC2PPV[-_]?\d+/i);
        if (fc2Match) return fc2Match[0];

        // 匹配數字前綴 AV code 格式:例如 390JAC-232 -> JAC-232
        const prefixedAvCodeMatch = panelTitle.match(/\b\d+([A-Za-z]{3,5})[-_\s]?(\d{2,6})\b/);
        if (prefixedAvCodeMatch) return `${prefixedAvCodeMatch[1]}-${prefixedAvCodeMatch[2]}`;

        // 匹配 AV code 格式:3-5個字母 + - + 2-6位數字
        const avCodeMatch = panelTitle.match(/\b([A-Za-z]{3,5})[-_]?(\d{2,6})\b/);
        if (avCodeMatch) return avCodeMatch[0];

        return null;
    }

    // ═══════════════════════════════════════════════════════════
    //  通用:開新分頁
    // ═══════════════════════════════════════════════════════════
    function openInTab(url) {
        var a = document.createElement('a');
        a.href = url;
        a.target = '_blank';
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    }

    // ═══════════════════════════════════════════════════════════
    //  通用:生成按鈕 (d.js 風格)
    // ═══════════════════════════════════════════════════════════
    function mkLinkBtn(text, href, cls) {
        return Object.assign(document.createElement('a'), {
            href: href,
            target: '_blank',
            textContent: text,
            className: 'jav-jumper-btn ' + cls
        });
    }

    function mkActionBtn(text, cls) {
        return Object.assign(document.createElement('button'), {
            textContent: text,
            className: 'jav-jumper-btn ' + cls
        });
    }

    // ═══════════════════════════════════════════════════════════
    //  通用:創建浮動面板 (三個按鈕:複製 + 兩個跳轉)
    // ═══════════════════════════════════════════════════════════
    function createFloatingPanel(番號, btn1Target, btn2Target, btn1Label, btn2Label, btn1Class, btn2Class) {
        var container = document.createElement('div');
        container.id = 'jav-jumper-container';

        // 複製按鈕
        var copyBtn = mkActionBtn('複製', 'jj-copy');
        copyBtn.onclick = async function() {
            try {
                await navigator.clipboard.writeText(番號);
                var orig = copyBtn.textContent;
                copyBtn.textContent = '已複製!';
                copyBtn.classList.add('jj-copied');
                setTimeout(function() {
                    copyBtn.textContent = orig;
                    copyBtn.classList.remove('jj-copied');
                }, 1500);
            } catch (e) {
                copyBtn.textContent = '失敗';
                setTimeout(function() { copyBtn.textContent = '複製'; }, 1500);
            }
        };

        // 跳轉按鈕 1 (上方)
        var btn1Url = btn1Target === 'missav'
            ? 'https://missav.ws/' + 番號.toLowerCase()
            : btn1Target === 'supjav'
                ? 'https://supjav.com/?s=' + encodeURIComponent(番號)
                : 'https://javdb.com/search?f=all&q=' + encodeURIComponent(番號);
        var btn1 = mkLinkBtn(btn1Label, btn1Url, btn1Class);

        // 跳轉按鈕 2 (下方)
        var btn2Url = btn2Target === 'missav'
            ? 'https://missav.ws/' + 番號.toLowerCase()
            : btn2Target === 'supjav'
                ? 'https://supjav.com/?s=' + encodeURIComponent(番號)
                : 'https://javdb.com/search?f=all&q=' + encodeURIComponent(番號);
        var btn2 = mkLinkBtn(btn2Label, btn2Url, btn2Class);

        container.append(copyBtn, btn1, btn2);
        document.body.appendChild(container);
    }

    // ═══════════════════════════════════════════════════════════
    //  分站分流
    // ═══════════════════════════════════════════════════════════

    if (window.location.href.includes('onejav.com')) {
        oneJavInit();
    } else if (window.location.href.includes('u9a9.com')) {
        u9a9Init();
    } else {
        floatingInit();
    }

    // ═══════════════════════════════════════════════════════════
    //  OneJav:在每個標題前插入按鈕
    // ═══════════════════════════════════════════════════════════
    function oneJavInit() {
        function add() {
            const titles = document.querySelectorAll('.title.is-4.is-spaced');
            if (titles.length === 0) return;

            titles.forEach(function(title) {
                let avid = title.querySelector('a').textContent
                    ? title.querySelector('a').textContent.replace(/[ ]/g, "").replace(/[\r\n]/g, "")
                    : 'Unknown';
                if (!(/(-)/g).test(avid)) {
                    avid = getAvCode(avid);
                }

                const container = document.createElement('div');
                container.className = 'jj-onejav-container';

                const btnMiss = mkLinkBtn(avid,
                    'https://missav.ws/' + avid,
                    'jj-missav');
                const btnSup = mkLinkBtn(avid,
                    'https://supjav.com/?s=' + encodeURIComponent(avid),
                    'jj-supjav');
                const btnJav = mkLinkBtn(avid,
                    'https://javdb.com/search?q=' + encodeURIComponent(avid),
                    'jj-javdb');

                container.appendChild(btnMiss);
                container.appendChild(btnSup);
                container.appendChild(btnJav);
                title.parentNode.insertBefore(container, title);
            });
        }

        window.addEventListener('load', add);
    }

    // ═══════════════════════════════════════════════════════════
    //  U9A9:浮動面板
    // ═══════════════════════════════════════════════════════════
    function u9a9Init() {
        function tryCreate() {
            if (document.getElementById('jav-jumper-container')) return;
            var raw = extractU9a9Code();
            if (!raw) return;
            var processedCode = getAvCode(raw);
            // 排列:複製 → SupJav → MissAV
            createFloatingPanel(processedCode,
                'supjav', 'missav',
                processedCode, processedCode,
                'jj-supjav', 'jj-missav');
        }

        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', function() {
                var check = setInterval(function() {
                    if (document.querySelector('.panel-heading .panel-title')) {
                        clearInterval(check);
                        tryCreate();
                    }
                }, 100);
                setTimeout(function() { clearInterval(check); }, 5000);
            });
        } else {
            var check = setInterval(function() {
                if (document.querySelector('.panel-heading .panel-title')) {
                    clearInterval(check);
                    tryCreate();
                }
            }, 100);
            setTimeout(function() { clearInterval(check); }, 5000);
        }

        new MutationObserver(function() {
            if (!document.getElementById('jav-jumper-container')) tryCreate();
        }).observe(document.body, { childList: true, subtree: true });
    }

    // ═══════════════════════════════════════════════════════════
    //  JavDB / MissAV / SupJav:浮動面板
    // ═══════════════════════════════════════════════════════════
    function floatingInit() {
        var 番號 = getCode();
        if (!番號) return;

        if (window.location.href.includes('javdb.com')) {
            // 複製 → SupJav → MissAV
            createFloatingPanel(番號,
                'supjav', 'missav',
                番號, 番號,
                'jj-supjav', 'jj-missav');
        } else if (window.location.href.includes('missav.ws') || window.location.href.includes('missav.ai')) {
            // 複製 → SupJav → JavDB
            createFloatingPanel(番號,
                'supjav', 'javdb',
                番號, 番號,
                'jj-supjav', 'jj-javdb');
        } else if (window.location.href.includes('supjav.com')) {
            // 複製 → MissAV → JavDB
            createFloatingPanel(番號,
                'missav', 'javdb',
                番號, 番號,
                'jj-missav', 'jj-javdb');
        }

        // ── JavDB 磁力連接下載按鈕修改 ──
        updateDownloadButtons();
        var observer = new MutationObserver(function() { updateDownloadButtons(); });
        var magnetsContent = document.querySelector('#magnets-content');
        if (magnetsContent) {
            observer.observe(magnetsContent, { childList: true, subtree: true });
        }
    }

    function updateDownloadButtons() {
        if (!window.location.href.includes('javdb.com')) return;
        var magnetItems = document.querySelectorAll('#magnets-content .item.columns');
        magnetItems.forEach(function(item) {
            var copyButton = item.querySelector('.copy-to-clipboard');
            var downloadButton = item.querySelector('a.button.is-info.is-small[href^="https://keepshare.org"]');
            if (copyButton && downloadButton) {
                var magnetLink = copyButton.getAttribute('data-clipboard-text');
                if (magnetLink) {
                    downloadButton.href = magnetLink;
                    downloadButton.removeAttribute('target');
                }
            }
        });
    }
})();