您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a 🚫 symbol without background in the top-left corner of the thumbnail for natively hiding videos and a button in the topbar to toggle the Shorts section on YouTube
// ==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.6.15 // @description Adds a 🚫 symbol without background in the top-left corner of the thumbnail for natively hiding videos and a button in the topbar to toggle the Shorts section on YouTube // @description:de Fügt ein 🚫 Symbol ohne Hintergrund in der oberen linken Ecke des Thumbnails hinzu, um Videos nativ auszublenden, und einen Button in der oberen Leiste, um den Shorts-Abschnitt auf YouTube ein-/auszublenden // @description:es Agrega un símbolo 🚫 sin fondo en la esquina superior izquierda de la miniatura para ocultar videos de forma nativa y un botón en la barra superior para alternar la sección de Shorts en YouTube // @description:fr Ajoute un symbole 🚫 sans fond dans le coin supérieur gauche de la miniature pour masquer nativement les vidéos et un bouton dans la barre supérieure pour activer/désactiver la section Shorts sur YouTube // @description:it Aggiunge un simbolo 🚫 senza sfondo nell'angolo superiore sinistro della miniatura per nascondere nativamente i video e un pulsante nella barra superiore per attivare/disattivare la sezione Shorts su YouTube // @icon https://youtube.com/favicon.ico // @author Copiis // @license MIT // @match https://www.youtube.com/* // @grant none // ==/UserScript== // If you find this script useful and would like to support my work, consider making a small donation! // Bitcoin (BTC): bc1quc5mkudlwwkktzhvzw5u2nruxyepef957p68r7 // PayPal: https://www.paypal.com/paypalme/Coopiis?country.x=DE&locale.x=de_DE (function () { 'use strict'; // Spracherkennung const userLang = (navigator.language || navigator.languages[0] || 'en').substring(0, 2); 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}', 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', shortsButtonTextHide: 'Hide Shorts', shortsButtonTextShow: 'Show Shorts', shortsButtonTextUnavailable: 'Shorts unavailable', 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}', 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', shortsButtonTextHide: 'Shorts ausblenden', shortsButtonTextShow: 'Shorts einblenden', shortsButtonTextUnavailable: 'Shorts nicht verfügbar', 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}', 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', shortsButtonTextHide: 'Ocultar Shorts', shortsButtonTextShow: 'Mostrar Shorts', shortsButtonTextUnavailable: 'Shorts no disponibles', 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}', 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', shortsButtonTextHide: 'Masquer les Shorts', shortsButtonTextShow: 'Afficher les Shorts', shortsButtonTextUnavailable: 'Shorts indisponibles', 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}', 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', shortsButtonTextHide: 'Nascondi Shorts', shortsButtonTextShow: 'Mostra Shorts', shortsButtonTextUnavailable: 'Shorts non disponibili', 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 Hinzufügen des Ausblende-Buttons für Videos function addHideButton() { const videoElements = document.querySelectorAll('ytd-rich-grid-media:not([data-hide-button-added])'); if (videoElements.length > 0) { console.log(formatTranslation('hideVideosFound', { count: videoElements.length })); } videoElements.forEach((video, index) => { const thumbnailContainer = video.querySelector('#thumbnail'); const thumbnailImage = thumbnailContainer?.querySelector('img.yt-core-image'); if (!thumbnailContainer || !thumbnailImage) { console.log(formatTranslation('hideNoThumbnail', { index })); 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: '9999', borderRadius: '50%', width: '32px', height: '32px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '18px', color: 'white', backgroundColor: 'transparent', pointerEvents: 'auto' }); thumbnailContainer.style.position = 'relative'; thumbnailContainer.appendChild(hideButton); video.setAttribute('data-hide-button-added', 'true'); console.log(formatTranslation('hideButtonAdded', { index })); hideButton.addEventListener('click', async () => { try { const menuButton = video.querySelector('yt-icon-button#button.dropdown-trigger'); if (!menuButton) { console.log(formatTranslation('hideNoMenuButton', { index })); return; } menuButton.querySelector('button')?.dispatchEvent(new MouseEvent('click', { bubbles: true })); console.log(formatTranslation('hideMenuOpened', { index })); await new Promise(resolve => requestAnimationFrame(() => setTimeout(resolve, 1500))); // Verzögerung auf 1500 ms erhöht const menu = document.querySelector('ytd-menu-popup-renderer'); if (!menu) { console.log(formatTranslation('hideOptionNotFound', { index }) + ' - No menu popup found'); return; } const menuItems = menu.querySelectorAll('ytd-menu-service-item-renderer, ytd-menu-navigation-item-renderer, tp-yt-paper-item'); const hideOption = Array.from(menuItems).find(item => { const textElement = item.querySelector('yt-formatted-string, span, div'); const text = textElement?.textContent?.trim().toLowerCase(); const ariaLabel = item.getAttribute('aria-label')?.toLowerCase(); const hasHideIcon = item.querySelector('yt-icon path[d="M12 2c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2zM3 12c0 2.31.87 4.41 2.29 6L18 5.29C16.41 3.87 14.31 3 12 3c-4.97 0-9 4.03-9 9zm15.71-6L6 18.71C7.59 20.13 9.69 21 12 21c4.97 0 9-4.03 9-9 0-2.31-.87-4.41-2.29-6z"]') !== null; console.log(`[Hide Debug] Checking menu item: text="${text || 'No text'}" aria-label="${ariaLabel || 'No aria-label'}" hasHideIcon=${hasHideIcon}`); return text === 'hide' || text === 'ausblenden' || text === 'video ausblenden' || text === 'ocultar' || text === 'masquer' || text === 'nascondi' || ariaLabel?.includes('ausblenden') || ariaLabel?.includes('hide') || ariaLabel?.includes('ocultar') || ariaLabel?.includes('masquer') || ariaLabel?.includes('nascondi') || hasHideIcon; }); if (hideOption) { console.log(`[Hide Debug] Found hide option: ${hideOption.outerHTML}`); hideOption.closest('ytd-menu-service-item-renderer')?.dispatchEvent(new MouseEvent('click', { bubbles: true })) || hideOption.dispatchEvent(new MouseEvent('click', { bubbles: true })); console.log(formatTranslation('hideOptionClicked', { index })); await new Promise(resolve => requestAnimationFrame(() => setTimeout(resolve, 500))); // Suche nach Bestätigungs-Popup const confirmButton = document.querySelector('yt-button-renderer#confirm-button, yt-button-renderer a[aria-label*="bestätigen" i], yt-button-renderer a[aria-label*="confirm" i], tp-yt-paper-button'); if (confirmButton) { console.log(`[Hide Debug] Found confirm button: ${confirmButton.outerHTML}`); confirmButton.querySelector('a, tp-yt-paper-button')?.dispatchEvent(new MouseEvent('click', { bubbles: true })) || confirmButton.dispatchEvent(new MouseEvent('click', { bubbles: true })); console.log(formatTranslation('hideConfirmClicked', { index })); } else { console.log(formatTranslation('hideConfirmNotFound', { index })); } await new Promise(resolve => requestAnimationFrame(() => setTimeout(resolve, 500))); document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })); } else { console.log(formatTranslation('hideOptionNotFound', { index })); console.log(`[Hide Debug] Full menu structure: ${menu.outerHTML}`); Array.from(menuItems).forEach(item => { const text = item.querySelector('yt-formatted-string, span, div')?.textContent?.trim() || 'No text'; const ariaLabel = item.getAttribute('aria-label') || 'No aria-label'; const hasHideIcon = item.querySelector('yt-icon path[d="M12 2c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2zM3 12c0 2.31.87 4.41 2.29 6L18 5.29C16.41 3.87 14.31 3 12 3c-4.97 0-9 4.03-9 9zm15.71-6L6 18.71C7.59 20.13 9.69 21 12 21c4.97 0 9-4.03 9-9 0-2.31-.87-4.41-2.29-6z"]') !== null; console.log(`[Hide Debug] Menu item: text="${text}" aria-label="${ariaLabel}" hasHideIcon=${hasHideIcon}`); }); } } catch (err) { console.error(formatTranslation('hideError', { index, error: err.message })); } }); }); } // Funktion zum Hinzufügen des Shorts-Toggle-Buttons in der oberen Leiste function addShortsToggleButton() { // Finde die obere Leiste und das YouTube-Logo const topbar = document.querySelector('ytd-masthead #start'); const logo = document.querySelector('ytd-topbar-logo-renderer'); if (!topbar || !logo) { console.log(formatTranslation('shortsNoTopbar')); return; } // Prüfe, ob der Button bereits hinzugefügt wurde let wrapper = topbar.querySelector('.shorts-toggle-wrapper'); let toggleButton = wrapper?.querySelector('.shorts-toggle-btn'); // Finde den Shorts-Abschnitt let shortsSection = document.querySelector('ytd-rich-shelf-renderer[is-shorts]'); console.log(formatTranslation('shortsDebugPrimary', { result: shortsSection ? 'found' : 'not found' })); // Fallback: Suche nach Titel in verschiedenen Sprachen if (!shortsSection) { const sections = document.querySelectorAll('ytd-rich-section-renderer, ytd-rich-shelf-renderer'); shortsSection = Array.from(sections).find(section => { const title = section.querySelector('span[id="title"]'); const titleText = title?.textContent.trim().toLowerCase(); const isShortsTitle = t.shortsTitles.some(shortsTitle => titleText === shortsTitle.toLowerCase()); console.log(formatTranslation('shortsDebugSection', { details: JSON.stringify({ tag: section.tagName, title: titleText || 'No title', isShorts: section.hasAttribute('is-shorts') }) })); return isShortsTitle; }); } // Wenn immer noch nicht gefunden, aber Button existiert, aktualisiere ihn nicht if (!shortsSection && toggleButton) { console.log(formatTranslation('shortsNoUpdate')); return; } // Erstelle oder aktualisiere den Toggle-Button if (!wrapper) { toggleButton = document.createElement('button'); toggleButton.className = 'shorts-toggle-btn'; wrapper = document.createElement('div'); wrapper.className = 'shorts-toggle-wrapper'; wrapper.style.cssText = ` display: inline-flex !important; align-items: center !important; margin-left: 8px !important; z-index: 10001 !important; `; wrapper.appendChild(toggleButton); logo.parentNode.insertBefore(wrapper, logo.nextSibling); topbar.setAttribute('data-shorts-button-added', 'true'); console.log(formatTranslation('shortsButtonAdded')); } // Button-Styling und Status toggleButton.textContent = shortsSection ? t.shortsButtonTextHide : t.shortsButtonTextUnavailable; Object.assign(toggleButton.style, { padding: '6px 12px', margin: '0 8px', backgroundColor: shortsSection ? '#ff4444' : '#cccccc', border: 'none', borderRadius: '4px', cursor: shortsSection ? 'pointer' : 'not-allowed', fontSize: '12px', fontWeight: '500', color: '#ffffff', zIndex: '10001', display: 'inline-flex', alignItems: 'center', height: '32px', verticalAlign: 'middle' }); toggleButton.disabled = !shortsSection; // Toggle-Logik if (shortsSection && !toggleButton.hasListener) { let isShortsHidden = false; toggleButton.addEventListener('click', () => { isShortsHidden = !isShortsHidden; shortsSection.style.display = isShortsHidden ? 'none' : ''; toggleButton.textContent = isShortsHidden ? t.shortsButtonTextShow : t.shortsButtonTextHide; console.log(formatTranslation(isShortsHidden ? 'shortsSectionHidden' : 'shortsSectionShown')); }); toggleButton.hasListener = true; } // Debugging if (!shortsSection) { console.log(formatTranslation('shortsNotFound')); const sections = document.querySelectorAll('ytd-rich-section-renderer, ytd-rich-shelf-renderer'); sections.forEach((section, index) => { const title = section.querySelector('span[id="title"]'); const attributes = Array.from(section.attributes).map(attr => `${attr.name}="${attr.value}"`).join(', '); console.log(formatTranslation('shortsDebugSection', { details: JSON.stringify({ index, title: title ? title.textContent.trim() : 'No title found', tag: section.tagName, attributes: attributes || 'No attributes', isShorts: section.hasAttribute('is-shorts') ? 'true' : 'false' }) })); }); } else { const title = shortsSection.querySelector('span[id="title"]'); const attributes = Array.from(shortsSection.attributes).map(attr => `${attr.name}="${attr.value}"`).join(', '); console.log(formatTranslation('shortsFound', { details: JSON.stringify({ tag: shortsSection.tagName, attributes: attributes || 'No attributes', title: title ? title.textContent.trim() : 'No title found', isShorts: shortsSection.hasAttribute('is-shorts') ? 'true' : 'false' }) })); } } // CSS hinzufügen const style = document.createElement('style'); style.textContent = ` .hide-video-btn { color: white !important; background-color: transparent !important; border-radius: 50% !important; font-size: 18px !important; width: 32px !important; height: 32px !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: 9999 !important; visibility: visible !important; opacity: 1 !important; } .hide-video-btn:hover { background-color: transparent !important; box-shadow: 0 0 10px 2px rgba(255, 215, 0, 0.8) !important; } .shorts-toggle-btn { transition: background-color 0.2s !important; } .shorts-toggle-btn:not(:disabled):hover { background-color: #cc0000 !important; } .shorts-toggle-wrapper { display: inline-flex !important; align-items: center !important; margin-left: 8px !important; z-index: 10001 !important; } `; document.head.appendChild(style); // Initiale Ausführung mit Verzögerung function initialize() { try { addHideButton(); addShortsToggleButton(); console.log(formatTranslation('initStarted')); // Wiederholte Prüfung für Shorts-Abschnitt let attempts = 0; const maxAttempts = 10; const interval = setInterval(() => { console.log(formatTranslation('initAttempt', { current: attempts + 1, max: maxAttempts })); const shortsSection = document.querySelector('ytd-rich-shelf-renderer[is-shorts]'); if (shortsSection || document.querySelector('span[id="title"][textContent*="Shorts" i]')) { addShortsToggleButton(); clearInterval(interval); } else if (attempts >= maxAttempts) { console.log(formatTranslation('initMaxAttempts')); clearInterval(interval); } attempts++; }, 1000); } 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)); } // Begrenzter MutationObserver 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-rich-shelf-renderer, ytd-rich-section-renderer, ytd-masthead, ytd-rich-shelf-renderer[is-shorts]') || mutation.target.querySelector('ytd-rich-grid-media, ytd-rich-shelf-renderer, ytd-rich-section-renderer, ytd-topbar-logo-renderer, ytd-rich-shelf-renderer[is-shorts]')) && !mutation.target.classList?.contains('shorts-toggle-wrapper') ); if (hasRelevantChanges) { addHideButton(); addShortsToggleButton(); } } catch (err) { console.error(formatTranslation('observerError', { error: err.message })); } }); observer.observe(document.body, { childList: true, subtree: true, attributes: false }); })();