// ==UserScript== // @name 即梦AI全能助手 // @namespace http://tampermonkey.net/ // @version 3.3 // @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 none // ==/UserScript== (function() { 'use strict'; const CONFIG = { ACTION_BAR_SELECTOR: '.topActionBar-b6PHoX', BUTTON_STYLE: ` margin-left: 12px; cursor: pointer; border-radius: 8px; padding: 8px 16px; transition: all 0.2s; background: #f0f2f5; border: 1px solid #d9d9d9; position: relative !important; `, HOVER_STYLE: ` background: #e6f7ff; border-color: #91d5ff; `, VIDEO_SCAN_INTERVAL: 3000, VIDEO_PANEL_STYLE: ` position: fixed; right: 20px; top: 100px; 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: 320px; z-index: 9999; max-height: 70vh; overflow: hidden; `, ASSET_PAGE_BUTTON_CONTAINER_STYLE: ` display: flex; position: fixed; top: 10px; right: 20px; z-index: 9999; background: rgba(255,255,255,0.9); padding: 8px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); ` }; class JimengEnhancer { constructor() { this.observer = null; this.actionBar = null; this.buttonsAdded = false; this.videoList = new Map(); this.videoPanel = null; this.assetButtonContainer = null; this.init(); } init() { this.setupDOMObserver(); this.setupGlobalListener(); this.initVideoMonitor(); this.createVideoPanel(); // 立即尝试几次查找按钮容器并添加按钮 setTimeout(() => this.safeUpdateButtons(), 1000); setTimeout(() => this.safeUpdateButtons(), 3000); setTimeout(() => this.safeUpdateButtons(), 5000); // 特别处理asset页面 this.handleAssetPage(); // 监听URL变化,以处理SPA导航 this.setupUrlChangeListener(); } setupDOMObserver() { const Observer = window.MutationObserver || window.WebKitMutationObserver; this.observer = new Observer(mutations => { mutations.forEach(mutation => { if (mutation.type === 'childList') { this.handleDOMChange(mutation); } }); }); this.observer.observe(document, { childList: true, subtree: true, attributes: false, characterData: false }); } setupGlobalListener() { document.addEventListener('click', this.handleGlobalClick.bind(this)); } handleDOMChange(mutation) { const shouldUpdate = Array.from(mutation.addedNodes).some(node => node.nodeType === 1 && (node.matches(CONFIG.ACTION_BAR_SELECTOR) || node.querySelector(CONFIG.ACTION_BAR_SELECTOR)) ) || !document.contains(this.actionBar); if (shouldUpdate) { this.safeUpdateButtons(); } // 在AI-tool/asset页面监测按钮容器 if (this.isAssetPage()) { if (!this.buttonsAdded) { this.safeUpdateButtons(); } } } safeUpdateButtons() { // 在asset页面,也尝试使用更广泛的选择器查找操作栏 if (this.isAssetPage()) { this.actionBar = document.querySelector(CONFIG.ACTION_BAR_SELECTOR) || document.querySelector('.topActionBar') || document.querySelector('.actionBar') || document.querySelector('.header-actions'); // 如果在asset页面仍找不到操作栏,尝试创建一个 if (!this.actionBar) { const potentialParent = document.querySelector('.layout-header') || document.querySelector('.header') || document.querySelector('header'); if (potentialParent && !document.querySelector('.jimeng-custom-action-bar')) { const customBar = document.createElement('div'); customBar.className = 'topActionBar-b6PHoX jimeng-custom-action-bar'; customBar.style = 'display: flex; align-items: center; justify-content: flex-end; padding: 8px 16px;'; potentialParent.appendChild(customBar); this.actionBar = customBar; } } } else { this.actionBar = document.querySelector(CONFIG.ACTION_BAR_SELECTOR); } if (this.actionBar && !this.buttonsAdded) { this.injectButtons(); this.buttonsAdded = true; } else if (!this.actionBar) { this.buttonsAdded = false; } } injectButtons() { if (!this.actionBar.querySelector('.jimeng-download-btn')) { ['download', 'video', 'open'].forEach(type => { this.actionBar.insertAdjacentHTML('beforeend', this.generateButton(type)); }); this.actionBar.querySelector('.jimeng-download-btn').onclick = () => this.handleDownload(); this.actionBar.querySelector('.jimeng-video-btn').onclick = () => this.toggleVideoPanel(); this.actionBar.querySelector('.jimeng-open-btn').onclick = () => this.handleOpen(); } } generateButton(type) { const templates = { download: () => `
下载PNG
`, video: () => `
视频下载 ${this.videoList.size}
`, open: () => `
打开图片
` }; return templates[type](); } getCurrentImage() { try { // 获取当前活跃或选中的图片 const getActiveImage = () => { // 查找带有selected、active等类的容器内的图片 const activeContainers = Array.from(document.querySelectorAll('.selected, .active, .current, .focused, [data-active="true"], [aria-selected="true"]')); for (const container of activeContainers) { const img = container.querySelector('img'); if (img && img.src && img.width > 100) return img; } return null; }; // 首先尝试获取活跃的图片 const activeImage = getActiveImage(); if (activeImage) { console.log('[即梦助手] 找到活跃图片:', activeImage.src); return activeImage; } // 支持AI-tool/image/generate页面的图片获取 if (this.isImageGeneratePage()) { const generateImage = document.querySelector('.imageResult img') || document.querySelector('.generatedImage img') || document.querySelector('.previewResult img') || document.querySelector('.result-container img'); if (generateImage) { console.log('[即梦助手] 找到生成图片:', generateImage.src); return generateImage; } } // 支持AI-tool/asset页面的图片获取 if (this.isAssetPage()) { const assetImage = document.querySelector('.viewerImage img') || document.querySelector('.media-container img') || document.querySelector('.assetPreview img') || document.querySelector('.mediaViewer img') || document.querySelector('.asset-preview img') || document.querySelector('.content-view img'); if (assetImage) { console.log('[即梦助手] 找到资源图片:', assetImage.src); return assetImage; } } // 一般页面的图片查找 const mainImage = document.querySelector('.imageContainer-Ey3Aqm img') || document.querySelector('.generatedImage-Ey3Aqm img') || document.querySelector('.previewArea-Ey3Aqm img'); if (mainImage) { console.log('[即梦助手] 找到主要图片:', mainImage.src); return mainImage; } // 回退策略:查找所有较大的图片 const allPossible = Array.from(document.querySelectorAll('img')) .filter(img => img.src?.includes('byteimg.com') && img.width > 200 && img.height > 200 && img.complete); if (allPossible.length > 0) { // 根据图片尺寸和可见性排序 allPossible.sort((a, b) => { // 可见性评分 const visibilityA = this.getElementVisibility(a); const visibilityB = this.getElementVisibility(b); if (visibilityA !== visibilityB) { return visibilityB - visibilityA; // 可见性优先 } // 尺寸评分 return (b.width * b.height) - (a.width * a.height); // 大尺寸优先 }); console.log('[即梦助手] 回退策略找到图片:', allPossible[0].src); return allPossible[0]; } return null; } catch (error) { console.error('图片查找失败:', error); return null; } } // 获取元素的可见性评分(0-10) getElementVisibility(element) { if (!element) return 0; try { const rect = element.getBoundingClientRect(); // 检查元素是否在视口内 if (rect.right < 0 || rect.bottom < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight) { return 0; // 不在视口内 } // 检查元素是否被隐藏 const style = window.getComputedStyle(element); if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') { return 0; } // 检查元素尺寸 if (rect.width < 50 || rect.height < 50) { return 2; // 元素太小 } // 计算元素在视口中的面积比例 const visibleWidth = Math.min(rect.right, window.innerWidth) - Math.max(rect.left, 0); const visibleHeight = Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0); const visibleArea = visibleWidth * visibleHeight; const elementArea = rect.width * rect.height; const visibilityRatio = visibleArea / elementArea; // 基于可见比例返回评分(0-10) return Math.round(visibilityRatio * 10); } catch (e) { console.error('计算元素可见性失败:', e); return 0; } } async handleDownload() { const img = this.getCurrentImage(); if (!img?.src) { console.error('[即梦助手] 未找到有效图片'); 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(); setTimeout(() => URL.revokeObjectURL(url), 1000); } 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, reject) => { 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.onerror = reject; 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: reject }); }); } /* 视频下载相关功能 */ initVideoMonitor() { setInterval(() => this.scanVideos(), CONFIG.VIDEO_SCAN_INTERVAL); } scanVideos() { console.log('[即梦助手] 正在扫描视频元素...'); // 找出所有视频元素 const videos = document.querySelectorAll('video'); console.log(`[即梦助手] 找到 ${videos.length} 个视频元素`); // 在asset页面特别处理 if (this.isAssetPage()) { const assetVideoPlayer = document.querySelector('.video-react-video') || document.querySelector('.asset-video video') || document.querySelector('.media-player video'); if (assetVideoPlayer && assetVideoPlayer.src) { const src = this.cleanVideoUrl(assetVideoPlayer.src); if (src && !this.videoList.has(src)) { console.log(`[即梦助手] 在asset页面发现新视频: ${src}`); this.videoList.set(src, { element: assetVideoPlayer, timestamp: Date.now() }); // 更新UI this.updateVideoUI(); } } } videos.forEach(video => { // 处理视频源 let src = ''; // 尝试从视频元素直接获取src if (video.src) { src = this.cleanVideoUrl(video.src); } // 如果直接src为空,检查source子元素 if (!src) { const sources = video.querySelectorAll('source'); for (const source of sources) { if (source.src) { src = this.cleanVideoUrl(source.src); if (src) break; } } } // 检查视频是否已经在列表中或src是否为空 if (!src) { console.log('[即梦助手] 跳过没有src的视频元素'); return; } if (this.videoList.has(src)) { return; } console.log(`[即梦助手] 发现新视频: ${src}`); // 添加到视频列表 this.videoList.set(src, { element: video, timestamp: Date.now() }); // 更新UI this.updateVideoUI(); }); // 在每次扫描后显示已找到的视频总数 console.log(`[即梦助手] 当前视频列表中共有 ${this.videoList.size} 个视频`); } cleanVideoUrl(url) { if (!url || url === '' || url === 'about:blank') { return ''; } try { // 对于相对路径,转换为绝对路径 if (url.startsWith('/')) { url = window.location.origin + url; } const u = new URL(url); // 移除可能导致水印的参数 u.searchParams.delete('watermark'); u.searchParams.delete('logo'); u.searchParams.delete('w'); u.searchParams.delete('h'); u.searchParams.delete('quality'); return u.toString(); } catch(e) { console.error(`[即梦助手] 清理视频URL失败: ${e.message}`, url); // 如果URL解析失败但非空,仍返回原始URL return url && url !== 'about:blank' ? url : ''; } } createVideoPanel() { this.videoPanel = document.createElement('div'); this.videoPanel.style = CONFIG.VIDEO_PANEL_STYLE; this.videoPanel.innerHTML = `
检测到 ${this.videoList.size} 个视频
`; document.body.appendChild(this.videoPanel); this.setupDrag(this.videoPanel); } updateVideoUI() { // 更新操作栏按钮 const countBadge = this.actionBar?.querySelector('.video-count'); if (countBadge) countBadge.textContent = this.videoList.size; // 更新asset页面的按钮 this.updateAssetButtonsVideoCount(); // 视频面板可能尚未创建 if (!this.videoPanel) { this.createVideoPanel(); } // 更新视频面板标题 const panelTitle = this.videoPanel.querySelector('div:first-child'); if (panelTitle) { panelTitle.textContent = `检测到 ${this.videoList.size} 个视频`; } // 更新视频面板 const content = this.videoPanel.querySelector('div:last-child'); content.innerHTML = ''; // 清空内容 Array.from(this.videoList.entries()).forEach(([url, info], index) => { const videoContainer = document.createElement('div'); videoContainer.style = 'padding:12px;border-bottom:1px solid #eee;'; // 创建视频信息标题 const titleDiv = document.createElement('div'); titleDiv.style = 'font-size:12px;color:#666;word-break:break-all;'; titleDiv.textContent = `视频 ${index + 1} - ${new Date(info.timestamp).toLocaleTimeString()}`; videoContainer.appendChild(titleDiv); // 创建视频预览区域 const previewDiv = document.createElement('div'); previewDiv.style = 'margin:8px 0;background:#f0f0f0;border-radius:4px;text-align:center;position:relative;height:120px;display:flex;justify-content:center;align-items:center;'; // 默认占位符 previewDiv.innerHTML = `
视频预览
`; videoContainer.appendChild(previewDiv); // 创建下载按钮 const downloadBtn = document.createElement('button'); downloadBtn.textContent = '下载无水印版'; downloadBtn.dataset.url = url; downloadBtn.style = 'margin-top:8px;padding:4px 8px;background:#007bff;color:white;border:none;border-radius:4px;'; downloadBtn.addEventListener('click', () => this.downloadVideo(url)); videoContainer.appendChild(downloadBtn); // 创建预览按钮 const previewBtn = document.createElement('button'); previewBtn.textContent = '预览视频'; previewBtn.style = 'margin-top:8px;margin-left:8px;padding:4px 8px;background:#28a745;color:white;border:none;border-radius:4px;'; previewBtn.addEventListener('click', () => { // 检查是否已经有预览视频 if (previewDiv.querySelector('video')) { previewDiv.innerHTML = `
视频预览
`; previewBtn.textContent = '预览视频'; } else { // 创建一个新的视频元素用于预览 const videoPreview = document.createElement('video'); videoPreview.src = url; videoPreview.controls = true; videoPreview.style = 'max-width:100%;max-height:120px;border-radius:4px;'; videoPreview.volume = 0.5; previewDiv.innerHTML = ''; previewDiv.appendChild(videoPreview); previewBtn.textContent = '关闭预览'; } }); videoContainer.appendChild(previewBtn); content.appendChild(videoContainer); }); } downloadVideo(url) { if (!this.videoList.has(url)) return; GM_download({ url: url, name: `即梦视频_${Date.now()}.mp4`, onload: () => { this.videoList.delete(url); this.updateVideoUI(); }, onerror: (e) => console.error('视频下载失败:', e) }); } toggleVideoPanel() { this.videoPanel.style.display = this.videoPanel.style.display === 'none' ? 'block' : 'none'; } setupDrag(element) { let isDragging = false; let startX = 0, startY = 0, initialX = 0, initialY = 0; element.querySelector('div:first-child').addEventListener('mousedown', e => { isDragging = true; startX = e.clientX; startY = e.clientY; initialX = element.offsetLeft; initialY = element.offsetTop; document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); }); const onMouseMove = e => { if (!isDragging) return; const dx = e.clientX - startX; const dy = e.clientY - startY; element.style.left = `${initialX + dx}px`; element.style.top = `${initialY + dy}px`; }; const onMouseUp = () => { isDragging = false; document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); }; } handleGlobalClick(event) { if (event.target.closest('.jimeng-download-btn, .jimeng-video-btn, .jimeng-open-btn')) { event.stopImmediatePropagation(); } } // 监听URL变化 setupUrlChangeListener() { let lastUrl = window.location.href; // 通过定期检查URL是否变化来模拟监听 setInterval(() => { const currentUrl = window.location.href; if (currentUrl !== lastUrl) { lastUrl = currentUrl; console.log('[即梦助手] 检测到URL变化:', currentUrl); // 根据URL做相应处理 this.handleUrlChange(); } }, 1000); } // 处理URL变化 handleUrlChange() { // 处理悬浮按钮 if (this.isImageGeneratePage()) { this.hideAssetPageButtons(); } else if (this.isAssetPage()) { this.injectAssetPageButtons(); } else { this.hideAssetPageButtons(); } // 重置按钮状态 this.buttonsAdded = false; setTimeout(() => this.safeUpdateButtons(), 1000); } // 隐藏悬浮按钮 hideAssetPageButtons() { const buttonContainer = document.querySelector('.jimeng-asset-buttons'); if (buttonContainer) { buttonContainer.style.display = 'none'; } } // 判断当前是否为asset页面 isAssetPage() { const url = window.location.href; return url.includes('/ai-tool/asset'); } // 判断当前是否为图片生成页面 isImageGeneratePage() { const url = window.location.href; return url.includes('/ai-tool/image/generate'); } // 针对asset页面的特殊处理 handleAssetPage() { if (!this.isAssetPage() || this.isImageGeneratePage()) return; // 设置定期检查,因为asset页面可能是动态加载的 const checkInterval = setInterval(() => { if (document.readyState === 'complete') { // 再次检查URL,因为在等待期间可能已经发生变化 if (this.isAssetPage() && !this.isImageGeneratePage()) { this.injectAssetPageButtons(); if (document.querySelector('.jimeng-asset-buttons')) { clearInterval(checkInterval); } } else { this.hideAssetPageButtons(); clearInterval(checkInterval); } } }, 1000); } // 在asset页面直接添加按钮 injectAssetPageButtons() { if (!this.isAssetPage() || this.isImageGeneratePage()) return; // 如果已经添加过按钮,则不再添加,但确保它是可见的 const existingButtons = document.querySelector('.jimeng-asset-buttons'); if (existingButtons) { existingButtons.style.display = 'flex'; return; } // 创建按钮容器 const buttonContainer = document.createElement('div'); buttonContainer.className = 'jimeng-asset-buttons'; buttonContainer.style = CONFIG.ASSET_PAGE_BUTTON_CONTAINER_STYLE; // 添加按钮 ['download', 'video', 'open'].forEach(type => { const buttonDiv = document.createElement('div'); buttonDiv.className = `jimeng-${type}-btn custom-btn`; buttonDiv.style = CONFIG.BUTTON_STYLE; buttonDiv.onmouseover = () => buttonDiv.style = `${CONFIG.BUTTON_STYLE}${CONFIG.HOVER_STYLE}`; buttonDiv.onmouseout = () => buttonDiv.style = CONFIG.BUTTON_STYLE; const templates = { download: () => ` 下载PNG `, video: () => ` 视频下载 ${this.videoList.size} `, open: () => ` 打开图片 ` }; buttonDiv.innerHTML = templates[type](); buttonContainer.appendChild(buttonDiv); // 添加点击事件 if (type === 'download') { buttonDiv.onclick = () => this.handleDownload(); } else if (type === 'video') { buttonDiv.onclick = () => this.toggleVideoPanel(); } else if (type === 'open') { buttonDiv.onclick = () => this.handleOpen(); } }); // 将按钮容器添加到页面 document.body.appendChild(buttonContainer); console.log('[即梦助手] 在asset页面添加按钮成功'); // 添加拖动功能 this.setupDrag(buttonContainer); // 保留一个引用以便后续操作 this.assetButtonContainer = buttonContainer; } // 更新asset页面按钮中视频计数 updateAssetButtonsVideoCount() { if (this.isAssetPage() && this.assetButtonContainer) { const countBadge = this.assetButtonContainer.querySelector('.video-count'); if (countBadge) countBadge.textContent = this.videoList.size; } } } new JimengEnhancer(); })();