可灵和即梦AI去水印下载脚本 (兼容增强修复版)

自动监测可灵和即梦AI网页端生成的视频和照片,提供完全独立的预览、勾选和无水印批量下载功能 (增强兼容性与修复视频预览问题)

// ==UserScript==
// @name         可灵和即梦AI去水印下载脚本 (兼容增强修复版)
// @namespace    http://tampermonkey.net/
// @version      3.4
// @description  自动监测可灵和即梦AI网页端生成的视频和照片,提供完全独立的预览、勾选和无水印批量下载功能 (增强兼容性与修复视频预览问题)
// @author       醉春风
// @license      Custom:ZuiChunFeng-Exclusive; 本脚本版权归"醉春风"所有,未经授权禁止复制、修改、分发或二次开发。仅限作者本人维护和更新。
// @match        https://app.klingai.com/*
// @match        https://klingai.kuaishou.com/*
// @match        https://klingai.com/*
// @match        https://jimeng.jianying.com/*
// @match        https://www.youtube.com/*
// @match        https://youtube.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_download
// @grant        GM_addStyle
// @grant        unsafeWindow
// @grant        GM_log
// @connect      *
// @run-at       document-idle
// ==/UserScript==

/*
 * =====================================================================
 * 版权声明:本脚本版权归"醉春风"所有,保留所有权利
 * 使用许可:仅限个人使用,未经授权禁止复制、修改、分发或二次开发
 * 维护权限:仅限作者本人维护和更新
 * 违规处理:任何未经授权的复制、修改或分发行为将被视为侵权
 * =====================================================================
 */

(function() {
    'use strict';
    
    // 版权信息
    console.log('[版权信息] 版权所有:醉春风');

    // --- 配置 --- 
    const CONFIG = {
        DEBUG: true,                // 是否开启调试日志
        INIT_DELAY: 5000,           // 初始延迟 (毫秒), 等待页面加载
        RETRY_DELAY: 3000,          // 重试延迟 (毫秒)
        MAX_RETRIES: 5,             // 最大重试次数
        BUTTON_CHECK_INTERVAL: 2000, // 按钮可见性检查间隔 (毫秒)
        AUTHOR: "醉春风",           // 作者署名
        VERSION: "3.4"              // 版本号
    };

    // --- 日志 --- 
    const log = (...args) => {
        if (CONFIG.DEBUG) {
            console.log(`[AI下载脚本 v${CONFIG.VERSION}]`, ...args);
            // GM_log(`[AI下载脚本 v${CONFIG.VERSION}] ` + args.join(' ')); // 可选:使用GM_log持久化日志
        }
    };
    const error = (...args) => {
        console.error(`[AI下载脚本 v${CONFIG.VERSION}]`, ...args);
        // GM_log(`[AI下载脚本 v${CONFIG.VERSION}] ERROR: ` + args.join(' ')); // 可选:使用GM_log持久化日志
    };

    // --- 样式注入 --- 
    GM_addStyle(`
        /* 主按钮样式 */
        .manus-ai-downloader-btn {
            position: fixed !important;
            bottom: 20px !important;
            right: 20px !important;
            background: linear-gradient(135deg, #3a8ffe 0%, #9259fe 100%) !important;
            color: white !important;
            border: none !important;
            border-radius: 4px !important;
            padding: 10px 15px !important;
            font-size: 14px !important;
            font-weight: bold !important;
            cursor: pointer !important;
            z-index: 2147483646 !important; /* Max z-index - 1 */
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2) !important;
            display: flex !important;
            align-items: center !important;
            transition: all 0.3s ease !important;
        }
        .manus-ai-downloader-btn:hover {
            transform: translateY(-2px) !important;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3) !important;
        }
        .manus-ai-downloader-btn svg {
            margin-right: 8px !important;
            fill: none !important;
            stroke: white !important;
            stroke-width: 2 !important;
            stroke-linecap: round !important;
            stroke-linejoin: round !important;
        }
        
        /* 手动扫描按钮 */
        .manus-ai-manual-scan-btn {
            position: fixed !important;
            bottom: 70px !important;
            right: 20px !important;
            background: #444 !important;
            color: white !important;
            border: none !important;
            border-radius: 4px !important;
            padding: 8px 12px !important;
            font-size: 14px !important;
            cursor: pointer !important;
            z-index: 2147483645 !important;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2) !important;
            transition: all 0.3s ease !important;
        }
        .manus-ai-manual-scan-btn:hover {
            background: #555 !important;
            transform: translateY(-2px) !important;
        }
        
        /* 通知样式 */
        .manus-ai-downloader-notification {
            position: fixed !important;
            top: 20px !important;
            right: 20px !important;
            background: rgba(0, 0, 0, 0.8) !important;
            color: white !important;
            padding: 10px 15px !important;
            border-radius: 4px !important;
            z-index: 2147483647 !important; /* Max z-index */
            font-size: 14px !important;
            max-width: 300px !important;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2) !important;
            animation: manusAiFadeInOut 3s forwards !important;
        }
        @keyframes manusAiFadeInOut {
            0% { opacity: 0; transform: translateY(-20px); }
            10% { opacity: 1; transform: translateY(0); }
            90% { opacity: 1; transform: translateY(0); }
            100% { opacity: 0; transform: translateY(-20px); }
        }
        
        /* 错误通知样式 */
        .manus-ai-downloader-error {
            position: fixed !important;
            top: 20px !important;
            right: 20px !important;
            background: rgba(255, 59, 48, 0.9) !important;
            color: white !important;
            padding: 10px 15px !important;
            border-radius: 4px !important;
            z-index: 2147483647 !important; /* Max z-index */
            font-size: 14px !important;
            max-width: 300px !important;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2) !important;
            animation: manusAiShakeError 0.5s forwards, manusAiFadeOutError 3s 0.5s forwards !important;
            display: flex !important;
            align-items: center !important;
        }
        .manus-ai-downloader-error svg {
            margin-right: 8px !important;
            fill: none !important;
            stroke: white !important;
            stroke-width: 2 !important;
            stroke-linecap: round !important;
            stroke-linejoin: round !important;
        }
        @keyframes manusAiShakeError {
            0%, 100% { transform: translateX(0); }
            10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
            20%, 40%, 60%, 80% { transform: translateX(5px); }
        }
        @keyframes manusAiFadeOutError {
            0%, 80% { opacity: 1; }
            100% { opacity: 0; transform: translateY(-20px); }
        }
        
        /* 预览面板样式 */
        .manus-ai-preview-panel {
            position: fixed !important;
            top: 10% !important;
            right: 0 !important;
            width: 30% !important;
            height: 80% !important;
            min-width: 300px !important;
            min-height: 400px !important;
            background: rgba(26, 26, 26, 0.95) !important; /* Slightly less transparent */
            border-radius: 8px 0 0 8px !important;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25) !important;
            z-index: 2147483646 !important; /* Max z-index - 1 */
            display: flex !important;
            flex-direction: column !important;
            transform: translateX(100%) !important;
            transition: transform 0.3s ease !important;
            border-left: 2px solid #3a8ffe !important;
            color: white !important; /* Ensure text color */
            font-family: sans-serif !important; /* Reset font */
        }
        .manus-ai-preview-panel.show {
            transform: translateX(0) !important;
        }
        
        /* 预览面板头部 */
        .manus-ai-preview-header {
            display: flex !important;
            justify-content: space-between !important;
            align-items: center !important;
            padding: 10px 15px !important;
            border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
            flex-shrink: 0 !important;
        }
        .manus-ai-preview-title {
            color: white !important;
            font-size: 16px !important;
            font-weight: bold !important;
        }
        .manus-ai-author-credit {
            color: #ffcc00 !important;
            font-size: 12px !important;
            font-style: italic !important;
            margin-left: 10px !important;
            font-weight: normal !important;
        }
        .manus-ai-preview-close {
            color: white !important;
            background: none !important;
            border: none !important;
            cursor: pointer !important;
            font-size: 18px !important;
            padding: 5px !important;
            display: flex !important;
            align-items: center !important;
            justify-content: center !important;
            border-radius: 50% !important;
            width: 24px !important;
            height: 24px !important;
            transition: background 0.2s !important;
        }
        .manus-ai-preview-close:hover {
            background: rgba(255, 255, 255, 0.1) !important;
        }
        
        /* 预览面板工具栏 */
        .manus-ai-preview-toolbar {
            display: flex !important;
            padding: 10px 15px !important;
            border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
            gap: 8px !important;
            flex-wrap: wrap !important;
            flex-shrink: 0 !important;
        }
        .manus-ai-preview-toolbar button {
            background: rgba(255, 255, 255, 0.1) !important;
            color: white !important;
            border: none !important;
            border-radius: 4px !important;
            padding: 5px 10px !important;
            font-size: 12px !important;
            cursor: pointer !important;
            transition: background 0.2s !important;
        }
        .manus-ai-preview-toolbar button:hover {
            background: rgba(255, 255, 255, 0.2) !important;
        }
        .manus-ai-preview-counter {
            margin-left: auto !important;
            color: rgba(255, 255, 255, 0.7) !important;
            font-size: 12px !important;
            display: flex !important;
            align-items: center !important;
        }
        
        /* 预览内容区域 */
        .manus-ai-preview-content {
            flex: 1 !important;
            overflow-y: auto !important;
            padding: 15px !important;
            display: grid !important;
            grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)) !important;
            gap: 10px !important;
        }
        .manus-ai-preview-content::-webkit-scrollbar {
            width: 6px !important;
        }
        .manus-ai-preview-content::-webkit-scrollbar-track {
            background: rgba(0, 0, 0, 0.1) !important;
        }
        .manus-ai-preview-content::-webkit-scrollbar-thumb {
            background: rgba(255, 255, 255, 0.2) !important;
            border-radius: 3px !important;
        }
        
        /* 媒体项样式 (完全自定义) */
        .manus-ai-media-item {
            position: relative !important;
            width: 100% !important;
            padding-bottom: 100% !important; /* 1:1 Aspect Ratio */
            border-radius: 4px !important;
            overflow: hidden !important;
            cursor: pointer !important;
            transition: transform 0.2s, box-shadow 0.2s !important;
            background-color: #2a2a2a !important;
        }
        .manus-ai-media-item:hover {
            transform: scale(1.05) !important;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3) !important;
            z-index: 1 !important;
        }
        .manus-ai-media-item.selected {
            border: 2px solid #3a8ffe !important;
            box-shadow: 0 0 0 2px rgba(58, 143, 254, 0.2) !important;
        }
        .manus-ai-media-thumbnail-container {
            position: absolute !important;
            top: 0 !important;
            left: 0 !important;
            width: 100% !important;
            height: 100% !important;
            display: flex !important;
            align-items: center !important;
            justify-content: center !important;
            background-color: #2a2a2a !important;
        }
        .manus-ai-media-thumbnail-container svg {
            width: 32px !important;
            height: 32px !important;
            opacity: 1 !important;
            fill: none !important;
            stroke: #ffffff !important;
            stroke-width: 2 !important;
            stroke-linecap: round !important;
            stroke-linejoin: round !important;
        }
        .manus-ai-media-checkbox {
            position: absolute !important;
            bottom: 5px !important;
            left: 5px !important;
            width: 18px !important;
            height: 18px !important;
            border: 2px solid white !important;
            border-radius: 2px !important;
            background: rgba(0, 0, 0, 0.5) !important;
            display: flex !important;
            align-items: center !important;
            justify-content: center !important;
            transition: background 0.2s !important;
            z-index: 3 !important;
        }
        .manus-ai-media-checkbox.checked {
            background: #3a8ffe !important;
        }
        .manus-ai-media-checkbox.checked::after {
            content: "" !important;
            width: 10px !important;
            height: 5px !important;
            border-left: 2px solid white !important;
            border-bottom: 2px solid white !important;
            transform: rotate(-45deg) translate(1px, -1px) !important;
        }
        
        /* 视频特殊样式 */
        .manus-ai-media-item.video .manus-ai-media-thumbnail-container::after {
            content: "" !important;
            position: absolute !important;
            top: 50% !important;
            left: 50% !important;
            transform: translate(-50%, -50%) !important;
            width: 0 !important;
            height: 0 !important;
            border-top: 10px solid transparent !important;
            border-bottom: 10px solid transparent !important;
            border-left: 15px solid rgba(255, 255, 255, 0.8) !important;
            filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.5)) !important;
            z-index: 1 !important;
        }
        .manus-ai-media-duration {
            position: absolute !important;
            bottom: 5px !important;
            right: 5px !important;
            background: rgba(0, 0, 0, 0.7) !important;
            color: white !important;
            font-size: 10px !important;
            padding: 2px 4px !important;
            border-radius: 2px !important;
            z-index: 3 !important;
        }
        
        /* 预览面板底部 */
        .manus-ai-preview-footer {
            padding: 10px 15px !important;
            border-top: 1px solid rgba(255, 255, 255, 0.1) !important;
            display: flex !important;
            justify-content: space-between !important;
            flex-shrink: 0 !important;
        }
        .manus-ai-download-btn {
            background: linear-gradient(135deg, #3a8ffe 0%, #9259fe 100%) !important;
            color: white !important;
            border: none !important;
            border-radius: 4px !important;
            padding: 8px 15px !important;
            font-size: 14px !important;
            font-weight: bold !important;
            cursor: pointer !important;
            transition: all 0.2s !important;
            display: flex !important;
            align-items: center !important;
        }
        .manus-ai-download-btn svg {
            margin-right: 6px !important;
            fill: none !important;
            stroke: white !important;
            stroke-width: 2 !important;
            stroke-linecap: round !important;
            stroke-linejoin: round !important;
        }
        .manus-ai-download-btn:hover {
            transform: translateY(-2px) !important;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2) !important;
        }
        .manus-ai-download-btn:disabled {
            background: #666 !important;
            cursor: not-allowed !important;
            transform: none !important;
            box-shadow: none !important;
        }
        .manus-ai-cancel-btn {
            background: rgba(255, 255, 255, 0.1) !important;
            color: white !important;
            border: none !important;
            border-radius: 4px !important;
            padding: 8px 15px !important;
            font-size: 14px !important;
            cursor: pointer !important;
            transition: background 0.2s !important;
        }
        .manus-ai-cancel-btn:hover {
            background: rgba(255, 255, 255, 0.2) !important;
        }
        
        /* 大预览样式 */
        .manus-ai-large-preview {
            position: fixed !important;
            top: 0 !important;
            left: 0 !important;
            width: 100% !important;
            height: 100% !important;
            background: rgba(0, 0, 0, 0.9) !important;
            z-index: 2147483647 !important; /* Max z-index */
            display: flex !important;
            align-items: center !important;
            justify-content: center !important;
            opacity: 0 !important;
            pointer-events: none !important;
            transition: opacity 0.3s ease !important;
        }
        .manus-ai-large-preview.show {
            opacity: 1 !important;
            pointer-events: auto !important;
        }
        .manus-ai-large-preview-close {
            position: absolute !important;
            top: 20px !important;
            right: 20px !important;
            color: white !important;
            background: rgba(0, 0, 0, 0.5) !important;
            border: none !important;
            border-radius: 50% !important;
            width: 40px !important;
            height: 40px !important;
            font-size: 24px !important;
            display: flex !important;
            align-items: center !important;
            justify-content: center !important;
            cursor: pointer !important;
            transition: background 0.2s !important;
            z-index: 2 !important;
        }
        .manus-ai-large-preview-close:hover {
            background: rgba(255, 255, 255, 0.2) !important;
        }
        .manus-ai-large-preview-container {
            max-width: 90% !important;
            max-height: 90% !important;
            position: relative !important;
            display: flex !important;
            align-items: center !important;
            justify-content: center !important;
        }
        .manus-ai-large-preview-container img {
            max-width: 100% !important;
            max-height: 100% !important;
            object-fit: contain !important;
            border-radius: 4px !important;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5) !important;
        }
        .manus-ai-large-preview-container video {
            max-width: 100% !important;
            max-height: 100% !important;
            object-fit: contain !important;
            border-radius: 4px !important;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5) !important;
            background: #000 !important;
        }
        .manus-ai-video-controls {
            position: absolute !important;
            bottom: 10px !important;
            left: 0 !important;
            width: 100% !important;
            display: flex !important;
            justify-content: center !important;
            padding: 10px !important;
            background: rgba(0, 0, 0, 0.5) !important;
            border-radius: 0 0 4px 4px !important;
            opacity: 0 !important;
            transition: opacity 0.3s !important;
        }
        .manus-ai-large-preview-container:hover .manus-ai-video-controls {
            opacity: 1 !important;
        }
        .manus-ai-video-error {
            color: white !important;
            background: rgba(255, 59, 48, 0.8) !important;
            padding: 10px 15px !important;
            border-radius: 4px !important;
            text-align: center !important;
            max-width: 80% !important;
        }
        
        /* 作者署名水印 */
        .manus-ai-author-watermark {
            position: absolute !important;
            bottom: 10px !important;
            left: 10px !important;
            color: rgba(255, 255, 255, 0.5) !important;
            font-size: 12px !important;
            font-style: italic !important;
            pointer-events: none !important;
            z-index: 1 !important;
            text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5) !important;
        }
    `);

    // --- 状态管理 --- 
    const state = {
        platform: null,
        mediaItems: [],
        selectedItems: [],
        isProcessing: false,
        isPanelOpen: false,
        downloadQueue: [],
        currentDownloading: null,
        largePreviewOpen: false,
        mediaMap: new Map(),
        mediaUrls: new Set(),
        windowLock: false,
        instanceId: 'manus_ai_downloader_' + Math.random().toString(36).substr(2, 9),
        initRetries: 0,
        observer: null, // MutationObserver实例
        buttonCheckIntervalId: null // 按钮检查定时器ID
    };

    // --- 图标 --- 
    const ICONS = {
        image: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg>`,
        video: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"></rect><line x1="7" y1="2" x2="7" y2="22"></line><line x1="17" y1="2" x2="17" y2="22"></line><line x1="2" y1="12" x2="22" y2="12"></line><line x1="2" y1="7" x2="7" y2="7"></line><line x1="2" y1="17" x2="7" y2="17"></line><line x1="17" y1="17" x2="22" y2="17"></line><line x1="17" y1="7" x2="22" y2="7"></line></svg>`,
        download: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>`,
        error: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>`,
        play: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"></circle><polygon points="10 8 16 12 10 16 10 8"></polygon></svg>`
    };

    // --- 媒体项类 --- 
    class MediaItem {
        constructor(element, type, url = null) {
            this.id = 'media_' + Math.random().toString(36).substr(2, 9);
            this.element = element; // 原始DOM元素引用 (可能为null)
            this.type = type; // 'image' 或 'video'
            this.url = url || (element ? (element.src || element.currentSrc) : null);
            this.highestQualityUrl = this.url;
            this.width = element ? (element.width || element.videoWidth || 0) : 0;
            this.height = element ? (element.height || element.videoHeight || 0) : 0;
            this.duration = type === 'video' && element ? element.duration : 0;
            this.selected = false;
            this.timestamp = Date.now(); // 添加时间戳
            this.videoLoaded = false; // 视频是否已加载成功
            this.thumbnailLoaded = false; // 缩略图是否已加载成功

            log(`创建媒体项: ${this.id}, 类型: ${this.type}, URL: ${this.url ? this.url.substring(0, 50) + '...' : 'N/A'}`);
        }

        // 创建缩略图元素 (完全自定义)
        createThumbnailElement() {
            const itemDiv = document.createElement('div');
            itemDiv.className = `manus-ai-media-item ${this.type}`;
            itemDiv.dataset.id = this.id;

            const thumbnailContainer = document.createElement('div');
            thumbnailContainer.className = 'manus-ai-media-thumbnail-container';
            
            // 尝试加载实际缩略图
            if (this.url) {
                try {
                    if (this.type === 'image') {
                        const img = document.createElement('img');
                        img.src = this.url;
                        img.style.width = '100%';
                        img.style.height = '100%';
                        img.style.objectFit = 'cover';
                        img.style.position = 'absolute';
                        img.style.top = '0';
                        img.style.left = '0';
                        img.crossOrigin = 'anonymous'; // 尝试解决跨域问题
                        img.loading = 'eager'; // 优先加载
                        img.decoding = 'async'; // 异步解码
                        
                        // 添加加载中占位符
                        const placeholder = document.createElement('div');
                        placeholder.innerHTML = ICONS.image;
                        placeholder.style.position = 'absolute';
                        placeholder.style.top = '0';
                        placeholder.style.left = '0';
                        placeholder.style.width = '100%';
                        placeholder.style.height = '100%';
                        placeholder.style.display = 'flex';
                        placeholder.style.alignItems = 'center';
                        placeholder.style.justifyContent = 'center';
                        placeholder.style.backgroundColor = '#2a2a2a';
                        thumbnailContainer.appendChild(placeholder);
                        
                        img.onload = () => {
                            this.thumbnailLoaded = true;
                            placeholder.remove();
                            log(`图片 ${this.id} 缩略图加载成功`);
                        };
                        
                        img.onerror = () => {
                            // 加载失败时保留占位符
                            log(`图片 ${this.id} 加载失败,保留占位符`);
                            
                            // 尝试使用备用方法加载
                            setTimeout(() => {
                                if (!this.thumbnailLoaded) {
                                    // 创建一个新的图片元素尝试再次加载
                                    const retryImg = new Image();
                                    retryImg.crossOrigin = 'anonymous';
                                    retryImg.src = this.url + '?retry=' + new Date().getTime(); // 添加时间戳避免缓存
                                    retryImg.onload = () => {
                                        img.src = retryImg.src;
                                        this.thumbnailLoaded = true;
                                        placeholder.remove();
                                        log(`图片 ${this.id} 重试加载成功`);
                                    };
                                }
                            }, 1000);
                        };
                        
                        thumbnailContainer.appendChild(img);
                    } else {
                        // 视频缩略图 - 尝试加载视频并获取第一帧
                        const video = document.createElement('video');
                        video.src = this.url;
                        video.crossOrigin = 'anonymous'; // 尝试解决跨域问题
                        video.muted = true;
                        video.preload = 'metadata'; // 只加载元数据
                        video.style.width = '100%';
                        video.style.height = '100%';
                        video.style.objectFit = 'cover';
                        video.style.position = 'absolute';
                        video.style.top = '0';
                        video.style.left = '0';
                        
                        // 添加视频占位符
                        const placeholder = document.createElement('div');
                        placeholder.innerHTML = ICONS.video;
                        placeholder.style.position = 'absolute';
                        placeholder.style.top = '0';
                        placeholder.style.left = '0';
                        placeholder.style.width = '100%';
                        placeholder.style.height = '100%';
                        placeholder.style.display = 'flex';
                        placeholder.style.alignItems = 'center';
                        placeholder.style.justifyContent = 'center';
                        placeholder.style.backgroundColor = '#2a2a2a';
                        thumbnailContainer.appendChild(placeholder);
                        
                        // 添加播放按钮图标覆盖层
                        const playIcon = document.createElement('div');
                        playIcon.style.position = 'absolute';
                        playIcon.style.top = '50%';
                        playIcon.style.left = '50%';
                        playIcon.style.transform = 'translate(-50%, -50%)';
                        playIcon.style.width = '30px';
                        playIcon.style.height = '30px';
                        playIcon.style.zIndex = '2';
                        playIcon.style.opacity = '0.8';
                        playIcon.innerHTML = ICONS.play;
                        
                        // 尝试加载视频并获取第一帧
                        video.addEventListener('loadeddata', () => {
                            this.videoLoaded = true;
                            this.thumbnailLoaded = true;
                            this.duration = video.duration || 0;
                            
                            // 更新时长显示
                            const durationEl = itemDiv.querySelector('.manus-ai-media-duration');
                            if (durationEl) {
                                durationEl.textContent = this.formatDuration(this.duration);
                            }
                            
                            // 视频加载成功,移除占位符
                            placeholder.remove();
                            thumbnailContainer.appendChild(playIcon);
                            log(`视频 ${this.id} 缩略图加载成功`);
                        });
                        
                        video.addEventListener('error', (e) => {
                            // 视频加载失败,保留占位符
                            log(`视频 ${this.id} 加载失败: ${e.target.error ? e.target.error.message : '未知错误'}`);
                            
                            // 尝试使用备用方法加载
                            setTimeout(() => {
                                if (!this.thumbnailLoaded) {
                                    // 尝试使用不同的方式加载视频
                                    const retryVideo = document.createElement('video');
                                    retryVideo.crossOrigin = 'anonymous';
                                    retryVideo.muted = true;
                                    retryVideo.src = this.url + '?retry=' + new Date().getTime(); // 添加时间戳避免缓存
                                    retryVideo.addEventListener('loadeddata', () => {
                                        video.src = retryVideo.src;
                                        this.videoLoaded = true;
                                        this.thumbnailLoaded = true;
                                        this.duration = retryVideo.duration || 0;
                                        
                                        // 更新时长显示
                                        const durationEl = itemDiv.querySelector('.manus-ai-media-duration');
                                        if (durationEl) {
                                            durationEl.textContent = this.formatDuration(this.duration);
                                        }
                                        
                                        placeholder.remove();
                                        thumbnailContainer.appendChild(playIcon);
                                        log(`视频 ${this.id} 重试加载成功`);
                                    });
                                }
                            }, 1000);
                        });
                        
                        thumbnailContainer.appendChild(video);
                        
                        // 设置一个超时,如果视频在一定时间内未加载,则保留占位符
                        setTimeout(() => {
                            if (!this.thumbnailLoaded) {
                                log(`视频 ${this.id} 加载超时,保留占位符`);
                            }
                        }, 5000);
                    }
                } catch (e) {
                    error('创建缩略图时出错:', e);
                    thumbnailContainer.innerHTML = this.type === 'image' ? ICONS.image : ICONS.video;
                }
            } else {
                thumbnailContainer.innerHTML = this.type === 'image' ? ICONS.image : ICONS.video;
            }
            
            itemDiv.appendChild(thumbnailContainer);

            if (this.type === 'video') {
                const durationSpan = document.createElement('span');
                durationSpan.className = 'manus-ai-media-duration';
                durationSpan.textContent = this.formatDuration(this.duration);
                itemDiv.appendChild(durationSpan);
            }

            const checkbox = document.createElement('div');
            checkbox.className = 'manus-ai-media-checkbox';
            if (this.selected) {
                checkbox.classList.add('checked');
            }
            itemDiv.appendChild(checkbox);

            itemDiv.addEventListener('click', (e) => {
                if (state.windowLock) return;

                const rect = checkbox.getBoundingClientRect();
                const isCheckboxClick = (
                    e.clientX >= rect.left && e.clientX <= rect.right &&
                    e.clientY >= rect.top && e.clientY <= rect.bottom
                );

                if (isCheckboxClick) {
                    this.toggleSelect();
                    UI.updateSelectedCounter();
                    UI.updateDownloadButton();
                } else {
                    if (!state.largePreviewOpen) {
                        state.windowLock = true;
                        setTimeout(() => { state.windowLock = false; }, 500);
                        UI.openLargePreview(this);
                    }
                }
            });

            return itemDiv;
        }

        toggleSelect() {
            this.selected = !this.selected;
            const itemElement = document.querySelector(`.manus-ai-media-item[data-id="${this.id}"]`);
            if (itemElement) {
                itemElement.classList.toggle('selected', this.selected);
                itemElement.querySelector('.manus-ai-media-checkbox').classList.toggle('checked', this.selected);
            }
            if (this.selected) {
                if (!state.selectedItems.includes(this.id)) state.selectedItems.push(this.id);
            } else {
                const index = state.selectedItems.indexOf(this.id);
                if (index !== -1) state.selectedItems.splice(index, 1);
            }
        }

        setSelected(selected) {
            if (this.selected !== selected) {
                this.toggleSelect();
            }
        }

        formatDuration(seconds) {
            if (!seconds || isNaN(seconds)) return '0:00';
            seconds = Math.round(seconds);
            const minutes = Math.floor(seconds / 60);
            seconds = seconds % 60;
            return `${minutes}:${seconds.toString().padStart(2, '0')}`;
        }
    }

    // --- UI 相关函数 --- 
    const UI = {
        // 创建主按钮 (增强)
        createMainButton: () => {
            const existingButton = document.querySelector('.manus-ai-downloader-btn');
            if (existingButton) {
                log('主按钮已存在');
                return existingButton;
            }

            const button = document.createElement('button');
            button.className = 'manus-ai-downloader-btn';
            button.innerHTML = `${ICONS.download} 无水印下载`;
            button.style.visibility = 'hidden'; // Initially hidden

            // Try appending to body first
            document.body.appendChild(button);
            log('尝试将按钮添加到 document.body');

            // Check visibility after a short delay
            setTimeout(() => {
                const rect = button.getBoundingClientRect();
                if (rect.width > 0 && rect.height > 0) {
                    button.style.visibility = 'visible';
                    log('主按钮已成功添加到 body 并可见');
                } else {
                    error('主按钮添加到 body 后不可见,尝试其他容器');
                    // Fallback: Try appending to documentElement if body fails
                    document.documentElement.appendChild(button);
                    setTimeout(() => {
                        const rect2 = button.getBoundingClientRect();
                        if (rect2.width > 0 && rect2.height > 0) {
                            button.style.visibility = 'visible';
                            log('主按钮已成功添加到 documentElement 并可见');
                        } else {
                            error('主按钮添加到 documentElement 后仍不可见,注入失败!');
                            UI.showError('无法创建下载按钮,请联系开发者');
                            button.remove(); // Clean up invisible button
                        }
                    }, 500);
                }
            }, 500);

            button.addEventListener('click', () => {
                if (state.windowLock) return;
                state.windowLock = true;
                setTimeout(() => { state.windowLock = false; }, 500);
                UI.togglePreviewPanel();
            });

            // Start periodic check for button visibility
            UI.startButtonVisibilityCheck();

            return button;
        },

        // 创建手动扫描按钮
        createManualScanButton: () => {
            const existingButton = document.querySelector('.manus-ai-manual-scan-btn');
            if (existingButton) {
                log('手动扫描按钮已存在');
                return existingButton;
            }

            const button = document.createElement('button');
            button.className = 'manus-ai-manual-scan-btn';
            button.textContent = '手动扫描媒体';
            
            document.body.appendChild(button);
            
            button.addEventListener('click', () => {
                log('用户触发手动扫描');
                findAndProcessMedia();
                UI.showNotification('手动扫描完成,检测到 ' + state.mediaItems.length + ' 个媒体项');
            });
            
            return button;
        },

        // 定期检查按钮可见性
        startButtonVisibilityCheck: () => {
            if (state.buttonCheckIntervalId) {
                clearInterval(state.buttonCheckIntervalId);
            }
            state.buttonCheckIntervalId = setInterval(() => {
                const button = document.querySelector('.manus-ai-downloader-btn');
                if (button) {
                    const rect = button.getBoundingClientRect();
                    if (rect.width === 0 || rect.height === 0 || button.style.display === 'none' || button.style.visibility === 'hidden') {
                        log('检测到下载按钮变得不可见,尝试重新显示...');
                        button.style.display = 'flex !important';
                        button.style.visibility = 'visible !important';
                        // Ensure it's still in the DOM, re-append if necessary
                        if (!document.body.contains(button) && !document.documentElement.contains(button)) {
                            log('按钮已从DOM移除,重新添加...');
                            document.body.appendChild(button);
                        }
                    }
                } else {
                    log('下载按钮丢失,尝试重新创建...');
                    UI.createMainButton(); // Recreate if lost
                }
                
                // 同时检查手动扫描按钮
                const scanButton = document.querySelector('.manus-ai-manual-scan-btn');
                if (!scanButton) {
                    log('手动扫描按钮丢失,重新创建...');
                    UI.createManualScanButton();
                }
            }, CONFIG.BUTTON_CHECK_INTERVAL);
        },

        // 创建预览面板 (单例)
        createPreviewPanel: () => {
            let panel = document.querySelector('.manus-ai-preview-panel');
            if (panel) return panel;

            panel = document.createElement('div');
            panel.className = 'manus-ai-preview-panel';
            panel.dataset.instanceId = state.instanceId;
            panel.innerHTML = `
                <div class="manus-ai-preview-header">
                    <div class="manus-ai-preview-title">媒体内容预览 <span class="manus-ai-author-credit">by ${CONFIG.AUTHOR}</span> (v${CONFIG.VERSION})</div>
                    <button class="manus-ai-preview-close">×</button>
                </div>
                <div class="manus-ai-preview-toolbar">
                    <button class="manus-ai-select-all">全选</button>
                    <button class="manus-ai-select-none">取消全选</button>
                    <button class="manus-ai-select-images">选择图片</button>
                    <button class="manus-ai-select-videos">选择视频</button>
                    <div class="manus-ai-preview-counter">已选: 0</div>
                </div>
                <div class="manus-ai-preview-content"></div>
                <div class="manus-ai-preview-footer">
                    <button class="manus-ai-download-btn" disabled>
                        ${ICONS.download}
                        下载选中项(0)
                    </button>
                    <button class="manus-ai-cancel-btn">取消</button>
                </div>
            `;
            document.body.appendChild(panel);

            panel.querySelector('.manus-ai-preview-close').addEventListener('click', () => UI.togglePreviewPanel(false));
            panel.querySelector('.manus-ai-cancel-btn').addEventListener('click', () => UI.togglePreviewPanel(false));
            panel.querySelector('.manus-ai-select-all').addEventListener('click', UI.selectAll);
            panel.querySelector('.manus-ai-select-none').addEventListener('click', UI.selectNone);
            panel.querySelector('.manus-ai-select-images').addEventListener('click', () => UI.selectByType('image'));
            panel.querySelector('.manus-ai-select-videos').addEventListener('click', () => UI.selectByType('video'));
            panel.querySelector('.manus-ai-download-btn').addEventListener('click', UI.startDownload);

            log('创建预览面板');
            return panel;
        },

        // 创建大预览 (单例)
        createLargePreview: () => {
            let preview = document.querySelector('.manus-ai-large-preview');
            if (preview) return preview;

            preview = document.createElement('div');
            preview.className = 'manus-ai-large-preview';
            preview.dataset.instanceId = state.instanceId;
            preview.innerHTML = `
                <button class="manus-ai-large-preview-close">×</button>
                <div class="manus-ai-large-preview-container"></div>
                <div class="manus-ai-author-watermark">© ${CONFIG.AUTHOR}</div>
            `;
            document.body.appendChild(preview);

            preview.addEventListener('click', (e) => { 
                if (e.target === preview) UI.closeLargePreview(); 
            });
            preview.querySelector('.manus-ai-large-preview-close').addEventListener('click', UI.closeLargePreview);

            log('创建大预览');
            return preview;
        },

        // 打开大预览
        openLargePreview: (mediaItem) => {
            if (state.largePreviewOpen) return;
            
            const preview = UI.createLargePreview();
            const container = preview.querySelector('.manus-ai-large-preview-container');
            container.innerHTML = ''; // 清除之前的内容
            
            if (!mediaItem || !mediaItem.url) {
                container.innerHTML = `<div class="manus-ai-video-error">无法加载媒体,URL无效</div>`;
                preview.classList.add('show');
                state.largePreviewOpen = true;
                return;
            }
            
            try {
                if (mediaItem.type === 'image') {
                    // 图片预览
                    const img = document.createElement('img');
                    img.src = mediaItem.url;
                    img.alt = '预览图片';
                    img.crossOrigin = 'anonymous'; // 尝试解决跨域问题
                    img.loading = 'eager'; // 优先加载
                    img.decoding = 'async'; // 异步解码
                    
                    // 添加加载中提示
                    const loadingDiv = document.createElement('div');
                    loadingDiv.textContent = '图片加载中...';
                    loadingDiv.style.position = 'absolute';
                    loadingDiv.style.top = '50%';
                    loadingDiv.style.left = '50%';
                    loadingDiv.style.transform = 'translate(-50%, -50%)';
                    loadingDiv.style.color = 'white';
                    loadingDiv.style.background = 'rgba(0,0,0,0.7)';
                    loadingDiv.style.padding = '10px 20px';
                    loadingDiv.style.borderRadius = '4px';
                    loadingDiv.style.zIndex = '3';
                    container.appendChild(loadingDiv);
                    
                    img.onload = () => {
                        loadingDiv.remove();
                        log(`大预览图片加载成功: ${mediaItem.url.substring(0, 50)}...`);
                    };
                    
                    img.onerror = () => {
                        loadingDiv.remove();
                        container.innerHTML = `<div class="manus-ai-video-error">图片加载失败</div>`;
                        error(`图片加载失败: ${mediaItem.url.substring(0, 50)}...`);
                        
                        // 尝试使用不同的方式加载图片
                        setTimeout(() => {
                            const retryImg = new Image();
                            retryImg.crossOrigin = 'anonymous';
                            retryImg.src = mediaItem.url + '?retry=' + new Date().getTime(); // 添加时间戳避免缓存
                            retryImg.onload = () => {
                                container.innerHTML = '';
                                const newImg = document.createElement('img');
                                newImg.src = retryImg.src;
                                newImg.alt = '预览图片';
                                container.appendChild(newImg);
                                log(`大预览图片重试加载成功: ${mediaItem.url.substring(0, 50)}...`);
                            };
                        }, 1000);
                    };
                    
                    container.appendChild(img);
                } else {
                    // 视频预览
                    const video = document.createElement('video');
                    video.src = mediaItem.url;
                    video.controls = true;
                    video.autoplay = true;
                    video.crossOrigin = 'anonymous'; // 尝试解决跨域问题
                    video.style.maxWidth = '100%';
                    video.style.maxHeight = '90vh';
                    
                    // 添加加载中提示
                    const loadingDiv = document.createElement('div');
                    loadingDiv.textContent = '视频加载中...';
                    loadingDiv.style.position = 'absolute';
                    loadingDiv.style.top = '50%';
                    loadingDiv.style.left = '50%';
                    loadingDiv.style.transform = 'translate(-50%, -50%)';
                    loadingDiv.style.color = 'white';
                    loadingDiv.style.background = 'rgba(0,0,0,0.7)';
                    loadingDiv.style.padding = '10px 20px';
                    loadingDiv.style.borderRadius = '4px';
                    loadingDiv.style.zIndex = '3';
                    container.appendChild(loadingDiv);
                    
                    // 视频加载成功
                    video.addEventListener('loadeddata', () => {
                        loadingDiv.remove();
                        log(`大预览视频加载成功: ${mediaItem.url.substring(0, 50)}...`);
                    });
                    
                    // 视频加载失败
                    video.addEventListener('error', (e) => {
                        loadingDiv.remove();
                        error(`视频加载失败:`, e.target.error);
                        
                        // 尝试使用不同的方式加载视频
                        container.innerHTML = `
                            <div class="manus-ai-video-error">
                                视频加载失败,可能是由于跨域限制或格式不支持。<br>
                                <a href="${mediaItem.url}" target="_blank" style="color:white;text-decoration:underline;">点击此处在新窗口打开视频</a>
                            </div>
                        `;
                        
                        // 尝试使用不同的方式加载视频
                        setTimeout(() => {
                            const retryVideo = document.createElement('video');
                            retryVideo.crossOrigin = 'anonymous';
                            retryVideo.muted = true;
                            retryVideo.src = mediaItem.url + '?retry=' + new Date().getTime(); // 添加时间戳避免缓存
                            retryVideo.addEventListener('loadeddata', () => {
                                container.innerHTML = '';
                                const newVideo = document.createElement('video');
                                newVideo.src = retryVideo.src;
                                newVideo.controls = true;
                                newVideo.autoplay = true;
                                newVideo.style.maxWidth = '100%';
                                newVideo.style.maxHeight = '90vh';
                                container.appendChild(newVideo);
                                log(`大预览视频重试加载成功: ${mediaItem.url.substring(0, 50)}...`);
                            });
                        }, 1000);
                    });
                    
                    container.appendChild(video);
                    
                    // 设置超时,如果视频长时间未加载则显示错误
                    setTimeout(() => {
                        if (container.contains(loadingDiv)) {
                            loadingDiv.remove();
                            if (video.readyState < 3) { // HAVE_FUTURE_DATA
                                error(`视频加载超时: ${mediaItem.url.substring(0, 50)}...`);
                                container.innerHTML = `
                                    <div class="manus-ai-video-error">
                                        视频加载超时,可能是由于网络问题或格式不支持。<br>
                                        <a href="${mediaItem.url}" target="_blank" style="color:white;text-decoration:underline;">点击此处在新窗口打开视频</a>
                                    </div>
                                `;
                            }
                        }
                    }, 10000); // 10秒超时
                }
                
                preview.classList.add('show');
                state.largePreviewOpen = true;
                log(`打开大预览: ${mediaItem.type}, ${mediaItem.url ? mediaItem.url.substring(0, 50) + '...' : 'N/A'}`);
                
            } catch (e) {
                error('打开大预览时出错:', e);
                container.innerHTML = `<div class="manus-ai-video-error">预览加载失败: ${e.message}</div>`;
                preview.classList.add('show');
                state.largePreviewOpen = true;
            }
        },

        // 关闭大预览
        closeLargePreview: () => {
            const preview = document.querySelector('.manus-ai-large-preview');
            if (preview) {
                preview.classList.remove('show');
                // 清除视频以停止播放
                const container = preview.querySelector('.manus-ai-large-preview-container');
                if (container) {
                    const video = container.querySelector('video');
                    if (video) {
                        video.pause();
                        video.src = '';
                        video.load();
                    }
                    container.innerHTML = '';
                }
            }
            state.largePreviewOpen = false;
            log('关闭大预览');
        },

        // 显示通知
        showNotification: (message, duration = 3000) => {
            const notification = document.createElement('div');
            notification.className = 'manus-ai-downloader-notification';
            notification.textContent = message;
            document.body.appendChild(notification);
            setTimeout(() => {
                notification.remove();
            }, duration);
        },

        // 显示错误
        showError: (message, duration = 3000) => {
            const notification = document.createElement('div');
            notification.className = 'manus-ai-downloader-error';
            notification.innerHTML = `${ICONS.error} ${message}`;
            document.body.appendChild(notification);
            setTimeout(() => {
                notification.remove();
            }, duration);
        },

        togglePreviewPanel: (show = null) => {
            const panel = UI.createPreviewPanel();
            const isCurrentlyShown = panel.classList.contains('show');
            const shouldShow = show !== null ? show : !isCurrentlyShown;

            if (shouldShow) {
                panel.classList.add('show');
                state.isPanelOpen = true;
                UI.refreshPreviewContent();
                log('打开预览面板');
            } else {
                panel.classList.remove('show');
                state.isPanelOpen = false;
                log('关闭预览面板');
            }
        },

        refreshPreviewContent: () => {
            const contentContainer = document.querySelector('.manus-ai-preview-content');
            if (!contentContainer) {
                error('找不到预览内容容器');
                return;
            }
            contentContainer.innerHTML = ''; // Clear previous items

            if (state.mediaItems.length === 0) {
                contentContainer.innerHTML = '<div style="grid-column: 1 / -1; color: #aaa; text-align: center; padding: 20px;">暂无检测到的媒体内容<br>请尝试点击"手动扫描媒体"按钮</div>';
            } else {
                // Sort items by timestamp, newest first
                const sortedItems = [...state.mediaItems].sort((a, b) => b.timestamp - a.timestamp);
                sortedItems.forEach(item => {
                    const thumbnailElement = item.createThumbnailElement();
                    contentContainer.appendChild(thumbnailElement);
                });
            }

            UI.updateSelectedCounter();
            UI.updateDownloadButton();
            log(`刷新预览内容,共 ${state.mediaItems.length} 项`);
        },

        updateSelectedCounter: () => {
            const counter = document.querySelector('.manus-ai-preview-counter');
            if (counter) counter.textContent = `已选: ${state.selectedItems.length}`;
        },

        updateDownloadButton: () => {
            const downloadBtn = document.querySelector('.manus-ai-preview-footer .manus-ai-download-btn');
            if (downloadBtn) {
                const count = state.selectedItems.length;
                downloadBtn.disabled = count === 0;
                downloadBtn.innerHTML = `${ICONS.download} 下载选中项(${count})`;
            }
        },

        selectAll: () => {
            state.mediaItems.forEach(item => item.setSelected(true));
            UI.updateSelectedCounter();
            UI.updateDownloadButton();
            log('全选媒体项');
        },

        selectNone: () => {
            state.mediaItems.forEach(item => item.setSelected(false));
            UI.updateSelectedCounter();
            UI.updateDownloadButton();
            log('取消全选');
        },

        selectByType: (type) => {
            state.mediaItems.forEach(item => item.setSelected(item.type === type));
            UI.updateSelectedCounter();
            UI.updateDownloadButton();
            log(`按类型选择: ${type}`);
        },

        startDownload: () => {
            // 下载逻辑实现
            UI.showNotification('开始下载选中项...');
            
            // 获取选中的媒体项
            const selectedMediaItems = state.mediaItems.filter(item => 
                state.selectedItems.includes(item.id)
            );
            
            if (selectedMediaItems.length === 0) {
                UI.showError('没有选中任何媒体项');
                return;
            }
            
            // 下载选中的媒体项
            selectedMediaItems.forEach((item, index) => {
                setTimeout(() => {
                    try {
                        if (!item.url) {
                            UI.showError(`媒体项 #${index+1} URL无效`);
                            return;
                        }
                        
                        // 生成文件名
                        const timestamp = new Date().toISOString().replace(/[-:]/g, '').replace('T', '_').split('.')[0];
                        const fileExt = item.type === 'video' ? 
                            (item.url.match(/\.(mp4|webm|mov)($|\?)/i) ? RegExp.$1 : 'mp4') : 
                            (item.url.match(/\.(jpg|jpeg|png|gif|webp)($|\?)/i) ? RegExp.$1 : 'jpg');
                        const fileName = `${item.type === 'video' ? 'video' : 'image'}_${timestamp}_${index}.${fileExt}`;
                        
                        // 使用GM_download下载
                        if (typeof GM_download !== 'undefined') {
                            GM_download({
                                url: item.url,
                                name: fileName,
                                onload: () => log(`媒体项 #${index+1} 下载完成`),
                                onerror: (e) => {
                                    error(`媒体项 #${index+1} 下载失败:`, e);
                                    UI.showError(`媒体项 #${index+1} 下载失败,请尝试右键另存为`);
                                }
                            });
                        } else {
                            // 回退方案:创建下载链接
                            const a = document.createElement('a');
                            a.href = item.url;
                            a.download = fileName;
                            a.target = '_blank';
                            a.style.display = 'none';
                            document.body.appendChild(a);
                            a.click();
                            setTimeout(() => {
                                document.body.removeChild(a);
                            }, 100);
                        }
                        
                        UI.showNotification(`正在下载 ${index+1}/${selectedMediaItems.length}`);
                    } catch (e) {
                        error(`下载媒体项 #${index+1} 时出错:`, e);
                        UI.showError(`下载出错: ${e.message}`);
                    }
                }, index * 500); // 每隔500ms下载一个,避免浏览器限制
            });
            
            UI.showNotification(`已开始下载 ${selectedMediaItems.length} 个媒体项`);
        }
    };

    // --- 工具函数 --- 
    const utils = {
        // 检测当前平台
        detectPlatform: () => {
            const url = window.location.href.toLowerCase();
            
            // 通过URL检测
            if (url.includes('klingai') || url.includes('kuaishou')) {
                return 'keling';
            } else if (url.includes('jimeng') || url.includes('jianying')) {
                return 'jimeng';
            }
            
            // 通过DOM特征检测
            if (document.querySelector('.kl-container') || document.querySelector('[data-kl]')) {
                return 'keling';
            }
            if (document.querySelector('.jm-container') || document.querySelector('[data-jm]')) {
                return 'jimeng';
            }
            
            // 如果无法确定,尝试通过页面标题或其他特征检测
            const title = document.title.toLowerCase();
            if (title.includes('可灵') || title.includes('kling')) {
                return 'keling';
            }
            if (title.includes('即梦') || title.includes('jimeng')) {
                return 'jimeng';
            }
            
            // 默认返回null,表示未知平台
            return null;
        },

        // 检查元素是否已处理 (使用URL)
        isUrlProcessed: (url) => {
            if (!url) return true; // Ignore empty URLs
            return state.mediaUrls.has(url);
        },

        // 标记URL为已处理
        markUrlAsProcessed: (url) => {
            if (url) {
                state.mediaUrls.add(url);
            }
        },

        // 清理重复的媒体项 (基于URL)
        cleanupDuplicateMedia: () => {
            const uniqueUrls = new Set();
            const uniqueItems = [];
            // Iterate in reverse to keep the latest instance if URLs are the same
            for (let i = state.mediaItems.length - 1; i >= 0; i--) {
                const item = state.mediaItems[i];
                if (item.url && !uniqueUrls.has(item.url)) {
                    uniqueUrls.add(item.url);
                    uniqueItems.push(item);
                }
            }
            // Reverse back to original order (or keep newest first)
            uniqueItems.reverse(); 

            if (state.mediaItems.length !== uniqueItems.length) {
                log(`清理重复媒体项: ${state.mediaItems.length} -> ${uniqueItems.length}`);
                state.mediaItems = uniqueItems;
            }
        }
    };

    // --- 平台特定处理 --- 
    const platforms = {
        keling: { 
            selectors: [
                'video[src]', 
                'img[src]', 
                '.media-container img', 
                '.media-preview img',
                '.media-item video',
                '.image-container img',
                '.video-container video',
                '.preview-container img',
                '.preview-container video',
                '.result-container img',
                '.result-container video',
                // 添加更多可能的选择器
                '[class*="media"] img',
                '[class*="image"] img',
                '[class*="photo"] img',
                '[class*="video"] video',
                '[class*="preview"] img',
                '[class*="preview"] video',
                '[class*="result"] img',
                '[class*="result"] video'
            ] 
        },
        jimeng: { 
            selectors: [
                'video[src]', 
                'img[src]',
                '.media-preview-item img',
                '.media-gallery img',
                '.video-container video',
                '.image-container img',
                '.preview-container img',
                '.preview-container video',
                '.result-container img',
                '.result-container video',
                // 添加更多可能的选择器
                '[class*="media"] img',
                '[class*="image"] img',
                '[class*="photo"] img',
                '[class*="video"] video',
                '[class*="preview"] img',
                '[class*="preview"] video',
                '[class*="result"] img',
                '[class*="result"] video'
            ]
        }
    };

    // --- 网络请求监听 ---
    function setupNetworkListener() {
        log('设置网络请求监听');
        
        // 监听XHR请求
        const originalXHR = window.XMLHttpRequest;
        window.XMLHttpRequest = function() {
            const xhr = new originalXHR();
            const originalOpen = xhr.open;
            
            xhr.open = function() {
                this.addEventListener('load', function() {
                    try {
                        const url = this.responseURL;
                        if (url && (url.match(/\.(jpg|jpeg|png|gif|webp|mp4|webm|mov)($|\?)/i) || 
                                   url.includes('/image/') || 
                                   url.includes('/video/'))) {
                            log(`检测到媒体URL: ${url}`);
                            // 创建新的媒体项
                            const type = url.match(/\.(mp4|webm|mov)($|\?)/i) || url.includes('/video/') ? 'video' : 'image';
                            if (!utils.isUrlProcessed(url)) {
                                const mediaItem = new MediaItem(null, type, url);
                                state.mediaItems.push(mediaItem);
                                utils.markUrlAsProcessed(url);
                                utils.cleanupDuplicateMedia();
                                if (state.isPanelOpen) {
                                    UI.refreshPreviewContent();
                                }
                            }
                        }
                    } catch (e) {
                        error('处理XHR响应时出错:', e);
                    }
                });
                return originalOpen.apply(this, arguments);
            };
            
            return xhr;
        };
        
        // 监听Fetch请求
        const originalFetch = window.fetch;
        window.fetch = function() {
            const fetchPromise = originalFetch.apply(this, arguments);
            
            fetchPromise.then(response => {
                try {
                    const url = response.url;
                    if (url && (url.match(/\.(jpg|jpeg|png|gif|webp|mp4|webm|mov)($|\?)/i) || 
                               url.includes('/image/') || 
                               url.includes('/video/'))) {
                        log(`检测到Fetch媒体URL: ${url}`);
                        // 创建新的媒体项
                        const type = url.match(/\.(mp4|webm|mov)($|\?)/i) || url.includes('/video/') ? 'video' : 'image';
                        if (!utils.isUrlProcessed(url)) {
                            const mediaItem = new MediaItem(null, type, url);
                            state.mediaItems.push(mediaItem);
                            utils.markUrlAsProcessed(url);
                            utils.cleanupDuplicateMedia();
                            if (state.isPanelOpen) {
                                UI.refreshPreviewContent();
                            }
                        }
                    }
                } catch (e) {
                    error('处理Fetch响应时出错:', e);
                }
            });
            
            return fetchPromise;
        };
        
        log('网络请求监听已设置');
    }

    // --- 核心逻辑 --- 

    // 查找并处理媒体元素
    function findAndProcessMedia() {
        log('开始扫描媒体元素...');
        log(`当前平台: ${state.platform}, 使用选择器: ${platforms[state.platform].selectors.join(', ')}`);
        
        let newMediaFound = false;
        const platformConfig = platforms[state.platform];
        if (!platformConfig) {
            error('未找到平台配置');
            return false;
        }

        platformConfig.selectors.forEach(selector => {
            try {
                const elements = document.querySelectorAll(selector);
                log(`选择器 ${selector} 找到 ${elements.length} 个元素`);
                
                elements.forEach(element => {
                    const url = element.src || element.currentSrc;
                    if (!url || utils.isUrlProcessed(url)) {
                        return; // Skip already processed URLs
                    }

                    // Basic filtering (e.g., ignore tiny images)
                    if (element.tagName === 'IMG' && (element.naturalWidth < 100 || element.naturalHeight < 100)) {
                        log(`忽略小图片: ${url.substring(0, 50)}...`);
                        return;
                    }

                    const type = element.tagName === 'VIDEO' ? 'video' : 'image';
                    
                    // Mark URL as processed immediately
                    utils.markUrlAsProcessed(url);

                    // Create MediaItem (element reference might be less reliable now)
                    const mediaItem = new MediaItem(null, type, url);
                    mediaItem.width = element.naturalWidth || element.videoWidth || 0;
                    mediaItem.height = element.naturalHeight || element.videoHeight || 0;
                    if (type === 'video') {
                        mediaItem.duration = element.duration || 0;
                    }

                    state.mediaItems.push(mediaItem);
                    newMediaFound = true;
                    log(`通过选择器 ${selector} 检测到新 ${type}: ${mediaItem.id}, URL: ${url.substring(0, 50)}...`);
                });
            } catch (e) {
                error(`使用选择器 ${selector} 时出错:`, e);
            }
        });

        // 如果没有找到媒体,输出调试信息
        if (!newMediaFound) {
            log('未找到媒体项,可能原因:选择器不匹配或媒体尚未加载');
            // 输出页面中所有img和video元素的信息,帮助调试
            document.querySelectorAll('img, video').forEach((el, i) => {
                if (el.src) {
                    log(`调试 - 元素 #${i}: ${el.tagName}, src: ${el.src.substring(0, 50)}..., 可见: ${el.offsetParent !== null}`);
                }
            });
        }

        if (newMediaFound) {
            utils.cleanupDuplicateMedia(); // Cleanup duplicates based on URL
            if (state.isPanelOpen) {
                UI.refreshPreviewContent();
            }
            UI.showNotification(`检测到 ${state.mediaItems.length} 个媒体项`);
        }
        log('媒体元素扫描结束.');
        return newMediaFound;
    }

    // 启动MutationObserver
    function startObserver() {
        if (state.observer) {
            state.observer.disconnect(); // Disconnect previous observer if any
        }
        
        log('启动 MutationObserver');
        state.observer = new MutationObserver((mutations) => {
            let potentiallyNewMedia = false;
            for (const mutation of mutations) {
                // Check added nodes or attribute changes that might indicate new media
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    potentiallyNewMedia = true;
                    break;
                }
                if (mutation.type === 'attributes' && (mutation.attributeName === 'src' || mutation.attributeName === 'currentSrc')) {
                     potentiallyNewMedia = true;
                     break;
                }
            }

            if (potentiallyNewMedia) {
                log('MutationObserver 检测到潜在变化,重新扫描媒体...');
                // Debounce the scan slightly
                setTimeout(findAndProcessMedia, 500);
            }
        });

        state.observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['src', 'currentSrc'] // Observe src changes
        });
    }

    // 初始化函数 (增强)
    function initializeScript() {
        log(`尝试初始化脚本 (第 ${state.initRetries + 1} 次)`);
        state.platform = utils.detectPlatform();
        if (!state.platform) {
            error('不支持的平台');
            UI.showError('不支持的平台,脚本可能无法正常工作');
            state.platform = 'keling'; // 默认使用可灵平台配置
        }
        log(`检测到平台: ${state.platform}`);

        // 1. 创建UI元素 (按钮和面板)
        UI.createMainButton();
        UI.createManualScanButton(); // 添加手动扫描按钮
        UI.createPreviewPanel(); // Create panel but don't show
        UI.createLargePreview(); // Create large preview container

        // 2. 设置网络请求监听
        setupNetworkListener();

        // 3. 初始扫描
        const foundInitially = findAndProcessMedia();

        // 4. 启动Observer
        startObserver();

        // 5. 如果初始扫描未找到任何内容且未达到最大重试次数,则安排重试
        if (!foundInitially && state.initRetries < CONFIG.MAX_RETRIES) {
            state.initRetries++;
            log(`初始扫描未找到媒体,将在 ${CONFIG.RETRY_DELAY}ms 后重试 (第 ${state.initRetries} 次)`);
            setTimeout(initializeScript, CONFIG.RETRY_DELAY);
            return; // Don't show success notification yet
        }

        // 6. 初始化完成 (或达到最大重试次数)
        if (foundInitially) {
            log(`可灵和即梦AI去水印下载脚本 (v${CONFIG.VERSION}) 已初始化`);
            UI.showNotification(`AI下载脚本 (v${CONFIG.VERSION}) 已启用 - by ${CONFIG.AUTHOR}`);
        } else {
            log(`达到最大重试次数 (${CONFIG.MAX_RETRIES}),仍未找到媒体。脚本将保持运行状态。`);
            UI.showNotification(`AI下载脚本 (v${CONFIG.VERSION}) 已启用 (未检测到媒体,请尝试手动扫描) - by ${CONFIG.AUTHOR}`);
        }
    }

    // --- 启动脚本 --- 
    log(`脚本开始执行,等待初始延迟... (v${CONFIG.VERSION} by ${CONFIG.AUTHOR})`);
    setTimeout(initializeScript, CONFIG.INIT_DELAY);

})();