hover tag, show translation
// ==UserScript==
// @name ehentai tag translator
// @namespace Violentmonkey Scripts
// @match https://exhentai.org/*
// @exclude-match https://exhentai.org/s/*
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @version 1.1
// @author -
// @description hover tag, show translation
// ==/UserScript==
/**
* find node
* if have node, define translation, css
* add translation div
*/
const $ = s => document.querySelector(s);
const $$ = s => document.querySelectorAll(s);
main();
function fetch2(detail) {
return new Promise((resolve, reject) => {
detail.onload = resolve;
detail.onerror = reject;
GM_xmlhttpRequest(detail);
});
}
async function updateDB(db) {
console.info('[ehentai_tag_translator] updating translation database.');
const DBversion = 6;
const dbUrl = 'https://github.com/EhTagTranslation/Database/releases/latest/download/db.text.json';
const remoteDB = await fetch2({ url: dbUrl, method: 'GET' }).then(rsp => JSON.parse(rsp.responseText)).catch(err => alert('failed to download tag database from github'));
if (remoteDB.version !== DBversion) {
// database changelog: https://github.com/EhTagTranslation/Database/wiki/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%BB%93%E6%9E%84%E7%89%88%E6%9C%AC%E4%BF%A1%E6%81%AF
alert('EhTag Database version changed, please update script');
return;
}
if (remoteDB.head.sha === db.sha) {
return;
}
db.sha = remoteDB.head.sha;
db.data = {};
for (let data of remoteDB.data) {
const key = data.namespace;
db.data[key] = {};
for (let subKey in data.data) {
// needs name translation only
const value = data.data[subKey]['name'];
if (subKey !== value) {
// ignore invalid translation
db.data[key][subKey] = value;
}
}
}
console.info('[ehentai_tag_translator] translation database updated.');
return db;
}
async function loadDB() {
let db = GM_getValue('db', {});
// update database after 7 days
const expiration = 7 * 24 * 60 * 60 * 1000;
if (db.lastUpdate === undefined) {
db.lastUpdate = 0; // needs update
}
const expired = Date.now() - db.lastUpdate > expiration ? true : false;
if (expired) {
db = await updateDB(db);
db.lastUpdate = Date.now();
GM_setValue('db', db);
}
// console.log(db);
return db;
}
function translateTagEl(el, translation) {
const translationEl = document.createElement('div');
translationEl.classList.add('eh-tag-translation');
translationEl.innerText = translation;
el.append(translationEl);
}
async function main() {
let tagEls = $$('div[class^="gt"]');
if (tagEls.length > 0) {
// load db only if needs translation
const db = await loadDB();
const css = `
.eh-tag-translation{
display:none;
position: absolute;
top: 20px;
left: 0;
z-index: 999;
background: #5f636b;
padding: 5px;
width: max-content;
max-width: 300px;
border-radius: 2px;
box-shadow: 1px 1px 2px 0 #0005;
transition-duration: 1000ms;
}
/*show translation in list page*/
.gl3c > a > div{
overflow: inherit!important;
}
div.gt,
div.gtl{
opacity: 1!important;
}
div.gt:hover .eh-tag-translation,
div.gtl:hover .eh-tag-translation{
display: block;
}
.eh-tag-translation::before {
border: 5px solid transparent;
border-bottom-color: transparent;
border-bottom-color: #5f636b;
content: '';
top: -10px;
width: 0;
height: 0;
position: absolute;
}
`;
GM_addStyle(css);
for (let el of tagEls) {
let arr;
if (el.id === 'ehv-panel-btn') {
continue;
} else if (el.id !== '') {
arr = el.id.replace('td_', '').split(':');
} else if (el.title !== '') {
arr = el.title.split(':');
} else {
console.log('tag not found in element', el);
continue;
}
if (arr.length === 2) {
const key = arr[0];
const subKey = arr[1].replace('_', ' ');
if(db.data[key]){
const translation = db.data[key][subKey];
if (translation) {
translateTagEl(el, translation);
} else {
console.log('[ehentai_tag_translator] translation not found:', { key, subKey });
}
}else{
console.log('[ehentai_tag_translator] unknown tag type: ', key);
}
} else {
console.log('[ehentai_tag_translator] unknown attribute:', arr);
}
}
}
}