Touhou.AI | 图片翻译器

(WIP) https://touhou.ai/imgtrans/ 的用户脚本版本,一键翻译 Pixiv 的图片

当前为 2021-12-26 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Touhou.AI | Manga Translator
// @name:zh-CN   Touhou.AI | 图片翻译器
// @namespace    https://github.com/VoileLabs/imgtrans-userscript
// @version      0.2.1
// @description  (WIP) Userscript for https://touhou.ai/imgtrans/, translate images on Pixiv.
// @description:zh-CN (WIP) https://touhou.ai/imgtrans/ 的用户脚本版本,一键翻译 Pixiv 的图片
// @author       QiroNT
// @license      MIT
// @supportURL   https://github.com/VoileLabs/imgtrans-userscript
// @include      http*://www.pixiv.net*
// @match        http://www.pixiv.net/
// @connect      i.pximg.net
// @connect      i-f.pximg.net
// @connect      i-cf.pximg.net
// @connect      touhou.ai
// @grant        GM.xmlHttpRequest
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// ==/UserScript==

'use strict';

async function submitTranslate(blob, suffix) {
    const formData = new FormData();
    formData.append('file', blob, 'image.' + suffix);
    const result = await GM.xmlHttpRequest({
        method: 'POST',
        url: 'https://touhou.ai/imgtrans/submit',
        // @ts-expect-error FormData is supported
        data: formData,
    });
    const json = JSON.parse(result.responseText);
    const id = json.task_id;
    return id;
}
async function getTranslateStatus(id) {
    const result = await GM.xmlHttpRequest({
        method: 'GET',
        url: 'https://touhou.ai/imgtrans/task-state?taskid=' + id,
    });
    const data = JSON.parse(result.responseText);
    return {
        state: data.state,
        waiting: (data.waiting || 0),
    };
}
function getStatusText(status) {
    switch (status.state) {
        case 'pending':
            if (status.waiting > 0) {
                return `正在等待,你的队列位置${status.waiting}`;
            }
            else {
                return `正在处理`;
            }
        case 'detection':
            return '正在检测文本';
        case 'ocr':
            return '正在识别文本';
        case 'mask_generation':
            return '正在生成文本掩码';
        case 'inpainting':
            return '正在修补图片';
        case 'translating':
            return '正在翻译';
        case 'render':
            return '正在渲染';
        case 'error':
            return '翻译出错';
        case 'error-lang':
            return '不支持的语言';
        default:
            return '未知状态';
    }
}
async function pullTransStatusUntilFinish(id, cb) {
    for (;;) {
        const timer = new Promise((resolve) => setTimeout(resolve, 500));
        const status = await getTranslateStatus(id);
        if (status.state === 'finished') {
            return;
        }
        else if (status.state === 'error') {
            throw new Error('翻译出错');
        }
        else if (status.state === 'error-lang') {
            throw new Error('不支持的语言');
        }
        else {
            cb(status);
        }
        await timer;
    }
}

var pixiv = () => {
    const images = new Set();
    const instances = new Map();
    const translatedMap = new Map();
    const translateEnabledMap = new Map();
    function rescanImages() {
        const imageNodes = Array.from(document.querySelectorAll('img')).filter((node) => node.hasAttribute('srcset') ||
            node.hasAttribute('data-trans') ||
            node.parentElement?.classList.contains('sc-1pkrz0g-1'));
        const removedImages = new Set(images);
        for (const node of imageNodes) {
            removedImages.delete(node);
            if (!images.has(node)) {
                // new image
                console.log('new', node);
                try {
                    instances.set(node, mountToNode(node));
                    images.add(node);
                }
                catch (e) {
                    // ignore
                }
            }
        }
        for (const node of removedImages) {
            // removed image
            console.log('remove', node);
            if (instances.has(node)) {
                const instance = instances.get(node);
                instance.stop();
                instances.delete(node);
                images.delete(node);
            }
        }
    }
    function mountToNode(imageNode) {
        // get current displayed image
        const src = imageNode.getAttribute('src');
        const srcset = imageNode.getAttribute('srcset');
        // get original image
        const parent = imageNode.parentElement;
        if (!parent)
            throw new Error('no parent');
        const originalSrc = parent.getAttribute('href') || src;
        const originalSrcSuffix = originalSrc.split('.').pop();
        // console.log(src, originalSrc)
        let originalImage;
        let translatedImage = translatedMap.get(originalSrc);
        let translateMounted = false;
        let buttonDisabled = false;
        async function getTranslatedImage() {
            if (translatedImage)
                return translatedImage;
            buttonDisabled = true;
            const text = button.innerText;
            button.innerText = '正在拉取原图';
            if (!originalImage) {
                // fetch original image
                const result = await GM.xmlHttpRequest({
                    method: 'GET',
                    responseType: 'blob',
                    url: originalSrc,
                    headers: { referer: 'https://www.pixiv.net/' },
                    overrideMimeType: 'text/plain; charset=x-user-defined',
                }).catch((e) => {
                    button.innerText = '拉取原图出错';
                    throw e;
                });
                originalImage = result.response;
            }
            button.innerText = '正在提交翻译';
            const id = await submitTranslate(originalImage, originalSrcSuffix).catch((e) => {
                button.innerText = '提交翻译出错';
                throw e;
            });
            button.innerText = '正在等待';
            await pullTransStatusUntilFinish(id, (status) => {
                const text = getStatusText(status);
                button.innerText = text;
            }).catch((e) => {
                button.innerText = String(e);
                throw e;
            });
            button.innerText = '正在下载图片';
            const image = await GM.xmlHttpRequest({
                method: 'GET',
                responseType: 'blob',
                url: 'https://touhou.ai/imgtrans/result/' + id + '/final.jpg',
            }).catch((e) => {
                button.innerText = '下载图片出错';
                throw e;
            });
            const imageUri = URL.createObjectURL(image.response);
            translatedImage = imageUri;
            translatedMap.set(originalSrc, translatedImage);
            button.innerText = text;
            buttonDisabled = false;
            return imageUri;
        }
        async function enable() {
            translateMounted = true;
            try {
                const translated = await getTranslatedImage();
                imageNode.setAttribute('data-trans', src);
                imageNode.setAttribute('src', translated);
                imageNode.removeAttribute('srcset');
                button.innerText = '还原';
            }
            catch (e) {
                buttonDisabled = false;
                translateMounted = false;
                throw e;
            }
        }
        function disable() {
            translateMounted = false;
            imageNode.setAttribute('src', src);
            if (srcset)
                imageNode.setAttribute('srcset', srcset);
            imageNode.removeAttribute('data-trans');
            button.innerText = '翻译';
        }
        // called on click
        function toggle() {
            if (buttonDisabled)
                return;
            if (!translateMounted) {
                translateEnabledMap.set(originalSrc, true);
                enable();
            }
            else {
                translateEnabledMap.delete(originalSrc);
                disable();
            }
        }
        // create a translate botton
        parent.style.position = 'relative';
        const container = document.createElement('div');
        container.style.position = 'absolute';
        container.style.zIndex = '1';
        container.style.bottom = '10px';
        container.style.right = '10px';
        const button = document.createElement('button');
        button.setAttribute('type', 'button');
        button.innerText = '翻译';
        button.style.fontSize = '1rem';
        button.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            toggle();
        });
        container.appendChild(button);
        parent.appendChild(container);
        // enable if enabled
        if (translateEnabledMap.get(originalSrc))
            enable();
        return {
            imageNode,
            stop: () => {
                parent.removeChild(container);
                if (translateMounted)
                    disable();
            },
            async enable() {
                translateEnabledMap.set(originalSrc, true);
                return await enable();
            },
            disable() {
                translateEnabledMap.delete(originalSrc);
                return disable();
            },
            isEnabled() {
                return translateMounted;
            },
        };
    }
    // translate all
    let removeTransAll;
    function refreshTransAll() {
        if (document.querySelector('.sc-emr523-2'))
            return;
        const bookmark = document.querySelector('.gtm-main-bookmark');
        if (bookmark) {
            const container = bookmark.parentElement.parentElement;
            if (container.querySelector('[data-transall]'))
                return;
            const el = document.createElement('div');
            el.innerText = '翻译全部';
            el.setAttribute('data-transall', 'true');
            el.style.display = 'inline-block';
            el.style.marginRight = '13px';
            el.style.padding = '0';
            el.style.color = 'inherit';
            el.style.height = '32px';
            el.style.lineHeight = '32px';
            el.style.cursor = 'pointer';
            el.style.fontWeight = '700';
            const transall = (e) => {
                e.preventDefault();
                e.stopPropagation();
                let finished = 0;
                const total = instances.size;
                el.innerText = `翻译中(0/${total})`;
                let erred = false;
                const inc = () => {
                    finished++;
                    if (finished === total) {
                        if (erred)
                            el.innerText = '翻译完成';
                        else
                            el.innerText = '翻译完成(有失败)';
                        el.removeEventListener('click', transall);
                    }
                    else {
                        el.innerText = `翻译中(${finished}/${total})`;
                    }
                };
                const err = () => {
                    erred = true;
                    inc();
                };
                for (const instance of instances.values()) {
                    if (instance.isEnabled())
                        inc();
                    else
                        instance.enable().then(inc).catch(err);
                }
            };
            el.addEventListener('click', transall);
            container.appendChild(el);
            removeTransAll = () => {
                container.removeChild(el);
            };
        }
    }
    const imageObserver = new MutationObserver((mutations) => {
        rescanImages();
        refreshTransAll();
    });
    imageObserver.observe(document.body, { childList: true, subtree: true });
    // unmount
    return () => {
        instances.forEach((instance) => instance.stop());
        removeTransAll?.();
    };
};

let currentURL;
let stopTranslator;
const installObserver = new MutationObserver(() => {
    if (currentURL !== location.href) {
        currentURL = location.href;
        // there is a navigation in the page
        /* unmount previous translator */
        if (stopTranslator)
            stopTranslator();
        /* mount new translator */
        // check if the page is a image page
        const url = new URL(location.href);
        // https://www.pixiv.net/(en/)artworks/<id>
        if (url.hostname.endsWith('pixiv.net') && url.pathname.match(/\/artworks\//)) {
            stopTranslator = pixiv();
        }
    }
});
installObserver.observe(document.body, { childList: true, subtree: true });
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1ndHJhbnMtdXNlcnNjcmlwdC51c2VyLmpzIiwic291cmNlcyI6WyIuLi9zcmMvY29yZS50cyIsIi4uL3NyYy9waXhpdi9pbmRleC50cyIsIi4uL3NyYy9tYWluLnRzIl0sInNvdXJjZXNDb250ZW50IjpbbnVsbCxudWxsLG51bGxdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFPLGVBQWUsZUFBZSxDQUFDLElBQVUsRUFBRSxNQUFjO0lBQzlELE1BQU0sUUFBUSxHQUFHLElBQUksUUFBUSxFQUFFLENBQUE7SUFDL0IsUUFBUSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFLFFBQVEsR0FBRyxNQUFNLENBQUMsQ0FBQTtJQUVoRCxNQUFNLE1BQU0sR0FBRyxNQUFNLEVBQUUsQ0FBQyxjQUFjLENBQUM7UUFDckMsTUFBTSxFQUFFLE1BQU07UUFDZCxHQUFHLEVBQUUsbUNBQW1DOztRQUV4QyxJQUFJLEVBQUUsUUFBUTtLQUNmLENBQUMsQ0FBQTtJQUVGLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFBO0lBQzVDLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQyxPQUFpQixDQUFBO0lBQ2pDLE9BQU8sRUFBRSxDQUFBO0FBQ1gsQ0FBQztBQU9NLGVBQWUsa0JBQWtCLENBQUMsRUFBVTtJQUNqRCxNQUFNLE1BQU0sR0FBRyxNQUFNLEVBQUUsQ0FBQyxjQUFjLENBQUM7UUFDckMsTUFBTSxFQUFFLEtBQUs7UUFDYixHQUFHLEVBQUUsK0NBQStDLEdBQUcsRUFBRTtLQUMxRCxDQUFDLENBQUE7SUFDRixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQTtJQUM1QyxPQUFPO1FBQ0wsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFlO1FBQzNCLE9BQU8sR0FBRyxJQUFJLENBQUMsT0FBTyxJQUFJLENBQUMs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