// ==UserScript== // @name 即梦AI全能助手 // @namespace http://tampermonkey.net/ // @version 3.31 // @description 支持图片/视频下载的即梦AI增强工具(悬浮按钮版+优化图片预览+视频缩略图) // @author Jackey // @license GPL-3.0 // @match https://jimeng.jianying.com/* // @icon https://lf3-lv-buz.vlabstatic.com/obj/image-lvweb-buz/common/images/dreamina-v5.ico // @grant GM.xmlHttpRequest // @grant GM_download // @grant GM_addStyle // @grant window.open // @connect byteimg.com // @connect *.byteimg.com // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js // @downloadURL https://update.greasyfork.icu/scripts/528695/%E5%8D%B3%E6%A2%A6AI%E5%85%A8%E8%83%BD%E5%8A%A9%E6%89%8B.user.js // @updateURL https://update.greasyfork.icu/scripts/528695/%E5%8D%B3%E6%A2%A6AI%E5%85%A8%E8%83%BD%E5%8A%A9%E6%89%8B.meta.js // ==/UserScript== (function() { 'use strict'; const CONFIG = { FLOATING_CONTAINER_STYLE: ` position: fixed; bottom: 20px; right: 20px; z-index: 9999; display: flex; flex-direction: column; gap: 8px; `, BUTTON_STYLE: ` cursor: pointer; border-radius: 8px; padding: 8px 16px; transition: all 0.2s; background: rgba(255,255,255,0.9); border: 1px solid #d9d9d9; box-shadow: 0 2px 8px rgba(0,0,0,0.1); display: flex; align-items: center; gap: 8px; `, HOVER_STYLE: ` background: #e6f7ff; border-color: #91d5ff; `, VIDEO_PANEL_STYLE: ` position: fixed; bottom: 80px; right: 20px; background: rgba(255,255,255,0.95); border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); width: 400px; max-height: 70vh; overflow: hidden; z-index: 9999; display: flex; flex-direction: column; `, VIDEO_ITEM_STYLE: ` display: flex; align-items: center; padding: 8px; border-bottom: 1px solid #eee; gap: 12px; `, PREVIEW_STYLE: ` width: 120px; height: 120px; background: rgba(255,255,255,0.9); border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); overflow: hidden; display: flex; align-items: center; justify-content: center; margin-bottom: 8px; `, VIDEO_SCAN_INTERVAL: 3000, PREVIEW_UPDATE_INTERVAL: 1000, DEFAULT_THUMBNAIL: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA8CAMAAABTa4nrAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAC1QTFRF4eHh+/v78PDw9fX17u7u/f396Ojo5OTk/v7+/Pz8+fn59vb25+fn8/Pz5ubmBUUEgAAAAPFJREFUeNrs1kEKg0AMBND4n0ST+5+3ZSibQltJJFPnbQRxHsOoRj+J8bWzbQYQIECA/wFGwm2tl4CmQvECNCqw/fYCNCCwBSBAgAABAnyE71kKM1sCmlKgBKBRgbrqCmgqB1oCGpXIVlOA3VIIrQO7HBMuAeUoqA4UGymgObDH3pXAJaBQoEGBfSG0CWhuBWICFQNFQDWQsLjVQMJ6qgYSZlY10N1DiS/Ag4Hx8M7hIyYQIECAAAECBAgQ4Ba47zfFMQuBlM9WJiCHlc9ZJqC8YO0EysvWTqC8cO0EykvXTqD8AfRkK0APcKO1AK9/J/vQ2AH8FeA7Ft9K2QAAAABJRU5ErkJggg==' }; class JimengEnhancer { constructor() { this.videoList = new Map(); this.floatingContainer = null; this.videoPanel = null; this.previewArea = null; this.isVideoPanelExpanded = false; this.currentImageSrc = null; this.init(); } init() { this.setupDOMObserver(); this.setupGlobalListener(); this.initVideoMonitor(); this.createFloatingButtons(); this.createVideoPanel(); this.startPreviewUpdate(); } setupDOMObserver() { const Observer = window.MutationObserver || window.WebKitMutationObserver; this.observer = new Observer(mutations => { mutations.forEach(mutation => { if (mutation.type === 'childList') { this.scanVideos(); } }); }); this.observer.observe(document, { childList: true, subtree: true }); } setupGlobalListener() { document.addEventListener('click', this.handleGlobalClick.bind(this)); } createFloatingButtons() { if (this.floatingContainer) return; this.floatingContainer = document.createElement('div'); this.floatingContainer.style = CONFIG.FLOATING_CONTAINER_STYLE; this.previewArea = document.createElement('div'); this.previewArea.style = CONFIG.PREVIEW_STYLE; this.updatePreview(); this.floatingContainer.appendChild(this.previewArea); ['download', 'video', 'open'].forEach(type => { const button = this.generateButton(type); this.floatingContainer.appendChild(button); }); document.body.appendChild(this.floatingContainer); this.setupDrag(this.floatingContainer); } generateButton(type) { const button = document.createElement('div'); button.className = `jimeng-${type}-btn`; button.style = CONFIG.BUTTON_STYLE; button.onmouseover = () => button.style = `${CONFIG.BUTTON_STYLE}${CONFIG.HOVER_STYLE}`; button.onmouseout = () => button.style = CONFIG.BUTTON_STYLE; const templates = { download: () => ` 下载PNG `, video: () => ` 视频下载 ${this.videoList.size} `, open: () => ` 打开图片 ` }; button.innerHTML = templates[type](); button.onclick = { download: () => this.handleDownload(), video: () => this.toggleVideoPanel(), open: () => this.handleOpen() }[type]; return button; } getCurrentImage() { try { const activeImage = document.querySelector('.selected img, .active img, .current img') || document.querySelector('.imageResult img, .generatedImage img, .previewResult img') || Array.from(document.querySelectorAll('img')) .filter(img => img.src?.includes('byteimg.com') && img.width > 200 && img.height > 200) .sort((a, b) => (b.width * b.height) - (a.width * a.height))[0]; return activeImage; } catch (error) { console.error('图片查找失败:', error); return null; } } async updatePreview() { const img = this.getCurrentImage(); const newSrc = img?.src || null; if (newSrc !== this.currentImageSrc) { this.currentImageSrc = newSrc; if (newSrc) { try { const blob = await this.fetchImageBlob(newSrc); const url = URL.createObjectURL(blob); const imgElement = document.createElement('img'); imgElement.src = url; imgElement.style = 'max-width:100%;max-height:100%;object-fit:contain;'; imgElement.alt = '预览图片'; imgElement.onload = () => { this.previewArea.innerHTML = ''; this.previewArea.appendChild(imgElement); if (this.lastPreviewUrl && this.lastPreviewUrl !== url) { URL.revokeObjectURL(this.lastPreviewUrl); } this.lastPreviewUrl = url; }; imgElement.onerror = () => { this.previewArea.innerHTML = `图片加载失败`; URL.revokeObjectURL(url); }; } catch (error) { console.error('预览图片加载失败:', error); this.previewArea.innerHTML = `图片加载失败`; } } else { this.previewArea.innerHTML = `未找到有效图片`; if (this.lastPreviewUrl) { URL.revokeObjectURL(this.lastPreviewUrl); this.lastPreviewUrl = null; } } } } startPreviewUpdate() { setInterval(() => this.updatePreview(), CONFIG.PREVIEW_UPDATE_INTERVAL); } async handleDownload() { const img = this.getCurrentImage(); if (!img?.src) { alert('未找到有效图片'); return; } try { const { blob, fileName } = await this.convertWebpToPng(img.src); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = fileName; link.click(); URL.revokeObjectURL(url); } catch (error) { alert(`下载失败: ${error.message}`); } } handleOpen() { const img = this.getCurrentImage(); if (img?.src) window.open(img.src, '_blank'); } async convertWebpToPng(src) { const blob = await this.fetchImageBlob(src); return new Promise((resolve) => { const img = new Image(); img.onload = () => { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; canvas.getContext('2d').drawImage(img, 0, 0); canvas.toBlob(pngBlob => resolve({ blob: pngBlob, fileName: `jimeng-ai-${Date.now()}.png` }), 'image/png'); }; img.src = URL.createObjectURL(blob); }); } fetchImageBlob(url) { return new Promise((resolve, reject) => { GM.xmlHttpRequest({ method: 'GET', url: url, responseType: 'blob', onload: res => resolve(res.response), onerror: err => reject(new Error('图片请求失败: ' + err.statusText)) }); }); } initVideoMonitor() { setInterval(() => this.scanVideos(), CONFIG.VIDEO_SCAN_INTERVAL); } scanVideos() { const videos = document.querySelectorAll('video'); videos.forEach(video => { const src = this.cleanVideoUrl(video.src || video.querySelector('source')?.src); if (src && !this.videoList.has(src)) { // 保存视频元素的引用,方便后续获取poster或截图 this.videoList.set(src, { timestamp: Date.now(), videoElement: video, poster: video.poster || null }); this.updateVideoCount(); } }); } cleanVideoUrl(url) { if (!url || url === 'about:blank') return ''; try { const u = new URL(url.startsWith('/') ? window.location.origin + url : url); ['watermark', 'logo', 'w', 'h', 'quality'].forEach(param => u.searchParams.delete(param)); return u.toString(); } catch (e) { return url; } } createVideoPanel() { this.videoPanel = document.createElement('div'); this.videoPanel.style = `${CONFIG.VIDEO_PANEL_STYLE};display:none;`; this.updateVideoPanelContent(); document.body.appendChild(this.videoPanel); this.setupDrag(this.videoPanel); } updateVideoPanelContent() { this.videoPanel.innerHTML = `