dmhy123

在动漫花园及其镜像站的详情页添加“转存123云盘”按钮,一键将磁力链接提交至123云盘离线下载,支持自动获取Token。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         dmhy123
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  在动漫花园及其镜像站的详情页添加“转存123云盘”按钮,一键将磁力链接提交至123云盘离线下载,支持自动获取Token。
// @author       Nagisa
// @match        *://share.dmhy.org/*
// @match        *://dmhy.myheartsite.com/*
// @match        *://*.myheartsite.com/*
// @match        *://*.dmhy.org/*
// @match        *://*.123pan.com/*
// @connect      www.123pan.com
// @icon         https://www.google.com/s2/favicons?sz=64&domain=123pan.com
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_openInTab
// @grant        GM_notification
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    const HOST = window.location.hostname;
    const PATH = window.location.pathname;
    const SEARCH = window.location.search;

    const IS_123 = HOST.includes('123pan.com');
    // 判定是否为文件管理页面 (首页或带路径参数的页面)
    const IS_123_HOME = IS_123 && (PATH === '/' && (!SEARCH || SEARCH.includes('homeFilePath')));

    const IS_MIRROR = HOST.includes('myheartsite.com') || !!document.querySelector('#app');

    // ================== CSS 样式 ==================
    const style = document.createElement('style');

    // 动漫花园按钮样式 (区分镜像与主站)
    let btnCss = '';
    if (IS_MIRROR) {
        btnCss = `
        .btn-123-save {
            display: inline-block; font-weight: 400; text-align: center; vertical-align: middle; user-select: none;
            border: 1px solid transparent; padding: .375rem .75rem; font-size: 1rem; line-height: 1.5; border-radius: .25rem;
            margin-left: 8px; color: #fff !important; background-color: #fd79a8;
            background-image: linear-gradient(315deg, #fd79a8 0%, #e66767 74%);
            cursor: pointer; transition: all .15s ease-in-out; box-shadow: 0 2px 5px rgba(253, 121, 168, 0.3);
        }
        .btn-123-save:hover { color: #fff; transform: translateY(-1px); box-shadow: 0 4px 8px rgba(253, 121, 168, 0.4); text-decoration: none; }
        .btn-123-save:active { transform: translateY(0); }`;
    } else {
        btnCss = `
        .btn-123-save {
            background-image: linear-gradient(315deg, #fd79a8 0%, #e66767 74%); color: white !important;
            border: none !important; margin-left: 10px; display: inline-flex; align-items: center; justify-content: center;
            border-radius: 4px; padding: 2px 10px; font-size: 12px; font-weight: bold; cursor: pointer;
            line-height: 1.5; text-decoration: none !important; vertical-align: middle;
            box-shadow: 0 2px 4px rgba(0,0,0,0.15); transition: all 0.2s;
        }
        .btn-123-save:hover { transform: translateY(-1px); box-shadow: 0 3px 6px rgba(0,0,0,0.25); filter: brightness(1.1); }`;
    }

    style.innerHTML = `
        ${btnCss}
        .btn-123-save:disabled { background: #95a5a6 !important; background-image: none !important; cursor: not-allowed; opacity: 0.7; transform: none !important; box-shadow: none !important; }
        .btn-123-icon { margin-right: 4px; }

        /* --- 123云盘悬浮组件容器 --- */
        #one23-float-container {
            position: fixed; bottom: 80px; right: 40px; z-index: 999999;
            display: flex; flex-direction: column; align-items: center; gap: 8px;
            transition: opacity 0.5s ease, transform 0.5s ease;
        }
        #one23-float-container.hiding { opacity: 0; transform: translateY(20px); pointer-events: none; }

        /* 主按钮 */
        #btn-123-token-update {
            width: 56px; height: 56px; background: rgba(52, 152, 219, 0.85); backdrop-filter: blur(5px);
            color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center;
            cursor: pointer; box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
            border: 1px solid rgba(255, 255, 255, 0.18); transition: all 0.3s;
        }
        #btn-123-token-update:hover { transform: scale(1.1); background: rgba(41, 128, 185, 0.95); }
        #btn-123-token-update.success { background: #2ecc71 !important; transform: scale(1); cursor: default; }

        /* 关闭小按钮 */
        #btn-123-close {
            width: 24px; height: 24px; background: rgba(0,0,0,0.3); color: white;
            border-radius: 50%; display: flex; align-items: center; justify-content: center;
            cursor: pointer; font-size: 14px; line-height: 1; transition: all 0.2s;
            opacity: 0.6;
        }
        #btn-123-close:hover { background: rgba(231, 76, 60, 0.8); opacity: 1; transform: scale(1.1); }

        .fab-icon svg { width: 28px; height: 28px; fill: white; }

        /* Toast */
        #one23-toast {
            position: fixed; top: 15%; left: 50%; transform: translateX(-50%) scale(0.9);
            padding: 12px 24px; background: rgba(33, 37, 41, 0.9); backdrop-filter: blur(4px);
            color: #fff; border-radius: 50px; z-index: 2147483647; font-size: 14px; font-weight: 500;
            opacity: 0; visibility: hidden; transition: all 0.3s; pointer-events: none;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2); display: flex; align-items: center; gap: 8px;
        }
        #one23-toast.show { opacity: 1; visibility: visible; transform: translateX(-50%) scale(1); }
    `;
    document.head.appendChild(style);

    // Toast
    const toast = document.createElement('div');
    toast.id = 'one23-toast';
    document.body.appendChild(toast);
    function showToast(html, duration = 3000) {
        toast.innerHTML = html;
        toast.classList.add('show');
        setTimeout(() => toast.classList.remove('show'), duration);
    }

    const ICONS = {
        cloud: `<svg viewBox="0 0 24 24"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/></svg>`,
        check: `<svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`,
        save: `💾`,
        close: `×`
    };

    // ================== 123云盘官网逻辑 (悬浮球) ==================
    if (IS_123) {
        // 关键逻辑:只在特定页面显示
        if (!IS_123_HOME) return;

        // 创建容器
        const container = document.createElement('div');
        container.id = 'one23-float-container';

        // 主按钮
        const mainBtn = document.createElement('div');
        mainBtn.id = 'btn-123-token-update';
        mainBtn.innerHTML = `<span class="fab-icon">${ICONS.cloud}</span>`;
        mainBtn.title = '点击同步登录Token给动漫花园助手';

        // 关闭按钮
        const closeBtn = document.createElement('div');
        closeBtn.id = 'btn-123-close';
        closeBtn.innerHTML = ICONS.close;
        closeBtn.title = '关闭悬浮球';

        container.appendChild(mainBtn);
        container.appendChild(closeBtn);
        document.body.appendChild(container);

        // 关闭逻辑
        closeBtn.onclick = (e) => {
            e.stopPropagation();
            container.classList.add('hiding');
            setTimeout(() => container.remove(), 500);
        };

        // 更新逻辑
        mainBtn.onclick = () => {
            let token = localStorage.getItem('authorToken');
            if (!token) {
                try {
                    const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
                    token = userInfo.token;
                } catch(e){}
            }

            if (token && token.length > 50) {
                GM_setValue('123_token', token);

                // 成功动画
                mainBtn.classList.add('success');
                mainBtn.innerHTML = `<span class="fab-icon">${ICONS.check}</span>`;
                showToast(`✅ Token 同步成功!`);

                // 1.5秒后自动消失
                setTimeout(() => {
                    container.classList.add('hiding');
                    setTimeout(() => container.remove(), 500);
                }, 1500);
            } else {
                showToast('❌ 未检测到登录状态,请先登录');
            }
        };
        return;
    }

    // ================== 动漫花园逻辑 (保持不变) ==================

    function getToken() { return GM_getValue('123_token', null); }

    function openAuthPage() {
        if (confirm("⚠️ 需要更新 123云盘 授权\n\n点击【确定】打开官网,点击右下角悬浮球即可同步。")) {
            GM_openInTab('https://www.123pan.com/', { active: true });
        }
    }

    function request(method, url, data) {
        return new Promise((resolve, reject) => {
            const token = getToken();
            if (!token) return reject("NO_TOKEN");

            GM_xmlhttpRequest({
                method: method,
                url: "https://www.123pan.com" + url,
                headers: {
                    'Authorization': 'Bearer ' + token,
                    'App-Version': '3',
                    'platform': 'web',
                    'Content-Type': 'application/json;charset=UTF-8',
                    'Origin': 'https://www.123pan.com',
                    'Referer': 'https://www.123pan.com/'
                },
                data: JSON.stringify(data),
                onload: function(response) {
                    if (response.status === 401) return reject("TOKEN_EXPIRED");
                    try { resolve(JSON.parse(response.responseText)); } catch (e) { reject("JSON解析失败"); }
                },
                onerror: (err) => reject("网络连接失败")
            });
        });
    }

    async function handleSaveTo123(magnetLink, btnElement) {
        const originalHTML = btnElement.innerHTML;
        btnElement.disabled = true;
        btnElement.innerHTML = `<span class="btn-123-icon">⏳</span> 解析...`;

        try {
            const resolveData = await request('POST', '/b/api/v2/offline_download/task/resolve', { urls: magnetLink });
            if (resolveData.code !== 0) throw new Error(resolveData.message);
            const taskInfo = resolveData.data.list[0];
            if (taskInfo.err_code !== 0) throw new Error(`Code:${taskInfo.err_code}`);

            btnElement.innerHTML = `<span class="btn-123-icon">🚀</span> 转存...`;
            const fileIds = taskInfo.files.map(f => f.id);
            const submitData = await request('POST', '/b/api/v2/offline_download/task/submit', {
                resource_list: [{ resource_id: taskInfo.id, select_file_id: fileIds }]
            });

            if (submitData.code === 0) {
                btnElement.innerHTML = `<span class="btn-123-icon">✅</span> 成功`;
                btnElement.style.background = '#00b894';
                btnElement.style.backgroundImage = 'none';
                showToast(`🎉 成功添加 ${fileIds.length} 个文件`);
                setTimeout(() => { btnElement.disabled = false; }, 3000);
            } else {
                throw new Error(submitData.message);
            }
        } catch (error) {
            btnElement.innerHTML = originalHTML;
            btnElement.disabled = false;
            if (error === "NO_TOKEN" || error === "TOKEN_EXPIRED") openAuthPage();
            else showToast("❌ " + (error.message || error));
        }
    }

    function createButton(magnetLink) {
        const btn = document.createElement('a');
        btn.href = 'javascript:void(0);';
        btn.className = 'btn-123-save';
        btn.innerHTML = `<span class="btn-123-icon">${ICONS.save}</span> 转存123盘`;
        btn.onclick = (e) => {
            e.preventDefault();
            handleSaveTo123(magnetLink, btn);
        };
        return btn;
    }

    // 注入逻辑:镜像站
    function injectMirrorSite() {
        document.querySelectorAll('input.form-control-plaintext').forEach(input => {
            const rowDiv = input.closest('.form-group.row');
            if (!rowDiv) return;
            const label = rowDiv.querySelector('label');
            if (!label || !label.innerText.toLowerCase().includes('magnet')) return;
            const btnContainer = rowDiv.querySelector('div:last-child');
            if (!btnContainer || btnContainer.querySelector('.btn-123-save')) return;
            const btn = createButton(input.value.trim());
            btnContainer.appendChild(btn);
        });
    }

    // 注入逻辑:主站
    function injectMainSite() {
        ['#a_magnet', '#magnet2'].forEach(selector => {
            const linkNode = document.querySelector(selector);
            if (linkNode && !linkNode.nextSibling?.classList?.contains('btn-123-save')) {
                const href = linkNode.getAttribute('href');
                if (href && href.startsWith('magnet:')) {
                    const btn = createButton(href);
                    linkNode.parentNode.insertBefore(btn, linkNode.nextSibling);
                }
            }
        });
    }

    function mainLoop() {
        if (IS_MIRROR) injectMirrorSite();
        else injectMainSite();
    }

    setInterval(mainLoop, 800);
})();