From a2dfc5e6fc2ba35c41b4e98bdc0375528b5529d1 Mon Sep 17 00:00:00 2001 From: Akamaru Date: Sun, 30 Nov 2025 19:39:46 +0100 Subject: [PATCH] Neu: YouTube Navigation-Links Fixer --- README.md | 18 +++ youtube-restore-nav-links.user.js | 198 ++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 youtube-restore-nav-links.user.js diff --git a/README.md b/README.md index e549f28..7e66346 100644 --- a/README.md +++ b/README.md @@ -292,6 +292,24 @@ Extrahiert und lädt MP4-Videos von Douyin (chinesisches TikTok) herunter. Fügt --- +### 16. `youtube-restore-nav-links.user.js` +**[Installieren](https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/youtube-restore-nav-links.user.js)** + +**Beschreibung:** +Stellt die Navigation-Links für "Abos" und "Verlauf" in der YouTube-Sidebar wieder her, die YouTube entfernt hat. Die Links erscheinen zwischen "Startseite" und "Shorts". + +**Funktionen:** +- Fügt "Abos"-Link zu `/feed/subscriptions` hinzu +- Fügt "Verlauf"-Link zu `/feed/history` hinzu +- Icons im YouTube-Style +- Funktioniert im Light und Dark Mode +- Hover-Effekte wie bei originalen YouTube-Links +- Automatische Anpassung bei SPA-Navigation +- Debounce-Mechanismus verhindert Performance-Probleme +- Kein Flackern durch intelligente Duplikat-Erkennung + +--- + ## Übersicht der enthaltenen UserStyles ### 1. `myanimelist-tweaks.user.css` diff --git a/youtube-restore-nav-links.user.js b/youtube-restore-nav-links.user.js new file mode 100644 index 0000000..e8182a4 --- /dev/null +++ b/youtube-restore-nav-links.user.js @@ -0,0 +1,198 @@ +// ==UserScript== +// @name YouTube Navigation-Links Fixer +// @namespace https://git.ponywave.de/Akamaru/Userscripts +// @version 1.0 +// @description Stellt Links für Abos und Verlauf in der Sidebar wieder her +// @author Akamaru +// @match https://www.youtube.com/* +// @icon https://www.google.com/s2/favicons?domain=youtube.com&sz=32 +// @grant none +// @updateURL https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/youtube-restore-nav-links.user.js +// @downloadURL https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/youtube-restore-nav-links.user.js +// ==/UserScript== + +(function() { + 'use strict'; + + // SVG Icons für die Navigation (d-Attribute) + const ICONS = { + subscriptions: 'M10 18v-6l5 3-5 3zm7-15H7v1h10V3zm3 3H4v1h16V6zm2 3H2v12h20V9zM3 10h18v10H3V10z', + history: 'M11.9 3.75c-4.55 0-8.23 3.7-8.23 8.25H.92l3.57 3.57.04.13 3.7-3.7H5.5c0-3.54 2.87-6.42 6.42-6.42 3.54 0 6.4 2.88 6.4 6.42s-2.86 6.42-6.4 6.42c-1.78 0-3.38-.73-4.54-1.9l-1.3 1.3c1.5 1.5 3.55 2.43 5.83 2.43 4.58 0 8.28-3.7 8.28-8.25 0-4.56-3.7-8.25-8.26-8.25zM11 8.33v4.6l3.92 2.3.66-1.1-3.2-1.9v-3.9H11z' + }; + + // Erstellt einen neuen Navigation-Eintrag als einfachen HTML-Link mit Styling + function createNavEntry(title, url, iconPath) { + // Erstelle einen Container + const container = document.createElement('div'); + container.style.cssText = 'display: flex; align-items: center; padding: 10px 12px; cursor: pointer; border-radius: 10px; color: var(--yt-spec-text-primary, #0f0f0f);'; + container.className = 'yt-nav-custom-entry'; + + // Icon SVG (manuell erstellen wegen Trusted Types) + const iconDiv = document.createElement('div'); + iconDiv.style.cssText = 'width: 24px; height: 24px; margin-right: 24px;'; + + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttribute('height', '24'); + svg.setAttribute('viewBox', '0 0 24 24'); + svg.setAttribute('width', '24'); + svg.setAttribute('focusable', 'false'); + svg.setAttribute('aria-hidden', 'true'); + svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;'; + + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', iconPath); + path.setAttribute('fill', 'currentColor'); + + svg.appendChild(path); + iconDiv.appendChild(svg); + + // Text + const textSpan = document.createElement('span'); + textSpan.textContent = title; + textSpan.style.cssText = 'font-size: 14px; font-weight: 400; line-height: 20px;'; + + container.appendChild(iconDiv); + container.appendChild(textSpan); + + // Click-Handler + container.addEventListener('click', () => { + window.location.href = url; + }); + + // Hover-Effekt + container.addEventListener('mouseenter', () => { + container.style.backgroundColor = 'var(--yt-spec-10-percent-layer, rgba(255,255,255,0.1))'; + }); + container.addEventListener('mouseleave', () => { + container.style.backgroundColor = ''; + }); + + return container; + } + + // Fügt die Navigation-Links hinzu + function addNavLinks() { + // Finde die erste Section (enthält Startseite und Shorts) + const firstSection = document.querySelector('ytd-guide-section-renderer[guide-persistent-and-visible]'); + if (!firstSection) { + console.log('YouTube Nav Restore: Section nicht gefunden, versuche erneut...'); + return false; + } + + const itemsContainer = firstSection.querySelector('#items'); + if (!itemsContainer) { + console.log('YouTube Nav Restore: Items Container nicht gefunden'); + return false; + } + + // Prüfe ob bereits hinzugefügt + const existingEntry = itemsContainer.querySelector('.yt-nav-custom-entry'); + if (existingEntry) { + console.log('YouTube Nav Restore: Links bereits vorhanden'); + return true; + } + + // Finde Startseite und Shorts Einträge + const entries = itemsContainer.querySelectorAll('ytd-guide-entry-renderer'); + if (entries.length < 2) { + console.log('YouTube Nav Restore: Nicht genug Einträge gefunden'); + return false; + } + + // Suche nach dem Shorts-Eintrag (nach Titel) + let shortsEntry = null; + for (const entry of entries) { + const title = entry.querySelector('.title'); + if (title && title.textContent.trim() === 'Shorts') { + shortsEntry = entry; + break; + } + } + + // Fallback: Nimm den zweiten Eintrag + if (!shortsEntry && entries.length >= 2) { + shortsEntry = entries[1]; + } + + if (!shortsEntry) { + console.log('YouTube Nav Restore: Shorts-Eintrag nicht gefunden'); + return false; + } + + // Erstelle die neuen Einträge + const subscriptionsEntry = createNavEntry('Abos', '/feed/subscriptions', ICONS.subscriptions); + const historyEntry = createNavEntry('Verlauf', '/feed/history', ICONS.history); + + if (!subscriptionsEntry || !historyEntry) { + console.log('YouTube Nav Restore: Konnte Einträge nicht erstellen'); + return false; + } + + // Füge die Einträge zwischen Startseite und Shorts ein + itemsContainer.insertBefore(subscriptionsEntry, shortsEntry); + itemsContainer.insertBefore(historyEntry, shortsEntry); + + console.log('YouTube Nav Restore: Navigation-Links erfolgreich hinzugefügt'); + return true; + } + + // Debounce und Lock um Endlosschleifen zu vermeiden + let debounceTimer = null; + let isAdding = false; + let lastUrl = location.href; + + function debouncedAddNavLinks() { + if (isAdding) return; + + if (debounceTimer) { + clearTimeout(debounceTimer); + } + + debounceTimer = setTimeout(() => { + if (!isAdding) { + isAdding = true; + addNavLinks(); + isAdding = false; + } + }, 300); + } + + // Versuche mehrmals die Links hinzuzufügen + function init() { + let attempts = 0; + const maxAttempts = 20; + + const interval = setInterval(() => { + attempts++; + + if (addNavLinks()) { + clearInterval(interval); + } else if (attempts >= maxAttempts) { + console.log('YouTube Nav Restore: Maximale Versuche erreicht'); + clearInterval(interval); + } + }, 500); + } + + // Starte wenn die Seite geladen ist + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } + + // Beobachte URL-Änderungen für SPA-Navigation + const titleElement = document.querySelector('title'); + if (titleElement) { + new MutationObserver(() => { + const url = location.href; + if (url !== lastUrl) { + lastUrl = url; + debouncedAddNavLinks(); + } + }).observe(titleElement, { + childList: true, + subtree: true + }); + } +})();