// ==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 // @downloadURL https://update.greasyfork.icu/scripts/537396/%E5%8F%AF%E7%81%B5%E5%92%8C%E5%8D%B3%E6%A2%A6AI%E5%8E%BB%E6%B0%B4%E5%8D%B0%E4%B8%8B%E8%BD%BD%E8%84%9A%E6%9C%AC%20%28%E5%85%BC%E5%AE%B9%E5%A2%9E%E5%BC%BA%E4%BF%AE%E5%A4%8D%E7%89%88%29.user.js // @updateURL https://update.greasyfork.icu/scripts/537396/%E5%8F%AF%E7%81%B5%E5%92%8C%E5%8D%B3%E6%A2%A6AI%E5%8E%BB%E6%B0%B4%E5%8D%B0%E4%B8%8B%E8%BD%BD%E8%84%9A%E6%9C%AC%20%28%E5%85%BC%E5%AE%B9%E5%A2%9E%E5%BC%BA%E4%BF%AE%E5%A4%8D%E7%89%88%29.meta.js // ==/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: ``, video: ``, download: ``, error: ``, play: `` }; // --- 媒体项类 --- 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 = `
媒体内容预览 by ${CONFIG.AUTHOR} (v${CONFIG.VERSION})
已选: 0
`; 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 = `
© ${CONFIG.AUTHOR}
`; 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 = `
无法加载媒体,URL无效
`; 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 = `
图片加载失败
`; 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 = `
视频加载失败,可能是由于跨域限制或格式不支持。
点击此处在新窗口打开视频
`; // 尝试使用不同的方式加载视频 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 = `
视频加载超时,可能是由于网络问题或格式不支持。
点击此处在新窗口打开视频
`; } } }, 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 = `
预览加载失败: ${e.message}
`; 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 = '
暂无检测到的媒体内容
请尝试点击"手动扫描媒体"按钮
'; } 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); })();