Joyn.de Advanced Live Filter

Advanced Filter Options for the Live TV Page on Joyn.de

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name            Joyn.de Advanced Live Filter
// @name:de         Joyn.de Erweiterter Live Filter
// @version         1.0.0
// @description     Advanced Filter Options for the Live TV Page on Joyn.de
// @description:de  Erweiterte Filtermöglichkeiten für die Live-TV-Seite auf Joyn.de
// @icon            https://www.joyn.de/favicon.ico
// @author          TalkLounge (https://github.com/TalkLounge)
// @namespace       https://github.com/TalkLounge/joyn.de-advanced-live-filter
// @license         MIT
// @match           https://www.joyn.de/*
// @grant           GM.setValue
// @grant           GM.getValue
// @run-at          document-start
// ==/UserScript==

(function () {
    'use strict';

    function newElement(tagName, attributes, content) {
        var tag = document.createElement(tagName);
        for (var key in attributes || {}) {
            if (attributes[key] !== undefined && attributes[key] !== null) {
                tag.setAttribute(key, attributes[key]);
            }
        }
        tag.innerHTML = content || "";
        return tag;
    }

    function injectStyling() {
        const s = document.createElement("style");
        s.innerHTML = `#JALF {
            position: absolute;
            top: 32px;
            left: 50%;
            transform: translateX(-50%);
        }

        #JALF h2 {
            font-size: 28px;
            font-family: Campton-Bold, Helvetica, sans-serif;
            margin-bottom: 1.75%;
            text-align: center;
        }

        #JALF input[type="text"] {
            color: white;
            border: 0px;
            background: rgba(255, 255, 255, 0.2);
            line-height: 18px;
            margin-bottom: 1.75%;
            width: 100%;
        }

        #JALF input[type="checkbox"] {
            display: none;
        }

        #JALF input[type="checkbox"] + label {
            position: relative;
            padding-left: 20px;
            display: inline-block;
            margin-right: 10px;
        }

        #JALF input[type="checkbox"] + label:before {
            content: "";
            position: absolute;
            left: 0;
            width: 14px;
            height: 14px;
            background: rgba(255, 255, 255, 0.2);
            cursor: pointer;
        }

        #JALF input[type="checkbox"]:checked + label:before {
            content: "✔";
            text-align: center;
        }

        #JALF .last {
            margin: 0px !important;
        }`;
        document.head.append(s);
    }

    async function injectFilters() {
        const div = newElement("div", {id: "JALF"});

        const h2 = newElement("h2", null, "Filter");
        div.append(h2);

        const input = newElement("input", {type: "text", id: "JALF-Search", placeholder: "Search...", value: await GM.getValue("search") || ""});
        input.addEventListener("input", filter);
        div.append(input);

        const br = newElement("br");
        div.append(br);

        const check1 = newElement("input", {type: "checkbox", id: "JALF-ChannelOnly", checked: await GM.getValue("isChannelOnly") ? true : undefined});
        check1.addEventListener("change", filter);
        div.append(check1);

        const label1 = newElement("label", {for: "JALF-ChannelOnly"}, "Search Channels only");
        div.append(label1);

        const check2 = newElement("input", {type: "checkbox", id: "JALF-Plus", checked: await GM.getValue("isPlus") ? true : undefined});
        check2.addEventListener("change", filter);
        div.append(check2);

        const label2 = newElement("label", {for: "JALF-Plus"}, "Show Plus Channels");
        div.append(label2);

        const check3 = newElement("input", {type: "checkbox", id: "JALF-VOD", checked: await GM.getValue("isVOD") ? true : undefined});
        check3.addEventListener("change", filter);
        div.append(check3);

        const label3 = newElement("label", {for: "JALF-VOD"}, "Show VOD Channels");
        div.append(label3);

        const check4 = newElement("input", {type: "checkbox", id: "JALF-SD", checked: await GM.getValue("isSD") || await GM.getValue("isSD") == undefined ? true : undefined});
        check4.addEventListener("change", filter);
        div.append(check4);

        const label4 = newElement("label", {for: "JALF-SD", class: "last"}, "Show SD Channels");
        div.append(label4);

        document.querySelector("header").append(div);
    }

    function filter() {
        const [search, isChannelOnly, isPlus, isVOD, isSD] = [document.querySelector("#JALF-Search").value, document.querySelector("#JALF-ChannelOnly").checked, document.querySelector("#JALF-Plus").checked, document.querySelector("#JALF-VOD").checked, document.querySelector("#JALF-SD").checked];
        GM.setValue("search", search);
        GM.setValue("isChannelOnly", isChannelOnly);
        GM.setValue("isPlus", isPlus);
        GM.setValue("isVOD", isVOD);
        GM.setValue("isSD", isSD);

        for (const child of document.querySelector("article div ol").children) {
            const foundChannel = child.querySelector("img").alt.toLowerCase().includes(search.toLowerCase());
            const foundProgramm = [...child.querySelectorAll("h6")].find(item => item.textContent.toLowerCase().includes(search.toLowerCase()));
            const foundPlus = child.querySelector("header").firstChild.tagName == "svg";
            const foundVOD = child.querySelector("header").children[1]?.tagName == "svg" && child.querySelector("header").children[1]?.children.length > 1;
            const foundHD = child.querySelector("header").lastChild.tagName == "svg" && child.querySelector("header").lastChild.children.length == 1;

            if (
                ((isChannelOnly && foundChannel) || (!isChannelOnly && (foundChannel || foundProgramm))) &&
                ((isPlus) || (!isPlus && !foundPlus)) &&
                ((isVOD) || (!isVOD && !foundVOD)) &&
                ((isSD) || (!isSD && foundHD))
            ) {
                child.style.display = "flex";
            } else {
                child.style.display = "none";
            }
        }
    }

    async function backInit() {
        if (! window.location.href.startsWith("https://www.joyn.de/play/live-tv") || ! document.querySelector("article")) {
            return init();
        }

        await new Promise(r => setTimeout(r, 500));
        return backInit();
    }

    async function init() {
        if (! window.location.href.startsWith("https://www.joyn.de/play/live-tv") || ! document.querySelector("article")) {
            await new Promise(r => setTimeout(r, 500));
            return init();
        }

        injectStyling();

        const observer = new MutationObserver(async (mutationList, observer) => {
            for (const mutation of mutationList) {
                if (mutation.addedNodes[0]?.tagName != "ASIDE") {
                    continue;
                }

                injectFilters();
                await new Promise(r => setTimeout(r, 50));
                [...document.querySelector("article div ol").querySelectorAll("h6")].forEach(item => item.parentNode.setAttribute("title", item.innerText));
                filter();
                document.querySelector("article div ol").scrollTop = 0;
                break;
            }
        });
        observer.observe(document.querySelector("article"), { childList: true });
        backInit();
    }

    init();
})();