// ==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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