Neu: YouTube Navigation-Links Fixer
This commit is contained in:
18
README.md
18
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`
|
||||
|
||||
198
youtube-restore-nav-links.user.js
Normal file
198
youtube-restore-nav-links.user.js
Normal file
@@ -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
|
||||
});
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user