您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
为即梦添加按钮,可以直接下载无水印原图
// ==UserScript== // @name 即梦下载无水印原图 // @namespace http://tampermonkey.net/ // @version 1.3 // @description 为即梦添加按钮,可以直接下载无水印原图 // @author psdoc烛光 // @match https://jimeng.jianying.com/* // @icon https://favicon.im/jimeng.jianying.com?larger=true&t=1755764200795 // @grant GM_download // @license MIT // ==/UserScript== (function() { 'use strict'; // 全局变量,存储观察者实例 let globalObserver = null; let retryCount = 0; const MAX_RETRY = 5; const RETRY_DELAY = 100; // 重试间隔 /** * 等待DOM加载完成后解析 * @returns {Promise} - 返回一个Promise,表示DOM加载完成的状态 */ function waitForDOM() { return new Promise(resolve => { if (document.readyState === 'complete' || document.readyState === 'interactive') { resolve(); } else { document.addEventListener('DOMContentLoaded', resolve); } }); } /** * 等待目标容器出现 * @param {string} selector - 目标容器的CSS选择器 * @param {number} timeout - 超时时间(毫秒) * @returns {Promise<Element>} - 返回找到的元素 */ function waitForElement(selector, timeout = 1000) { return new Promise((resolve, reject) => { const element = document.querySelector(selector); if (element) { resolve(element); return; } const observer = new MutationObserver((mutations) => { const targetElement = document.querySelector(selector); if (targetElement) { observer.disconnect(); resolve(targetElement); } }); observer.observe(document.body, { childList: true, subtree: true }); // 设置超时 setTimeout(() => { observer.disconnect(); reject(new Error(`等待元素 ${selector} 超时`)); }, timeout); }); } /** * 创建操作区域内的下载原图按钮 * @returns {HTMLDivElement} - 返回创建好的下载按钮元素 */ function createOperationAreaDownloadButton() { const button = document.createElement('div'); button.tabIndex = 0; button.className = 'operation-button-PU0Wce'; button.innerHTML = `<svg width="1em" height="1em" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" fill="none" role="presentation" xmlns="http://www.w3.org/2000/svg" class="operation-icon-q9NsDV"><g><path data-follow-fill="currentColor" d="M12 2a1 1 0 0 1 1 1v10.312l4.023-4.021a1 1 0 0 1 1.414 1.414l-5.73 5.728a1 1 0 0 1-1.414 0l-5.73-5.728A1 1 0 1 1 6.977 9.29L11 13.312V3a1 1 0 0 1 1-1ZM3 20.002a1 1 0 0 1 1-1L20 19a1 1 0 0 1 0 2l-16 .002a1 1 0 0 1-1-1Z" clip-rule="evenodd" fill-rule="evenodd" fill="currentColor"></path></g></svg> <span>下载原图</span>`; // 添加点击事件 button.addEventListener('click', function() { downloadOriginalImage(); }); return button; } /** * 创建原有的下载原图按钮 * @returns {HTMLDivElement} - 返回创建好的下载按钮元素 */ function createDownloadButton() { const button = document.createElement('div'); button.tabIndex = 0; button.className = 'operation-button-PU0Wce'; button.innerHTML = `<svg width="1em" height="1em" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" fill="none" role="presentation" xmlns="http://www.w3.org/2000/svg" class="operation-icon-q9NsDV"><g><path data-follow-fill="currentColor" d="M12 2a1 1 0 0 1 1 1v10.312l4.023-4.021a1 1 0 0 1 1.414 1.414l-5.73 5.728a1 1 0 0 1-1.414 0l-5.73-5.728A1 1 0 1 1 6.977 9.29L11 13.312V3a1 1 0 0 1 1-1ZM3 20.002a1 1 0 0 1 1-1L20 19a1 1 0 0 1 0 2l-16 .002a1 1 0 0 1-1-1Z" clip-rule="evenodd" fill-rule="evenodd" fill="currentColor"></path></g></svg> <span>下载原图</span>`; // 添加点击事件 button.addEventListener('click', function() { downloadOriginalImage(); }); return button; } /** * 获取当前选中的图片元素 * @returns {HTMLImageElement|null} - 返回当前选中的图片元素,如果找不到则返回null */ function getCurrentSelectedImage() { // 首先尝试查找当前激活的图片容器中的图片 const activeImageContainer = document.querySelector('.image-container.active, .image-item.selected, [class*="active"], [class*="selected"]'); if (activeImageContainer) { const imageInContainer = activeImageContainer.querySelector('img[crossorigin="anonymous"]'); // 检查图片是否在avatar容器内,如果是则跳过 if (imageInContainer && !imageInContainer.closest('.avatar-container-UQ4YgV')) { console.log('通过活动容器找到图片:', imageInContainer.src); return imageInContainer; } } // 备用方案:查找所有符合条件的图片,排除avatar容器内的图片 const allImages = document.querySelectorAll('img[crossorigin="anonymous"]'); const filteredImages = Array.from(allImages).filter(img => !img.closest('.avatar-container-UQ4YgV')); if (filteredImages.length > 0) { // 优先查找带有特定属性的图片 const specificImage = filteredImages.find(img => img.hasAttribute('data-apm-action') && img.getAttribute('data-apm-action') === 'ai-generated-image-detail-card'); if (specificImage) { console.log('找到特定属性图片:', specificImage.src); return specificImage; } // 如果没有特定属性的图片,返回第一个符合条件的图片 console.log('使用第一个符合条件的图片:', filteredImages[0].src); return filteredImages[0]; } console.log('未找到任何符合条件的图片'); return null; } /** * 创建在新标签页打开原图的按钮 * @returns {HTMLDivElement} - 返回创建好的按钮元素 */ function createEmptyButton() { const button = document.createElement('div'); button.tabIndex = 0; button.className = 'operation-button-PU0Wce'; button.innerHTML = `<svg width="1em" height="1em" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" fill="none" role="presentation" xmlns="http://www.w3.org/2000/svg" class="operation-icon-q9NsDV"><g><path data-follow-fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.477 2 2 6.477 2 12c0 5.523 4.477 10 10 10s10-4.477 10-10c0-5.523-4.477-10-10-10zm0 18c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8z" fill="currentColor"></path></g></svg> <span>打开原图</span>`; // 添加点击事件,在新标签页打开原图 button.addEventListener('click', function() { const targetImage = getCurrentSelectedImage(); if (targetImage && targetImage.src) { console.log('找到目标图片,URL:', targetImage.src); // 在新标签页打开图片 window.open(targetImage.src, '_blank'); } else { alert('未找到可查看的图片'); } }); return button; } /** * 下载原图功能 */ function downloadOriginalImage() { const targetImage = getCurrentSelectedImage(); if (targetImage && targetImage.src) { console.log('找到目标图片,准备下载,URL:', targetImage.src); const imageUrl = targetImage.src; const imageName = imageUrl.split('/').pop().split('?')[0] || 'downloaded_image'; try { // 使用GM_download下载图片 GM_download({ url: imageUrl, name: imageName, saveAs: true, onerror: function(error) { console.error('下载失败:', error); alert('下载失败,请重试'); }, onprogress: function(progress) { console.log('下载进度:', progress); } }); } catch (error) { console.error('GM_download调用失败:', error); alert('下载功能不可用,请检查Tampermonkey设置'); } } else { alert('未找到可下载的图片'); } } /** * 将按钮添加到目标容器 * @param {HTMLDivElement} button - 要添加的按钮元素 * @param {string} targetSelector - 目标容器的CSS选择器 * @returns {boolean} - 添加成功返回true,否则返回false */ function addButtonToTarget(button, targetSelector) { const targetContainer = document.querySelector(targetSelector); if (targetContainer) { // 检查是否已经存在相同的按钮 const existingButtons = targetContainer.querySelectorAll('.operation-button-PU0Wce'); for (let existingBtn of existingButtons) { if (existingBtn.textContent === button.textContent) { console.log('按钮已存在,跳过添加'); return true; } } targetContainer.appendChild(button); console.log('成功添加按钮到容器'); return true; } else { console.log(`未找到目标容器 ${targetSelector}`); return false; } } /** * 修改目标容器样式为两行布局 * @param {string} targetSelector - 目标容器的CSS选择器 */ function modifyContainerLayout(targetSelector) { const targetContainer = document.querySelector(targetSelector); if (targetContainer) { // 添加内联样式使容器变成两行布局,宽度自适应 targetContainer.style.display = 'flex'; targetContainer.style.flexWrap = 'wrap'; targetContainer.style.maxHeight = '80px'; // 根据需要调整高度 targetContainer.style.width = 'fit-content'; // 宽度自适应 targetContainer.style.alignItems = 'flex-start'; console.log('成功修改容器布局为两行'); } else { console.log(`未找到目标容器 ${targetSelector}`); } } /** * 在操作区域添加下载按钮 * @param {string} operationAreaSelector - 操作区域的CSS选择器 * @returns {boolean} - 添加成功返回true,否则返回false */ async function addButtonToOperationArea(operationAreaSelector) { try { // 等待操作区域出现 const operationArea = await waitForElement(operationAreaSelector); if (operationArea) { // 检查是否已经存在相同的按钮 const existingButtons = operationArea.querySelectorAll('.operation-button-PU0Wce'); for (let existingBtn of existingButtons) { if (existingBtn.textContent.trim() === '下载原图') { console.log('操作区域下载按钮已存在,跳过添加'); return true; } } // 创建下载按钮 const operationAreaButton = createOperationAreaDownloadButton(); // 根据容器内元素的排列顺序找到合适的插入位置 const publishButton = operationArea.querySelector('.publish-button-LkMPnt'); const operationButtons = operationArea.querySelector('.operation-buttons-H5Rhlq'); // 隐藏发布按钮 if (publishButton) { publishButton.style.display = 'none'; console.log('已隐藏发布按钮'); } if (publishButton && operationButtons) { // 如果发布按钮和操作按钮容器都存在,将下载按钮插入到它们之间 operationArea.insertBefore(operationAreaButton, operationButtons); console.log('成功在发布按钮和操作按钮容器之间插入下载按钮'); } else if (publishButton) { // 如果只有发布按钮,将下载按钮插入到发布按钮之后 publishButton.parentNode.insertBefore(operationAreaButton, publishButton.nextSibling); console.log('成功在发布按钮之后插入下载按钮'); } else if (operationButtons) { // 如果只有操作按钮容器,将下载按钮插入到操作按钮容器之前 operationArea.insertBefore(operationAreaButton, operationButtons); console.log('成功在操作按钮容器之前插入下载按钮'); } else { // 如果都没有,直接添加到容器末尾 operationArea.appendChild(operationAreaButton); console.log('成功在操作区域末尾添加下载按钮'); } return true; } } catch (error) { console.error('在操作区域添加按钮失败:', error); return false; } } /** * 初始化按钮和布局 * @param {string} targetSelector - 目标容器的CSS选择器 * @param {string} operationAreaSelector - 操作区域的CSS选择器 */ async function initializeButtons(targetSelector, operationAreaSelector) { try { // 等待目标容器出现 await waitForElement(targetSelector); console.log('目标容器已找到,开始初始化'); // 修改容器布局 modifyContainerLayout(targetSelector); // 创建并添加按钮 const downloadButton = createDownloadButton(); const emptyButton = createEmptyButton(); const addedDownload = addButtonToTarget(downloadButton, targetSelector); const addedEmpty = addButtonToTarget(emptyButton, targetSelector); // 在操作区域添加下载按钮 const addedOperationAreaButton = await addButtonToOperationArea(operationAreaSelector); if (addedDownload && addedEmpty && addedOperationAreaButton) { console.log('所有按钮初始化成功'); retryCount = 0; // 重置重试计数 return true; } else { console.log('部分按钮初始化失败'); return false; } } catch (error) { console.error('初始化按钮失败:', error); return false; } } /** * 设置全局MutationObserver来监控DOM变化 * 用于处理单页应用的页面切换 * @param {string} targetSelector - 目标容器的CSS选择器 * @param {string} operationAreaSelector - 操作区域的CSS选择器 */ function setupGlobalObserver(targetSelector, operationAreaSelector) { // 如果已经存在观察者,先断开 if (globalObserver) { globalObserver.disconnect(); } globalObserver = new MutationObserver((mutations) => { // 检查是否有目标容器的变化 const targetContainer = document.querySelector(targetSelector); if (targetContainer) { // 检查容器中是否已经有我们的按钮 const existingButtons = targetContainer.querySelectorAll('.operation-button-PU0Wce'); if (existingButtons.length === 0) { console.log('检测到容器变化但缺少按钮,重新初始化'); initializeButtons(targetSelector, operationAreaSelector); } } // 检查操作区域是否缺少按钮 const operationArea = document.querySelector(operationAreaSelector); if (operationArea) { const operationAreaButtons = operationArea.querySelectorAll('.operation-button-PU0Wce'); let hasDownloadButton = false; for (let btn of operationAreaButtons) { if (btn.textContent.trim() === '下载原图') { hasDownloadButton = true; break; } } if (!hasDownloadButton) { console.log('检测到操作区域变化但缺少下载按钮,重新添加'); addButtonToOperationArea(operationAreaSelector); } } }); // 开始观察DOM变化 globalObserver.observe(document.body, { childList: true, subtree: true, attributes: false, characterData: false }); console.log('全局DOM观察者已启动'); } /** * 重试初始化机制 * @param {string} targetSelector - 目标容器的CSS选择器 * @param {string} operationAreaSelector - 操作区域的CSS选择器 */ async function retryInitialization(targetSelector, operationAreaSelector) { if (retryCount >= MAX_RETRY) { console.log('已达到最大重试次数,停止重试'); return; } retryCount++; console.log(`第 ${retryCount} 次重试初始化`); setTimeout(async () => { const success = await initializeButtons(targetSelector, operationAreaSelector); if (!success) { retryInitialization(targetSelector, operationAreaSelector); } }, RETRY_DELAY); } // 主函数 /** * 脚本主函数,协调整个功能的执行 */ async function main() { await waitForDOM(); // 目标容器选择器 const targetSelector = '.action-buttons-wrapper-v1aogC'; // 操作区域选择器(红框位置) const operationAreaSelector = '.operation-area-Kuc_sT'; console.log('脚本开始执行'); // 首次初始化 const success = await initializeButtons(targetSelector, operationAreaSelector); if (!success) { console.log('首次初始化失败,启动重试机制'); retryInitialization(targetSelector, operationAreaSelector); } // 设置全局观察者 setupGlobalObserver(targetSelector, operationAreaSelector); // 监听路由变化(针对单页应用) let lastUrl = location.href; new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; console.log('检测到URL变化,重新初始化按钮'); setTimeout(() => { initializeButtons(targetSelector, operationAreaSelector); }, 1000); // 延迟1秒等待DOM更新 } }).observe(document, { subtree: true, childList: true }); console.log('脚本初始化完成'); } // 执行主函数 main(); })();