Jav Multi-Site Jumper

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

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

Advertisement:

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

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');
                }
            }
        });
    }
})();