// ==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 = `
检测到 ${this.videoList.size} 个视频
${Array.from(this.videoList.entries()).map(([url, info], index) => `
视频缩略图
视频 ${index + 1} - ${new Date(info.timestamp).toLocaleTimeString()}
`).join('')}
`; this.videoPanel.className = 'jimeng-enhancer-video-panel'; const collapseBtn = this.videoPanel.querySelector('.collapse-btn'); collapseBtn.addEventListener('click', () => this.toggleVideoPanel()); const downloadBtns = this.videoPanel.querySelectorAll('.download-btn'); downloadBtns.forEach(btn => { btn.addEventListener('click', () => { const url = btn.getAttribute('data-url'); GM_download({ url: url, name: `即梦视频_${Date.now()}.mp4`, onload: () => { this.videoList.delete(url); this.updateVideoCount(); this.updateVideoPanelContent(); } }); }); }); const thumbnails = this.videoPanel.querySelectorAll('.video-thumbnail'); thumbnails.forEach(thumbnail => { const url = thumbnail.getAttribute('data-url'); const videoInfo = this.videoList.get(url); // 如果没有poster,尝试生成缩略图 if (!videoInfo.poster) { this.generateVideoThumbnail(url, thumbnail, videoInfo.videoElement); } }); } generateVideoThumbnail(url, imgElement, videoElement) { // 优先使用视频元素生成缩略图 if (videoElement && !videoElement.seeking && videoElement.readyState >= 2) { try { // 直接使用已加载的视频元素生成缩略图 const canvas = document.createElement('canvas'); canvas.width = 80; canvas.height = 60; const ctx = canvas.getContext('2d'); ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height); imgElement.src = canvas.toDataURL('image/jpeg', 0.7); // 更新视频列表中的poster信息 const videoInfo = this.videoList.get(url); if (videoInfo) { videoInfo.poster = imgElement.src; } return; } catch (e) { console.warn('使用已有视频元素生成缩略图失败:', e); // 失败后继续尝试下面的方法 } } // 创建新的视频元素加载视频 const tempVideo = document.createElement('video'); tempVideo.muted = true; tempVideo.crossOrigin = "anonymous"; // 允许跨域访问 tempVideo.preload = "metadata"; // 设置超时以避免长时间加载 const timeoutId = setTimeout(() => { cleanupAndShowFallback(); }, 5000); const cleanupAndShowFallback = () => { clearTimeout(timeoutId); if (document.body.contains(tempVideo)) { document.body.removeChild(tempVideo); } if (imgElement.src === CONFIG.DEFAULT_THUMBNAIL) { // 只有当图片还是默认图时才需要更新 imgElement.src = CONFIG.DEFAULT_THUMBNAIL; } }; tempVideo.onloadedmetadata = function() { try { tempVideo.currentTime = Math.min(1, tempVideo.duration / 3); } catch (e) { cleanupAndShowFallback(); } }; tempVideo.onseeked = function() { try { // 视频已经跳转到指定时间点,创建缩略图 const canvas = document.createElement('canvas'); canvas.width = 80; canvas.height = 60; const ctx = canvas.getContext('2d'); ctx.drawImage(tempVideo, 0, 0, canvas.width, canvas.height); imgElement.src = canvas.toDataURL('image/jpeg', 0.7); // 更新视频列表中的poster信息 const videoInfo = this.videoList?.get?.(url); if (videoInfo) { videoInfo.poster = imgElement.src; } clearTimeout(timeoutId); document.body.removeChild(tempVideo); } catch (e) { cleanupAndShowFallback(); } }.bind(this); tempVideo.onerror = cleanupAndShowFallback; // 隐藏视频元素并添加到DOM tempVideo.style.display = 'none'; document.body.appendChild(tempVideo); // 最后设置src以开始加载 tempVideo.src = url; } updateVideoCount() { const countBadge = this.floatingContainer?.querySelector('.video-count'); if (countBadge) countBadge.textContent = this.videoList.size; if (this.isVideoPanelExpanded) this.updateVideoPanelContent(); } toggleVideoPanel() { this.isVideoPanelExpanded = !this.isVideoPanelExpanded; this.videoPanel.style.display = this.isVideoPanelExpanded ? 'flex' : 'none'; const videoBtn = this.floatingContainer.querySelector('.jimeng-video-btn'); if (videoBtn) videoBtn.style.display = this.isVideoPanelExpanded ? 'none' : 'flex'; if (this.isVideoPanelExpanded) this.updateVideoPanelContent(); } setupDrag(element) { let isDragging = false, startX, startY, initialX, initialY; const header = element.querySelector('div:first-child'); header.addEventListener('mousedown', e => { isDragging = true; startX = e.clientX; startY = e.clientY; initialX = element.offsetLeft; initialY = element.offsetTop; }); document.addEventListener('mousemove', e => { if (!isDragging) return; const dx = e.clientX - startX; const dy = e.clientY - startY; element.style.right = 'auto'; element.style.bottom = 'auto'; element.style.left = `${initialX + dx}px`; element.style.top = `${initialY + dy}px`; }); document.addEventListener('mouseup', () => { isDragging = false; }); } handleGlobalClick(event) { if (!event.target.closest('.jimeng-enhancer-video-panel, .jimeng-download-btn, .jimeng-video-btn, .jimeng-open-btn')) { return; } event.stopImmediatePropagation(); } } new JimengEnhancer(); })();