// ==UserScript== // @name Xiaomi MiMo Studio 去水印 // @namespace http://tampermonkey.net/ // @version 1.2.0 // @description 自动检测并移除 Xiaomi MiMo Studio 页面中的水印内容(动态获取水印) // @author AlanWang // @license MIT // @match https://aistudio.xiaomimimo.com/* // @match https://aistudio.xiaomimimo.com/#/* // @grant none // @run-at document-start // @downloadURL none // ==/UserScript== (function() { 'use strict'; // ========== 配置选项 ========== // 日志开关(设置为 true 启用日志,false 关闭日志) const ENABLE_LOG = false; // ========== 日志函数 ========== const logger = { log: (...args) => { if (ENABLE_LOG) { console.log('[去水印脚本]', ...args); } }, warn: (...args) => { if (ENABLE_LOG) { console.warn('[去水印脚本]', ...args); } }, error: (...args) => { if (ENABLE_LOG) { console.error('[去水印脚本]', ...args); } } }; // 水印内容(将从 API 动态获取) let WATERMARK_TEXT = null; // API 端点 const USER_API_URL = 'https://aistudio.xiaomimimo.com/open-apis/user/mi/get'; // 已处理的元素缓存,避免重复处理 const processedElements = new WeakSet(); // 防抖函数 function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // 检查文本内容是否包含水印 function containsWatermark(text) { if (!text || typeof text !== 'string' || !WATERMARK_TEXT) return false; return text.includes(WATERMARK_TEXT); } // 检查元素是否包含水印文本 function elementContainsWatermark(element) { if (!element || processedElements.has(element)) return false; // 检查 textContent if (containsWatermark(element.textContent)) return true; // 检查 innerText if (containsWatermark(element.innerText)) return true; // 检查 innerHTML if (containsWatermark(element.innerHTML)) return true; // 检查 value 属性(用于 input、textarea 等) if (element.value && containsWatermark(element.value)) return true; // 检查所有属性值 if (element.attributes) { for (let attr of element.attributes) { if (containsWatermark(attr.value)) return true; } } return false; } // 检查图片是否包含水印 function imageContainsWatermark(element) { if (!element) return false; // 检查 img src if (element.tagName === 'IMG' && element.src) { if (containsWatermark(element.src)) return true; } // 检查背景图片 const style = window.getComputedStyle(element); const bgImage = style.backgroundImage; if (bgImage && bgImage !== 'none' && containsWatermark(bgImage)) { return true; } // 检查内联样式 if (element.style && element.style.backgroundImage) { if (containsWatermark(element.style.backgroundImage)) return true; } return false; } // 移除水印元素 function removeWatermark(element) { if (!element || processedElements.has(element)) return; try { // 标记为已处理 processedElements.add(element); // 如果是图片水印,尝试清除背景或移除元素 if (imageContainsWatermark(element)) { // 尝试清除背景图片 if (element.style) { element.style.backgroundImage = 'none'; element.style.background = 'none'; } // 如果是 img 标签,隐藏或移除 if (element.tagName === 'IMG') { element.style.display = 'none'; element.style.visibility = 'hidden'; element.remove(); return; } } // 检查是否是文本水印 if (elementContainsWatermark(element)) { // 如果元素只包含水印文本,直接移除 const text = element.textContent || element.innerText || ''; if (WATERMARK_TEXT && (text.trim() === WATERMARK_TEXT || text.includes(WATERMARK_TEXT))) { element.style.display = 'none'; element.style.visibility = 'hidden'; element.remove(); return; } // 如果元素包含其他内容,尝试移除水印文本部分 // 转义特殊字符以避免正则表达式错误 const escapedWatermark = WATERMARK_TEXT.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const watermarkRegex = new RegExp(escapedWatermark, 'g'); if (element.textContent) { element.textContent = element.textContent.replace(watermarkRegex, ''); } if (element.innerText) { element.innerText = element.innerText.replace(watermarkRegex, ''); } if (element.innerHTML) { element.innerHTML = element.innerHTML.replace(watermarkRegex, ''); } // 如果移除文本后元素为空,则移除元素 if (!element.textContent || element.textContent.trim() === '') { element.style.display = 'none'; element.style.visibility = 'hidden'; element.remove(); } } } catch (e) { logger.warn('移除水印时出错:', e); } } // 检测并移除水印 function detectAndRemoveWatermarks(root = document.body) { if (!root || !WATERMARK_TEXT) return; // 限制检测范围,避免遍历整个 DOM 树 const maxDepth = 10; let depth = 0; function traverse(node) { if (depth > maxDepth || !node || processedElements.has(node)) return; depth++; try { // 检查当前节点 if (elementContainsWatermark(node) || imageContainsWatermark(node)) { removeWatermark(node); depth--; return; } // 遍历子节点 if (node.children) { for (let child of node.children) { traverse(child); } } // 检查文本节点 if (node.childNodes) { for (let child of node.childNodes) { if (child.nodeType === Node.TEXT_NODE) { if (containsWatermark(child.textContent)) { // 移除包含水印的文本节点 child.remove(); } } else if (child.nodeType === Node.ELEMENT_NODE) { traverse(child); } } } } catch (e) { logger.warn('检测水印时出错:', e); } depth--; } traverse(root); } // 防抖版本的检测函数 const debouncedDetect = debounce(detectAndRemoveWatermarks, 100); // 初始化检测 function init() { // 等待 DOM 加载完成 if (document.body) { detectAndRemoveWatermarks(); } else { // 如果 body 还未加载,等待 DOMContentLoaded if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { detectAndRemoveWatermarks(); }); } } } // 创建 MutationObserver 监听 DOM 变化 function setupObserver() { const observer = new MutationObserver((mutations) => { let shouldCheck = false; mutations.forEach((mutation) => { // 检查新增的节点 if (mutation.addedNodes && mutation.addedNodes.length > 0) { shouldCheck = true; } // 检查属性变化(可能影响样式或内容) if (mutation.type === 'attributes') { const attrName = mutation.attributeName; if (attrName === 'style' || attrName === 'src' || attrName === 'class') { shouldCheck = true; } } }); if (shouldCheck) { debouncedDetect(); } }); // 配置观察选项 const config = { childList: true, // 监听子节点的添加和删除 subtree: true, // 监听所有后代节点 attributes: true, // 监听属性变化 attributeFilter: ['style', 'src', 'class', 'background-image'] // 只监听特定属性 }; // 开始观察 if (document.body) { observer.observe(document.body, config); } else { // 等待 body 加载后再开始观察 const bodyObserver = new MutationObserver((mutations, obs) => { if (document.body) { observer.observe(document.body, config); obs.disconnect(); } }); bodyObserver.observe(document.documentElement, { childList: true }); } } // Canvas 水印检测(拦截 Canvas 绘制操作) function interceptCanvas() { // 拦截 CanvasRenderingContext2D 的绘制方法 const originalFillText = CanvasRenderingContext2D.prototype.fillText; const originalStrokeText = CanvasRenderingContext2D.prototype.strokeText; const originalDrawImage = CanvasRenderingContext2D.prototype.drawImage; CanvasRenderingContext2D.prototype.fillText = function(...args) { const text = args[0]; if (text && containsWatermark(String(text))) { return; // 不绘制包含水印的文本 } return originalFillText.apply(this, args); }; CanvasRenderingContext2D.prototype.strokeText = function(...args) { const text = args[0]; if (text && containsWatermark(String(text))) { return; // 不绘制包含水印的文本 } return originalStrokeText.apply(this, args); }; CanvasRenderingContext2D.prototype.drawImage = function(...args) { // 检查图片源是否包含水印 const image = args[0]; if (image && image.src && containsWatermark(image.src)) { return; // 不绘制包含水印的图片 } return originalDrawImage.apply(this, args); }; } // 拦截 CSS 样式(检查伪元素) function interceptStyles() { // 拦截 styleSheet 的插入 const originalInsertRule = CSSStyleSheet.prototype.insertRule; CSSStyleSheet.prototype.insertRule = function(rule, index) { if (rule && WATERMARK_TEXT && containsWatermark(rule)) { return -1; // 不插入包含水印的规则 } return originalInsertRule.call(this, rule, index); }; // 拦截 style 元素的文本内容 const originalAppendChild = Node.prototype.appendChild; Node.prototype.appendChild = function(child) { if (child && child.tagName === 'STYLE' && WATERMARK_TEXT) { if (child.textContent && containsWatermark(child.textContent)) { // 移除水印内容 child.textContent = child.textContent.replace(new RegExp(WATERMARK_TEXT.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), ''); } } return originalAppendChild.call(this, child); }; } // 从 API 获取用户信息和水印内容 async function fetchWatermark() { try { logger.log('开始获取水印内容...'); const response = await fetch(USER_API_URL, { method: 'GET', headers: { 'accept': '*/*', 'accept-language': 'system', 'cache-control': 'no-cache', 'content-type': 'application/json', 'dnt': '1', 'pragma': 'no-cache', 'referer': 'https://aistudio.xiaomimimo.com/', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-origin', 'x-timezone': 'Asia/Shanghai' }, credentials: 'include' // 包含 cookies }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); logger.log('API 响应:', result); if (result.code === 0 && result.data && result.data.watermark) { WATERMARK_TEXT = result.data.watermark; logger.log('成功获取水印内容:', WATERMARK_TEXT); return true; } else { logger.warn('API 响应中未找到水印内容:', result); return false; } } catch (error) { logger.error('获取水印内容失败:', error); return false; } } // 从页面中检测水印(备选方案) function detectWatermarkFromPage() { if (WATERMARK_TEXT) return true; // 如果已经有水印,不需要检测 logger.log('尝试从页面中检测水印...'); // 检查常见的 base64 编码字符串模式(水印通常是 base64) const base64Pattern = /[A-Za-z0-9+/]{20,}={0,2}/g; const allText = document.body ? document.body.innerText : ''; const matches = allText.match(base64Pattern); if (matches && matches.length > 0) { // 查找最可能的水印(通常是较短的 base64 字符串) for (let match of matches) { // 检查是否是合理的水印长度(通常在 20-30 个字符左右) if (match.length >= 20 && match.length <= 50) { // 检查是否在页面的可见位置 const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_TEXT, null ); let node; while (node = walker.nextNode()) { if (node.textContent && node.textContent.includes(match)) { WATERMARK_TEXT = match; logger.log('从页面检测到水印:', WATERMARK_TEXT); return true; } } } } } return false; } // 带重试的获取水印函数 async function fetchWatermarkWithRetry(maxRetries = 5, delay = 1000) { for (let i = 0; i < maxRetries; i++) { logger.log(`尝试获取水印 (${i + 1}/${maxRetries})...`); const success = await fetchWatermark(); if (success && WATERMARK_TEXT) { return true; } // 如果不是最后一次尝试,等待后重试 if (i < maxRetries - 1) { logger.log(`等待 ${delay}ms 后重试...`); await new Promise(resolve => setTimeout(resolve, delay)); delay *= 1.5; // 指数退避 } } // 如果 API 获取失败,尝试从页面检测 logger.log('API 获取失败,尝试从页面检测水印...'); if (detectWatermarkFromPage()) { return true; } logger.error('无法获取水印内容,脚本将无法正常工作'); return false; } // 启动检测和移除功能 function startWatermarkRemoval() { if (!WATERMARK_TEXT) { logger.warn('水印内容为空,无法启动移除功能'); return false; } logger.log('启动水印移除功能,水印内容:', WATERMARK_TEXT); // 拦截 Canvas 和样式 interceptCanvas(); interceptStyles(); // 初始化检测 init(); // 设置观察器 setupObserver(); // 定期检测(作为备用方案) setInterval(() => { if (WATERMARK_TEXT) { debouncedDetect(); } }, 2000); return true; } // 主函数 async function main() { logger.log('脚本开始运行...'); // 首先尝试获取水印内容(带重试) const watermarkFetched = await fetchWatermarkWithRetry(5, 1000); // 如果成功获取水印,启动移除功能 if (watermarkFetched && WATERMARK_TEXT) { startWatermarkRemoval(); } else { // 如果仍然失败,等待页面完全加载后再试一次 logger.log('等待页面完全加载后重试...'); window.addEventListener('load', async () => { await new Promise(resolve => setTimeout(resolve, 2000)); // 再等待 2 秒 const retrySuccess = await fetchWatermarkWithRetry(3, 2000); if (retrySuccess && WATERMARK_TEXT) { startWatermarkRemoval(); } else { // 最后尝试从页面检测 if (detectWatermarkFromPage()) { startWatermarkRemoval(); } } }); } } // 运行主函数 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } })();