nihongo_dict

毎日日本語を勉強しましょう

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name         nihongo_dict
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  毎日日本語を勉強しましょう
// @author       Ch4p1
// @match        *
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tv-asahi.co.jp
// @grant           GM_xmlhttpRequest
// @grant           GM.xmlHttpRequest
// @grant           GM_getResourceText
// @grant      GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @resource IMPORTED_CSS https://li1862-245.members.linode.com/audio/audio/tampermonkey.css
// @require      https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js
// ==/UserScript==
(function () {
    'use strict';
    const my_css = GM_getResourceText("IMPORTED_CSS");
    GM_addStyle(my_css);
    var player;
    var t = 0;
    var speed = 1;
    var touchL = false;
    var loopT = [];
    //next page
    const splitArr = window.location.href.split(/[\/,.=?]+/);
    var lastNumber = splitArr.findLast(v => parseInt(v) > 0);
    const selInd = window.location.href.lastIndexOf(lastNumber);
    //init ui
    let menu = document.createElement("div");
    menu.classList = "menu";
    let menuBtns = document.createElement("div");
    menuBtns.classList = "btns";
    menu.appendChild(menuBtns);
    createBtn('ㄆ', () => { oepnHelp() });
    createBtn('X', (e) => { e.target.parentElement.remove() });
    document.body.appendChild(menu);

    //get serifu
    var gotSerifu = false;
    //get serifu for kktv
    var serifuArr = {};
    if (window.location.hostname == "kktv.me" || window.location.hostname == "www.kktv.me") {
        XMLHttpRequest.prototype.realSend = XMLHttpRequest.prototype.send;
        XMLHttpRequest.prototype.send = function (value) {
            this.addEventListener("progress", function (e) {
                //zh-Hant.vtt ja.vtt
                var filename = e.target.responseURL.split('/').pop()
                if (filename == "zh-Hant.vtt" || filename == "ja.vtt") {
                    var matchArr = e.target.response.match(/\d+:\d+:\d+.\d+ --> \d+:\d+:\d+.\d+\n.+/gm, "");
                    matchArr.map(v => {
                        var tarr = v.split("-->");
                        var serifu = tarr[1].split("\n");
                        if (filename == "ja.vtt") {
                            if (!serifuArr[tarr[0].trim()]) serifuArr[tarr[0].trim()] = {}
                            serifuArr[tarr[0].trim()].endT = serifu[0].trim();
                            serifuArr[tarr[0].trim()].jp = serifu[1].trim();
                        }
                        else {
                            if (!serifuArr[tarr[0].trim()]) serifuArr[tarr[0].trim()] = {}
                            serifuArr[tarr[0].trim()].endT = serifu[0].trim();
                            serifuArr[tarr[0].trim()].tw = serifu[1].trim();
                        }
                    });
                    if (gotSerifu == false) {
                        tempAlert("字幕loading...", 2000);
                        createBtn('字幕', () => { copyKktv() });
                        gotSerifu = true;
                    }
                }

            }, false);
            this.realSend(value);
        };
    }
    /* function time2sec(s) {
        const sec = s.split(":").reduce((a, c, ind) => {
            var s = 1;
            if (ind == 0) s = 3600;
            else if (ind == 1) s = 60;
            return a + (parseFloat(c) * s)
        }, 0);
        return sec;
    }
    var responseTextArr = {};
    var temp_serifuArr = [];
    if (window.location.hostname == "www.netflix.com") {
        XMLHttpRequest.prototype.realSend = XMLHttpRequest.prototype.send;
        XMLHttpRequest.prototype.send = function (value) {
            this.addEventListener("progress", function (e) {
                //1 下載字幕 區分出中日
                try {
                    if ( e.target.responseText.substring(0, 6) == "WEBVTT") {
                        var lang = e.target.responseText.includes("c.japanese") ? "jp" : "tw";
                        console.log(">>>>",lang)
                        responseTextArr[lang] = e.target.responseText;
                        if (responseTextArr.jp && responseTextArr.tw) {
                            //process jp
                            var matchArr = responseTextArr.jp.match(/\d+:.+\n.+/gm, "");
                            matchArr.map(v => {
                                var teimArr = v.match(/\d+:\d+:\d+.\d+/gm, "");
                                var serifu = v.match(/(?<=bg_transparent>)(.+)(?=<\/c.bg)/gm);
                                if (serifu && serifu.length > 0)
                                    temp_serifuArr.push({ start: time2sec(teimArr[0]), end: time2sec(teimArr[1]), jp: serifu[0].trim(), tw: "" })
                                else
                                    console.log("~~~~~~~~~~", serifu, v);
                            });
                            console.log("~~~~")
                            //process tw
                            matchArr = responseTextArr.tw.match(/\d+:.+\n.+/gm, "");
                            matchArr.map(v => {
                                var teimArr = v.match(/\d+:\d+:\d+.\d+/gm, "");
                                var serifu = v.match(/(?<=bg_transparent>)(.+)(?=<\/c.bg)/gm);
                                if (serifu && serifu.length > 0) {
                                    var startSec = time2sec(teimArr[0]);
                                    var findObj = temp_serifuArr.find(vv => (startSec >= vv.start && startSec <= vv.end));
                                    if (findObj) {
                                        findObj.tw += serifu[0].trim();
                                    }
                                }
                                else {
                                    console.log("~~~~~~~~~~", serifu, v);
                                }
                            });
                            console.log("---------", temp_serifuArr);
                            tempAlert("字幕loading...", 2000);
                            createBtn('字幕', () => { copyKktv() });
                            gotSerifu = true;
                        }
                    }
                }
                catch (err) {
                    console.log(err);
                }
            }, false);
            this.realSend(value);
        };
    } */
    if (window.location.hostname == "ncode.syosetu.com") {
        var matchArr = document.querySelector(".novel_view").innerText.match(/.+\n/gm, "");

        if (matchArr.length > 0) {
            matchArr.map((v, k) => {
                serifuArr["f_" + k] = { jp: v.trim() };
            });
            tempAlert("字幕loading...", 2000);
            createBtn('字幕', () => { copyKktv() });
            gotSerifu = true;
        }
    }

    function getPlayer() {
        return document.querySelector("video") ? document.querySelector("video") : document.querySelector("audio");
    }

    setInterval(() => {
        if (!player) {
            player = getPlayer();
            if (player && (window.location.hostname == "kktv.me" || window.location.hostname == "www.kktv.me")) {
                const observer = new MutationObserver(function (mutations) {
                    const targetDiv = document.querySelector(".main-subtitle");
                    if (targetDiv) {
                        targetDiv.style.userSelect = "text";
                    }
                });
                const div = document.querySelector(".kktv-player");
                observer.observe(div, {
                    childList: true,
                    attributes: true,
                    characterData: true,
                });
            }
        }
        if (loopT.length == 2 && player.currentTime > loopT[1]) {
            player.currentTime = loopT[0];
            player.play();
        }
    }, 200);



    function createBtn(title, fn) {
        let btn = document.createElement("div");
        btn.classList = "menuBtn";
        btn.innerHTML = title;
        btn.onclick = fn
        menuBtns.appendChild(btn);
    }
    var hotkeyList = [
        { cmd: "mediaPlayStart", hotkey: "O", tip: "標記影片返回點" },
        { cmd: "mediaPlayEnd", hotkey: "P", tip: "按住回到O點並播放,放開停止" },
        { cmd: "mediaPause", hotkey: "SPACE", tip: "暫/播放" },
        { cmd: "mediaSetLoop", hotkey: "Q", tip: "設定循環播放範圍,連按2次清除" },
        { cmd: "mediaRepaet", hotkey: "‰", tip: "設定repeat" },
        { cmd: "nihongoHira", hotkey: "⁄", tip: "日文單字(若找的)顯示拼音" },
        { cmd: "nihongoDict", hotkey: "€", tip: "中日字典" },
        { cmd: "nihongoSakura", hotkey: "‹", tip: "日日字典sakura" },
        { cmd: "nihongoGoo", hotkey: "Í", tip: "日日字典goo" },
        { cmd: "nihongoSpeak", hotkey: "Å", tip: "日文發音" },
        { cmd: "enTranslate", hotkey: "Œ", tip: "英文字典google" },
        { cmd: "google", hotkey: "¸", tip: "google" },
        { cmd: "nextPage", hotkey: "˘", tip: "下一頁" },
        { cmd: "prevPage", hotkey: "¯", tip: "上一頁" },
        { cmd: "comm", hotkey: "-", tip: "↑↓速度←→前後" },
    ];
    hotkeyList.map(v => {
        const _key = GM_getValue(v.cmd);
        if (_key && _key != "")
            v.hotkey = _key;
    });

    function createInfo(obj) {
        let div = document.createElement("div");
        let hotkey = document.createElement("input");
        let tip = document.createElement("span");
        div.classList = "memo";
        if (obj.cmd != "comm") {
            hotkey.type = "text";
            hotkey.maxLength = "1";
            hotkey.value = obj.hotkey;
            hotkey.style = "width: 18px;";
            hotkey.onkeyup = (e) => {
                console.log("e.target.value", e.target.value, obj);
                obj.hotkey = e.target.value;
                GM_setValue(obj.cmd, obj.hotkey);
            };
            div.appendChild(hotkey);
        }
        tip.innerHTML = obj.tip;
        div.appendChild(tip);
        return div;
    }
    function tempAlert(msg, duration) {
        document.querySelectorAll(".tempAlert").forEach(el => {
            el.style.bottom = (parseInt(el.style.bottom) + 24) + "px";
        });
        var el = document.createElement("div");
        el.className = "tempAlert";
        el.setAttribute("style", "position:absolute;bottom:5px;left:1%;padding: 0 1rem;background-color:black;color: white;font-size: 20px;");
        el.innerHTML = msg;
        setTimeout(function () {
            el.parentNode.removeChild(el);
        }, duration);
        document.body.appendChild(el);
    }
    function copyKktv() {
        try {
            document.querySelector(".modalxx").remove();
        }
        catch (e) {
            /* var data = [];
            document.querySelectorAll(".kktv-player>div>:nth-child(2)>:nth-child(2)>button").forEach(button => {
                const divs = button.querySelectorAll("div");
                data.push({ id: button.id, jp: divs[0].innerText, tw: divs[1].innerText });
            }); */
            let headers = {
                "Content-Type": "application/json",
                "Accept": "application/json",
            }
            fetch('https://li1862-245.members.linode.com:3006/serifu', {
                method: "POST",
                headers: headers,
                body: JSON.stringify({ str: serifuArr })
            })
                .then((response) => {
                    return response.json();
                }).then((ret) => {
                    let canvas = document.createElement("div");
                    canvas.classList = "modalxx";
                    let img = document.createElement("img");
                    img.src = 'https://chart.googleapis.com/chart?chs=300x300&cht=qr&chl=https://li1862-245.members.linode.com/kktvreader/build/?key=' + ret.key + '&choe=UTF-8';
                    img.onclick = (e) => {
                        window.open(
                            'https://li1862-245.members.linode.com/kktvreader/build/?key=' + ret.key, "_blank");
                    }
                    img.target = "_blank";
                    canvas.appendChild(img);
                    menu.appendChild(canvas);
                }).catch((err) => {
                    console.log('錯誤:', err);
                });
        }
    }
    function oepnHelp() {
        try {
            document.querySelector(".modalxx").remove();
        }
        catch (e) {
            let canvas = document.createElement("div");
            canvas.classList = "modalxx";
            hotkeyList.map(v => {
                canvas.appendChild(createInfo(v));
            });
            menu.appendChild(canvas);
        }
    }
    function addLoop() {
        if (loopT.length === 1) {
            loopT.push(player.currentTime);
            tempAlert("add loop end", 2000);
        }
        else if (loopT.length === 0) {
            loopT.push(player.currentTime);
            tempAlert("add loop start", 2000);
        }
    }
    function isRepeatKey(event) {
        return (event.key == lastKey.key && (performance.now() - lastKey.t) < 1000)
    }
    function clearAllLoop() {
        loopT = [];
    }
    function padLeft(str, len) {
        str = '' + str;
        if (str.length >= len) {
            return str;
        } else {
            return padLeft("0" + str, len);
        }
    }
    var lastKey = { key: "", t: 0 };

    document.body.addEventListener("keyup", function (event) {
        const hotkeyObj = hotkeyList.find(v => { return v.hotkey.toLowerCase() == event.key.toLowerCase() });
        if (player) {
            if (hotkeyObj.cmd === "mediaPlayEnd") {//L roolback
                console.log("keyup mediaPlayEnd")
                touchL = false;
                player.pause();
            }
        }
    });

    document.body.addEventListener("keydown", function (event) {
        var selectTxt = "";
        const hotkeyObj = hotkeyList.find(v => { return v.hotkey.toLowerCase() == event.key.toLowerCase() });
        if (player) {
            if (hotkeyObj.cmd === "mediaPlayEnd" && touchL == false) {//L roolback
                console.log("mediaPlayStart")
                touchL = true;
                player.currentTime = t;
                player.play();
            }
            else if ((hotkeyObj.cmd === "mediaPlayStart")) {//k set point
                if (isRepeatKey(event)) {
                    t -= 0.5;
                    player.currentTime = t;
                    player.play();
                }
                else {
                    t = player.currentTime;
                }
            }
            else if (hotkeyObj.cmd === "mediaSetLoop") {
                if (isRepeatKey(event)) {
                    clearAllLoop();
                    tempAlert("clear loop", 2000);
                }
                else {
                    addLoop();
                }
            }
            else if (hotkeyObj.cmd === "mediaRepaet") {//L roolback
                player.loop = !player.loop;
                tempAlert("repeat " + player.loop, 2000);
            }
            else if (event.key === "ArrowRight") {//back
                player.currentTime += 1;
            }
            else if (event.key === "ArrowLeft") {//font
                player.currentTime -= 1;
            }
            else if (event.key === "ArrowUp") {//up speed up
                speed += 0.1;
                if (speed >= 1) {
                    speed = 1;
                }
                player.playbackRate = speed;
            }
            else if (event.key === "ArrowDown") {//down speed down
                speed -= 0.1;
                if (speed <= 0.1) {
                    speed = 0.1;
                }
                player.playbackRate = speed;
            }
        }
        if (hotkeyObj.cmd === "nihongoHira") {
            //日文拼音
            if (window.getSelection) {
                selectTxt = window.getSelection();
            } else if (window.document.getSelection) {
                selectTxt = window.document.getSelection();
            } else if (window.document.selection) {
                selectTxt = window.document.selection.createRange().text;
            }
            GM.xmlHttpRequest({
                method: "GET",
                url: "https://dict.asia/jc/" + selectTxt.toString(),
                headers: {
                    "User-Agent": "Mozilla/5.0",    // If not specified, navigator.userAgent will be used.
                    "Accept": "text/xml"            // If not specified, browser defaults will be used.
                },
                onload: function (ret) {
                    console.log(ret);
                    let parser = new DOMParser();
                    const document = parser.parseFromString(ret.responseText, "text/html");
                    var kijis = document.querySelectorAll("#jp_comment");
                    if (kijis != null && kijis.length > 0) {
                        const kiji = kijis[0];
                        var kana = kiji.querySelector(".trs_jp");
                        var kanaStr = "";
                        if (kana != null) {
                            kanaStr = kana.textContent.replaceAll(/【|】/g, '');
                            selectTxt.anchorNode.parentElement.innerHTML = selectTxt.anchorNode.parentElement.innerHTML.replaceAll(selectTxt.toString(), "<ruby>" + selectTxt.toString() + "<rt>" + kanaStr + "</rt></ruby>")
                        }
                    }
                    else {
                        tempAlert("can't find", 2000);
                    }
                }
            });
        }
        else if (hotkeyObj.cmd === "nihongoDict") {
            //日文字典 2
            if (window.getSelection) {
                selectTxt = window.getSelection();
            } else if (window.document.getSelection) {
                selectTxt = window.document.getSelection();
            } else if (window.document.selection) {
                selectTxt = window.document.selection.createRange().text;
            }
            window.open(
                "https://dict.asia/jc/" + selectTxt.toString(), "_blank");
        }
        else if (hotkeyObj.cmd === "nihongoSakura") {
            //日文字典 3
            if (window.getSelection) {
                selectTxt = window.getSelection();
            } else if (window.document.getSelection) {
                selectTxt = window.document.getSelection();
            } else if (window.document.selection) {
                selectTxt = window.document.selection.createRange().text;
            }
            window.open(
                "https://sakura-paris.org/dict/%E5%BA%83%E8%BE%9E%E8%8B%91/prefix/" + selectTxt.toString(), "_blank");
        }
        else if (hotkeyObj.cmd === "nihongoGoo") {
            //日文字典goo找前面一致 s
            if (window.getSelection) {
                selectTxt = window.getSelection();
            } else if (window.document.getSelection) {
                selectTxt = window.document.getSelection();
            } else if (window.document.selection) {
                selectTxt = window.document.selection.createRange().text;
            }
            window.open(
                "https://dictionary.goo.ne.jp/srch/jn/" + selectTxt.toString() + "/m0u/", "_blank");
        }
        else if (hotkeyObj.cmd === "nihongoSpeak") {
            //發音 a
            if (window.getSelection) {
                selectTxt = window.getSelection();
            } else if (window.document.getSelection) {
                selectTxt = window.document.getSelection();
            } else if (window.document.selection) {
                selectTxt = window.document.selection.createRange().text;
            }
            window.open(
                "https://ja.forvo.com/word/" + selectTxt.toString() + "/#ja", "_blank");
        }
        else if (hotkeyObj.cmd === "enTranslate") {
            //google translate q
            if (window.getSelection) {
                selectTxt = window.getSelection();
            } else if (window.document.getSelection) {
                selectTxt = window.document.getSelection();
            } else if (window.document.selection) {
                selectTxt = window.document.selection.createRange().text;
            }
            window.open(
                "https://translate.google.com.tw/?hl=zh-TW&tab=rT&sl=en&tl=zh-TW&text=" + selectTxt.toString() + "&op=translate", "_blank");
        }
        else if (hotkeyObj.cmd === "google") {
            //google Z
            if (window.getSelection) {
                selectTxt = window.getSelection();
            } else if (window.document.getSelection) {
                selectTxt = window.document.getSelection();
            } else if (window.document.selection) {
                selectTxt = window.document.selection.createRange().text;
            }
            window.open(
                "https://www.google.com/search?q=" + selectTxt.toString(), "_blank");
        }
        else if (hotkeyObj.cmd === "nextPage") {
            //next page >

            lastNumber = padLeft(Number(lastNumber) + 1, lastNumber.length);
            window.location.href = window.location.href.substring(0, selInd) + lastNumber + window.location.href.substring(selInd + lastNumber.length);
        }
        else if (hotkeyObj.cmd === "prevPage") {
            //prev page >
            lastNumber = padLeft(Number(lastNumber) - 1, lastNumber.length);
            window.location.href = window.location.href.substring(0, selInd) + lastNumber + window.location.href.substring(selInd + lastNumber.length);
        }
        lastKey = { key: event.key, t: performance.now() };
    });
})();