Greasy Fork

Greasy Fork is available in English.

Xiaomi MiMo Studio 去水印

自动检测并移除 Xiaomi MiMo Studio 页面中的水印内容(动态获取水印)

当前为 2025-12-17 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Xiaomi MiMo Studio 去水印
// @namespace    http://tampermonkey.net/
// @version      1.2.0
// @description  自动检测并移除 Xiaomi MiMo Studio 页面中的水印内容(动态获取水印)
// @author       AlanWang
// @license      MIT
// @homepageURL  https://github.com/wang93wei/Xiaomi-MiMo-Studio-Watermark-Remover
// @supportURL   https://github.com/wang93wei/Xiaomi-MiMo-Studio-Watermark-Remover/issues
// @match        https://aistudio.xiaomimimo.com/*
// @match        https://aistudio.xiaomimimo.com/#/*
// @grant        none
// @run-at       document-start
// ==/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();
    }

})();