Get label from Fernsehserien.de

Offers Fernsehserien.de labels based on the episode number or title as Wikidata label

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Get label from Fernsehserien.de
// @version      0.7.5
// @description  Offers Fernsehserien.de labels based on the episode number or title as Wikidata label
// @author       CennoxX
// @namespace    https://greasyfork.org/users/21515
// @homepage     https://github.com/CennoxX/userscripts
// @supportURL   https://github.com/CennoxX/userscripts/issues/new?title=[Get%20label%20from%20Fernsehserien.de]%20
// @match        https://www.wikidata.org/wiki/*
// @connect      www.fernsehserien.de
// @icon         https://www.google.com/s2/favicons?sz=64&domain=www.wikidata.org
// @grant        GM.xmlHttpRequest
// @license      MIT
// ==/UserScript==
/* jshint esversion: 10 */
/* eslint quotes: ["warn", "double", {"avoidEscape": true}] */
/* eslint curly: "off" */
/* globals jQuery, $, mw */

(function(){
    "use strict";
    var wait = setInterval(()=>{
        if (typeof(mw) == "undefined"){
            return;
        }else{
            clearInterval(wait);
        }
        var correct = false;
        var fsid = "";
        var seriesId = "";
        var fsidNotSet = false;
        var itemId = mw.config.get("wbEntityId");
        if (!itemId){
            return;
        }
        function compareString(title){
            return title.trim().toLowerCase().normalize("NFD").replace(/\p{Diacritic}/gu, "").replace(/^(.*?)(?=[^\d]{2}.) ?[,:\-–]? \(?(?:(?:part|teil) )?(\d+)\)? *$/i,"$1$2").replace(/&/i, "and").replace(/^the |^a |[\u200B-\u200D\uFEFF]| |\.|'|’|\(|\)|:|,|‚|\?|!|„|“|"|‘|…|\.|—|–|-/gi,"");
        }
        async function checkTitle(ep, oldTitle, tryByNumber){
            var titles = [...ep.querySelectorAll("div:nth-child(7)>span")].map(i => i.innerText);
            var german = titles[0].replace(/^(.*?)(?=[^\d]{2}.) ?[,:\-–]? \(?(?:Teil )?(\d+)\)? *$/i,"$1 – Teil $2");
            var english = titles[1].replace(/^(.*?)(?=[^\d]{2}.) ?[,:\-–]? \(?(?:part )?(\d+)\)? *$/i,"$1, part $2");
            var deLabel = null;

            var insertElem = '<span class="wikibase-entitytermsview-aliases-alias"> ' +
                '<a class="wb-item-delabel" href="" title="Approve this label for German"></a> <span title="de" style="color:#72777d"></span></span>';
            var deLabelsDiv = $("div.wikibase-entitytermsview-labels");
            deLabelsDiv.append(insertElem);
            deLabelsDiv.find(".wb-item-delabel").click(submitDeLabel);
            return await new Promise(resolve => {
                var stopInterval = setInterval(()=>{
                    if (deLabel != null){
                        if ([...document.querySelectorAll(".wb-item-delabel")].filter(i => i.innerText==german).length==0){
                            var descr = deLabel.nextElementSibling;
                            descr.innerText = `(${english})`;
                            var titleA = compareString(oldTitle);
                            var titleB = compareString(english);
                            correct = titleA == titleB && german != "–";
                            if (!correct && !tryByNumber && german != "–" && (titleA==titleB.replace(/\d/g,"") || titleB==titleA.replace(/\d/g,""))){
                                german = german.split(" – Teil ")[0];
                                correct = true;
                            }
                            deLabel.innerText = german;
                            var color = "";
                            if (tryByNumber && correct)
                                color = "lightgreen";
                            else if (!tryByNumber && correct)
                                color = "lightyellow";
                            else
                                color = "lightpink";
                            descr.style.backgroundColor = color;
                        }
                        clearInterval(stopInterval);
                        resolve(correct);
                    }
                    var deLabels = document.querySelectorAll(".wb-item-delabel");
                    deLabel = deLabels[deLabels.length-1];
                },500);
            });
        }
        function getByTitle(ep, oldTitle, html, tryByTitle){
            var originalTitle = [...html.querySelectorAll(".episodenliste-schmal")].filter(i => {
                var titleA = compareString(i.innerText);
                var titleB = compareString(oldTitle);
                if (tryByTitle)
                    return titleA == titleB;
                else
                    return titleA.replace(/\d/g,"") == titleB.replace(/\d/g,"");
            });
            if (originalTitle.length>0)
                ep = originalTitle[0].closest("a");
            return ep;
        }
        function getByLevenshteinDistance(ep, oldTitle, html){
            var distMap = [...html.querySelectorAll(".episodenliste-schmal")].map(i => {
                var titleA = compareString(i.innerText);
                var titleB = compareString(oldTitle);
                var dist = levenshteinDistance(titleA,titleB);
                return({ep:i,dist});
            });
            var minDist = distMap.reduce(function(prev, curr){
                return prev.dist < curr.dist ? prev : curr;
            });
            console.log("levenshtein distance:",minDist.dist);
            return minDist.ep.closest("a");
        }
        function levenshteinDistance(str1, str2){
            var track = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));
            for (let i = 0; i <= str1.length; i += 1){
                track[0][i] = i;
            }
            for (let j = 0; j <= str2.length; j += 1){
                track[j][0] = j;
            }
            for (let j = 1; j <= str2.length; j += 1){
                for (let i = 1; i <= str1.length; i += 1){
                    var indicator = str1[i - 1] === str2[j - 1] ? 0 : 1;
                    track[j][i] = Math.min(
                        track[j][i - 1] + 1,
                        track[j - 1][i] + 1,
                        track[j - 1][i - 1] + indicator,
                    );
                }
            }
            return track[str2.length][str1.length];
        }
        var mainLoop = setInterval(async()=>{
            if (typeof $ != "undefined"){
                if (document.querySelector(".wikibase-entitytermsforlanguageview-de .wb-empty.wikibase-labelview")){
                    var oldTitle = document.querySelector(".wikibase-title-label span")?.innerText;
                    if (oldTitle == null){
                        if (document.querySelector(".wb-empty .wikibase-title-label") == null){
                            oldTitle = document.querySelector(".wikibase-title-label")?.innerText;
                        } else {
                            oldTitle = document.querySelector('[data-property-id="P1476"] .wikibase-snakview-value span')?.innerText ?? "";
                        }
                    }
                    if (oldTitle != null){
                        clearInterval(mainLoop);
                        var season = document.querySelector('[data-property-id="P4908"] .wikibase-snakview-value')?.innerText.split("/Staffel ").pop();
                        var episode = document.querySelector('[data-property-id="P4908"] [href="/wiki/Property:P1545"]')?.closest(".wikibase-snakview").querySelector(".wikibase-snakview-value").innerText;
                        var episodeNumber = "";
                        if (episode != null)
                            episodeNumber = season+"x"+(episode[1]?"":"0")+episode;
                        var series = document.querySelector('[data-property-id="P179"] .wikibase-snakview-value a');
                        seriesId = series.href.split("/")[4];
                        var response = await fetch(`https://www.wikidata.org/w/api.php?action=wbgetentities&format=json&props=claims&ids=${seriesId}`);
                        var data = await response.json();
                        fsid = data.entities[seriesId].claims.P5327?.[0].mainsnak.datavalue.value;
                        fsidNotSet = fsid == null;
                        if (fsidNotSet){
                            fsid = series.innerText.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().replace(/&/g,"and").replace(/[^a-z_\d ]/g,"").replace(/ +/g,"-");
                        }
                        var epGuide = `https://www.fernsehserien.de/${fsid}/episodenguide`;
                        var result = await GM.xmlHttpRequest({
                            method: "GET",
                            url: epGuide,
                            onload: function(response){
                                return response;
                            }
                        });
                        var html = document.createElement("div");
                        html.innerHTML = result.responseText;
                        fsid = html.querySelector('meta[property="og:url"]').content.split("/")[3];
                        var ep = null;
                        if (html.querySelector(".fs-fehlermeldung")?.innerText.includes("Fehler 404")){
                            epGuide = "https://www.fernsehserien.de/suche/" + series.innerText;
                        }
                        var deLabelsParent = $("#wb-item-" + itemId + " div.wikibase-entitytermsview-heading");
                        var deLabelsDOM = $('<div class="wikibase-entitytermsview-heading-labels">Bezeichnungen von <a id="fsLink" href="'+epGuide+'">Fernsehserien.de</a>:</div>');
                        var deLabelsDiv = $('<div class="wikibase-entitytermsview-labels"></div>');
                        deLabelsDOM.append(deLabelsDiv);
                        deLabelsParent.prepend(deLabelsDOM);
                        if (episode){
                            console.log("try by episode number");
                            ep = html.querySelector(`a[href^='/${fsid}/folgen/${episodeNumber}']`);
                            if (ep)
                            {
                                correct = await checkTitle(ep, oldTitle, true);
                            }
                        }
                        if (!correct){
                            console.log("try by title, with matching numbers");
                            ep = getByTitle(ep, oldTitle, html, true);
                            if (ep)
                            {
                                correct = await checkTitle(ep, oldTitle, false);
                            }
                        }
                        if (!correct){
                            console.log("try by title, without matching numbers");
                            ep = getByTitle(ep, oldTitle, html, false);
                            if (ep){
                                correct = await checkTitle(ep, oldTitle, false);
                            }
                        }
                        if (!correct){
                            console.log("get episode by levenshtein distance of title");
                            ep = getByLevenshteinDistance(ep, oldTitle, html);
                            if (ep){
                                correct = await checkTitle(ep, oldTitle, false);
                            }
                        }
                    }
                }
            }
        },100);

        function submitDeLabel(ev){
            ev.preventDefault();
            var selectedLabel = $(ev.target).text();
            console.log("selected de label: " + selectedLabel);

            var labels = {};
            labels.de = {
                "language": "de",
                "value": selectedLabel
            };
            setItem(itemId, JSON.stringify({
                "labels": labels,
            }), "set de label from Fernsehserien.de: " + selectedLabel );

            if (fsidNotSet){
                (async ()=>{
                    var response = await fetch(`https://www.wikidata.org/w/api.php?action=wbgetentities&format=json&props=claims&ids=${seriesId}`);
                    var data = await response.json();
                    var fsidCheck = data.entities[seriesId].claims.P5327?.[0].mainsnak.datavalue.value;
                    if (fsidCheck == null){
                        setItem(seriesId, JSON.stringify({"claims":[{"mainsnak":{"snaktype":"value","property":"P5327","datavalue":{"value":fsid,"type":"string"}},"type":"statement","rank":"normal"}]}), "set ID from Fernsehserien.de: " + fsid);
                    }
                })();
            }
        }

        function setItem(itemId, data, summary){
            $.ajax({
                type: "POST",
                url: mw.util.wikiScript("api"),
                data: {
                    format: "json",
                    action: "wbeditentity",
                    id: itemId,
                    type: "item",
                    token: mw.user.tokens.get("csrfToken"),
                    data: data,
                    summary: summary,
                    exclude: "pageid|ns|title|lastrevid|touched|sitelinks"
                }
            })
                .done(function (data){
                if (data.hasOwnProperty("error")){
                    mw.notify("API Error" + JSON.stringify(data), {title: "Bezeichnung hinzufügen", tag: "fs"});
                    $("#green-box").empty();
                    $("#red-box").empty();
                    $("#red-box").append(data.error.info.replace(/\n/g, " "));
                } else {
                    $("#green-box").empty();
                    $("#green-box").append(summary);
                    mw.notify("Änderung speichern …", {title: "Bezeichnung hinzufügen", tag: "fs"});
                    window.location.reload(true);
                }
            })
                .fail(function (){
                mw.notify("API Error", {title: "Bezeichnung hinzufügen", tag: "fs"});
                $("#green-box").empty();
                $("#red-box").empty();
                $("#red-box").append("API Error");
            });
        }
    },500);
})();