tagDuplicator

Duplicate tags for translated galleries from original galleries

// ==UserScript==
// @name        tagDuplicator
// @namespace   tagDuplicator
// @description   Duplicate tags for translated galleries from original galleries
// @include     https://exhentai.org/g/*
// @include     https://e-hentai.org/g/*
// @version     alpha
// @grant       none
// @run-at      document-end
// ==/UserScript==
/**
 * Created by atashiyuki on 2017/2/27.
 */

var exclude_namespaces=[
    "language",
    "reclass",
];

var prompt_map={
    "zh-CN":"请输入要导入tag的画廊地址",
    "en-US":"please input the link of the gallery you want to import tags from",
    "default":"please input the link of the gallery you want to import tags from",
};

var confirm_map={
    "zh-CN":"两个画廊看起来并不像同一个作品的说...即使如此仍然想要导入tag吗?",
    "en-US":"the two galleries are not likely to be the same piece, still want to import tags?",
    "default":"the two galleries are not likely to be the same piece, still want to import tags?",
}

var wrong_url_map={
    "zh-CN":"请输入从\"https\"开始的完整的画廊链接",
    "en-US":"please input the complete gallery link, from \"https\"",
    "default":"please input the complete gallery link, from \"https\"",
}

function get_text_in_local_language(map){
    var user_language = navigator.language || navigator.userLanguage;
    var text=map[user_language];
    if(text==undefined)
        text=map.default;
    return text;
}

function get_source_sync(url){
    var req=new XMLHttpRequest();
    req.open('GET',url,false);
    req.send();
    return req.response;
}

function get_source_async(url,call_back){
    var req=new XMLHttpRequest();
    req.open('GET',url,true);
    req.onreadystatechange=function(){
        if(req.readyState === XMLHttpRequest.DONE && req.status === 200)
            call_back(req.response);
    };
    req.send();
}

function check_gallery(current_source, target_source) {
    var regexp="<h1 id=\"gj\">(.+?)\\[(.+?)\\](.+?)\\[.+?</h1>";
    var result_current=current_source.match(regexp);
    var result_target=target_source.match(regexp);
    var diff=0;

    if(result_current==result_target)
        return false;
    for(let i=1;i<4;++i){
        if((""+result_current[i]).trim()!=(""+result_target[i]).trim())
            diff+=1;
    }

    return diff<=1;
}

function parse_tags(source_text) {
    var ret={};
    var regexp=/return toggle_tagmenu\('(.+?)',/g;
    var result;
    while(result=regexp.exec(source_text)){
        var namespace_tag=result[1].split(':');
        if(namespace_tag.length==1){
            namespace_tag=["misc",namespace_tag[0]];
        }
        // do not add excluded namespaces
        if(exclude_namespaces.includes(namespace_tag[0]))
            continue;

        if(ret[namespace_tag[0]]==undefined)
            ret[namespace_tag[0]]=[];
        ret[namespace_tag[0]].push(namespace_tag[1]);
    }
    return ret;
}

function fill_tag_field(tags){
    var field=document.getElementById('newtagfield');
    var text="";
    for(let namespace in tags){
        for(let tag of tags[namespace]){
            text+=namespace+':'+tag+',';
        }
    }
    field.value=text;

    if(text.length==0)
        field.placeholder="no tags to add...";
}

// this tool is to add tag to a translated gallery, not to farm tagging MP
// please do not abuse this
function subtract_tags(current_tags, tags_to_add){
    var ret={};
    for(let namespace in tags_to_add){
        if(current_tags[namespace]==undefined){
            ret[namespace]=tags_to_add[namespace];
            continue;
        }
        for(let tag of tags_to_add[namespace]){
            if(current_tags[namespace].includes(tag))
                continue;
            if(ret[namespace]==undefined)
                ret[namespace]=[];
            ret[namespace].push(tag);
        }
    }
    return ret;
}

function make_callbacks(parse,subtract,fill) {
    var current_finished_getting=false;
    var current_source="";
    var target_finished_getting=false;
    var target_source="";

    var action=function() {
        if(current_finished_getting&&target_finished_getting){
            if(!check_gallery(current_source,target_source)){
                var confirm_text=get_text_in_local_language(confirm_map);
                var keep=confirm(confirm_text);
                if(!keep)
                    return;
            }
            var tags_current=parse(current_source);
            var tags_target=parse(target_source);
            var tags_to_add=subtract(tags_current,tags_target);
            fill(tags_to_add);
        }
    }

    return {
        current_callback:function(text) {
            current_finished_getting=true;
            current_source=text;
            action();
        },
        target_callback:function(text) {
            target_finished_getting=true;
            target_source=text;
            action();
        }
    }
}

function start() {
    var prompt_text=get_text_in_local_language(prompt_map);

    var url=prompt(prompt_text);
    if(url.substr(0,23)!=location.href.substr(0,23))
        alert(get_text_in_local_language(wrong_url_map));

    var callbacks=make_callbacks(parse_tags,subtract_tags,fill_tag_field);
    get_source_async(window.location.href,callbacks.current_callback);
    get_source_async(url,callbacks.target_callback);
}

function init() {
    var div=document.getElementById("tagmenu_new");
    var btn=document.createElement('input'); // use input rather than button to inherit some style
    btn.type="submit";
    btn.name="submit";
    btn.value="duplicate tags from another gallery";
    btn.style.position="relative";
    btn.style.bottom="55px";
    btn.style.margin="0px";
    btn.style.cursor="pointer";
    btn.onclick=start;
    div.appendChild(btn);
}


init();