// ==UserScript==
// @name YouTubeAdSolutions
// @name:ar YouTubeAdSolutions
// @name:be YouTubeAdSolutions
// @name:bg YouTubeAdSolutions
// @name:ckb YouTubeAdSolutions
// @name:cs YouTubeAdSolutions
// @name:da YouTubeAdSolutions
// @name:de YouTubeAdSolutions
// @name:el YouTubeAdSolutions
// @name:en YouTubeAdSolutions
// @name:eo YouTubeAdSolutions
// @name:es YouTubeAdSolutions
// @name:es-419 YouTubeAdSolutions
// @name:fi YouTubeAdSolutions
// @name:fr YouTubeAdSolutions
// @name:fr-CA YouTubeAdSolutions
// @name:he YouTubeAdSolutions
// @name:hr YouTubeAdSolutions
// @name:hu YouTubeAdSolutions
// @name:id YouTubeAdSolutions
// @name:it YouTubeAdSolutions
// @name:ja YouTubeAdSolutions
// @name:ka YouTubeAdSolutions
// @name:ko YouTubeAdSolutions
// @name:mr YouTubeAdSolutions
// @name:nb YouTubeAdSolutions
// @name:nl YouTubeAdSolutions
// @name:pl YouTubeAdSolutions
// @name:pt-BR YouTubeAdSolutions
// @name:ro YouTubeAdSolutions
// @name:ru YouTubeAdSolutions
// @name:sk YouTubeAdSolutions
// @name:sr YouTubeAdSolutions
// @name:sv YouTubeAdSolutions
// @name:th YouTubeAdSolutions
// @name:tr YouTubeAdSolutions
// @name:uk YouTubeAdSolutions
// @name:ug YouTubeAdSolutions
// @name:vi YouTubeAdSolutions
// @name:zh-CN YouTubeAdSolutions
// @name:zh-TW YouTubeAdSolutions
// @description Premium-Logo, Downloader, Audio-Features, Skip-Highlight, Picture-in-Picture, Miniplayer, Speed-Control, Shorts Volume Slider, Advanced Loop Mode, Screenshot Tool & Auto-Confirm.
// @description:ar شعار بريميوم، أداة تنزيل، ميزات الصوت، تخطي التمييز، صورة داخل صورة، مشغل مصغر، التحكم بالسرعة، منزلق صوت Shorts، وضع التكرار المتقدم، أداة لقطات الشاشة والتأكيد التلقائي.
// @description:be Прэміум-лагатып, загрузнік, аўдыя-функцыі, прапуск падсветкі, малюнак у малюнку, міні-плэер, кантроль хуткасці, рэгулятар гучнасці Shorts, пашыраны рэжым паўтору, інструмент скрыншотаў і аўтапацверджанне.
// @description:bg Premium лого, изтегляне, аудио функции, пропускане на акценти, картина в картината, мини плейър, контрол на скоростта, плъзгач за силата на Shorts, разширен режим на повторение, инструмент за екранни снимки и автоматично потвърждение.
// @description:ckb لۆگۆی پرێمیوم، داگرتن، تایبەتمەندیی دەنگ، تێپەڕاندنی دیاریکردن، وێنە لە ناو وێنەدا، مینی پلەیەر، کۆنترۆڵی خێرایی، سلایدەری دەنگی Shorts، دۆخی دووبارەکردنەوەی پێشکەوتوو، ئامرازی وێنەگرتن و پشتڕاستکردنەوەی خۆکار.
// @description:cs Prémiové logo, stahování, zvukové funkce, přeskočení zvýraznění, obraz v obraze, minipřehrávač, ovládání rychlosti, posuvník hlasitosti Shorts, pokročilý režim smyčky, nástroj pro snímky obrazovky a automatické potvrzení.
// @description:da Premium-logo, downloader, lydfunktioner, spring markeringer over, billede-i-billede, miniafspiller, hastighedskontrol, lydstyrkeskyder til Shorts, avanceret loop-tilstand, screenshotværktøj og automatisk bekræftelse.
// @description:de Premium-Logo, Downloader, Audio-Funktionen, Skip-Highlight, Picture-in-Picture, Miniplayer, Geschwindigkeitskontrolle, Lautstärkeregler für Shorts, Erweiterter Loop-Modus, Screenshot-Tool & Auto-Bestätigung.
// @description:el Λογότυπο Premium, λήψη, λειτουργίες ήχου, παράλειψη επισήμανσης, εικόνα σε εικόνα, mini player, έλεγχος ταχύτητας, ρυθμιστικό έντασης Shorts, προηγμένη λειτουργία επανάληψης, εργαλείο στιγμιότυπων οθόνης και αυτόματη επιβεβαίωση.
// @description:en Premium logo, downloader, audio features, skip highlight, picture-in-picture, miniplayer, speed control, Shorts volume slider, advanced loop mode, screenshot tool & auto-confirm.
// @description:eo Premium-emblemo, elŝutilo, sonaj funkcioj, preterpasi emfazon, bildo-en-bildo, mini-ludilo, rapideca kontrolo, glitilo de Shorts-laŭteco, altnivela ripeta reĝimo, ekranbilda ilo kaj aŭtomata konfirmo.
// @description:es Logotipo Premium, descargador, funciones de audio, omitir resaltado, imagen en imagen, minirreproductor, control de velocidad, control de volumen de Shorts, modo de bucle avanzado, herramienta de captura de pantalla y confirmación automática.
// @description:es-419 Logotipo Premium, descargador, funciones de audio, omitir resaltados, imagen en imagen, minirreproductor, control de velocidad, control de volumen de Shorts, modo de bucle avanzado, herramienta de captura de pantalla y confirmación automática.
// @description:fi Premium-logo, latausohjelma, ääniominaisuudet, korostusten ohitus, kuva kuvassa, minisoitin, nopeudensäätö, Shorts-äänenvoimakkuussäädin, edistynyt toistotila, kuvakaappaustyökalu ja automaattinen vahvistus.
// @description:fr Logo Premium, téléchargeur, fonctions audio, saut de surbrillance, image dans l’image, mini-lecteur, contrôle de vitesse, curseur de volume Shorts, mode boucle avancé, outil de capture d’écran et confirmation automatique.
// @description:fr-CA Logo Premium, téléchargeur, fonctions audio, saut de surbrillance, image dans l’image, mini-lecteur, contrôle de vitesse, curseur de volume Shorts, mode boucle avancé, outil de capture d’écran et confirmation automatique.
// @description:he לוגו פרימיום, הורדה, תכונות שמע, דילוג על הדגשות, תמונה בתוך תמונה, נגן מוקטן, שליטת מהירות, מחוון עוצמת קול ל-Shorts, מצב לולאה מתקדם, כלי צילום מסך ואישור אוטומטי.
// @description:hr Premium logo, preuzimanje, audio funkcije, preskakanje isticanja, slika u slici, mini player, kontrola brzine, klizač glasnoće za Shorts, napredni način petlje, alat za snimke zaslona i automatska potvrda.
// @description:hu Prémium logó, letöltő, hangfunkciók, kiemelések kihagyása, kép a képben, mini lejátszó, sebességszabályzás, Shorts hangerőcsúszka, fejlett ismétlési mód, képernyőkép eszköz és automatikus megerősítés.
// @description:id Logo Premium, pengunduh, fitur audio, lewati sorotan, gambar dalam gambar, pemutar mini, kontrol kecepatan, penggeser volume Shorts, mode loop lanjutan, alat tangkapan layar dan konfirmasi otomatis.
// @description:it Logo Premium, downloader, funzioni audio, salta evidenziazioni, picture-in-picture, mini player, controllo velocità, cursore volume Shorts, modalità loop avanzata, strumento screenshot e conferma automatica.
// @description:ja プレミアムロゴ、ダウンローダー、音声機能、ハイライトのスキップ、ピクチャーインピクチャー、ミニプレーヤー、速度調整、Shorts音量スライダー、高度なループモード、スクリーンショットツールと自動確認。
// @description:ka პრემიუმ ლოგო, ჩამოტვირთვა, აუდიო ფუნქციები, გამოკვეთების გამოტოვება, სურათი სურათში, მინი დამკვრელი, სიჩქარის კონტროლი, Shorts ხმის რეგულატორი, გაფართოებული გამეორების რეჟიმი, ეკრანის გადაღების ინსტრუმენტი და ავტომატური დადასტურება.
// @description:ko 프리미엄 로고, 다운로드, 오디오 기능, 하이라이트 건너뛰기, 화면 속 화면, 미니 플레이어, 속도 조절, Shorts 볼륨 슬라이더, 고급 반복 모드, 스크린샷 도구 및 자동 확인.
// @description:mr प्रीमियम लोगो, डाउनलोडर, ऑडिओ वैशिष्ट्ये, हायलाइट वगळा, पिक्चर-इन-पिक्चर, मिनी प्लेअर, गती नियंत्रण, Shorts व्हॉल्यूम स्लायडर, प्रगत लूप मोड, स्क्रीनशॉट साधन आणि स्वयंचलित पुष्टी.
// @description:nb Premium-logo, nedlasting, lydfunksjoner, hopp over høydepunkter, bilde-i-bilde, minispiller, hastighetskontroll, volumskyveknapp for Shorts, avansert loop-modus, skjermbildeverktøy og automatisk bekreftelse.
// @description:nl Premium-logo, downloader, audiofuncties, markeringen overslaan, picture-in-picture, minispeler, snelheidsregeling, volumeschuif voor Shorts, geavanceerde lusmodus, screenshottool en automatische bevestiging.
// @description:pl Logo Premium, pobieranie, funkcje audio, pomijanie wyróżnień, obraz w obrazie, mini odtwarzacz, kontrola prędkości, suwak głośności Shorts, zaawansowany tryb pętli, narzędzie do zrzutów ekranu i automatyczne potwierdzanie.
// @description:pt-BR Logotipo Premium, downloader, recursos de áudio, pular destaques, picture-in-picture, mini player, controle de velocidade, controle de volume do Shorts, modo de loop avançado, ferramenta de captura de tela e confirmação automática.
// @description:ro Logo Premium, descărcare, funcții audio, sărire evidențieri, imagine în imagine, mini player, control viteză, glisor volum Shorts, mod buclă avansat, instrument captură ecran și confirmare automată.
// @description:ru Премиум-логотип, загрузчик, аудиофункции, пропуск выделений, картинка в картинке, мини-плеер, контроль скорости, регулятор громкости Shorts, расширенный режим повтора, инструмент скриншотов и автоподтверждение.
// @description:sk Prémiové logo, sťahovanie, zvukové funkcie, preskočenie zvýraznení, obraz v obraze, mini prehrávač, ovládanie rýchlosti, posuvník hlasitosti Shorts, pokročilý režim slučky, nástroj na snímky obrazovky a automatické potvrdenie.
// @description:sr Premium logo, preuzimanje, audio funkcije, preskakanje isticanja, slika u slici, mini plejer, kontrola brzine, klizač jačine zvuka za Shorts, napredni režim petlje, alat za snimke ekrana i automatska potvrda.
// @description:sv Premium-logotyp, nedladdning, ljudfunktioner, hoppa över markeringar, bild-i-bild, minispelare, hastighetskontroll, volymreglage för Shorts, avancerat loop-läge, skärmdumpsverktyg och automatisk bekräftelse.
// @description:th โลโก้พรีเมียม, ดาวน์โหลด, ฟีเจอร์เสียง, ข้ามไฮไลต์, ภาพซ้อนภาพ, มินิเพลเยอร์, ควบคุมความเร็ว, ตัวปรับเสียง Shorts, โหมดวนซ้ำขั้นสูง, เครื่องมือจับภาพหน้าจอ และยืนยันอัตโนมัติ
// @description:tr Premium logo, indirici, ses özellikleri, vurguları atla, resim içinde resim, mini oynatıcı, hız kontrolü, Shorts ses kaydırıcısı, gelişmiş döngü modu, ekran görüntüsü aracı ve otomatik onay.
// @description:uk Преміум-логотип, завантажувач, аудіофункції, пропуск виділень, зображення в зображенні, мініплеєр, контроль швидкості, регулятор гучності Shorts, розширений режим повтору, інструмент скріншотів і автопідтвердження.
// @description:ug Premium لوگو، چۈشۈرگۈچ، ئاۋاز ئىقتىدارلىرى، يورۇتۇشنى ئۆتكۈزۈش، رەسىم ئىچىدە رەسىم، كىچىك قويغۇچ، سۈرئەت كونترول، Shorts ئاۋاز سىيرىغۇچى، ئىلغار ئايلاندۇرۇش ھالىتى، ئېكران رەسىم قورالى ۋە ئاپتوماتىك دەلىللەش.
// @description:vi Logo Premium, trình tải xuống, tính năng âm thanh, bỏ qua nổi bật, hình trong hình, trình phát mini, điều khiển tốc độ, thanh âm lượng Shorts, chế độ lặp nâng cao, công cụ chụp màn hình và xác nhận tự động.
// @description:zh-CN 高级徽标、下载器、音频功能、跳过高亮、画中画、迷你播放器、速度控制、Shorts 音量滑块、高级循环模式、截图工具和自动确认。
// @description:zh-TW 高級標誌、下載器、音訊功能、跳過重點、子母畫面、迷你播放器、速度控制、Shorts 音量滑桿、進階循環模式、截圖工具與自動確認。
// @namespace http://tampermonkey.net/
// @version 2.4.2
// @author Pascal
// @match https://www.youtube.com/*
// @grant none
// @icon https://www.youtube.com/s/desktop/ee47b5e0/img/logos/favicon_144x144.png
// @run-at document-start
// @license MIT
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
let audioCtx, source, gainNode, bassFilter;
let isAudioInited = false;
let isAdActive = false;
const tripleControlClass = 'custom-audio-control';
// Versteckt die nervigen roten YouTube-Fehler in der Konsole
const originalError = console.error;
console.error = (...args) => {
if (args[0] && typeof args[0] === 'string' && (args[0].includes('403') || args[0].includes('ERR_FAILED'))) return;
originalError.apply(console, args);
};
// --- 0. ANTI-BLOCK-ENFORCEMENT ---
function removeEnforcementMessage() {
const enforcement = document.querySelector('ytd-enforcement-message-view-model');
if (enforcement) {
enforcement.remove();
const video = document.querySelector('video');
if (video && video.paused) video.play();
console.log("YouTube Sperr-Dialog entfernt.");
}
const overlay = document.querySelector('tp-yt-iron-overlay-backdrop');
if (overlay) { overlay.remove(); }
}
// --- 1. TRUSTED TYPES BYPASS ---
if (window.trustedTypes && trustedTypes.createPolicy) {
if (!trustedTypes.defaultPolicy) {
try {
const passThroughFn = (x) => x;
trustedTypes.createPolicy('default', {
createHTML: passThroughFn, createScriptURL: passThroughFn, createScript: passThroughFn,
});
} catch (e) {}
}
}
// --- 2. CSS ANPASSUNGEN (Global, Shorts & Settings-Menü) ---
const style = document.createElement('style');
style.innerHTML = `
ytd-masthead, #masthead-container { z-index: 50000 !important; }
#search-form, yt-searchbox { z-index: 60000 !important; position: relative !important; }
.ytp-pip-btn svg, #custom-miniplayer-button svg { fill: white !important; }
/* SHORTS SLIDER STYLING */
.custom-shorts-slider {
position: fixed;
right: 85px;
bottom: 50%;
transform: rotate(-90deg) translateX(50%);
transform-origin: right center;
width: 600px;
height: 8px;
accent-color: #ff0000;
z-index: 999999;
cursor: pointer;
opacity: 0;
transition: opacity 0.3s, visibility 0.3s;
visibility: hidden;
filter: drop-shadow(0px 0px 4px rgba(0,0,0,0.6));
}
body[is-shorts] .custom-shorts-slider {
opacity: 0.5;
visibility: visible;
}
body[is-shorts] .custom-shorts-slider:hover {
opacity: 1;
}
/* NEU: SETTINGS DROPDOWN ANIMATION & DESIGN */
#custom-settings-dropdown {
position: absolute;
background: #282828;
border-radius: 12px;
padding: 18px;
box-shadow: 0 12px 32px rgba(0,0,0,0.7);
z-index: 2147483647; /* Damit es wirklich über allem liegt */
width: 260px;
transform-origin: top right;
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease-out;
opacity: 0;
transform: scale(0.9) translateY(-10px);
visibility: hidden;
border: 1px solid rgba(255,255,255,0.1);
font-family: "Roboto", Arial, sans-serif;
pointer-events: none;
}
#custom-settings-dropdown.open {
opacity: 1;
transform: scale(1) translateY(0);
visibility: visible;
pointer-events: auto;
}
`;
document.head.appendChild(style);
// --- KONFIGURATION ---
let config = {
pipKey: localStorage.getItem('custom-pip-key') || 'p'
};
// --- 3. AUDIO LOGIK ---
function initAudio() {
const video = document.querySelector('video');
if (!video || isAudioInited) return;
try {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
source = audioCtx.createMediaElementSource(video);
gainNode = audioCtx.createGain();
bassFilter = audioCtx.createBiquadFilter();
bassFilter.type = "lowshelf";
bassFilter.frequency.value = 150;
source.connect(bassFilter);
bassFilter.connect(gainNode);
gainNode.connect(audioCtx.destination);
isAudioInited = true;
} catch (e) { console.warn("Audio Fehler:", e); }
}
function createControlPair(color, title) {
const $container = document.createElement('span');
$container.className = tripleControlClass;
$container.style.cssText = 'display: inline-flex; align-items: center; height: 100%; vertical-align: middle; margin-right: 8px;';
const $btn = document.createElement('button');
$btn.className = 'ytp-button';
$btn.title = title;
$btn.innerHTML = ``;
$btn.style.cssText = 'display: inline-flex; align-items: center; justify-content: center; min-width: 36px; height: 100%; cursor: pointer; background: none; border: none;';
const $slider = document.createElement('input');
$slider.type = 'range';
$slider.style.cssText = `display: none; width: 8vw; accent-color: ${color}; margin: 0 5px; cursor: pointer;`;
const $label = document.createElement('span');
$label.style.cssText = `display: none; color: ${color}; font-size: 11px; font-weight: bold; min-width: 30px; font-family: Roboto;`;
$btn.onclick = (e) => {
e.preventDefault();
const show = ($slider.style.display === 'none');
$slider.style.display = $label.style.display = show ? 'inline-block' : 'none';
};
$container.append($btn, $slider, $label);
return { $container, $slider, $label };
}
// --- 4. PiP LOGIK ---
async function togglePiP() {
const video = document.querySelector('video');
if (!video) return;
try {
if (document.pictureInPictureElement) { await document.exitPictureInPicture(); }
else { await video.requestPictureInPicture(); }
} catch (err) {}
}
// --- 5. PREMIUM LOGO (Klassisch & Vordergrund-Fix) ---
const PREMIUM_SVG_CONTENT = `
`;
function applyPremiumLogo() {
// 1. Entferne "Yoodles" (Spezial-Grafiken neben dem Logo), damit nur unser Logo da ist
const yoodles = document.querySelectorAll('ytd-yoodle-renderer');
yoodles.forEach(y => {
if (y) {
y.style.display = 'none'; // Versteckt das K-Pop Bild etc.
const parent = y.closest('ytd-topbar-logo-renderer');
if (parent) {
// Aktiviert das Standard-Logo-Feld wieder, falls es durch Yoodle versteckt wurde
const hiddenLogo = parent.querySelector('#logo div[hidden]');
if (hiddenLogo) hiddenLogo.removeAttribute('hidden');
}
}
});
// 2. Ersetze die SVGs durch unser Premium-Design
const logoSelectors = [
'ytd-topbar-logo-renderer #logo svg',
'ytd-guide-renderer #logo svg',
'ytd-logo svg',
'svg#yt-ringo2-svg_yt5',
'svg#yt-ringo2-svg_yt4',
'svg#yt-ringo2-svg_yt6',
'svg#yt-ringo2-svg_yt10'
];
logoSelectors.forEach(selector => {
const elements = document.querySelectorAll(selector);
elements.forEach(svg => {
// Nur ändern, wenn noch nicht geschehen oder wenn es überschrieben wurde
if (svg && svg.getAttribute('data-is-premium') !== 'true') {
svg.setAttribute('width', '101');
svg.setAttribute('height', '20');
svg.setAttribute('viewBox', '0 0 101 20');
svg.setAttribute('data-is-premium', 'true');
svg.style.width = '101px';
svg.style.height = '20px';
svg.innerHTML = PREMIUM_SVG_CONTENT;
}
});
});
}
// --- 6. SKIP-BUTTON HIGHLIGHT ---
function highlightSkip() {
// Wir suchen breit gefächert nach allem, was ein Skip-Button sein könnte
const selectors = [
'.ytp-skip-ad-button',
'.ytp-ad-skip-button',
'.ytp-ad-skip-button-modern',
'.ytp-ad-player-overlay-layout__skip-or-preview-container button'
];
let btn = document.querySelector(selectors.join(', '));
// Falls über Klassen nichts gefunden wurde: Suche nach dem Text "Überspringen"
if (!btn) {
const buttons = document.querySelectorAll('button');
btn = Array.from(buttons).find(b => b.textContent.includes('Überspringen'));
}
if (btn && !btn.getAttribute('data-hl')) {
// Prüfen, ob der Button wirklich sichtbar gerendert wird
const rect = btn.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
btn.setAttribute('data-hl', 'true');
// Styles mit !important, um YT-Default-Styles zu überschreiben
btn.style.setProperty('border', '3px solid #ff00ff', 'important');
btn.style.setProperty('box-shadow', '0 0 15px #ff00ff', 'important');
btn.style.setProperty('border-radius', '4px', 'important');
btn.style.setProperty('z-index', '9999', 'important');
console.log("%c[Premium] Skip-Button markiert!", "color: #f06292; font-weight: bold; font-size: 12px;");
}
}
}
// --- 7. DOWNLOADER ---
function initializDownload() {
const rightControls = document.querySelector('.ytp-right-controls');
if (!rightControls || document.getElementById('dwnldBtnPlayer')) return;
const dwnldBtn = document.createElement('button');
dwnldBtn.id = 'dwnldBtnPlayer';
dwnldBtn.className = 'ytp-button';
dwnldBtn.setAttribute('title', 'Download');
dwnldBtn.style.cssText = `display: inline-flex; align-items: center; justify-content: center; vertical-align: middle; width: 60px;`;
dwnldBtn.innerHTML = ``;
let menu = document.getElementById('dwnldMenuGlobal');
if (!menu) {
menu = document.createElement('div');
menu.id = 'dwnldMenuGlobal';
document.body.appendChild(menu);
}
menu.style.cssText = `display: none; position: fixed; background: rgba(28, 28, 28, 0.98); border-radius: 12px; min-width: 200px; box-shadow: 0 8px 32px rgba(0,0,0,0.8); z-index: 2147483647; padding: 8px 0; border: 1px solid rgba(255,255,255,0.15); font-family: 'Roboto', sans-serif; backdrop-filter: blur(12px);`;
dwnldBtn.addEventListener('click', (e) => {
e.preventDefault(); e.stopPropagation();
const rect = dwnldBtn.getBoundingClientRect();
const currentId = new URLSearchParams(window.location.search).get('v');
const sources = [
{ name: '🎞️ MP3 / MP4 (Y2Mate)', url: 'https://evdfrance.fr/convert/?id=' + currentId },
{ name: '🎶 MP3 (YTMP3)', url: 'https://ytmp3.as/#' + currentId + '/mp3' },
{ name: '🚀 4K Video (Loader)', url: 'https://loader.to/api/card/?url=https://www.youtube.com/watch?v=' + currentId },
{ name: '🟢 MP4 (SaveFrom)', url: 'https://ssyoutube.com/watch?v=' + currentId },
{ name: '✂️ DVR / Edit (Dirpy)', url: 'https://dirpy.com/studio?url=https://www.youtube.com/watch?v=' + currentId },
{ name: '📺 Multi-Format (noTube)', url: 'https://notube.net/convert/de?url=https://www.youtube.com/watch?v=' + currentId },
{ name: '🛡️ Cobalt (No Ads)', url: 'https://cobalt.tools/' }
];
menu.innerHTML = "";
sources.forEach(src => {
const item = document.createElement('a');
item.href = src.url; item.target = "_blank"; item.innerText = src.name;
item.style.cssText = "display: block; padding: 12px 20px; color: white; text-decoration: none; font-size: 14px;";
item.addEventListener('click', () => { menu.style.display = 'none'; });
item.onmouseover = () => item.style.backgroundColor = "rgba(255,255,255,0.1)";
item.onmouseout = () => item.style.backgroundColor = "transparent";
menu.appendChild(item);
});
menu.style.display = 'block';
const menuRect = menu.getBoundingClientRect();
menu.style.left = (rect.left + (rect.width / 2) - (menuRect.width / 2)) + 'px';
menu.style.top = (rect.top - menuRect.height - 15) + 'px';
const close = (ev) => { if (!dwnldBtn.contains(ev.target) && !menu.contains(ev.target)) { menu.style.display = 'none'; window.removeEventListener('mousedown', close); } };
setTimeout(() => window.addEventListener('mousedown', close), 1);
}, true);
const pipBtn = document.querySelector('.ytp-pip-btn') || document.querySelector('.ytp-miniplayer-button');
if (pipBtn) { rightControls.insertBefore(dwnldBtn, pipBtn); } else { rightControls.prepend(dwnldBtn); }
}
// --- 8. LOOP LOGIK (Optimiert für YouTube-Player) ---
let loopMode = 0; // 0 = Aus, 1 = Einmal, 2 = Endlos
let hasRepeatedOnce = false;
function setupLoopWatcher() {
const video = document.querySelector('video');
if (!video || video.dataset.loopWatcherActive) return;
video.dataset.loopWatcherActive = "true";
// Wir nutzen 'timeupdate' anstatt 'onended', weil YouTube 'onended' oft blockiert
video.addEventListener('timeupdate', () => {
if (loopMode === 0) return;
// Wenn das Video fast am Ende ist (0.2 Sek davor)
if (video.currentTime >= video.duration - 0.2) {
if (loopMode === 2) {
// ENDLOS: Einfach zurück zum Anfang
video.currentTime = 0;
video.play();
} else if (loopMode === 1 && !hasRepeatedOnce) {
// EINMAL: Zurück zum Anfang, aber Status merken
video.currentTime = 0;
video.play();
hasRepeatedOnce = true;
loopMode = 0; // Danach deaktivieren
setTimeout(() => updateLoopUI(), 500);
}
}
});
}
function updateLoopUI() {
const loopBtn = document.getElementById('premium-loop-btn');
if (!loopBtn) return;
const svg = loopBtn.querySelector('svg');
const path = loopBtn.querySelector('path');
// Badge suchen oder erstellen
let badge = document.getElementById('loop-badge');
if (!badge) {
badge = document.createElement('div');
badge.id = 'loop-badge';
badge.style = 'position:absolute; top:5px; right:5px; font-size:10px; font-weight:bold; color:white; background:rgba(0,0,0,0.7); border-radius:4px; padding:1px 3px; pointer-events:none; z-index:10;';
loopBtn.style.position = 'relative';
loopBtn.appendChild(badge);
}
if (loopMode === 0) {
svg.style.filter = 'none';
path.setAttribute('stroke', 'white');
badge.style.display = 'none';
} else if (loopMode === 1) {
svg.style.filter = 'drop-shadow(0 0 5px #ff9a00)';
path.setAttribute('stroke', '#ff9a00');
badge.innerText = '1';
badge.style.display = 'block';
} else if (loopMode === 2) {
svg.style.filter = 'drop-shadow(0 0 5px #3ea6ff)';
path.setAttribute('stroke', '#3ea6ff');
badge.innerText = '∞';
badge.style.display = 'block';
}
}
function toggleVideoLoop() {
const video = document.querySelector('video');
if (!video) return;
setupLoopWatcher(); // Sicherstellen, dass der Watcher läuft
loopMode = (loopMode + 1) % 3;
hasRepeatedOnce = false;
// Wir deaktivieren YouTubes eigenen Loop, um keine Konflikte zu haben
video.loop = false;
updateLoopUI();
}
function setupLoopButton() {
const rightControls = document.querySelector('.ytp-right-controls');
if (!rightControls || document.getElementById('premium-loop-btn')) return;
const loopBtn = document.createElement('button');
loopBtn.id = 'premium-loop-btn';
loopBtn.className = 'ytp-button';
loopBtn.title = 'Loop Modus';
loopBtn.style.width = '46px';
loopBtn.style.display = 'inline-flex';
loopBtn.style.alignItems = 'center';
loopBtn.style.justifyContent = 'center';
loopBtn.innerHTML = `
`;
loopBtn.onclick = (e) => {
e.preventDefault();
toggleVideoLoop();
};
const screenBtn = document.getElementById('premium-screenshot-btn');
if (screenBtn) {
rightControls.insertBefore(loopBtn, screenBtn);
} else {
rightControls.prepend(loopBtn);
}
setupLoopWatcher();
updateLoopUI();
}
// --- 9. SCREENSHOT LOGIK ---
function takeScreenshot() {
const video = document.querySelector('video');
if (!video) return;
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
// Dateiname säubern (Titel ohne Sonderzeichen)
const fileName = document.title.replace(/[\\/:*?"<>|]/g, '').replace(/\s*-\s*YouTube/i, '');
canvas.toBlob(blob => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `Shot_${fileName}.png`;
a.click();
URL.revokeObjectURL(url);
}, 'image/png');
}
function setupScreenshotButton() {
const rightControls = document.querySelector('.ytp-right-controls');
if (!rightControls || document.getElementById('premium-screenshot-btn')) return;
const screenBtn = document.createElement('button');
screenBtn.id = 'premium-screenshot-btn';
screenBtn.className = 'ytp-button';
screenBtn.title = 'Screenshot speichern';
screenBtn.style.width = '46px';
screenBtn.style.display = 'inline-flex';
screenBtn.style.alignItems = 'center';
screenBtn.style.justifyContent = 'center';
// Das Icon (Kamera-Stil)
screenBtn.innerHTML = `
`;
screenBtn.onclick = (e) => {
e.preventDefault();
takeScreenshot();
};
// Positionierung neben dem Downloader oder PiP
const referenceBtn = document.getElementById('dwnldBtnPlayer') ||
rightControls.querySelector('.ytp-pip-btn');
if (referenceBtn) {
rightControls.insertBefore(screenBtn, referenceBtn);
} else {
rightControls.prepend(screenBtn);
}
}
// --- 10. SETUP INTERFACE ---
function setupInterface() {
const rightControls = document.querySelector('.ytp-right-controls');
const timeDisp = document.querySelector('.ytp-time-display');
const videoPlayer = document.getElementById('movie_player');
if (timeDisp && !document.querySelector('.' + tripleControlClass)) {
const savedVol = sessionStorage.getItem('custom-player-volume') ?? 0.948;
if (videoPlayer && videoPlayer.setVolume) videoPlayer.setVolume((savedVol**2)*100);
const vol = createControlPair('#FF0000', 'Volume');
vol.$slider.min = '0'; vol.$slider.max = '1'; vol.$slider.step = '0.01';
vol.$slider.value = savedVol;
vol.$label.textContent = Math.round((savedVol**2)*100) + "%";
vol.$slider.oninput = () => {
if (videoPlayer && videoPlayer.setVolume) videoPlayer.setVolume((vol.$slider.value**2)*100);
vol.$label.textContent = Math.round((vol.$slider.value**2)*100) + "%";
sessionStorage.setItem('custom-player-volume', vol.$slider.value);
};
const savedBoost = sessionStorage.getItem('custom-player-boost') ?? 1;
const boost = createControlPair('#FFFF00', 'Booster');
boost.$slider.min = '1'; boost.$slider.max = '10'; boost.$slider.step = '0.1';
boost.$slider.value = savedBoost;
boost.$label.textContent = savedBoost + "x";
boost.$slider.oninput = () => {
initAudio();
if(gainNode) gainNode.gain.value = boost.$slider.value;
boost.$label.textContent = boost.$slider.value + "x";
sessionStorage.setItem('custom-player-boost', boost.$slider.value);
};
const savedBass = sessionStorage.getItem('custom-player-bass') ?? 0;
const bass = createControlPair('#FFA500', 'Bass');
bass.$slider.min = '0'; bass.$slider.max = '30'; bass.$slider.step = '1';
bass.$slider.value = savedBass;
bass.$label.textContent = savedBass + "dB";
bass.$slider.oninput = () => {
initAudio();
if(bassFilter) bassFilter.gain.value = bass.$slider.value;
bass.$label.textContent = bass.$slider.value + "dB";
sessionStorage.setItem('custom-player-bass', bass.$slider.value);
};
timeDisp.after(bass.$container);
timeDisp.after(boost.$container);
timeDisp.after(vol.$container);
}
if (rightControls && !document.querySelector('.ytp-pip-btn')) {
const pipBtn = document.createElement('button');
pipBtn.className = 'ytp-button ytp-pip-btn';
pipBtn.title = 'Picture-in-Picture (p)';
pipBtn.style.cssText = 'display: inline-flex; align-items: center; justify-content: center;';
pipBtn.innerHTML = ``;
pipBtn.onclick = (e) => { e.preventDefault(); togglePiP(); };
rightControls.prepend(pipBtn);
}
if (rightControls && !document.getElementById('custom-miniplayer-button')) {
const miniBtn = document.createElement('button');
miniBtn.id = 'custom-miniplayer-button';
miniBtn.className = 'ytp-button';
miniBtn.title = 'Miniplayer (i)';
miniBtn.style.cssText = 'display: inline-flex; align-items: center; justify-content: center;';
miniBtn.innerHTML = ``;
miniBtn.onclick = (e) => { e.preventDefault(); document.dispatchEvent(new KeyboardEvent('keydown', { key: 'i', keyCode: 73, bubbles: true })); };
rightControls.appendChild(miniBtn);
}
}
// --- 11. SPEED CONTROLLER ---
let manualSpeed = 1.0;
function setupSpeedInterface() {
const actionsMenu = document.querySelector('#actions-inner #menu ytd-menu-renderer #top-level-buttons-computed');
const video = document.querySelector('video');
if (!actionsMenu || !video || document.getElementById('premium-speed-control')) return;
const control = document.createElement('div');
control.id = 'premium-speed-control';
control.className = 'premium-speed-logic top-level-buttons style-scope ytd-menu-renderer';
control.innerHTML = `
${video.playbackRate}x
`;
const btnDown = control.querySelector('#speed-down-btn');
const btnUp = control.querySelector('#speed-up-btn');
const display = control.querySelector('#premium-speed-display');
const updateSpeed = (delta) => {
manualSpeed = Math.round((video.playbackRate + delta) * 100) / 100;
manualSpeed = Math.max(0.25, Math.min(10, manualSpeed));
video.playbackRate = manualSpeed;
display.textContent = `${manualSpeed}x`;
};
btnDown.onclick = (e) => { e.stopPropagation(); updateSpeed(-0.25); };
btnUp.onclick = (e) => { e.stopPropagation(); updateSpeed(0.25); };
video.addEventListener('ratechange', () => {
if (!isAdActive && video.playbackRate !== manualSpeed) { video.playbackRate = manualSpeed; display.textContent = `${manualSpeed}x`; }
});
actionsMenu.appendChild(control);
}
// --- 12. HANDLE ADS ---
function handleAds() {
const video = document.querySelector('video');
const ad = document.querySelector('.ad-showing, .ad-interrupting, .ytp-ad-player-overlay');
const speedDisplay = document.querySelector('#premium-speed-display');
if (video && ad) {
isAdActive = true;
if (!video.muted) { video.muted = true; video.volume = 0; }
if (video.playbackRate !== 2.0) { video.playbackRate = 2.0; }
if (speedDisplay && speedDisplay.textContent !== "2x") { speedDisplay.textContent = "2x"; }
} else if (video && isAdActive) {
isAdActive = false; video.muted = false;
const savedVol = sessionStorage.getItem('custom-player-volume') || 0.8;
video.volume = savedVol;
video.playbackRate = manualSpeed;
if (speedDisplay) speedDisplay.textContent = manualSpeed + "x";
}
}
// --- 12-b. PRESTIGE AD-REMOVER (Optimized with Debouncing) ---
let adObserver;
let adCleanupTimer;
function setupAdRemover() {
// 1. Verhindere mehrfaches Initialisieren des Observers
if (window.hasAdObserverActive) return;
// Trusted Types Bypass (nur einmalig prüfen)
if (window.trustedTypes && trustedTypes.createPolicy && !trustedTypes.defaultPolicy) {
try {
const passThroughFn = (x) => x;
trustedTypes.createPolicy('default', {
createHTML: passThroughFn,
createScriptURL: passThroughFn,
createScript: passThroughFn,
});
} catch (e) { /* Policy existiert evtl. schon */ }
}
const cleanAds = () => {
// A) Badge-Suche (Entfernt Video-Ads im Feed)
const badges = document.querySelectorAll('badge-shape.yt-badge-shape--ad');
badges.forEach(badge => {
const adContainer = badge.closest('yt-lockup-view-model, ytd-rich-item-renderer, ytd-ad-slot-renderer, ytd-player-legacy-desktop-watch-ads-renderer');
if (adContainer) adContainer.remove();
});
// B) Selektoren-Suche (Hier wurde der neue Selektor am Ende hinzugefügt)
const adSelectors = [
'#player-ads',
'ytd-player-legacy-desktop-watch-ads-renderer',
'#masthead-ad',
'ytd-display-ad-renderer',
'ytd-companion-slot-renderer',
'ytd-promoted-sparkles-web-renderer',
'#rendering-content.ytd-fake-update-renderer',
'ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"]',
];
adSelectors.forEach(s => {
const elements = document.querySelectorAll(s);
elements.forEach(el => el.remove());
});
};
// 2. Der "Debounced" Observer
// Er reagiert auf Änderungen, wartet aber 150ms ab, bevor er die CPU belastet
adObserver = new MutationObserver(() => {
clearTimeout(adCleanupTimer);
adCleanupTimer = setTimeout(() => {
// Nutze requestAnimationFrame für maximale Geschmeidigkeit
window.requestAnimationFrame(() => cleanAds());
}, 150);
});
if (document.body) {
adObserver.observe(document.body, { childList: true, subtree: true });
window.hasAdObserverActive = true;
cleanAds(); // Initialer Run
console.log("%c[Premium] Ad-Observer aktiv (Debounced Mode)", "color: #3ea6ff");
}
}
// --- 13. SHORTS VOLUME SLIDER ---
function setupShortsUI() {
// Sicherheits-Check: Falls body noch nicht existiert oder der Slider bereits da ist, abbrechen.
if (!document.body || document.querySelector('.custom-shorts-slider')) return;
const savedVol = sessionStorage.getItem('custom-player-volume') || 0.8;
const $slider = document.createElement('input');
$slider.className = 'custom-shorts-slider';
$slider.type = 'range'; $slider.min = '0'; $slider.max = '1'; $slider.step = '0.01';
$slider.value = savedVol;
const updateAllShorts = (val) => {
document.querySelectorAll('video').forEach(v => {
if (window.location.href.includes('/shorts/')) { v.volume = val; v.muted = (val == 0); }
});
};
$slider.oninput = () => updateAllShorts($slider.value);
$slider.onchange = () => sessionStorage.setItem('custom-player-volume', $slider.value);
// Sicherstellen, dass wir an den Body anhängen
document.body.appendChild($slider);
window.removeEventListener('yt-navigate-finish', checkShortsStatus); // Dubletten vermeiden
window.addEventListener('yt-navigate-finish', checkShortsStatus);
checkShortsStatus();
}
function checkShortsStatus() {
if (window.location.href.includes('/shorts/')) {
document.body.setAttribute('is-shorts', '');
const v = document.querySelector('video');
if (v) v.volume = sessionStorage.getItem('custom-player-volume') || 0.8;
} else { document.body.removeAttribute('is-shorts'); }
}
// --- 14. ANTI-PAUSE-DIALOG LOGIK ---
function autoConfirmVideoPaused() {
const confirmBtn = document.querySelector('yt-confirm-dialog-renderer #confirm-button button');
if (confirmBtn) {
confirmBtn.click();
console.log("%c[Premium] 'Video angehalten'-Dialog automatisch bestätigt.", "color: #00ffff");
const video = document.querySelector('video');
if (video && video.paused) { video.play(); }
}
}
// --- 15: SETTINGS ZAHNRAD (AN KERNSCHNITT GEKOPPELT) ---
function addSettingsGear() {
const GEAR_ID = 'premium-settings-gear';
const SPEED_DISPLAY_ID = 'premium-speed-display';
// 1. Suche die Speed-Anzeige als Ankerpunkt
const speedDisplay = document.getElementById(SPEED_DISPLAY_ID);
if (!speedDisplay) {
// Falls Speed-Anzeige noch nicht da, kurz warten (YouTube Speed-UI braucht oft länger)
setTimeout(addSettingsGear, 250);
return;
}
// 2. Prüfen, ob das Zahnrad schon existiert
if (document.getElementById(GEAR_ID)) return;
// 3. Den Eltern-Container der Speed-Anzeige finden (das Div mit flex)
const anchorContainer = speedDisplay.parentElement.parentElement;
// 4. Das Zahnrad erstellen
const gearButton = document.createElement('button');
gearButton.id = GEAR_ID;
gearButton.className = 'yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m yt-spec-button-shape-next--icon-button';
// Style-Optimierung: Direkt daneben mit etwas Abstand
gearButton.style.marginLeft = '12px';
gearButton.style.display = 'inline-flex';
gearButton.style.alignItems = 'center';
gearButton.style.justifyContent = 'center';
gearButton.title = 'Script Settings';
gearButton.innerHTML = `
`;
gearButton.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
toggleSettingsMenu(gearButton);
};
// 5. Direkt NACH der Speed-Steuerung einfügen
anchorContainer.appendChild(gearButton);
}
// --- 16. SLIDE-OUT SETTINGS MENU (ENGLISH) ---
function toggleSettingsMenu(anchor) {
let menu = document.getElementById('custom-settings-dropdown');
if (!menu) {
menu = document.createElement('div');
menu.id = 'custom-settings-dropdown';
document.body.appendChild(menu);
}
const isOpen = menu.classList.contains('open');
if (isOpen) {
menu.classList.remove('open');
} else {
menu.innerHTML = `
Script Settings
PREMIUM
`;
const rect = anchor.getBoundingClientRect();
menu.style.top = (rect.bottom + window.scrollY + 12) + 'px';
menu.style.left = (rect.left + window.scrollX - 210) + 'px';
const input = menu.querySelector('#new-pip-key');
// Auto-Replace Logic
input.onkeydown = (e) => {
if (e.key.length === 1) {
input.value = e.key.toLowerCase();
e.preventDefault();
}
};
setTimeout(() => {
menu.classList.add('open');
input.focus();
input.select();
}, 10);
// Save
document.getElementById('save-settings-btn').onclick = () => {
const val = input.value.toLowerCase();
if (val && val.length === 1) {
config.pipKey = val;
localStorage.setItem('custom-pip-key', val);
menu.classList.remove('open');
console.log("%c[Premium] Settings saved!", "color: #3ea6ff");
}
};
// Reset
document.getElementById('reset-settings-btn').onclick = () => {
config.pipKey = 'p';
localStorage.setItem('custom-pip-key', 'p');
menu.classList.remove('open');
console.log("%c[Premium] Reset to default", "color: #ff4d4d");
};
const closeHandler = (ev) => {
if (!menu.contains(ev.target) && !anchor.contains(ev.target)) {
menu.classList.remove('open');
window.removeEventListener('mousedown', closeHandler);
}
};
window.addEventListener('mousedown', closeHandler);
}
}
// --- 17. DYNAMISCHE TASTATUR-STEUERUNG (Pascal-Fix) ---
document.addEventListener('keydown', (e) => {
// Verhindert das Auslösen, wenn du in Suchfeldern tippst
const activeEl = document.activeElement;
if (activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA' || activeEl.isContentEditable) return;
// Holt die aktuell gespeicherte Taste aus deiner Config
const currentPipKey = config.pipKey.toLowerCase();
if (e.key.toLowerCase() === currentPipKey) {
const video = document.querySelector('video');
if (video) {
e.preventDefault();
e.stopImmediatePropagation(); // WICHTIG: YouTube darf die Taste nicht mehr hören!
togglePiP(); // Deine Funktion aus Part 1
console.log(`%c[Premium] PiP Modus mit Taste "${currentPipKey}" aktiv!`, "color: #3ea6ff; font-weight: bold;");
}
}
}, true); // Das 'true' sorgt dafür, dass DEIN Script die Taste VOR YouTube abfängt.
// --- 18. Engine (Optimiert für Speed) ---
let navTimer;
function smoothRun(fn, priority = false) {
// Wenn priority=true, verkürzen wir den Timeout drastisch
const waitTime = priority ? 300 : 1200;
if (window.requestIdleCallback) {
window.requestIdleCallback(() => fn(), { timeout: waitTime });
} else {
window.requestAnimationFrame(() => fn());
}
}
function triggerMasterUpdate(isFastTrack = false) {
// Führt alle wichtigen UI-Elemente aus
const coreTasks = [
setupInterface,
initializDownload,
setupScreenshotButton,
setupLoopButton,
setupSpeedInterface,
setupShortsUI,
applyPremiumLogo,
removeEnforcementMessage,
setupAdRemover,
addSettingsGear
];
coreTasks.forEach(task => smoothRun(task, isFastTrack));
}
// Sofort-Start
if (document.readyState === 'complete') triggerMasterUpdate(true);
else window.addEventListener('load', () => triggerMasterUpdate(true));
// --- 19. OPTIMIZED SELF-CHECK ENGINE (Mit Logging) ---
let repairStats = { checks: 0, repairs: 0 };
async function runGlobalRepair(isFastTrack = false) {
repairStats.checks++;
const tasks = [
{ name: "Logo", fn: applyPremiumLogo },
{ name: "Ad-Remover", fn: setupAdRemover },
{ name: "Interface", fn: setupInterface },
{ name: "Downloader", fn: initializDownload },
{ name: "Screenshot", fn: setupScreenshotButton },
{ name: "Loop", fn: setupLoopButton },
{ name: "Speed-UI", fn: setupSpeedInterface },
{ name: "Anti-Block", fn: removeEnforcementMessage },
{ name: "Settings-Gear", fn: addSettingsGear },
{ name: "Auto-Confirm", fn: autoConfirmVideoPaused },
{ name: "Skip-Highlight", fn: highlightSkip }
];
for (const task of tasks) {
smoothRun(task.fn, isFastTrack);
// PRÄZISER CHECK FÜR ALLE BUTTONS
const checkList = {
"Settings-Gear": "#premium-settings-gear",
"Speed-UI": "#premium-speed-control",
"Screenshot": "#premium-screenshot-btn",
"Loop": "#premium-loop-btn"
};
if (checkList[task.name]) {
const selector = checkList[task.name];
const el = document.querySelector(selector);
if (!el) {
task.fn(); // Neu erstellen, wenn gelöscht
} else if (window.getComputedStyle(el).display === 'none' || el.hasAttribute('hidden')) {
// Sichtbarkeit erzwingen
el.style.setProperty('display', 'flex', 'important');
el.removeAttribute('hidden');
}
}
if (isFastTrack) await new Promise(resolve => setTimeout(resolve, 0));
}
// Logs aus der alten Version
if (isFastTrack) {
console.log(`%c[System] Alle ${tasks.length} Module aktiv.`, "color: #bada55; font-size: 12px;");
}
if (repairStats.checks % 10 === 0) {
console.log(`%c[Smart-Check] Puls: Aktiv | Durchlauf: ${repairStats.checks}`, "color: #3ea6ff; font-size: 12px;");
}
}
// Navigation-Event
window.addEventListener('yt-navigate-finish', () => {
clearTimeout(navTimer);
navTimer = setTimeout(() => {
console.log("%c[Navigation] Seite gewechselt - Fast-Track Reparatur...", "color: #00ff00; font-weight: bold; font-size: 12px;");
runGlobalRepair(true);
}, 50);
});
// Hintergrund-Check (alle 30 Sek)
setInterval(() => {
if (!document.hidden) {
runGlobalRepair(false);
}
}, 30000);
// Der 250ms Loop für schnelle Reaktionen (Ads & UI-Fix)
setInterval(() => {
window.requestAnimationFrame(() => {
// Ad-Handling
handleAds();
// NEU: Highlight-Check (sehr wichtig für die schnelle Anzeige)
highlightSkip();
// Interface-Fix unter dem Video
const actionContainer = document.querySelector('#top-level-buttons-computed') ||
document.querySelector('#actions-inner #menu ytd-menu-renderer .top-level-buttons');
if (actionContainer) {
if (!document.getElementById('premium-speed-control')) setupSpeedInterface();
if (!document.getElementById('premium-settings-gear')) addSettingsGear();
}
});
}, 250);
// --- ENDE ---
})();