Greasy Fork

Greasy Fork is available in English.

豆包&即梦AI下载无水印图片视频

实现豆包&即梦生成的图片视频免费无水印下载

当前为 2025-02-20 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         豆包&即梦AI下载无水印图片视频
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  实现豆包&即梦生成的图片视频免费无水印下载
// @author       微信11208596
// @license      UNLICENSED
// @match        https://www.doubao.com/*
// @match        https://jimeng.jianying.com/ai-tool/*
// @grant        GM_download
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(function () {
    'use strict';

    // 添加激活码相关功能
    const ACTIVATION_KEY = 'doubao_activation_status';
    const SECRET_KEY = 'db94xy20240322'; // 使用一个固定的密钥值
    const VALID_DAYS = 30; // 激活码有效期(天)

    // 生成激活码
    function generateActivationCode(deviceId) {
        // 添加时间戳
        const timestamp = Math.floor(Date.now() / 1000);
        const expireTime = timestamp + (VALID_DAYS * 24 * 60 * 60);

        // 混合设备ID和时间戳
        const data = `${deviceId}|${expireTime}`;

        // 使用更复杂的加密算法
        const encrypted = encryptData(data);

        // 添加校验位
        const checksum = generateChecksum(encrypted);

        // 组合最终激活码
        return `${encrypted}-${checksum}-${expireTime.toString(36)}`;
    }

    // 验证激活码
    function verifyActivationCode(deviceId, inputCode) {
        try {
            // 检查激活码是否在黑名单中
            const blacklist = JSON.parse(localStorage.getItem('activation_blacklist') || '[]');
            if (blacklist.includes(inputCode)) {
                console.log('激活码已被取消');
                return false;
            }

            console.log('开始验证:', {deviceId, inputCode});

            const [encrypted, checksum, expireTimeStr] = inputCode.split('-');
            console.log('解析激活码:', {encrypted, checksum, expireTimeStr});

            const validChecksum = generateChecksum(encrypted);
            console.log('校验和:', {valid: validChecksum, input: checksum});

            if (generateChecksum(encrypted) !== checksum) {
                console.log('校验和不匹配');
                return false;
            }

            const expireTime = parseInt(expireTimeStr, 36);
            const now = Math.floor(Date.now() / 1000);
            console.log('时间验证:', {expireTime, now, valid: now <= expireTime});

            if (now > expireTime) {
                console.log('激活码已过期');
                return false;
            }

            const decrypted = decryptData(encrypted);
            console.log('解密结果:', decrypted);

            const [originalDeviceId, originalExpireTime] = decrypted.split('|');
            console.log('解析数据:', {originalDeviceId, originalExpireTime});

            const isValid = originalDeviceId === deviceId;
            console.log('设备ID匹配结果:', isValid);

            if (isValid) {
                // 保存激活码以便后续验证过期时间
                localStorage.setItem('activation_code', inputCode);
            }
            return isValid;
        } catch (e) {
            console.error('验证过程出错:', e);
            return false;
        }
    }

    // 加密函数
    function encryptData(data) {
        let result = '';
        const key = SECRET_KEY;

        // 使用简单的异或加密
        for (let i = 0; i < data.length; i++) {
            const charCode = data.charCodeAt(i);
            const keyChar = key.charCodeAt(i % key.length);
            const encrypted = (charCode ^ keyChar) % 256;
            result += encrypted.toString(16).padStart(2, '0');
        }

        return result;
    }

    // 解密函数
    function decryptData(encrypted) {
        let result = '';
        const key = SECRET_KEY;

        // 解密过程
        for (let i = 0; i < encrypted.length; i += 2) {
            const hex = encrypted.substr(i, 2);
            const encryptedChar = parseInt(hex, 16);
            const keyChar = key.charCodeAt((i/2) % key.length);
            const decrypted = (encryptedChar ^ keyChar) % 256;
            result += String.fromCharCode(decrypted);
        }

        return result;
    }

    // 生成校验位
    function generateChecksum(data) {
        let checksum = 0;
        for (let i = 0; i < data.length; i++) {
            checksum = ((checksum << 5) - checksum + data.charCodeAt(i)) >>> 0;
        }
        return checksum.toString(36);
    }

    // 检查激活状态
    function checkActivation() {
        const activationStatus = localStorage.getItem(ACTIVATION_KEY);
        const deviceId = localStorage.getItem('deviceId');
        const activationCode = localStorage.getItem('activation_code');

        if (!deviceId) {
            const newDeviceId = Math.random().toString(36).substring(2) + Date.now().toString(36);
            localStorage.setItem('deviceId', newDeviceId);
            return false;
        }

        // 检查激活码是否在黑名单中
        if (activationCode) {
            const blacklist = JSON.parse(localStorage.getItem('activation_blacklist') || '[]');
            if (blacklist.includes(activationCode)) {
                // 如果在黑名单中,立即清除激活状态
                localStorage.removeItem(ACTIVATION_KEY);
                localStorage.removeItem('activation_code');
                showFloatingTip('激活码已被禁用,请重新激活');
                return false;
            }
        }

        return activationStatus === 'activated' && deviceId && activationCode;
    }

    // 创建激活对话框
    function createActivationDialog() {
        const overlay = document.createElement('div');
        overlay.className = 'download-confirm-overlay';

        const dialog = document.createElement('div');
        dialog.className = 'download-confirm-dialog';

        const deviceId = localStorage.getItem('deviceId');

        // 检查是否已激活
        const isActivated = checkActivation();

        dialog.innerHTML = `
            <h3>软件激活</h3>
            ${isActivated ? `
                <p>软件已激活</p>
                <div class="buttons">
                    <button class="deactivate-btn" style="background: #ff3b30;">取消激活</button>
                </div>
            ` : `
                <p>请输入激活码以继续使用</p>
                <div class="input-container">
                    <label>设备ID</label>
                    <input type="text" id="deviceId" value="${deviceId}" readonly>
                    <div class="tip">请复制设备ID并联系微信(11208596)获取激活码</div>
                </div>
                <div class="input-container">
                    <label>激活码</label>
                    <input type="text" id="activationCode" placeholder="请输入激活码">
                </div>
                <div class="buttons">
                    <button class="cancel-btn">取消</button>
                    <button class="confirm-btn">激活</button>
                </div>
            `}
        `;

        document.body.appendChild(overlay);
        document.body.appendChild(dialog);

        const confirmBtn = dialog.querySelector('.confirm-btn');
        const cancelBtn = dialog.querySelector('.cancel-btn');
        const activationInput = dialog.querySelector('#activationCode');
        const deviceIdInput = dialog.querySelector('#deviceId');

        // 复制设备ID功能
        deviceIdInput.addEventListener('click', () => {
            deviceIdInput.select();
            document.execCommand('copy');
            showFloatingTip('设备ID已复制到剪贴板');
        });

        function closeDialog() {
            document.body.removeChild(overlay);
            document.body.removeChild(dialog);
        }

        if (isActivated) {
            const deactivateBtn = dialog.querySelector('.deactivate-btn');
            deactivateBtn.addEventListener('click', () => {
                if (confirm('确定要取消激活吗?取消后需要重新激活才能继续使用。')) {
                    localStorage.removeItem(ACTIVATION_KEY);
                    localStorage.removeItem('activation_code');
                    showFloatingTip('已取消激活');
                    closeDialog();
                }
            });
        } else {
            confirmBtn.addEventListener('click', () => {
                const code = activationInput.value.trim();
                if (!code) {
                    showFloatingTip('请输入激活码');
                    return;
                }

                confirmBtn.disabled = true;
                confirmBtn.textContent = '验证中...';

                // 验证激活码
                if (verifyActivationCode(deviceId, code)) {
                    localStorage.setItem(ACTIVATION_KEY, 'activated');
                    showFloatingTip('激活成功');
                    closeDialog();
                } else {
                    showFloatingTip('激活码无效');
                    confirmBtn.disabled = false;
                    confirmBtn.textContent = '激活';
                }
            });
        }

        cancelBtn.addEventListener('click', closeDialog);
    }

    // 添加一个激活码生成工具函数(仅供开发使用)
    function generateActivationCodeForDevice(deviceId) {
        return generateActivationCode(deviceId);
    }

    // 显示浮动提示
    function showFloatingTip(message) {
        const tip = document.createElement('div');
        tip.className = 'floating-tip';
        tip.innerHTML = `
            <div class="icon">i</div>
            <span>${message}</span>
        `;

        document.body.appendChild(tip);

        setTimeout(() => {
            tip.classList.add('show');
        }, 100);

        setTimeout(() => {
            tip.classList.remove('show');
            setTimeout(() => {
                document.body.removeChild(tip);
            }, 300);
        }, 3000);
    }

    // 将函数暴露到全局作用域
    window.deactivateCode = function(code) {
        if (!confirm('确定要取消此激活码吗?取消后此激活码将无法继续使用。')) {
            return;
        }

        try {
            // 将激活码添加到黑名单
            let blacklist = JSON.parse(localStorage.getItem('activation_blacklist') || '[]');
            if (!blacklist.includes(code)) {
                blacklist.push(code);
                localStorage.setItem('activation_blacklist', JSON.stringify(blacklist));

                // 如果当前用户正在使用这个激活码,立即取消激活
                const currentCode = localStorage.getItem('activation_code');
                if (currentCode === code) {
                    localStorage.removeItem(ACTIVATION_KEY);
                    localStorage.removeItem('activation_code');
                }

                // 广播黑名单更新事件
                window.dispatchEvent(new CustomEvent('blacklistUpdated', { detail: { code } }));
            }

            // 更新查询结果显示
            const resultDiv = document.getElementById('queryResult');
            resultDiv.innerHTML = `
                <div style="background: #f5f5f7; padding: 15px; border-radius: 8px;">
                    <div style="color: #ff3b30;">此激活码已被禁用,无法继续使用</div>
                </div>
            `;

            showFloatingTip('激活码已成功取消');

            // 强制刷新页面以确保状态更新
            setTimeout(() => {
                window.location.reload();
            }, 1500);
        } catch (e) {
            console.error('取消激活码失败:', e);
            showFloatingTip('取消激活码失败');
        }
    };

    // 添加黑名单更新监听器
    window.addEventListener('blacklistUpdated', function(event) {
        // 检查当前激活码是否被取消
        const currentCode = localStorage.getItem('activation_code');
        if (currentCode === event.detail.code) {
            localStorage.removeItem(ACTIVATION_KEY);
            localStorage.removeItem('activation_code');
            showFloatingTip('激活码已被禁用,请重新激活');
            window.location.reload();
        }
    });

    // 添加定期检查机制
    function startBlacklistCheck() {
        setInterval(() => {
            if (checkActivation() === false) {
                // 如果检查失败,可能需要刷新页面
                window.location.reload();
            }
        }, 30000); // 每30秒检查一次
    }

    // 在脚本初始化时启动检查
    startBlacklistCheck();

    window.queryActivationCode = function() {
        const code = document.getElementById('queryCode').value.trim();
        const resultDiv = document.getElementById('queryResult');

        if (!code) {
            showFloatingTip('请输入激活码');
            return;
        }

        try {
            // 首先检查黑名单
            const blacklist = JSON.parse(localStorage.getItem('activation_blacklist') || '[]');
            if (blacklist.includes(code)) {
                resultDiv.innerHTML = `
                    <div style="background: #f5f5f7; padding: 15px; border-radius: 8px;">
                        <div style="color: #ff3b30;">此激活码已被禁用,无法继续使用</div>
                    </div>
                `;
                resultDiv.style.display = 'block';
                return;
            }

            const [encrypted, checksum, expireTimeStr] = code.split('-');

            if (generateChecksum(encrypted) !== checksum) {
                resultDiv.innerHTML = '<div style="color: #ff3b30;">无效的激活码</div>';
                resultDiv.style.display = 'block';
                return;
            }

            const decrypted = decryptData(encrypted);
            const [deviceId, expireTime] = decrypted.split('|');

            const now = Math.floor(Date.now() / 1000);
            const expireTimeNum = parseInt(expireTimeStr, 36);
            const isExpired = now > expireTimeNum;

            resultDiv.innerHTML = `
                <div style="background: #f5f5f7; padding: 15px; border-radius: 8px;">
                    <div><strong>设备ID:</strong> ${deviceId}</div>
                    <div><strong>到期时间:</strong> ${new Date(expireTimeNum * 1000).toLocaleString()}</div>
                    <div><strong>状态:</strong> <span style="color: ${isExpired ? '#ff3b30' : '#00c853'}">${isExpired ? '已过期' : '有效'}</span></div>
                    ${!isExpired ? `
                        <div style="margin-top: 15px;">
                            <button onclick="deactivateCode('${code}')"
                                    style="background: #ff3b30; padding: 8px 16px; font-size: 13px;">
                                取消此激活码
                            </button>
                        </div>
                    ` : ''}
                </div>
            `;
            resultDiv.style.display = 'block';

        } catch (e) {
            resultDiv.innerHTML = '<div style="color: #ff3b30;">无效的激活码格式</div>';
            resultDiv.style.display = 'block';
        }
    };

    // 修改下载验证函数,添加强制刷新机制
    function downloadWithActivationCheck(mediaUrl, mediaType, downloadFunction) {
        // 首先检查是否有激活状态
        const isActivated = checkActivation();
        const activationCode = localStorage.getItem('activation_code');
        const deviceId = localStorage.getItem('deviceId');

        if (!isActivated || !activationCode || !deviceId) {
            createActivationDialog();
            return;
        }

        // 验证激活码
        try {
            // 1. 检查黑名单
            const blacklist = JSON.parse(localStorage.getItem('activation_blacklist') || '[]');
            if (blacklist.includes(activationCode)) {
                localStorage.removeItem(ACTIVATION_KEY);
                localStorage.removeItem('activation_code');
                showFloatingTip('激活码已被禁用,请重新激活');
                // 强制刷新页面
                setTimeout(() => {
                    window.location.reload();
                }, 1500);
                return;
            }

            // 2. 验证激活码格式和校验和
            const [encrypted, checksum, expireTimeStr] = activationCode.split('-');
            if (!encrypted || !checksum || !expireTimeStr || generateChecksum(encrypted) !== checksum) {
                localStorage.removeItem(ACTIVATION_KEY);
                localStorage.removeItem('activation_code');
                showFloatingTip('激活码无效,请重新激活');
                createActivationDialog();
                return;
            }

            // 3. 验证过期时间
            const expireTime = parseInt(expireTimeStr, 36);
            const now = Math.floor(Date.now() / 1000);
            if (now > expireTime) {
                localStorage.removeItem(ACTIVATION_KEY);
                localStorage.removeItem('activation_code');
                showFloatingTip('激活码已过期,请重新激活');
                createActivationDialog();
                return;
            }

            // 4. 验证设备ID匹配
            const decrypted = decryptData(encrypted);
            const [originalDeviceId] = decrypted.split('|');
            if (originalDeviceId !== deviceId) {
                localStorage.removeItem(ACTIVATION_KEY);
                localStorage.removeItem('activation_code');
                showFloatingTip('设备ID不匹配,请重新激活');
                createActivationDialog();
                return;
            }

            // 所有验证都通过,允许下载
            createConfirmDialog(mediaUrl, mediaType, downloadFunction);

        } catch (e) {
            console.error('验证激活码出错:', e);
            localStorage.removeItem(ACTIVATION_KEY);
            localStorage.removeItem('activation_code');
            showFloatingTip('激活码验证失败,请重新激活');
            createActivationDialog();
        }
    }

    // 修改确认对话框样式
    const style = document.createElement('style');
    style.textContent = `
        @keyframes dialogShow {
            from {
                opacity: 0;
                transform: translate(-50%, -48%) scale(0.96);
            }
            to {
                opacity: 1;
                transform: translate(-50%, -50%) scale(1);
            }
        }

        @keyframes overlayShow {
            from { opacity: 0; }
            to { opacity: 1; }
        }

        .download-confirm-dialog {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(255, 255, 255, 0.8);
            backdrop-filter: blur(20px) saturate(180%);
            -webkit-backdrop-filter: blur(20px) saturate(180%);
            padding: 28px 24px;
            border-radius: 14px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12),
                        0 0 0 1px rgba(0, 0, 0, 0.05);
            z-index: 10000;
            min-width: 320px;
            max-width: 400px;
            text-align: center;
            font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Arial, sans-serif;
            animation: dialogShow 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        }

        .download-confirm-dialog h3 {
            margin: 0 0 8px 0;
            color: #1d1d1f;
            font-size: 19px;
            font-weight: 600;
            letter-spacing: -0.022em;
        }

        .download-confirm-dialog p {
            margin: 0 0 20px 0;
            color: #86868b;
            font-size: 14px;
            line-height: 1.4;
            letter-spacing: -0.016em;
        }

        .download-confirm-dialog .input-container {
            margin: 20px 0;
            text-align: left;
        }

        .download-confirm-dialog label {
            display: block;
            margin-bottom: 8px;
            color: #1d1d1f;
            font-size: 13px;
            font-weight: 500;
            letter-spacing: -0.016em;
        }

        .download-confirm-dialog input {
            width: 100%;
            padding: 12px 16px;
            border: 1px solid rgba(0, 0, 0, 0.1);
            border-radius: 10px;
            font-size: 15px;
            color: #1d1d1f;
            background: rgba(255, 255, 255, 0.8);
            transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
            box-sizing: border-box;
        }

        .download-confirm-dialog input:focus {
            outline: none;
            border-color: #0071e3;
            box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.15);
            background: #ffffff;
        }

        .download-confirm-dialog .buttons {
            margin-top: 28px;
            display: flex;
            gap: 12px;
            justify-content: center;
        }

        .download-confirm-dialog button {
            min-width: 128px;
            padding: 12px 24px;
            border: none;
            border-radius: 10px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 500;
            letter-spacing: -0.016em;
            transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
        }

        .download-confirm-dialog .confirm-btn {
            background: #0071e3;
            color: white;
            transform: scale(1);
        }

        .download-confirm-dialog .confirm-btn:hover {
            background: #0077ED;
            transform: scale(1.02);
        }

        .download-confirm-dialog .confirm-btn:active {
            transform: scale(0.98);
        }

        .download-confirm-dialog .confirm-btn:disabled {
            background: #999999;
            cursor: not-allowed;
            opacity: 0.7;
            transform: scale(1);
        }

        .download-confirm-dialog .cancel-btn {
            background: rgba(0, 0, 0, 0.05);
            color: #1d1d1f;
            backdrop-filter: blur(20px);
            -webkit-backdrop-filter: blur(20px);
        }

        .download-confirm-dialog .cancel-btn:hover {
            background: rgba(0, 0, 0, 0.1);
        }

        .download-confirm-dialog .cancel-btn:active {
            background: rgba(0, 0, 0, 0.15);
        }

        .download-confirm-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.4);
            backdrop-filter: blur(5px);
            -webkit-backdrop-filter: blur(5px);
            z-index: 9999;
            animation: overlayShow 0.3s ease-out;
        }

        @media (prefers-color-scheme: dark) {
            .download-confirm-dialog {
                background: rgba(40, 40, 45, 0.8);
            }

            .download-confirm-dialog h3 {
                color: #ffffff;
            }

            .download-confirm-dialog p {
                color: #98989d;
            }

            .download-confirm-dialog label {
                color: #ffffff;
            }

            .download-confirm-dialog input {
                background: rgba(60, 60, 65, 0.8);
                border-color: rgba(255, 255, 255, 0.1);
                color: #ffffff;
            }

            .download-confirm-dialog input:focus {
                background: rgba(70, 70, 75, 0.8);
            }

            .download-confirm-dialog .cancel-btn {
                background: rgba(255, 255, 255, 0.1);
                color: #ffffff;
            }

            .download-confirm-dialog .cancel-btn:hover {
                background: rgba(255, 255, 255, 0.15);
            }
        }

        .download-confirm-dialog .tip {
            font-size: 12px;
            color: #86868b;
            margin-top: 6px;
            text-align: left;
        }

        .download-confirm-dialog .progress-text {
            margin-top: 12px;
            font-size: 13px;
            color: #1d1d1f;
            letter-spacing: -0.016em;
        }

        .download-confirm-dialog .success-icon {
            display: inline-block;
            width: 16px;
            height: 16px;
            border-radius: 50%;
            background: #00c853;
            position: relative;
            margin-right: 6px;
            transform: scale(0);
            transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        }

        .download-confirm-dialog .success-icon:after {
            content: '';
            position: absolute;
            width: 8px;
            height: 4px;
            border: 2px solid white;
            border-top: 0;
            border-right: 0;
            transform: rotate(-45deg);
            top: 4px;
            left: 4px;
        }

        .download-confirm-dialog .success-icon.show {
            transform: scale(1);
        }

        @media (prefers-color-scheme: dark) {
            .download-confirm-dialog .tip {
                color: #98989d;
            }
            .download-confirm-dialog .progress-text {
                color: #ffffff;
            }
        }

        .floating-tip {
            position: fixed;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%) translateY(100px);
            background: rgba(0, 0, 0, 0.8);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            padding: 12px 20px;
            border-radius: 10px;
            color: white;
            font-size: 14px;
            z-index: 9999;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
            opacity: 0;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Arial, sans-serif;
            display: flex;
            align-items: center;
            gap: 8px;
            pointer-events: none;
        }

        .floating-tip.show {
            transform: translateX(-50%) translateY(0);
            opacity: 1;
        }

        .floating-tip .icon {
            width: 18px;
            height: 18px;
            background: #fff;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: bold;
            font-size: 12px;
            color: #000;
        }

        @media (prefers-color-scheme: dark) {
            .floating-tip {
                background: rgba(255, 255, 255, 0.9);
                color: #1d1d1f;
            }
            .floating-tip .icon {
                background: #1d1d1f;
                color: #fff;
            }
        }

        .usage-tip {
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%) translateY(-100px);
            background: rgba(0, 0, 0, 0.9);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            padding: 16px 24px;
            border-radius: 12px;
            color: white;
            font-size: 15px;
            line-height: 1.4;
            z-index: 9999;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
            opacity: 0;
            transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
            font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Arial, sans-serif;
            display: flex;
            align-items: center;
            gap: 12px;
            cursor: pointer;
            max-width: 90%;
            width: auto;
            border: 1px solid rgba(255, 255, 255, 0.1);
        }

        .usage-tip.show {
            transform: translateX(-50%) translateY(0);
            opacity: 1;
        }

        .usage-tip .icon {
            font-size: 24px;
            flex-shrink: 0;
        }

        .usage-tip .content {
            display: flex;
            flex-direction: column;
            gap: 4px;
        }

        .usage-tip .main-text {
            font-weight: 500;
        }

        .usage-tip .contact {
            font-size: 13px;
            color: rgba(255, 255, 255, 0.8);
        }

        @media (prefers-color-scheme: dark) {
            .usage-tip {
                background: rgba(255, 255, 255, 0.95);
                color: #1d1d1f;
                border: 1px solid rgba(0, 0, 0, 0.1);
            }
            .usage-tip .contact {
                color: rgba(0, 0, 0, 0.6);
            }
        }

        .remaining-time {
            background: rgba(0, 0, 0, 0.03);
            padding: 8px 12px;
            border-radius: 8px;
            text-align: center;
        }

        @media (prefers-color-scheme: dark) {
            .remaining-time {
                background: rgba(255, 255, 255, 0.05);
                color: #98989d;
            }
        }
    `;
    document.head.appendChild(style);

    // 获取当前网站域名
    const currentDomain = window.location.hostname;

    // 创建确认对话框
    function createConfirmDialog(mediaUrl, mediaType, downloadFunction) {
        const overlay = document.createElement('div');
        overlay.className = 'download-confirm-overlay';

        const dialog = document.createElement('div');
        dialog.className = 'download-confirm-dialog';

        // 获取激活码和过期时间信息
        const activationCode = localStorage.getItem('activation_code');
        let remainingTimeText = '';

        if (activationCode) {
            try {
                const [, , expireTimeStr] = activationCode.split('-');
                const expireTime = parseInt(expireTimeStr, 36);
                const now = Math.floor(Date.now() / 1000);
                const remainingSeconds = expireTime - now;

                if (remainingSeconds > 0) {
                    const days = Math.floor(remainingSeconds / (24 * 60 * 60));
                    const hours = Math.floor((remainingSeconds % (24 * 60 * 60)) / (60 * 60));
                    const minutes = Math.floor((remainingSeconds % (60 * 60)) / 60);

                    remainingTimeText = `
                        <div class="remaining-time" style="margin-bottom: 15px; color: #86868b; font-size: 13px;">
                            激活码剩余时间:${days}天${hours}小时${minutes}分钟
                        </div>
                    `;
                }
            } catch (e) {
                console.error('解析过期时间出错:', e);
            }
        }

        // 获取当前日期时间作为默认文件名的备选
        const now = new Date();
        const dateStr = `${now.getFullYear()}${(now.getMonth()+1).toString().padStart(2,'0')}${now.getDate().toString().padStart(2,'0')}`;

        // 获取提示词
        let promptText = '';
        // 根据不同类型使用不同的选择器
        let promptElement;
        if (mediaType === 'video') {
            // 视频页面的提示词选择器
            promptElement = document.querySelector('span.lv-typography[class*="promptText-sTGKI"]');
        } else {
            // 图片页面的提示词选择器 - 添加豆包新的选择器
            promptElement = document.querySelector('span.lv-typography[class*="promptText-"]') ||
                           document.querySelector('.message-text-aF_36u[data-testid="message_text_content"]');
        }

        if (promptElement) {
            // 获取完整的提示词文本
            let text = promptElement.textContent.trim();

            // 如果是豆包的提示词,移除"帮我生成图片:"前缀
            if (text.startsWith('帮我生成图片:')) {
                text = text.replace('帮我生成图片:', '');
            }

            promptText = text
                // 移除多余的空格和换行
                .replace(/\s+/g, ' ')
                // 移除引号和破折号
                .replace(/^[""]-|[""]$/g, '')
                // 移除文件名中的非法字符
                .replace(/[\\/:*?"<>|]/g, '_')
                // 移除比例信息
                .replace(/比例["\d+:\d+"]/, '')
                .trim()
                .substring(0, 100); // 限制长度

            console.log(`获取到的${mediaType}提示词:`, promptText); // 调试用
        } else {
            console.log(`未找到${mediaType}提示词元素`); // 调试用
        }

        // 默认文件名使用提示词,如果没有提示词则使用日期
        const defaultFileName = promptText || dateStr;

        // 修改对话框内容,添加剩余时间显示
        dialog.innerHTML = `
            <h3>下载${mediaType === 'video' ? '视频' : '图片'}</h3>
            ${remainingTimeText}
            <p>请确认下载信息</p>
            <div class="input-container">
                <label for="fileName">文件名称</label>
                <input type="text"
                       id="fileName"
                       value="${defaultFileName}"
                       placeholder="请输入文件名称"
                       spellcheck="false"
                       autocomplete="off">
                <div class="tip">提示:右键点击${mediaType === 'video' ? '视频' : '图片'}即可下载,文件名将自动使用AI提示词</div>
            </div>
            <div class="progress-text" style="display: none;">
                <span class="success-icon"></span>
                <span class="status-text"></span>
            </div>
            <div class="buttons">
                <button class="cancel-btn">取消</button>
                <button class="confirm-btn">下载</button>
            </div>
        `;

        document.body.appendChild(overlay);
        document.body.appendChild(dialog);

        const confirmBtn = dialog.querySelector('.confirm-btn');
        const cancelBtn = dialog.querySelector('.cancel-btn');
        const fileNameInput = dialog.querySelector('#fileName');

        const progressText = dialog.querySelector('.progress-text');
        const statusText = dialog.querySelector('.status-text');
        const successIcon = dialog.querySelector('.success-icon');

        function closeDialog() {
            document.body.removeChild(overlay);
            document.body.removeChild(dialog);
        }

        function handleDownloadProgress(percent) {
            if (percent) {
                progressText.style.display = 'block';
                statusText.textContent = `正在下载...${percent}%`;
            }
        }

        function handleDownloadSuccess() {
            confirmBtn.style.display = 'none';
            progressText.style.display = 'block';
            successIcon.classList.add('show');
            statusText.textContent = '下载完成';
            setTimeout(() => {
                closeDialog();
            }, 1500);
        }

        function handleDownloadError(error) {
            progressText.style.display = 'block';
            statusText.textContent = `下载失败: ${error}`;
            statusText.style.color = '#ff3b30';
            confirmBtn.disabled = false;
            confirmBtn.textContent = '重试';
        }

        confirmBtn.addEventListener('click', () => {
            confirmBtn.disabled = true;
            confirmBtn.textContent = '准备下载...';
            const customFileName = fileNameInput.value.trim();

            downloadFunction(
                mediaUrl,
                handleDownloadSuccess,
                customFileName,
                handleDownloadProgress,
                handleDownloadError
            );
        });

        cancelBtn.addEventListener('click', closeDialog);
    }

    // 处理视频URL,移除水印
    function processVideoUrl(url) {
        try {
            if (url.includes('vlabvod.com')) {
                const urlObj = new URL(url);
                const paramsToRemove = [
                    'lr', 'watermark', 'display_watermark_busi_user',
                    'cd', 'cs', 'ds', 'ft', 'btag', 'dy_q', 'feature_id'
                ];

                paramsToRemove.forEach(param => {
                    urlObj.searchParams.delete(param);
                });

                if (urlObj.searchParams.has('br')) {
                    const br = parseInt(urlObj.searchParams.get('br'));
                    urlObj.searchParams.set('br', Math.max(br, 6000).toString());
                    urlObj.searchParams.set('bt', Math.max(br, 6000).toString());
                }

                urlObj.searchParams.delete('l');
                return urlObj.toString();
            }
            return url;
        } catch (e) {
            console.error('处理视频URL时出错:', e);
            return url;
        }
    }

    // 获取真实视频URL
    async function getRealVideoUrl(videoElement) {
        let videoUrl = videoElement.src;
        if (!videoUrl) {
            const sourceElement = videoElement.querySelector('source');
            if (sourceElement) {
                videoUrl = sourceElement.src;
            }
        }
        if (!videoUrl) {
            videoUrl = videoElement.getAttribute('data-src');
        }
        return videoUrl;
    }

    // 获取文件扩展名
    function getFileExtension(url) {
        const extension = url.split('?')[0].match(/\.(jpg|jpeg|png|gif|mp4|webm)$/i);
        return extension ? extension[0] : '.jpg';
    }

    // 下载图片的函数
    function downloadImage(imageUrl, callback, customFileName, onProgress, onError) {
        const fileExtension = getFileExtension(imageUrl);
        const fileName = customFileName ? `${customFileName}${fileExtension}` : getFileNameFromUrl(imageUrl);

        GM_xmlhttpRequest({
            method: 'GET',
            url: imageUrl,
            responseType: 'blob',
            headers: {
                'Accept': 'image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
                'Accept-Encoding': 'gzip, deflate, br',
                'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
                'Referer': currentDomain.includes('doubao') ?
                          'https://www.doubao.com/' :
                          'https://jimeng.jianying.com/',
                'Origin': currentDomain.includes('doubao') ?
                         'https://www.doubao.com' :
                         'https://jimeng.jianying.com',
                'User-Agent': navigator.userAgent
            },
            onprogress: function(progress) {
                if (progress.lengthComputable) {
                    const percent = Math.round((progress.loaded / progress.total) * 100);
                    if (onProgress) onProgress(percent);
                }
            },
            onload: function(response) {
                if (response.status === 200) {
                    const blob = response.response;
                    const url = URL.createObjectURL(blob);
                    const link = document.createElement('a');
                    link.href = url;
                    link.download = fileName;
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                    setTimeout(() => URL.revokeObjectURL(url), 100);
                    if (callback) callback();
                } else {
                    if (onError) onError(`HTTP ${response.status}`);
                }
            },
            onerror: function(error) {
                if (onError) onError(error.message || '网络错误');
            }
        });
    }

    // 下载视频的函数
    function downloadVideo(videoUrl, callback, customFileName, onProgress, onError) {
        const processedUrl = processVideoUrl(videoUrl);
        const fileExtension = '.mp4';
        const fileName = customFileName ? `${customFileName}${fileExtension}` : getFileNameFromUrl(processedUrl);

        let progressDialog = document.querySelector('.download-confirm-dialog');
        let progressBtn = progressDialog?.querySelector('.confirm-btn');

        GM_xmlhttpRequest({
            method: 'GET',
            url: processedUrl,
            responseType: 'blob',
            headers: {
                'Accept': 'video/mp4,video/*;q=0.9,*/*;q=0.8',
                'Accept-Encoding': 'gzip, deflate, br',
                'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
                'Range': 'bytes=0-',
                'Referer': currentDomain.includes('doubao') ?
                          'https://www.doubao.com/' :
                          'https://jimeng.jianying.com/',
                'Origin': currentDomain.includes('doubao') ?
                         'https://www.doubao.com' :
                         'https://jimeng.jianying.com',
                'User-Agent': navigator.userAgent
            },
            onprogress: function(progress) {
                if (progress.lengthComputable && progressBtn) {
                    const percent = Math.round((progress.loaded / progress.total) * 100);
                    progressBtn.textContent = `下载中 ${percent}%`;
                }
            },
            onload: function(response) {
                if (response.status === 200 || response.status === 206) {
                    const blob = response.response;
                    const url = URL.createObjectURL(blob);
                    const link = document.createElement('a');
                    link.href = url;
                    link.download = fileName;
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                    setTimeout(() => URL.revokeObjectURL(url), 100);
                    if (callback) callback();
                } else {
                    console.error('下载失败:', response.status);
                    alert('下载失败,请检查控制台获取详细信息');
                    if (callback) callback();
                }
            },
            onerror: function(error) {
                console.error('请求失败:', error);
                alert('下载失败,请检查控制台获取详细信息');
                if (callback) callback();
            }
        });
    }

    // 从 URL 中提取文件名
    function getFileNameFromUrl(url) {
        url = url.split('?')[0];
        const urlParts = url.split('/');
        let fileName = urlParts[urlParts.length - 1];

        if (fileName.includes('~')) {
            fileName = fileName.split('~')[0];
        }

        if (!fileName.match(/\.(mp4|webm|jpg|jpeg|png)$/i)) {
            fileName += url.includes('video') ? '.mp4' : '.jpeg';
        }

        return fileName;
    }

    // 修改事件监听器,使用新的下载函数
    document.addEventListener('contextmenu', async function (event) {
        const target = event.target;

        if (target.tagName.toLowerCase() === 'img') {
            const imageUrl = target.src;
            if (imageUrl) {
                downloadWithActivationCheck(imageUrl, 'image', downloadImage);
                event.preventDefault();
            }
        }
        else if (target.tagName.toLowerCase() === 'video' ||
                 target.closest('video')) {
            const videoElement = target.tagName.toLowerCase() === 'video' ?
                               target : target.closest('video');

            if (videoElement) {
                const videoUrl = await getRealVideoUrl(videoElement);
                if (videoUrl) {
                    downloadWithActivationCheck(videoUrl, 'video', downloadVideo);
                    event.preventDefault();
                }
            }
        }
    }, true);

    // 修改显示提示的函数
    function showUsageTip() {
        // 移除检查localStorage的逻辑,每次都显示提示
        const tip = document.createElement('div');
        tip.className = 'usage-tip';
        tip.innerHTML = `
            <span class="icon">💡</span>
            <div class="content">
                <span class="main-text">点击图片或视频,单击鼠标右键即可免费下载无水印的图片或视频</span>
                <span class="contact">有问题联系微信:11208596</span>
            </div>
        `;
        document.body.appendChild(tip);

        // 显示提示
        setTimeout(() => {
            tip.classList.add('show');
        }, 500);

        // 10秒后自动隐藏提示
        setTimeout(() => {
            tip.classList.remove('show');
            setTimeout(() => {
                document.body.removeChild(tip);
            }, 600);
        }, 10000);

        // 点击可以提前关闭提示
        tip.addEventListener('click', () => {
            tip.classList.remove('show');
            setTimeout(() => {
                document.body.removeChild(tip);
            }, 600);
        });
    }

    // 修改页面加载时的提示逻辑
    function initUsageTip() {
        if (window.location.hostname.includes('doubao.com') ||
            window.location.hostname.includes('jimeng.jianying.com')) {

            // 页面加载完成后显示提示
            if (document.readyState === 'complete') {
                showUsageTip();
            } else {
                window.addEventListener('load', showUsageTip);
            }

            // 监听页面可见性变化
            document.addEventListener('visibilitychange', () => {
                if (document.visibilityState === 'visible') {
                    showUsageTip();
                }
            });

            // 监听页面焦点变化
            window.addEventListener('focus', showUsageTip);
        }
    }

    // 初始化提示
    initUsageTip();
})();