// ==UserScript== // @name YouTube Video Hider with 🚫 Icon and Shorts Toggle // @name:de YouTube Video Ausblender mit 🚫 Symbol und Shorts Umschalter // @name:es Ocultador de Videos de YouTube con Icono 🚫 y Alternador de Shorts // @name:fr Masqueur de Vidéos YouTube avec Icône 🚫 et Basculeur de Shorts // @name:it Nascondi Video YouTube con Icona 🚫 e Interruttore Shorts // @namespace http://tampermonkey.net/ // @version 2025.9.14 // @description Adds a 🚫 symbol to video thumbnails for hiding videos and a compact Shorts toggle button // @description:de Fügt ein 🚫 Symbol zu Video-Thumbnails hinzu und einen kompakten Button zum Ein-/Ausblenden von Shorts // @description:es Agrega un símbolo 🚫 a las miniaturas de video y un botón compacto para alternar Shorts // @description:fr Ajoute un symbole 🚫 aux vignettes et un bouton compact pour activer/désactiver les Shorts // @description:it Aggiunge un simbolo 🚫 alle miniature e un pulsante compatto per attivare/disattivare i Shorts // @icon https://youtube.com/favicon.ico // @author Copiis // @license MIT // @match https://www.youtube.com/* // @grant none // @downloadURL none // ==/UserScript== (function () { 'use strict'; // Konfigurationsobjekt für Benutzeranpassungen const config = { hideButtonSize: '32px', hideButtonOpacity: '0.7', shortsCheckInterval: 500, maxShortsAttempts: 3, debugMode: true }; // Spracherkennung const userLang = (navigator.language || navigator.languages[0] || 'en').substring(0, 2); if (config.debugMode) console.log(`[Initializer] Erkannte Sprache: ${userLang}`); // Übersetzungsobjekt const translations = { en: { hideVideosFound: 'Found videos: ${count}', hideNoThumbnail: 'Video ${index}: No thumbnail or image found', hideButtonAdded: 'Video ${index}: Button added', hideNoMenuButton: 'Video ${index}: No menu button found', hideMenuOpened: 'Video ${index}: Menu opened', hideOptionClicked: 'Video ${index}: Hide option clicked', hideOptionNotFound: 'Video ${index}: Hide option not found', hideError: 'Video ${index}: Error while hiding: ${error}', hideConfirmClicked: 'Video ${index}: Confirm button clicked', hideConfirmNotFound: 'Video ${index}: Confirm button not found', shortsNoTopbar: 'Topbar or YouTube logo not found', shortsButtonExists: 'Toggle button already exists, skipping', shortsButtonAdded: 'Toggle button added to topbar', shortsNotFound: 'Shorts section not found', shortsFound: 'Shorts section found: ${details}', shortsSectionHidden: 'Shorts section: hidden', shortsSectionShown: 'Shorts section: shown', shortsButtonText: 'Shorts', shortsDebugPrimary: 'Primary selector ytd-rich-shelf-renderer[is-shorts]: ${result}', shortsDebugSection: 'Checking section: ${details}', shortsNoUpdate: 'No Shorts section found, button remains disabled', initStarted: 'Script initialized', initAttempt: 'Attempt ${current} of ${max} for Shorts section', initMaxAttempts: 'Maximum attempts reached, no Shorts section found', initError: 'Error during initialization: ${error}', observerError: 'Error in MutationObserver: ${error}', shortsTitles: ['shorts', 'short videos'] }, de: { hideVideosFound: 'Gefundene Videos: ${count}', hideNoThumbnail: 'Video ${index}: Kein Thumbnail oder Bild gefunden', hideButtonAdded: 'Video ${index}: Button hinzugefügt', hideNoMenuButton: 'Video ${index}: Kein Menü-Button gefunden', hideMenuOpened: 'Video ${index}: Menü geöffnet', hideOptionClicked: 'Video ${index}: Ausblenden geklickt', hideOptionNotFound: 'Video ${index}: Ausblenden-Option nicht gefunden', hideError: 'Video ${index}: Fehler beim Ausblenden: ${error}', hideConfirmClicked: 'Video ${index}: Bestätigen-Button geklickt', hideConfirmNotFound: 'Video ${index}: Bestätigen-Button nicht gefunden', shortsNoTopbar: 'Obere Leiste oder YouTube-Logo nicht gefunden', shortsButtonExists: 'Toggle-Button bereits vorhanden, überspringe', shortsButtonAdded: 'Toggle-Button in oberer Leiste hinzugefügt', shortsNotFound: 'Shorts-Abschnitt nicht gefunden', shortsFound: 'Shorts-Abschnitt gefunden: ${details}', shortsSectionHidden: 'Shorts-Abschnitt: ausgeblendet', shortsSectionShown: 'Shorts-Abschnitt: eingeblendet', shortsButtonText: 'Shorts', shortsDebugPrimary: 'Primärer Selektor ytd-rich-shelf-renderer[is-shorts]: ${result}', shortsDebugSection: 'Prüfe Abschnitt: ${details}', shortsNoUpdate: 'Kein Shorts-Abschnitt gefunden, Button bleibt deaktiviert', initStarted: 'Skript initialisiert', initAttempt: 'Versuch ${current} von ${max} für Shorts-Abschnitt', initMaxAttempts: 'Maximale Versuche erreicht, kein Shorts-Abschnitt gefunden', initError: 'Fehler bei der Initialisierung: ${error}', observerError: 'Fehler im MutationObserver: ${error}', shortsTitles: ['shorts', 'kurzvideos'] }, es: { hideVideosFound: 'Videos encontrados: ${count}', hideNoThumbnail: 'Video ${index}: No se encontró miniatura o imagen', hideButtonAdded: 'Video ${index}: Botón añadido', hideNoMenuButton: 'Video ${index}: No se encontró botón de menú', hideMenuOpened: 'Video ${index}: Menú abierto', hideOptionClicked: 'Video ${index}: Opción de ocultar clicada', hideOptionNotFound: 'Video ${index}: Opción de ocultar no encontrada', hideError: 'Video ${index}: Error al ocultar: ${error}', hideConfirmClicked: 'Video ${index}: Botón de confirmar clicado', hideConfirmNotFound: 'Video ${index}: Botón de confirmar no encontrado', shortsNoTopbar: 'Barra superior o logo de YouTube no encontrados', shortsButtonExists: 'Botón de alternancia ya existe, omitiendo', shortsButtonAdded: 'Botón de alternancia añadido a la barra superior', shortsNotFound: 'Sección de Shorts no encontrada', shortsFound: 'Sección de Shorts encontrada: ${details}', shortsSectionHidden: 'Sección de Shorts: oculta', shortsSectionShown: 'Sección de Shorts: mostrada', shortsButtonText: 'Shorts', shortsDebugPrimary: 'Selector primario ytd-rich-shelf-renderer[is-shorts]: ${result}', shortsDebugSection: 'Verificando sección: ${details}', shortsNoUpdate: 'No se encontró sección de Shorts, el botón permanece deshabilitado', initStarted: 'Script inicializado', initAttempt: 'Intento ${current} de ${max} para la sección de Shorts', initMaxAttempts: 'Máximo de intentos alcanzado, no se encontró sección de Shorts', initError: 'Error durante la inicialización: ${error}', observerError: 'Error en MutationObserver: ${error}', shortsTitles: ['shorts', 'cortos', 'videos cortos'] }, fr: { hideVideosFound: 'Vidéos trouvées : ${count}', hideNoThumbnail: 'Vidéo ${index} : Aucune vignette ou image trouvée', hideButtonAdded: 'Vidéo ${index} : Bouton ajouté', hideNoMenuButton: 'Vidéo ${index} : Aucun bouton de menu trouvé', hideMenuOpened: 'Vidéo ${index} : Menu ouvert', hideOptionClicked: 'Vidéo ${index} : Option de masquer cliquée', hideOptionNotFound: 'Vidéo ${index} : Option de masquer non trouvée', hideError: 'Vidéo ${index} : Erreur lors du masquage : ${error}', hideConfirmClicked: 'Vidéo ${index} : Bouton de confirmation cliqué', hideConfirmNotFound: 'Vidéo ${index} : Bouton de confirmation non trouvé', shortsNoTopbar: 'Barre supérieure ou logo YouTube non trouvés', shortsButtonExists: 'Bouton de bascule déjà présent, ignoré', shortsButtonAdded: 'Bouton de bascule ajouté à la barre supérieure', shortsNotFound: 'Section Shorts non trouvée', shortsFound: 'Section Shorts trouvée : ${details}', shortsSectionHidden: 'Section Shorts : masquée', shortsSectionShown: 'Section Shorts : affichée', shortsButtonText: 'Shorts', shortsDebugPrimary: 'Sélecteur principal ytd-rich-shelf-renderer[is-shorts] : ${result}', shortsDebugSection: 'Vérification de la section : ${details}', shortsNoUpdate: 'Aucune section Shorts trouvée, le bouton reste désactivé', initStarted: 'Script initialisé', initAttempt: 'Tentative ${current} sur ${max} pour la section Shorts', initMaxAttempts: 'Nombre maximum de tentatives atteint, aucune section Shorts trouvée', initError: 'Erreur lors de l’initialisation : ${error}', observerError: 'Erreur dans MutationObserver : ${error}', shortsTitles: ['shorts', 'vidéos courtes'] }, it: { hideVideosFound: 'Video trovati: ${count}', hideNoThumbnail: 'Video ${index}: Nessuna miniatura o immagine trovata', hideButtonAdded: 'Video ${index}: Pulsante aggiunto', hideNoMenuButton: 'Video ${index}: Nessun pulsante di menu trovato', hideMenuOpened: 'Video ${index}: Menu aperto', hideOptionClicked: 'Video ${index}: Opzione nascondi cliccata', hideOptionNotFound: 'Video ${index}: Opzione nascondi non trovata', hideError: 'Video ${index}: Errore durante la nascondazione: ${error}', hideConfirmClicked: 'Video ${index}: Pulsante di conferma cliccato', hideConfirmNotFound: 'Video ${index}: Pulsante di conferma non trovato', shortsNoTopbar: 'Barra superiore o logo YouTube non trovati', shortsButtonExists: 'Pulsante di attivazione già presente, salto', shortsButtonAdded: 'Pulsante di attivazione aggiunto alla barra superiore', shortsNotFound: 'Sezione Shorts non trovata', shortsFound: 'Sezione Shorts trovata: ${details}', shortsSectionHidden: 'Sezione Shorts: nascosta', shortsSectionShown: 'Sezione Shorts: mostrata', shortsButtonText: 'Shorts', shortsDebugPrimary: 'Selettore primario ytd-rich-shelf-renderer[is-shorts]: ${result}', shortsDebugSection: 'Controllo sezione: ${details}', shortsNoUpdate: 'Nessuna sezione Shorts trovata, il pulsante rimane disabilitato', initStarted: 'Script inizializzato', initAttempt: 'Tentativo ${current} di ${max} per la sezione Shorts', initMaxAttempts: 'Massimo numero di tentativi raggiunto, nessuna sezione Shorts trovata', initError: 'Errore durante l’inizializzazione: ${error}', observerError: 'Errore in MutationObserver: ${error}', shortsTitles: ['shorts', 'video brevi'] } }; // Wähle Übersetzungen basierend auf der Sprache (Fallback: Englisch) const t = translations[userLang] || translations.en; // Funktion zum Formatieren von Übersetzungen mit Platzhaltern function formatTranslation(key, params = {}) { let str = t[key] || translations.en[key] || key; Object.keys(params).forEach(param => { str = str.replace(`\${${param}}`, params[param]); }); return str; } // Funktion zum Warten auf ein Element (optimiert für Performance) async function waitForElement(selector, timeout = 2000, maxAttempts = 3) { for (let attempt = 1; attempt <= maxAttempts; attempt++) { const start = Date.now(); while (Date.now() - start < timeout) { const element = document.querySelector(selector); if (element) return element; await new Promise(resolve => setTimeout(resolve, 50)); } if (config.debugMode) console.log(`[Wait Debug] Versuch ${attempt}/${maxAttempts}: Element ${selector} nicht gefunden, warte...`); await new Promise(resolve => setTimeout(resolve, 100)); } return null; } // Funktion zum Simulieren eines robusten Klicks function simulateClick(element) { const rect = element.getBoundingClientRect(); const events = [ new PointerEvent('pointerdown', { bubbles: true, cancelable: true, view: window, clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2, isTrusted: true, pointerType: 'mouse' }), new MouseEvent('mousedown', { bubbles: true, cancelable: true, view: window, clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2 }), new MouseEvent('click', { bubbles: true, cancelable: true, view: window, clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2 }), new PointerEvent('pointerup', { bubbles: true, cancelable: true, view: window, clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2, isTrusted: true, pointerType: 'mouse' }), new MouseEvent('mouseup', { bubbles: true, cancelable: true, view: window, clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2 }) ]; element.focus(); events.forEach(event => { element.dispatchEvent(event); if (config.debugMode) console.log(`[Click Debug] Event ${event.type} ausgelöst auf: ${element.tagName}`); }); } // Debounce-Funktion zur Begrenzung wiederholter Aufrufe function debounce(func, wait) { let timeout; return function (...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } // Funktion zum Hinzufügen des Ausblende-Buttons für Videos const addHideButton = debounce(() => { const thumbnailContainers = document.querySelectorAll( 'ytd-rich-grid-media:not([data-hide-button-added]) #thumbnail, ' + 'ytd-grid-video-renderer:not([data-hide-button-added]) #thumbnail, ' + 'ytd-compact-video-renderer:not([data-hide-button-added]) #thumbnail, ' + 'ytd-thumbnail:not([data-hide-button-added]), ' + 'ytm-shorts-lockup-view-model-v2:not([data-hide-button-added]) .shortsLockupViewModelHostThumbnailContainer' ); if (thumbnailContainers.length > 0) { console.log(formatTranslation('hideVideosFound', { count: thumbnailContainers.length })); } else if (config.debugMode) { console.log('[Debug] Keine Thumbnails gefunden mit erweitertem Selektor'); } thumbnailContainers.forEach((thumbnailContainer, index) => { const video = thumbnailContainer.closest('ytd-rich-grid-media, ytd-grid-video-renderer, ytd-compact-video-renderer, ytm-shorts-lockup-view-model-v2'); const thumbnailImage = thumbnailContainer.querySelector('img.yt-core-image, img.yt-img-shadow, img'); if (!thumbnailImage || !video) { console.log(formatTranslation('hideNoThumbnail', { index })); if (config.debugMode) console.log(`[Hide Debug] Thumbnail-Container: ${thumbnailContainer.outerHTML}`); return; } const hideButton = document.createElement('div'); hideButton.className = 'hide-video-btn'; hideButton.textContent = '🚫'; Object.assign(hideButton.style, { position: 'absolute', top: '8px', left: '8px', cursor: 'pointer', zIndex: '10002 !important', borderRadius: '50%', width: config.hideButtonSize, height: config.hideButtonSize, display: 'flex !important', alignItems: 'center', justifyContent: 'center', fontSize: '18px', color: 'white !important', backgroundColor: `rgba(0, 0, 0, ${config.hideButtonOpacity}) !important`, pointerEvents: 'auto !important', visibility: 'visible !important', opacity: '1 !important' }); const parentContainer = thumbnailContainer.tagName === 'YTD-THUMBNAIL' || thumbnailContainer.classList.contains('shortsLockupViewModelHostThumbnailContainer') ? thumbnailContainer : thumbnailContainer.parentElement; if (parentContainer) { parentContainer.style.position = 'relative'; parentContainer.appendChild(hideButton); video.setAttribute('data-hide-button-added', 'true'); console.log(formatTranslation('hideButtonAdded', { index })); } else { console.log(`[Hide Debug] Kein parentContainer für Video ${index} gefunden`); } hideButton.addEventListener('click', async () => { try { const menuButton = video.querySelector( 'yt-icon-button#button.dropdown-trigger, ' + 'button.yt-icon-button, ' + '.shortsLockupViewModelHostOutsideMetadataMenu button.yt-spec-button-shape-next' ); if (!menuButton) { console.log(formatTranslation('hideNoMenuButton', { index })); if (config.debugMode) console.log(`[Hide Debug] Kein Menü-Button gefunden für Video ${index}`); return; } const menuButtonElement = menuButton.querySelector('button') || menuButton; simulateClick(menuButtonElement); console.log(formatTranslation('hideMenuOpened', { index })); const menu = await waitForElement('ytd-menu-popup-renderer, tp-yt-paper-listbox, ytm-reel-popup-renderer', 2000, 3); if (!menu) { console.log(formatTranslation('hideOptionNotFound', { index }) + ' - Kein Menü-Popup gefunden nach 3 Versuchen'); return; } await new Promise(resolve => setTimeout(resolve, 750)); if (config.debugMode) console.log(`[Hide Debug] Menü-Popup nach Verzögerung: ${menu.outerHTML}`); const menuItems = menu.querySelectorAll('ytd-menu-service-item-renderer, ytd-menu-navigation-item-renderer, tp-yt-paper-item, ytm-reel-menu-item-renderer'); const hideOption = Array.from(menuItems).find(item => { const textElement = item.querySelector('yt-formatted-string, span, div'); const text = textElement?.textContent?.trim().toLowerCase(); const hasHideIcon = item.querySelector('yt-icon path[d*="M12 2c5.52 0 10 4.48 10 10s-4.48 10-10 10"]') !== null; return hasHideIcon || text?.includes('ausblenden') || text?.includes('hide') || text?.includes('ocultar') || text?.includes('masquer') || text?.includes('nascondi'); }); if (hideOption) { if (config.debugMode) console.log(`[Hide Debug] Gefundene Ausblende-Option: ${hideOption.outerHTML}`); const clickableItem = hideOption.querySelector('tp-yt-paper-item, ytm-reel-menu-item-renderer') || hideOption; if (clickableItem.getAttribute('aria-disabled') !== 'true') { simulateClick(clickableItem); console.log(formatTranslation('hideOptionClicked', { index })); } else { if (config.debugMode) console.log('[Hide Debug] Ausblende-Option nicht klickbar:', { tagName: clickableItem.tagName, ariaDisabled: clickableItem.getAttribute('aria-disabled'), tabindex: clickableItem.getAttribute('tabindex'), outerHTML: clickableItem.outerHTML }); return; } } else { console.log(formatTranslation('hideOptionNotFound', { index })); if (config.debugMode) console.log('[Hide Debug] Verfügbare Menü-Elemente:', Array.from(menuItems).map(item => item.outerHTML)); return; } const confirmDialog = await waitForElement('tp-yt-paper-dialog, ytd-popup-container, ytd-engagement-panel-section-list-renderer', 2000, 3); if (!confirmDialog) { if (config.debugMode) console.log('[Hide Debug] Kein Bestätigungs-Dialog gefunden nach 3 Versuchen'); return; } const confirmButton = await waitForElement( 'yt-button-renderer#confirm-button, ' + 'yt-button-renderer:not([disabled]) a, ' + 'tp-yt-paper-button:not([disabled]), ' + 'tp-yt-paper-button[aria-label*="bestätigen" i], ' + 'tp-yt-paper-button[aria-label*="confirm" i], ' + 'yt-button-renderer a.yt-simple-endpoint[aria-label*="bestätigen" i], ' + 'yt-button-renderer a.yt-simple-endpoint[aria-label*="confirm" i]', 2000, 3 ); if (confirmButton) { if (config.debugMode) console.log(`[Hide Debug] Gefundener Bestätigungs-Button: ${confirmButton.outerHTML}`); const clickableConfirm = confirmButton.querySelector('a.yt-simple-endpoint, tp-yt-paper-button') || confirmButton; const buttonText = clickableConfirm.textContent?.trim().toLowerCase(); if (buttonText.includes('bestätigen') || buttonText.includes('confirm')) { simulateClick(clickableConfirm); console.log(formatTranslation('hideConfirmClicked', { index })); } else { if (config.debugMode) console.log('[Hide Debug] Bestätigungs-Button hat unerwarteten Text:', buttonText); } } else { console.log(formatTranslation('hideConfirmNotFound', { index })); if (config.debugMode) console.log('[Hide Debug] Kein Bestätigungs-Button gefunden nach 3 Versuchen'); if (config.debugMode) console.log('[Hide Debug] Dialog-Inhalt:', confirmDialog.outerHTML); } } catch (err) { console.error(formatTranslation('hideError', { index, error: err.message })); } }); }); }, 100); // Funktion zum Hinzufügen des Shorts-Toggle-Buttons let shortsButton = null; let shortsSection = null; let isShortsHidden = false; function addShortsToggleButton() { const topbar = document.querySelector('ytd-masthead #masthead-container') || document.querySelector('ytd-masthead'); if (!topbar) { console.log(formatTranslation('shortsNoTopbar')); return; } if (document.querySelector('.shorts-toggle-wrapper')) { if (config.debugMode) console.log(formatTranslation('shortsButtonExists')); return; } const toggleWrapper = document.createElement('div'); toggleWrapper.className = 'shorts-toggle-wrapper'; shortsButton = document.createElement('button'); shortsButton.className = 'shorts-toggle-btn'; const textSpan = document.createElement('span'); textSpan.textContent = formatTranslation('shortsButtonText'); const iconSpan = document.createElement('span'); iconSpan.className = 'shorts-toggle-icon'; iconSpan.textContent = '🚫'; shortsButton.appendChild(textSpan); shortsButton.appendChild(iconSpan); Object.assign(shortsButton.style, { position: 'relative', padding: '2px 8px', border: 'none', borderRadius: '4px', backgroundColor: 'transparent', color: 'white', cursor: 'pointer', fontSize: '12px', display: 'flex', alignItems: 'center', gap: '4px' }); toggleWrapper.appendChild(shortsButton); const logoContainer = topbar.querySelector('#logo') || topbar.querySelector('#container'); if (logoContainer) { logoContainer.appendChild(toggleWrapper); console.log(formatTranslation('shortsButtonAdded')); } // Suche nach Shorts-Sektion const checkShortsSection = () => { shortsSection = document.querySelector( 'ytd-rich-shelf-renderer[is-shorts], ' + 'ytd-rich-shelf-renderer span#title[textContent*="Shorts" i], ' + 'ytd-rich-section-renderer span#title[textContent*="Shorts" i]' ); if (shortsSection) { if (config.debugMode) console.log(formatTranslation('shortsFound', { details: shortsSection.outerHTML.slice(0, 100) })); shortsButton.disabled = false; iconSpan.style.display = isShortsHidden ? 'none' : 'inline'; if (isShortsHidden) { shortsSection.style.display = 'none'; if (config.debugMode) console.log(formatTranslation('shortsSectionHidden')); } } else { if (config.debugMode) console.log(formatTranslation('shortsNotFound')); shortsButton.disabled = true; iconSpan.style.display = 'none'; } }; checkShortsSection(); shortsButton.addEventListener('click', () => { if (shortsSection) { isShortsHidden = !isShortsHidden; shortsSection.style.display = isShortsHidden ? 'none' : ''; iconSpan.style.display = isShortsHidden ? 'none' : 'inline'; console.log(isShortsHidden ? formatTranslation('shortsSectionHidden') : formatTranslation('shortsSectionShown')); } }); } // CSS hinzufügen const style = document.createElement('style'); style.textContent = ` .hide-video-btn { color: white !important; background-color: rgba(0, 0, 0, ${config.hideButtonOpacity}) !important; border-radius: 50% !important; font-size: 18px !important; width: ${config.hideButtonSize} !important; height: ${config.hideButtonSize} !important; display: flex !important; align-items: center !important; justify-content: center !important; position: absolute !important; top: 8px !important; left: 8px !important; cursor: pointer !important; pointer-events: auto !important; z-index: 10002 !important; visibility: visible !important; opacity: 1 !important; } .hide-video-btn:hover { background-color: rgba(0, 0, 0, 0.9) !important; box-shadow: 0 0 10px 2px rgba(255, 215, 0, 0.8) !important; } ytd-thumbnail, ytd-rich-grid-media, ytd-grid-video-renderer, ytd-compact-video-renderer, ytm-shorts-lockup-view-model-v2 { position: relative !important; } ytd-thumbnail[overlay-style="DEFAULT"] > .yt-img-shadow, ytd-thumbnail[overlay-style="DEFAULT"] > .thumbnail-overlay { z-index: 0 !important; } .shorts-toggle-btn { transition: color 0.2s !important; } .shorts-toggle-btn:not(:disabled):hover { color: #cc0000 !important; } .shorts-toggle-wrapper { display: inline-flex !important; align-items: center !important; margin-left: 8px !important; z-index: 10001 !important; } .shorts-toggle-icon { display: none; font-size: 12px; width: 16px; height: 16px; border-radius: 50%; background-color: rgba(0, 0, 0, 0.7); text-align: center; line-height: 16px; color: white; } `; document.head.appendChild(style); // Initiale Ausführung mit Verzögerung function initialize() { try { addHideButton(); addShortsToggleButton(); console.log(formatTranslation('initStarted')); let attempts = 0; const interval = setInterval(() => { console.log(formatTranslation('initAttempt', { current: attempts + 1, max: config.maxShortsAttempts })); const shortsSection = document.querySelector( 'ytd-rich-shelf-renderer[is-shorts], ' + 'ytd-rich-shelf-renderer span#title[textContent*="Shorts" i], ' + 'ytd-rich-section-renderer span#title[textContent*="Shorts" i]' ); if (shortsSection) { addShortsToggleButton(); clearInterval(interval); } else if (attempts >= config.maxShortsAttempts) { console.log(formatTranslation('initMaxAttempts')); clearInterval(interval); } attempts++; }, config.shortsCheckInterval); } catch (err) { console.error(formatTranslation('initError', { error: err.message })); } } if (document.readyState === 'complete' || document.readyState === 'interactive') { setTimeout(initialize, 1000); } else { document.addEventListener('DOMContentLoaded', () => setTimeout(initialize, 1000)); } // Optimierter MutationObserver const observerTarget = document.querySelector('ytd-app #contents') || document.body; const observer = new MutationObserver((mutations, obs) => { try { const hasRelevantChanges = mutations.some(mutation => mutation.addedNodes.length > 0 && mutation.addedNodes[0]?.nodeType === Node.ELEMENT_NODE && (mutation.target.matches('ytd-rich-grid-media, ytd-grid-video-renderer, ytd-compact-video-renderer, ytd-rich-shelf-renderer, ytd-rich-section-renderer, ytd-masthead, ytm-shorts-lockup-view-model-v2') || mutation.target.querySelector('ytd-rich-grid-media, ytd-grid-video-renderer, ytd-compact-video-renderer, ytd-rich-shelf-renderer[is-shorts], ytd-rich-shelf-renderer span#title[textContent*="Shorts" i], ytm-shorts-lockup-view-model-v2')) && !mutation.target.classList?.contains('shorts-toggle-wrapper') ); if (hasRelevantChanges) { addHideButton(); addShortsToggleButton(); } } catch (err) { console.error(formatTranslation('observerError', { error: err.message })); } }); observer.observe(observerTarget, { childList: true, subtree: true, attributes: false }); })();