Greasy Fork

Host Selector

Enhanced GUI for aniworld.to and s.to websites, allowing you to effortlessly choose your preferred video host and have it automatically opened. A convenient green button positioned at the bottom right corner of the page will appear, prompting you to click and select your desired host from a user-friendly drop-down menu. Enjoy seamless video streaming with the host of your choice!

目前为 2025-04-15 提交的版本。查看 最新版本

// ==UserScript==
// @name Host Selector
// @version 3
// @description:de Ermöglicht Auswahl von Video-Host und speichert die Auswahl. Funktioniert bei https://aniworld.to/ und https://s.to
// @description:en Enables selection of video host and saves the choice. Works on https://aniworld.to/ and https://s.to
// @description:ja ビデオホストの選択と選択内容の保存を可能にします。https://aniworld.to/ および https://s.to で動作します。
// @author       𝕭𝖚𝖉𝖚𝖒𝖟
// @icon https://w0.peakpx.com/wallpaper/40/813/HD-wallpaper-walpaper-zedge.jpg
// @match https://aniworld.to/*
// @match https://s.to/serie/stream/*
// @grant GM_addStyle
// @namespace http://tampermonkey.net/
// @description Enhanced GUI for aniworld.to and s.to websites, allowing you to effortlessly choose your preferred video host and have it automatically opened. A convenient green button positioned at the bottom right corner of the page will appear, prompting you to click and select your desired host from a user-friendly drop-down menu. Enjoy seamless video streaming with the host of your choice!
// ==/UserScript==


(function () {
    'use strict';
    console.log('[Host Selector] Skript startet...');

    // --- Prüfen, ob GM_* Funktionen verfügbar sind ---
    if (typeof GM_getValue === 'undefined' || typeof GM_setValue === 'undefined' || typeof GM_addStyle === 'undefined') {
        console.error('[Host Selector] Fehler: GM_getValue, GM_setValue oder GM_addStyle nicht verfügbar! Stellen Sie sicher, dass Tampermonkey/Greasemonkey korrekt läuft und die @grant-Anweisungen vorhanden sind.');
        alert('[Host Selector] Fehler: Benötigte GM_* Funktionen nicht gefunden. Skript kann nicht korrekt ausgeführt werden.');
        return; // Skript beenden, wenn wichtige Funktionen fehlen
    } else {
        console.log('[Host Selector] GM_* Funktionen verfügbar.');
    }

    // --- Konfiguration ---
    const hosterListSelector = 'ul.row > li'; // Selektor für die Listenelemente der Hoster
    const hosterLinkSelector = 'a.watchEpisode'; // Selektor für die Links zu den einzelnen Hostern (innerhalb li)
    const hosterNameSelector = 'h4';             // Selektor für den Namen des Hosters innerhalb des Links
    const hosterButtonSelector = '.hosterSiteVideoButton'; // Selektor für den eigentlichen Klick-Button im Link
    const clickDelay = 250;                     // Etwas längere Verzögerung vor dem Klick (in Millisekunden)
    const localStorageKey = 'preferredHoster';   // Schlüssel für GM Speicher
    const OBSERVER_TIMEOUT_MS = 15000;           // Max. Wartezeit für Auto-Klick (15 Sekunden)

    // --- Funktion zum Klicken des bevorzugten Hoster-Links ---
    // Gibt true zurück, wenn der Klick-Versuch gestartet wurde, sonst false.
    function clickHosterLink(preferredHoster) {
        const hosterListItems = document.querySelectorAll(hosterListSelector);
        console.log(`[Host Selector] clickHosterLink: Aufgerufen mit preferredHoster="${preferredHoster}". Fand ${hosterListItems.length} Listenelemente.`);
        let foundAndClicked = false; // Wird true, wenn der Klick-Versuch startet

        for (const item of hosterListItems) {
            // Prüfen, ob das Element sichtbar ist
            if (window.getComputedStyle(item).display === 'none') {
                // console.log('[Host Selector] clickHosterLink: Überspringe unsichtbares Listenelement.'); // Optional: Weniger Logs
                continue;
            }

            const link = item.querySelector(hosterLinkSelector);
            if (!link) continue;

            const hosterNameElement = link.querySelector(hosterNameSelector);
            const button = link.querySelector(hosterButtonSelector);

            if (hosterNameElement) {
                const currentHosterName = hosterNameElement.innerText.trim();
                // console.log(`[Host Selector] Prüfe sichtbaren Hoster: "${currentHosterName}"`); // Optional: Weniger Logs
                if (currentHosterName === preferredHoster) {
                    console.log(`[Host Selector] Match gefunden für "${preferredHoster}"!`);
                    if (button) {
                        console.log(`[Host Selector] Button gefunden. Versuche Klick nach ${clickDelay}ms...`);
                        // Klick wird verzögert ausgeführt
                        setTimeout(() => {
                            try {
                                button.click();
                                console.log(`[Host Selector] Klick erfolgreich ausgelöst für "${preferredHoster}".`);
                            } catch (e) {
                                console.error(`[Host Selector] Fehler während button.click():`, e);
                            }
                        }, clickDelay);
                        foundAndClicked = true; // WICHTIG: Setze auf true, da der Klick *versucht* wird
                        break; // Schleife verlassen, Hoster gefunden und Klick initiiert
                    } else {
                        console.warn(`[Host Selector] Hoster "${preferredHoster}" gematcht, aber Button mit Selektor "${hosterButtonSelector}" nicht im Link gefunden.`);
                    }
                }
            } else {
                 // console.warn(`[Host Selector] Sichtbarer Link gefunden, aber Hoster-Namenselement mit Selektor "${hosterNameSelector}" nicht darin gefunden.`); // Optional: Weniger Logs
            }
        }
        // Gib zurück, ob der Klick-Versuch für den bevorzugten Hoster gestartet wurde
        if (!foundAndClicked && hosterListItems.length > 0) {
             console.log(`[Host Selector] clickHosterLink: Bevorzugter Hoster "${preferredHoster}" wurde unter den ${hosterListItems.length} sichtbaren Hostern nicht gefunden.`);
        } else if (hosterListItems.length === 0) {
             console.log(`[Host Selector] clickHosterLink: Keine Hoster-Listenelemente gefunden.`);
        }
        return foundAndClicked; // Gib den Status zurück
    }


    // --- Funktion zur Behandlung des automatischen Klickens beim Laden der Seite ---
    function autoClickPreferredHoster() {
        console.log('[Host Selector] Lese Hoster aus Speicher...');
        const storedPreferredHoster = GM_getValue(localStorageKey, null);
        console.log(`[Host Selector] Wert gelesen für Schlüssel "${localStorageKey}":`, storedPreferredHoster);

        if (!storedPreferredHoster) {
            console.log('[Host Selector] Kein bevorzugter Hoster im Speicher gefunden. Automatisches Klicken übersprungen.');
            return;
        }

        console.log(`[Host Selector] Bevorzugter Hoster gefunden: "${storedPreferredHoster}". Warte auf Hoster-Links...`);

        // --- Variablen für den Observer und Timeout ---
        let observer = null;
        let observerTimeout = null;

        // Funktion, die prüft und klickt. Gibt true zurück, wenn Klick ausgelöst wurde.
        const checkForLinksAndClick = () => {
            console.log(`[Host Selector] checkForLinksAndClick: Suche nach "${storedPreferredHoster}".`);
            const hosterItemsExist = document.querySelector(hosterListSelector);
            if (hosterItemsExist) {
                console.log(`[Host Selector] Mindestens ein Listenelement gefunden. Rufe clickHosterLink auf.`);
                // clickHosterLink gibt true zurück, wenn der Klick-Versuch gestartet wurde
                return clickHosterLink(storedPreferredHoster);
            }
            console.log(`[Host Selector] Noch keine Listenelemente gefunden.`);
            return false; // Hoster-Liste noch nicht gefunden
        };

        // Funktion zum Stoppen des Observers und des Timeouts
        const stopObserver = (reason) => {
            if (observer) {
                observer.disconnect();
                observer = null;
                console.log(`[Host Selector] MutationObserver gestoppt (${reason}).`);
            }
            if (observerTimeout) {
                clearTimeout(observerTimeout);
                observerTimeout = null;
                console.log(`[Host Selector] Observer-Timeout gelöscht (${reason}).`);
            }
        };

        // 1. Zuerst prüfen, ob die Links vielleicht schon da sind
        if (checkForLinksAndClick()) {
            console.log('[Host Selector] Klick bei initialer Prüfung erfolgreich.');
            return; // Kein Observer nötig
        }

        // 2. Wenn nicht, MutationObserver verwenden
        console.log('[Host Selector] Starte MutationObserver, um auf Listenelemente zu warten...');
        observer = new MutationObserver((mutationsList, obs) => {
            console.log('[Host Selector] MutationObserver ausgelöst.');

            // Prüfen, ob der bevorzugte Hoster jetzt geklickt werden kann
            if (checkForLinksAndClick()) {
                console.log('[Host Selector] Klick nach DOM-Änderung erfolgreich.');
                stopObserver("Hoster gefunden und geklickt");
            } else {
                // Noch nicht erfolgreich, warte auf weitere Mutationen oder Timeout
                console.log(`[Host Selector] Bevorzugter Hoster "${storedPreferredHoster}" nach Mutation noch nicht klickbar/gefunden.`);
            }
        });

        // Beobachte Änderungen im Body
        observer.observe(document.body, {
            childList: true, // Achte auf hinzugefügte/entfernte Kind-Elemente
            subtree: true    // Beobachte auch alle Unterelemente
        });

        // 3. Setze ein Timeout, um den Observer zu stoppen, falls der Hoster nie erscheint
        console.log(`[Host Selector] Setze Observer-Timeout auf ${OBSERVER_TIMEOUT_MS / 1000} Sekunden.`);
        observerTimeout = setTimeout(() => {
            console.warn(`[Host Selector] Observer-Timeout erreicht. Bevorzugter Hoster "${storedPreferredHoster}" wurde nicht gefunden oder konnte nicht geklickt werden.`);
            stopObserver("Timeout");
        }, OBSERVER_TIMEOUT_MS);
    }


    // --- Funktion zum Erstellen und Anzeigen des GUI-Popups ---
    function createGUI() {
        console.log('[Host Selector] createGUI aufgerufen.');
        const existingPopup = document.getElementById('hostSelectorPopup');
        if (existingPopup) {
            document.body.removeChild(existingPopup);
        }

        // --- Verfügbare Hoster dynamisch ermitteln ---
        const hosterListItems = document.querySelectorAll(hosterListSelector);
        const availableHostersSet = new Set();
        console.log(`[Host Selector] GUI: Fand ${hosterListItems.length} potenzielle Hoster-Listenelemente.`);

        hosterListItems.forEach(item => {
            if (window.getComputedStyle(item).display !== 'none') {
                const hosterNameElement = item.querySelector(hosterNameSelector);
                if (hosterNameElement) {
                    const hosterName = hosterNameElement.innerText.trim();
                    if (hosterName) {
                        availableHostersSet.add(hosterName);
                        console.log(`[Host Selector] GUI: Füge sichtbaren Hoster "${hosterName}" zur Liste hinzu.`);
                    }
                }
            } else {
                 // console.log('[Host Selector] GUI: Überspringe unsichtbares Hoster-Listenelement.');
            }
        });

        const availableHosters = Array.from(availableHostersSet).sort();
        console.log('[Host Selector] GUI: Verfügbare Hoster ermittelt:', availableHosters);

        let optionsHTML = '';
        if (availableHosters.length > 0) {
            optionsHTML = availableHosters.map(hoster =>
                `<option value="${hoster}">${hoster}</option>`
            ).join('');
        } else {
            optionsHTML = '<option value="">Keine Hoster gefunden</option>';
             console.warn('[Host Selector] GUI: Keine sichtbaren Hoster gefunden, um die Dropdown-Liste zu füllen.');
        }
        // --- Ende Hoster ermitteln ---

        const popupDiv = document.createElement('div');
        popupDiv.id = 'hostSelectorPopup';
        // Style für das Popup
        popupDiv.style.position = 'fixed';
        popupDiv.style.top = '50%';
        popupDiv.style.left = '50%';
        popupDiv.style.transform = 'translate(-50%, -50%)';
        popupDiv.style.background = '#ffffff';
        popupDiv.style.border = '1px solid #ddd';
        popupDiv.style.boxShadow = '0px 0px 15px rgba(0, 0, 0, 0.2)';
        popupDiv.style.maxWidth = '400px';
        popupDiv.style.width = '90%';
        popupDiv.style.padding = '25px';
        popupDiv.style.borderRadius = '8px';
        popupDiv.style.fontFamily = 'Arial, sans-serif';
        popupDiv.style.zIndex = '10000';
        popupDiv.style.boxSizing = 'border-box';

        // HTML-Inhalt des Popups
        popupDiv.innerHTML = `
            <h2 style="font-size: 20px; margin-top:0; margin-bottom: 20px; color: #333; text-align: center;">Bevorzugter Hoster</h2>
            <label for="preferredHosterSelect" style="display: block; font-size: 16px; color: #555; margin-bottom: 8px;">Verfügbarer Hoster:</label>
            <select id="preferredHosterSelect" style="width: 100%; padding: 12px; margin-bottom: 25px; font-size: 16px; border: 1px solid #ccc; border-radius: 4px; color: #555; box-sizing: border-box;">
                ${optionsHTML}
            </select>
            <div style="display: flex; justify-content: space-between; gap: 10px;">
                <button id="submitButton" style="flex-grow: 1; background-color: #4CAF50; color: white; padding: 12px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px;">Auswählen & Speichern</button>
                <button id="closeButton" style="background-color: #f44336; color: white; padding: 12px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px;">Schließen</button>
            </div>
        `;
        document.body.appendChild(popupDiv);

        // Gespeicherten Hoster im Dropdown vorauswählen
        const storedPreferredHoster = GM_getValue(localStorageKey, null);
        const preferredHosterSelect = document.getElementById('preferredHosterSelect');
        console.log(`[Host Selector] GUI: Lade gespeicherten Wert "${storedPreferredHoster}" in Select-Box.`);

        if (preferredHosterSelect && storedPreferredHoster) {
            if (Array.from(preferredHosterSelect.options).some(option => option.value === storedPreferredHoster)) {
                preferredHosterSelect.value = storedPreferredHoster;
                console.log(`[Host Selector] GUI: Gespeicherter Hoster "${storedPreferredHoster}" ist verfügbar und wurde ausgewählt.`);
            } else {
                 console.log(`[Host Selector] GUI: Gespeicherter Hoster "${storedPreferredHoster}" ist aktuell nicht verfügbar.`);
            }
        } else if (preferredHosterSelect && preferredHosterSelect.options.length > 0 && preferredHosterSelect.options[0].value !== "") {
             console.log('[Host Selector] GUI: Kein Hoster gespeichert. Erster verfügbarer Hoster:', preferredHosterSelect.options[0]?.value);
        }

        // Event Listener für Buttons
        const submitButton = document.getElementById('submitButton');
        submitButton.addEventListener('click', () => {
            if (preferredHosterSelect.value && preferredHosterSelect.value !== "") {
                const selectedHoster = preferredHosterSelect.value;
                console.log(`[Host Selector] GUI: "Auswählen" geklickt. Speichere "${selectedHoster}"...`);
                GM_setValue(localStorageKey, selectedHoster);
                console.log(`[Host Selector] GUI: Wert für Schlüssel "${localStorageKey}" gespeichert.`);

                console.log('[Host Selector] GUI: Versuche sofortigen Klick nach Auswahl...');
                clickHosterLink(selectedHoster); // Versuche sofort zu klicken

                document.body.removeChild(popupDiv);
                console.log('[Host Selector] GUI: Popup geschlossen.');
            } else {
                console.warn('[Host Selector] GUI: Kein gültiger Hoster zum Speichern ausgewählt.');
                alert("Bitte wähle einen verfügbaren Hoster aus.");
            }
        });

        const closeButton = document.getElementById('closeButton');
        closeButton.addEventListener('click', () => {
            console.log('[Host Selector] GUI: "Schließen" geklickt.');
            document.body.removeChild(popupDiv);
        });
    }


    // --- Funktion zum Hinzufügen des GUI-Buttons zur Seite ---
    function addGUIButton() {
        console.log('[Host Selector] Füge GUI-Button hinzu...');
        const existingButton = document.getElementById('hostSelectorButton');
        if (existingButton) {
             // Optional: Entfernen, falls alter Button Probleme macht
             // document.body.removeChild(existingButton);
             console.log('[Host Selector] GUI-Button existiert bereits.');
             return; // Nicht erneut hinzufügen
        }

        const button = document.createElement('button');
        button.id = 'hostSelectorButton';
        button.innerText = '⚙️ Hoster';
        button.style.position = 'fixed';
        button.style.bottom = '15px';
        button.style.right = '15px';
        button.style.backgroundColor = '#673AB7';
        button.style.color = 'white';
        button.style.padding = '8px 12px';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';
        button.style.fontSize = '14px';
        button.style.zIndex = '9999';
        button.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)';
        button.title = 'Bevorzugten Hoster auswählen';
        button.addEventListener('click', createGUI);
        document.body.appendChild(button);
        console.log('[Host Selector] GUI-Button hinzugefügt.');
    }


    // --- Hauptausführung ---
    console.log('[Host Selector] Starte Hauptausführung...');

    // Füge den Button hinzu (oder stelle sicher, dass er da ist)
    // Die Prüfung auf Existenz erfolgt jetzt in addGUIButton selbst.
    addGUIButton();

    // Starte den Versuch, automatisch zu klicken
    autoClickPreferredHoster();

    console.log('[Host Selector] Hauptausführung initial abgeschlossen (Observer läuft ggf. weiter).');

})(); // Ende der IIFE