199 lines
7.2 KiB
JavaScript
199 lines
7.2 KiB
JavaScript
// ==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
|
|
});
|
|
}
|
|
})();
|