Greasy Fork

Greasy Fork is available in English.

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