// ==UserScript== // @name Boosteroid Optimizer Plus by Derfog // @name:fr Boosteroid Optimizer Plus par Derfog // @namespace https://github.com/derfog // @version 3.7.2 // @description Ultimate Boosteroid optimizer: SLIM & FAST, Smart Resolution, 5 Presets, requestIdleCallback, Zero VRAM leak // @description:fr Optimiseur ultime Boosteroid: SLIM & FAST, Résolution intelligente, 5 Presets, requestIdleCallback, Zéro fuite VRAM // @author Derfog // @license MIT // @copyright 2024-2025, Derfog (https://github.com/derfog) // @homepageURL https://github.com/derfog/boosteroid-optimizer-plus // @supportURL https://github.com/derfog/boosteroid-optimizer-plus/issues // @match https://cloud.boosteroid.com/* // @match https://*.boosteroid.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain_url=https%3A%2F%2Fboosteroid.com // @run-at document-start // @grant unsafeWindow // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // ==/UserScript== /** * BOOSTEROID OPTIMIZER PLUS v3.7.2 "Smart Quality" by DERFOG * Copyright (c) 2024-2025 Derfog - MIT License * * v3.7.2: Smart Quality Edition * - NEW: Presets désactivés par défaut (tier OFF) - l'utilisateur choisit * - NEW: getScreenDetails API for screen detection * - SECURITY: Removed global SmartResolutionDetector exposure (XSS prevention) * - Upscale Only + SUPPORTED_RESOLUTIONS whitelist * - Retry system for filter presets */ (function () { 'use strict'; // =============================================================================== // AXE 1: ENVIRONMENT DETECTION & PROFILING (avec fallbacks robustes) // =============================================================================== const ENV_PROFILE = (function() { // Helpers pour accès sécurisé aux APIs navigateur const safeGet = (fn, fallback) => { try { return fn() ?? fallback; } catch (e) { return fallback; } }; const ua = navigator.userAgent || ''; const cores = safeGet(() => navigator.hardwareConcurrency, 4); const memory = safeGet(() => navigator.deviceMemory, 4); // GB - non supporté sur Firefox/Safari // matchMedia peut échouer sur certains navigateurs TV let isTouch = false; try { isTouch = window.matchMedia && matchMedia('(pointer: coarse)').matches; } catch (e) { isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0; } const width = safeGet(() => window.innerWidth || document.documentElement.clientWidth, 1920); const height = safeGet(() => window.innerHeight || document.documentElement.clientHeight, 1080); const dpr = safeGet(() => window.devicePixelRatio, 1); // Network Information API (non supportée partout) let connection = null; let effectiveType = '4g'; let downlink = null; let saveData = false; try { connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; if (connection) { effectiveType = connection.effectiveType || '4g'; downlink = connection.downlink || null; saveData = connection.saveData || false; } } catch (e) { console.log('[Optimizer+] Network API non disponible, utilisation des valeurs par défaut'); } // Détection UA const isMobile = /Android|iPhone|iPad|iPod|Windows Phone/i.test(ua); const isTablet = /iPad|Android(?!.*Mobile)/i.test(ua); // Fix: Ajout patterns TV modernes (Steam Link, Shield, Chromecast, Android TV, tvOS) const isTVByUA = /SmartTV|Tizen|WebOS|NetCast|HbbTV|BRAVIA|AFT|Fire TV|Hisense|VIDAA|Roku|PlayStation|Xbox|Steam Link|SHIELD|Chromecast|Android TV|tvOS|GoogleTV|Apple TV/i.test(ua); // Heuristique: Grand écran (>1920) + DPR 1.0 + pas mobile/tablet = probablement TV const isTVByHeuristic = (width > 1920 && height > 1080 && dpr === 1.0 && !isMobile && !isTablet); const isTV = isTVByUA || isTVByHeuristic; const isFirefox = /Firefox/i.test(ua); const isChrome = /Chrome|Chromium|CriOS/i.test(ua); const isSafari = /Safari/i.test(ua) && !/Chrome/i.test(ua); const isEdge = /Edg/i.test(ua); const isOldBrowser = /MSIE|Trident|Edge\/\d+\./i.test(ua); // Ancien Edge non-Chromium // Fix: Ne pas utiliser effectiveType pour classifier Low-End sur desktop (faux positifs '4g') const isSlowByNetwork = isMobile && (effectiveType === '3g' || effectiveType === '2g'); // Classification de puissance (avec logique améliorée) const isLowEnd = cores <= 4 || memory <= 4 || isSlowByNetwork || isOldBrowser; const isHighEnd = cores > 8 && memory > 8 && !isMobile && !isTablet; const isMidRange = !isLowEnd && !isHighEnd; // Classification d'écran const isSmallScreen = width < 1280 || height < 720; const isMediumScreen = width >= 1280 && width < 1920; const isLargeScreen = width >= 1920 && height >= 1080; return { // Device type isMobile, isTablet, isTV, isSmallScreen, isMediumScreen, isLargeScreen, // Performance class cores, memory, isLowEnd, isMidRange, isHighEnd, isOldBrowser, // Browser & rendering isFirefox, isChrome, isSafari, isEdge, // Network effectiveType, downlink, saveData, // Fix: isSlowNetwork utilise downlink en priorité, effectiveType seulement sur mobile isSlowNetwork: (downlink !== null && downlink > 0 && downlink < 5) || (isMobile && (effectiveType === '3g' || effectiveType === '2g')), isFastNetwork: (downlink !== null && downlink >= 15) || (!isMobile && effectiveType === '4g'), // Display width, height, dpr, // Input isTouch, // Summary string for logging summary() { const device = this.isTV ? 'TV' : (this.isMobile ? 'Mobile' : (this.isTablet ? 'Tablet' : 'Desktop')); const perf = this.isLowEnd ? 'Low-End' : (this.isHighEnd ? 'High-End' : 'Mid-Range'); const net = this.isSlowNetwork ? 'Slow' : (this.isFastNetwork ? 'Fast' : 'Normal'); return `${device} | ${perf} (${this.cores}c/${this.memory}GB) | ${net} | ${this.width}x${this.height}`; } }; })(); console.log('[Optimizer+] ======================================='); console.log('[Optimizer+] Device Profile:', ENV_PROFILE.summary()); // =============================================================================== // SIGNATURE & PROTECTION // =============================================================================== const SCRIPT_SIGNATURE = { author: 'Derfog', version: '3.7.2', hash: 'BOP372SQ2025', // Smart Quality - v3.7.2 verify: () => { const meta = document.querySelector('meta[name="optimizer-author"]'); if (!meta) { const m = document.createElement('meta'); m.name = 'optimizer-author'; m.content = 'Derfog'; document.head.appendChild(m); } return true; } }; SCRIPT_SIGNATURE.verify(); // =============================================================================== // BASE CONFIGURATION (High-End defaults) // =============================================================================== const CONFIG = { // Résolution forcée - v3.7.2: isAuto=true utilise la résolution native de l'écran resolution: { width: 3840, height: 2160, pixelRatio: 2, isAuto: true // v3.7.2: Mode auto par défaut = résolution native }, // Codec préférences codecs: { forceAV1: true, forceHEVC: true, forceVP9: true, preferHardware: true }, // Bitrate et qualité streaming: { maxBitrate: 50000000, minBitrate: 15000000, targetBitrate: 35000000, bufferSize: 2000, forceHighQuality: true, interceptorEnabled: false // Opt-in: Stream Interceptor désactivé par défaut }, // PERFORMANCE & LATENCE performance: { lowLatencyMode: true, targetLatency: 12, jitterBufferTarget: 40, jitterBufferMax: 80, decodeLatencyTarget: 4, prioritizeFramerate: true, gpuAcceleration: true, reducedFiltersInGame: true, // v3.6.1: maxFiltersActive dynamique selon profil matériel maxFiltersActive: ENV_PROFILE.isHighEnd ? 5 : (ENV_PROFILE.isMidRange ? 3 : 2), disableLogsInGame: true, adaptiveQuality: true, fpsThreshold: 55, streamInterceptor: true // Opt-in: intercepte configs pour forcer HW decode }, // Video Enhancer enhancer: { enabled: false, // v3.7.2: Désactivé par défaut - l'utilisateur choisit sharpness: 0.45, contrast: 1.0, // Valeurs neutres saturation: 1.0, brightness: 1.0 }, // Filtres vidéo avancés filters: { enabled: false, // v3.7.2: Désactivé par défaut - l'utilisateur choisit preset: null, // v3.7.2: Aucun preset actif par défaut usm: { enabled: false, amount: 0.35, radius: 0.9, threshold: 0.04 }, cas: { enabled: false, sharpness: 0.45 }, clarity: { enabled: false, amount: 0.2 }, denoise: { enabled: false, strength: 0.2 }, vibrance: { enabled: false, amount: 0.2 }, gamma: { enabled: false, value: 1.0 }, exposure: { enabled: false, value: 0 }, deband: { enabled: false, strength: 0.3 } }, // DRM Bypass drm: { forceDolbyVision: false, forceHDCP: false, forceUHD: true, forceALL: false }, // Display & Ultrawide (v3.6) display: { ultrawideMode: false, // Ultrawide stretch mode toggle autoDetect: true, // Auto-activer ultrawide si écran 21:9+ performanceMode: false // Mode performance: désactive les filtres lourds }, // Langue (auto-détectée ou choisie) language: 'auto' }; // Valeurs par défaut pour le Reset (copie immutable) const DEFAULT_CONFIG = { resolution: { width: 3840, height: 2160, pixelRatio: 2, isAuto: true }, enhancer: { enabled: true, sharpness: 0.45, contrast: 1.04, saturation: 1.01, brightness: 1.0 }, filters: { enabled: true, preset: 'default', usm: { enabled: true, amount: 0.35, radius: 0.9, threshold: 0.04 }, cas: { enabled: true, sharpness: 0.45 }, clarity: { enabled: false, amount: 0.2 }, denoise: { enabled: false, strength: 0.2 }, vibrance: { enabled: false, amount: 0.2 }, gamma: { enabled: false, value: 1.0 }, exposure: { enabled: false, value: 0 }, deband: { enabled: false, strength: 0.3 } }, language: 'auto' }; // =============================================================================== // AXE 1: ADAPTIVE CONFIG BUILDER // =============================================================================== function buildAdaptiveConfig(baseConfig, envProfile) { const cfg = JSON.parse(JSON.stringify(baseConfig)); // ------------------------------------------------------------------------- // Tier 1 : Low-End / Slow Network // ------------------------------------------------------------------------- if (envProfile.isLowEnd || envProfile.isSlowNetwork) { console.log('[Optimizer+] Adapting to Low-End/Slow profile'); cfg.resolution = { width: 1920, height: 1080, pixelRatio: 1, isAuto: false }; cfg.streaming = { maxBitrate: 15000000, minBitrate: 8000000, targetBitrate: 12000000, bufferSize: 3000, forceHighQuality: true }; cfg.performance.maxFiltersActive = 2; cfg.performance.adaptiveQuality = true; cfg.filters.usm.amount = 0.25; cfg.filters.cas.sharpness = 0.35; cfg.filters.clarity.enabled = false; cfg.filters.denoise.enabled = false; cfg.filters.vibrance.enabled = false; cfg.filters.deband.enabled = false; } // ------------------------------------------------------------------------- // Tier 2 : Mid-Range // ------------------------------------------------------------------------- else if (envProfile.isMidRange) { console.log('[Optimizer+] Adapting to Mid-Range profile'); cfg.resolution = { width: 2560, height: 1440, pixelRatio: 1, isAuto: false }; cfg.streaming = { maxBitrate: 30000000, minBitrate: 12000000, targetBitrate: 25000000, bufferSize: 2500, forceHighQuality: true }; cfg.performance.maxFiltersActive = 4; cfg.filters.usm.amount = 0.35; cfg.filters.cas.sharpness = 0.45; cfg.filters.clarity.enabled = true; cfg.filters.deband.enabled = true; } // Tier 3 : High-End = config par défaut (isAuto: true) // ------------------------------------------------------------------------- // Device-specific overrides // ------------------------------------------------------------------------- if (envProfile.isMobile || envProfile.isTablet) { cfg.streaming.maxBitrate = Math.min(cfg.streaming.maxBitrate, 20000000); cfg.streaming.targetBitrate = Math.min(cfg.streaming.targetBitrate, 18000000); cfg.performance.maxFiltersActive = Math.min(cfg.performance.maxFiltersActive, 3); } if (envProfile.isTV) { cfg.performance.lowLatencyMode = true; cfg.performance.targetLatency = 10; cfg.performance.jitterBufferTarget = 30; } if (envProfile.isSmallScreen) { cfg.resolution.width = Math.min(cfg.resolution.width, 1920); cfg.resolution.height = Math.min(cfg.resolution.height, 1080); } if (envProfile.isSafari) { cfg.performance.preferHardware = false; } if (envProfile.saveData) { cfg.streaming.maxBitrate = Math.round(cfg.streaming.maxBitrate * 0.7); cfg.filters.enabled = false; } return cfg; } // Appliquer la configuration adaptative const EFFECTIVE_CONFIG = buildAdaptiveConfig(CONFIG, ENV_PROFILE); // Remplacer CONFIG par EFFECTIVE_CONFIG pour le reste du script Object.assign(CONFIG, EFFECTIVE_CONFIG); console.log('[Optimizer+] Adaptive config applied:', `${CONFIG.resolution.width}x${CONFIG.resolution.height}`, `@ ${Math.round(CONFIG.streaming.maxBitrate/1000000)}Mbps` ); // =============================================================================== // AXE 3: FILTER TIERING SYSTEM // =============================================================================== const FilterTiers = { // v3.7.2: OFF = aucun filtre activé par défaut, l'utilisateur choisit OFF: { name: 'Désactivé', filters: { usm: { enabled: false }, cas: { enabled: false }, clarity: { enabled: false }, denoise: { enabled: false }, vibrance: { enabled: false }, gamma: { enabled: false }, exposure: { enabled: false }, deband: { enabled: false } }, enhancer: { contrast: 1.0, saturation: 1.0, brightness: 1.0 } }, SAFE: { name: 'Safe Mode', filters: { usm: { enabled: false }, cas: { enabled: false }, clarity: { enabled: false }, denoise: { enabled: false }, vibrance: { enabled: false }, gamma: { enabled: false }, exposure: { enabled: false }, deband: { enabled: false } }, enhancer: { contrast: 1.02, saturation: 1.01, brightness: 1.0 } }, LIGHT: { name: 'Light', filters: { usm: { enabled: true, amount: 0.25 }, cas: { enabled: true, sharpness: 0.35 }, clarity: { enabled: false }, denoise: { enabled: false }, vibrance: { enabled: false }, gamma: { enabled: false }, exposure: { enabled: false }, deband: { enabled: false } }, enhancer: { contrast: 1.04, saturation: 1.01, brightness: 1.0 } }, NORMAL: { name: 'Balanced', filters: { usm: { enabled: true, amount: 0.35 }, cas: { enabled: true, sharpness: 0.45 }, clarity: { enabled: true, amount: 0.2 }, denoise: { enabled: false }, vibrance: { enabled: false }, gamma: { enabled: false }, exposure: { enabled: false }, deband: { enabled: true, strength: 0.2 } }, enhancer: { contrast: 1.04, saturation: 1.01, brightness: 1.0 } }, ULTRA: { name: 'Ultra', filters: { usm: { enabled: true, amount: 0.45 }, cas: { enabled: true, sharpness: 0.55 }, clarity: { enabled: true, amount: 0.3 }, denoise: { enabled: true, strength: 0.15 }, vibrance: { enabled: true, amount: 0.15 }, gamma: { enabled: false }, exposure: { enabled: false }, deband: { enabled: true, strength: 0.3 } }, enhancer: { contrast: 1.05, saturation: 1.02, brightness: 1.0 } } }; // v3.7.2: Par défaut, aucun preset actif - l'utilisateur choisit function getInitialFilterTier(envProfile) { // Retourner OFF par défaut - l'utilisateur active manuellement le preset souhaité return 'OFF'; } const FilterState = { currentTier: getInitialFilterTier(ENV_PROFILE), adaptiveEnabled: CONFIG.performance.adaptiveQuality, fpsHistory: [], fpsThreshold: CONFIG.performance.fpsThreshold || 55, lastAutoChange: 0, // v3.6.2: Cooldown pour éviter boucle infinie updateTierBasedOnFps(currentFps) { if (!this.adaptiveEnabled) return; // v3.6.2: Cooldown de 10 secondes entre changements auto if (Date.now() - this.lastAutoChange < 10000) return; this.fpsHistory.push(currentFps); // v3.6.4: Limiter à 20 éléments pour éviter memory leak if (this.fpsHistory.length > 20) this.fpsHistory.shift(); const avgFps = this.fpsHistory.reduce((a, b) => a + b, 0) / this.fpsHistory.length; // v3.6.2: Auto-activation du Performance Mode si FPS critiques if (avgFps < this.fpsThreshold && !CONFIG.display.performanceMode) { console.warn('[Optimizer+] [!] FPS critique (' + Math.round(avgFps) + '), activation auto du Performance Mode'); CONFIG.display.performanceMode = true; // Désactiver les filtres lourds CONFIG.filters.clarity.enabled = false; CONFIG.filters.denoise.enabled = false; CONFIG.filters.deband.enabled = false; CONFIG.performance.gpuAcceleration = false; // Forcer le tier SAFE this.setFilterTier('SAFE'); // Mettre à jour les filtres if (typeof videoEnhancer !== 'undefined' && videoEnhancer.updateFilterString) { videoEnhancer.updateFilterString(); videoEnhancer.applyFiltersToAllVideos(); } // Notification utilisateur if (typeof showNotification === 'function') { showNotification(' Performance Mode auto-activé (FPS < ' + this.fpsThreshold + ')'); } // Mettre à jour le toggle UI si présent const toggle = document.getElementById('optimizer-performance-mode'); if (toggle) toggle.checked = true; return; // Ne pas continuer le calcul de tier } const newTier = this.calculateOptimalTier(avgFps); if (newTier !== this.currentTier) { this.setFilterTier(newTier); this.lastAutoChange = Date.now(); // v3.6.2: Reset cooldown } }, calculateOptimalTier(avgFps) { if (avgFps >= this.fpsThreshold) { if (this.currentTier === 'SAFE') return 'LIGHT'; if (this.currentTier === 'LIGHT') return 'NORMAL'; if (this.currentTier === 'NORMAL') return 'ULTRA'; } else if (avgFps < this.fpsThreshold * 0.7) { if (this.currentTier === 'ULTRA') return 'NORMAL'; if (this.currentTier === 'NORMAL') return 'LIGHT'; if (this.currentTier === 'LIGHT') return 'SAFE'; } return this.currentTier; }, // v3.7.0: Pending tier pour retry si videoEnhancer n'existe pas encore _pendingTier: null, _retryCount: 0, _maxRetries: 50, // 50 * 100ms = 5 secondes max setFilterTier(tierName) { if (tierName === this.currentTier) return; const tier = FilterTiers[tierName]; if (!tier) return; console.log(`[Optimizer+] Filter tier: ${this.currentTier} -> ${tierName}`); this.currentTier = tierName; // Si OFF, désactiver tous les filtres et ne rien appliquer if (tierName === 'OFF') { Object.keys(tier.filters).forEach(filterName => { if (CONFIG.filters[filterName]) { CONFIG.filters[filterName].enabled = false; } }); Object.assign(CONFIG.enhancer, tier.enhancer); // Retirer les filtres des vidéos existantes if (typeof videoEnhancer !== 'undefined' && videoEnhancer.removeFiltersFromAllVideos) { videoEnhancer.removeFiltersFromAllVideos(); } else { // Fallback: retirer manuellement les filtres document.querySelectorAll('video').forEach(video => { video.style.filter = ''; }); } console.log('[Optimizer+] [OK] Tous les filtres désactivés'); return; } // Appliquer les filtres du tier en mémoire Object.keys(tier.filters).forEach(filterName => { if (CONFIG.filters[filterName]) { Object.assign(CONFIG.filters[filterName], tier.filters[filterName]); } }); Object.assign(CONFIG.enhancer, tier.enhancer); // v3.7.0: Système de retry robuste this._pendingTier = tierName; this._retryCount = 0; this._applyWithRetry(); }, // v3.7.0: Appliquer les filtres avec retry automatique _applyWithRetry() { if (!this._pendingTier) return; if (typeof videoEnhancer !== 'undefined' && videoEnhancer.updateFilterString) { // videoEnhancer existe, appliquer les filtres videoEnhancer.updateFilterString(); videoEnhancer.updateSVGFilters(); videoEnhancer.applyFiltersToAllVideos(); // Valider que les filtres sont appliqués setTimeout(() => this._validateFiltersApplied(), 200); this._pendingTier = null; this._retryCount = 0; console.log('[Optimizer+] [OK] Filters applied successfully'); } else if (this._retryCount < this._maxRetries) { // Retry après 100ms this._retryCount++; setTimeout(() => this._applyWithRetry(), 100); if (this._retryCount === 1) { console.log('[Optimizer+] videoEnhancer not ready, retrying...'); } } else { // Timeout après 5 secondes console.warn('[Optimizer+] [!] Failed to apply filters after 5s - videoEnhancer not available'); this._pendingTier = null; this._retryCount = 0; } }, // v3.7.0: Valider que les filtres sont vraiment appliqués _validateFiltersApplied() { const videos = document.querySelectorAll('video'); if (videos.length === 0) return; // Pas de vidéo encore let allValid = true; videos.forEach(video => { const hasFilter = video.style.filter && video.style.filter.includes('url(#optimizer-'); const svgExists = document.getElementById('optimizer-usm-filter'); if (!hasFilter && !svgExists) { allValid = false; } }); if (!allValid && this._retryCount < 10) { // Retry l'application this._retryCount++; if (typeof videoEnhancer !== 'undefined') { videoEnhancer.applyFiltersToAllVideos(); } setTimeout(() => this._validateFiltersApplied(), 300); } } }; console.log('[Optimizer+] Initial filter tier:', FilterState.currentTier); // =============================================================================== // AXE 3: FPS MONITOR CLASS // =============================================================================== class FpsMonitor { constructor() { this.fps = 0; this.lastTime = performance.now(); this.frameCount = 0; this.animationFrameId = null; this.enabled = false; this.callbacks = []; } start() { if (this.enabled) return; this.enabled = true; const loop = () => { this.frameCount++; const now = performance.now(); const elapsed = now - this.lastTime; // v3.6.1: Intervalle adaptatif - 1000ms si fps < 55, sinon 500ms const updateInterval = this.fps > 0 && this.fps < 55 ? 1000 : 500; if (elapsed >= updateInterval) { this.fps = Math.round((this.frameCount * 1000) / elapsed); this.frameCount = 0; this.lastTime = now; // Adapter le tier de filtres FilterState.updateTierBasedOnFps(this.fps); // Exécuter les callbacks this.callbacks.forEach(cb => cb(this.fps)); } if (this.enabled) { this.animationFrameId = requestAnimationFrame(loop); } }; this.animationFrameId = requestAnimationFrame(loop); console.log('[Optimizer+] FPS monitor started'); } stop() { if (!this.enabled) return; this.enabled = false; if (this.animationFrameId) { cancelAnimationFrame(this.animationFrameId); this.animationFrameId = null; } console.log('[Optimizer+] FPS monitor stopped'); } onFpsUpdate(callback) { this.callbacks.push(callback); } getFps() { return this.fps; } } // Instance globale du FPS Monitor (sera démarré en streaming) let fpsMonitor = null; // =============================================================================== // v3.5 AXE 1: STREAM INTERCEPTOR - Force HW Decode + GPU Acceleration // Inspiré de BetterXCloud pattern pour intercepter les configs streaming // =============================================================================== const StreamInterceptor = { originalFetch: null, enabled: false, /** * Intercepter les réponses de configuration pour forcer HW decode */ enable() { if (this.enabled) return; this.enabled = true; // Sauvegarder le fetch original this.originalFetch = window.fetch; const self = this; window.fetch = async function(...args) { const request = args[0]; const url = typeof request === 'string' ? request : request?.url; // Intercepter /configuration pour forcer décodage HW if (url && (url.includes('/configuration') || url.includes('/session') || url.includes('/streaming'))) { try { const response = await self.originalFetch.apply(window, args); if (response.ok) { const clonedResponse = response.clone(); try { const config = await clonedResponse.json(); // Force HW decoding + GPU accel if (config) { if (!config.clientStreamingConfigOverrides) { config.clientStreamingConfigOverrides = '{}'; } let overrides = {}; try { overrides = JSON.parse(config.clientStreamingConfigOverrides); } catch (e) { overrides = {}; } // [*] Force codec le plus performant overrides.videoConfiguration = overrides.videoConfiguration || {}; overrides.videoConfiguration.enableHardwareDecoding = true; overrides.videoConfiguration.hardwareDecoderProfile = 'high'; overrides.videoConfiguration.enableRtcStatsCollection = true; overrides.videoConfiguration.preferredCodec = 'av1'; // AV1 si supporté // Force high bitrate settings overrides.bitrateConfiguration = overrides.bitrateConfiguration || {}; overrides.bitrateConfiguration.maxBitrate = CONFIG.streaming.maxBitrate; overrides.bitrateConfiguration.targetBitrate = CONFIG.streaming.targetBitrate; config.clientStreamingConfigOverrides = JSON.stringify(overrides); console.log('[Optimizer+] [OK] StreamInterceptor: Config enrichie avec HW decode + codec optimisé'); return new Response(JSON.stringify(config), { status: response.status, statusText: response.statusText, headers: response.headers }); } } catch (parseError) { // Pas du JSON, retourner la réponse originale } } return response; } catch (e) { console.warn('[Optimizer+] StreamInterceptor fetch error:', e); return self.originalFetch.apply(window, args); } } return self.originalFetch.apply(window, args); }; console.log('[Optimizer+] [OK] StreamInterceptor enabled (HW decode + GPU accel)'); }, disable() { if (!this.enabled) return; this.enabled = false; if (this.originalFetch) { window.fetch = this.originalFetch; this.originalFetch = null; } console.log('[Optimizer+] StreamInterceptor disabled'); } }; // =============================================================================== // v3.6 AXE 6: ULTRAWIDE & ASPECT RATIO EXPANSION // Full-screen support for 21:9, 32:9+ displays - Game-changing feature! // =============================================================================== const UltrawideSupport = { enabled: false, styleElement: null, resizeHandler: null, /** * Calculer l'aspect ratio de l'écran */ getScreenAspectRatio() { const width = window.innerWidth; const height = window.innerHeight; return (width / height).toFixed(2); }, /** * Déterminer si l'écran est "ultrawide" (> 1.7 ratio) */ isUltrawideScreen() { const ratio = parseFloat(this.getScreenAspectRatio()); return ratio > 1.7; // 16:9 = 1.78, 21:9 = 2.33, 32:9 = 3.56 }, /** * Obtenir les infos de l'écran pour logging */ getScreenInfo() { const ratio = parseFloat(this.getScreenAspectRatio()); let screenType = '16:9 (Standard)'; if (ratio >= 3.4) screenType = '32:9 (Super Ultrawide)'; else if (ratio >= 2.2) screenType = '21:9 (Ultrawide)'; else if (ratio >= 1.8) screenType = '16:10 (Widescreen)'; else if (ratio <= 1.3) screenType = 'Tablet / Vertical'; return { ratio: ratio.toFixed(2), type: screenType, width: window.innerWidth, height: window.innerHeight, isUltrawide: this.isUltrawideScreen() }; }, /** * CSS pour le mode ultrawide - ÉTIREMENT INTELLIGENT * Garde les filtres vidéo (sharpness, contrast, etc.) fonctionnels * PRÉSERVE les fenêtres flottantes de Boosteroid */ getUltrawideCSS() { return ` /* =================================================================== */ /* ULTRAWIDE MODE v3.6.0 - ÉTIREMENT (object-fit: fill) */ /* L'image 16:9 est ÉTIRÉE horizontalement pour remplir le 21:9/32:9 */ /* PAS de zoom, PAS de crop - juste un étirement des côtés */ /* =================================================================== */ html.optimizer-ultrawide-mode, html.optimizer-ultrawide-mode body { overflow: hidden !important; margin: 0 !important; padding: 0 !important; background: #000 !important; } /* ================================================================ */ /* VIDÉO: object-fit: fill = ÉTIRE pour remplir (pas de zoom) */ /* ================================================================ */ html.optimizer-ultrawide-mode video { position: fixed !important; top: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important; max-width: 100vw !important; max-height: 100vh !important; object-fit: fill !important; background: #000 !important; } /* Canvas (WebRTC) - même traitement */ html.optimizer-ultrawide-mode canvas { position: fixed !important; top: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important; max-width: 100vw !important; max-height: 100vh !important; } /* Conteneurs stream */ html.optimizer-ultrawide-mode [class*="player"], html.optimizer-ultrawide-mode [class*="Player"], html.optimizer-ultrawide-mode [class*="stream"], html.optimizer-ultrawide-mode [class*="Stream"] { position: fixed !important; top: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important; margin: 0 !important; padding: 0 !important; overflow: visible !important; } /* Fenêtres flottantes Boosteroid - toujours au-dessus */ html.optimizer-ultrawide-mode [class*="modal"], html.optimizer-ultrawide-mode [class*="Modal"], html.optimizer-ultrawide-mode [class*="popup"], html.optimizer-ultrawide-mode [class*="Popup"], html.optimizer-ultrawide-mode [class*="dialog"], html.optimizer-ultrawide-mode [class*="Dialog"], html.optimizer-ultrawide-mode [class*="menu"], html.optimizer-ultrawide-mode [class*="Menu"], html.optimizer-ultrawide-mode [class*="panel"], html.optimizer-ultrawide-mode [class*="Panel"], html.optimizer-ultrawide-mode [class*="settings"], html.optimizer-ultrawide-mode [class*="Settings"], html.optimizer-ultrawide-mode [role="dialog"], html.optimizer-ultrawide-mode [role="menu"] { z-index: 100000 !important; } /* Optimizer UI */ html.optimizer-ultrawide-mode #optimizer-section { z-index: 100001 !important; } /* Indicateur */ html.optimizer-ultrawide-mode::after { content: 'ULTRAWIDE'; position: fixed; top: 10px; right: 10px; background: rgba(0, 163, 255, 0.9); color: white; padding: 5px 10px; border-radius: 4px; font-size: 11px; font-weight: bold; z-index: 100002; pointer-events: none; animation: ultrawide-fade 3s ease-out forwards; } @keyframes ultrawide-fade { 0%, 70% { opacity: 1; } 100% { opacity: 0; } } `; }, /** * Activer le mode ultrawide */ enable() { if (this.enabled) return; console.log('[Optimizer+] Ultrawide mode: ENABLING'); const screenInfo = this.getScreenInfo(); this.enabled = true; CONFIG.display.ultrawideMode = true; // v3.6.1: "Cheap mode" - Désactiver les filtres lourds si prioritizeFramerate if (CONFIG.performance.prioritizeFramerate || CONFIG.display.performanceMode) { console.log('[Optimizer+] Ultrawide: Mode performance - désactivation filtres lourds'); CONFIG.filters.clarity.enabled = false; CONFIG.filters.denoise.enabled = false; CONFIG.filters.deband.enabled = false; if (typeof videoEnhancer !== 'undefined' && videoEnhancer.updateFilterString) { videoEnhancer.updateFilterString(); videoEnhancer.applyFiltersToAllVideos(); } } // Injecter le CSS ultrawide if (!this.styleElement) { this.styleElement = document.createElement('style'); this.styleElement.id = 'optimizer-ultrawide-styles'; this.styleElement.textContent = this.getUltrawideCSS(); document.head.appendChild(this.styleElement); } // Ajouter la classe de base document.documentElement.classList.add('optimizer-ultrawide-mode'); console.log('[Optimizer+] [OK] Ultrawide mode activated'); console.log(`[Optimizer+] Screen: ${screenInfo.width}x${screenInfo.height} (${screenInfo.type})`); // Notifier if (typeof showNotification === 'function') { showNotification(`Ultrawide: ${screenInfo.type}`); } // Écouter les changements de taille de fenêtre this.resizeHandler = () => this.onWindowResize(); window.addEventListener('resize', this.resizeHandler); // Sauvegarder la config if (typeof Storage !== 'undefined' && Storage.set) { Storage.set('config', CONFIG); } }, /** * Désactiver le mode ultrawide */ disable() { if (!this.enabled) return; console.log('[Optimizer+] Ultrawide mode: DISABLING'); this.enabled = false; CONFIG.display.ultrawideMode = false; // Retirer la classe CSS document.documentElement.classList.remove('optimizer-ultrawide-mode'); // Retirer le style element if (this.styleElement && this.styleElement.parentNode) { this.styleElement.parentNode.removeChild(this.styleElement); this.styleElement = null; } // Retirer le listener resize if (this.resizeHandler) { window.removeEventListener('resize', this.resizeHandler); this.resizeHandler = null; } console.log('[Optimizer+] [OK] Ultrawide mode deactivated'); if (typeof showNotification === 'function') { showNotification('Ultrawide désactivé'); } // Sauvegarder la config if (typeof Storage !== 'undefined' && Storage.set) { Storage.set('config', CONFIG); } }, /** * Handle window resize */ onWindowResize() { // Rien à faire, le CSS gère tout }, /** * Toggle ultrawide on/off */ toggle() { if (this.enabled) { this.disable(); } else { this.enable(); } return this.enabled; } }; // v3.6.4: Suppression de l'exposition globale pour raisons de sécurité (SEC-01) // UltrawideSupport reste accessible uniquement dans le scope de l'IIFE // =============================================================================== // v3.7.1 SMART RESOLUTION DETECTOR - Auto-détection écran et résolutions adaptées // UPSCALE ONLY: Ne propose que des résolutions >= native // =============================================================================== const SmartResolutionDetector = { // Cache des résultats - invalidé à chaque nouvelle version _cache: null, _cacheTime: 0, _version: '3.7.2', // Incrémenté pour invalider le cache (Smart Quality) CACHE_TTL: 5000, // 5 secondes /** * Invalider le cache manuellement */ invalidateCache() { this._cache = null; this._cacheTime = 0; console.log('[Optimizer+] Cache résolution invalidé'); }, /** * Obtenir les dimensions de l'écran actuel (support multi-moniteurs) * Utilise getScreenDetails API si disponible (Chrome 100+) * Fallback sur screen.width/height sinon * @returns {Promise} Dimensions de l'écran */ async getScreenDetailsAsync() { try { // Chrome 100+ : Window Management API if ('getScreenDetails' in window) { const screenDetails = await window.getScreenDetails(); const currentScreen = screenDetails.currentScreen; if (currentScreen) { console.log(`[Optimizer+] Screen detection: ${screenDetails.screens.length} écran(s) détecté(s)`); return { width: currentScreen.width, height: currentScreen.height, availWidth: currentScreen.availWidth || currentScreen.width, availHeight: currentScreen.availHeight || currentScreen.height, devicePixelRatio: currentScreen.devicePixelRatio || window.devicePixelRatio || 1, isMultiMonitor: screenDetails.screens.length > 1, screenLabel: currentScreen.label || 'Primary' }; } } } catch (e) { // Permission refusée ou API non supportée console.log('[Optimizer+] getScreenDetails non disponible, fallback screen.width'); } // Fallback standard return this.getScreenDimensions(); }, /** * Obtenir les dimensions réelles de l'écran (hardware natif) * Fix: Utilise screen.width/height pour éviter les bugs de zoom navigateur */ getScreenDimensions() { // Heuristique: Si window est très différent de screen, user peut être sur autre écran const screenW = window.screen.width || window.screen.availWidth || window.innerWidth; const screenH = window.screen.height || window.screen.availHeight || window.innerHeight; const windowW = window.innerWidth; const windowH = window.innerHeight; // Si fenêtre plein écran sur écran différent, innerWidth peut être plus fiable let width = screenW; let height = screenH; // Détection heuristique: fenêtre plus grande que l'écran détecté = probablement autre écran if (windowW > screenW * 1.1 || windowH > screenH * 1.1) { console.log('[Optimizer+] Heuristique: fenêtre sur écran externe détectée'); width = windowW; height = windowH; } return { width: width, height: height, availWidth: window.screen.availWidth || width, availHeight: window.screen.availHeight || height, devicePixelRatio: window.devicePixelRatio || 1, isMultiMonitor: false, screenLabel: 'Default' }; }, /** * Calculer le ratio de l'écran et le classifier * Fix: Utilise tolérance de 5% pour éviter faux positifs */ detectAspectRatio() { const screen = this.getScreenDimensions(); const ratio = screen.width / screen.height; // Classification STRICTE basée sur des plages de tolérance réalistes // Chaque ratio standard a une tolérance de ±3% maximum let ratioType, ratioName; // 32:9 = 3.556 (tolérance: 3.45 - 3.7) if (ratio >= 3.45) { ratioType = '32:9'; ratioName = 'Super Ultrawide'; } // 21:9 = 2.333-2.388 (tolérance: 2.25 - 2.5) else if (ratio >= 2.25 && ratio < 2.5) { ratioType = '21:9'; ratioName = 'Ultrawide'; } // 19.5:9 = 2.167 (iPhone ratio, tolérance: 2.1 - 2.25) else if (ratio >= 2.1 && ratio < 2.25) { ratioType = '19.5:9'; ratioName = 'Mobile Tall'; } // 18:9 = 2.0 (tolérance: 1.95 - 2.1) else if (ratio >= 1.95 && ratio < 2.1) { ratioType = '18:9'; ratioName = 'Mobile Wide'; } // 16:9 = 1.778 (tolérance STRICTE: 1.74 - 1.82) else if (ratio >= 1.74 && ratio < 1.82) { ratioType = '16:9'; ratioName = 'Standard'; } // 16:10 = 1.6 (tolérance: 1.55 - 1.65) else if (ratio >= 1.55 && ratio < 1.65) { ratioType = '16:10'; ratioName = 'Widescreen'; } // 3:2 = 1.5 (tolérance: 1.45 - 1.55) else if (ratio >= 1.45 && ratio < 1.55) { ratioType = '3:2'; ratioName = 'Classic'; } // 4:3 = 1.333 (tolérance: 1.28 - 1.38) else if (ratio >= 1.28 && ratio < 1.38) { ratioType = '4:3'; ratioName = 'Legacy'; } // Tout autre ratio = Custom (non-standard) else { ratioType = 'custom'; // Donner un nom descriptif basé sur le ratio if (ratio > 2.5) ratioName = 'Super Wide Custom'; else if (ratio > 1.82) ratioName = 'Wide Custom'; else if (ratio < 1.28) ratioName = 'Tall Custom'; else ratioName = 'Custom'; } return { ratio: ratio, ratioExact: ratio.toFixed(4), ratioType, ratioName, width: screen.width, height: screen.height, isUltrawide: ratio >= 2.0, isSuperUltrawide: ratio >= 3.4, isNonStandard: !this.isStandardResolution(screen.width, screen.height) }; }, /** * Vérifie si c'est une résolution standard connue */ isStandardResolution(w, h) { const standardResolutions = [ // 16:9 [1920, 1080], [2560, 1440], [3840, 2160], [1280, 720], [1366, 768], // 16:10 [1920, 1200], [2560, 1600], [1680, 1050], [1440, 900], // 21:9 [2560, 1080], [3440, 1440], [3840, 1600], // 32:9 [3840, 1080], // 4:3 [1600, 1200], [1024, 768], // Mobile 19.5:9 (iPhone) [2688, 1242], [2778, 1284], [2796, 1290], [2556, 1179], [2436, 1125], // Mobile 18:9 / 18.5:9 [2960, 1440], [2880, 1440], [2560, 1312] ]; // Tolérance de ±8 pixels pour l'arrondi codec return standardResolutions.some(([sw, sh]) => Math.abs(sw - w) <= 8 && Math.abs(sh - h) <= 8 ); }, /** * Arrondir à un multiple de 8 (compatibilité codec vidéo) */ roundToMultipleOf8(value) { return Math.round(value / 8) * 8; }, /** * Liste des résolutions gaming STANDARD à partir de 2K * Basée sur les résolutions utilisées dans les jeux vidéo */ GAMING_RESOLUTIONS: { // 16:9 Standard - Les plus courantes en gaming '16:9': [ { w: 2560, h: 1440, label: '2K (1440p)' }, { w: 2880, h: 1620, label: '3K (1620p)' }, { w: 3200, h: 1800, label: 'QHD+ (1800p)' }, { w: 3840, h: 2160, label: '4K (2160p)' } ], // 16:10 Widescreen - Moniteurs productivité/gaming '16:10': [ { w: 2560, h: 1600, label: '2K (WQXGA)' }, { w: 3072, h: 1920, label: '3K (16:10)' }, { w: 3840, h: 2400, label: '4K (WQUXGA)' } ], // 21:9 Ultrawide - Gaming immersif '21:9': [ { w: 2560, h: 1080, label: 'UWFHD (1080p UW)' }, { w: 3440, h: 1440, label: 'UWQHD (1440p UW)' }, { w: 3840, h: 1600, label: 'UWQHD+ (1600p UW)' } ], // 32:9 Super Ultrawide - Double écran gaming '32:9': [ { w: 3840, h: 1080, label: 'DFHD (1080p SW)' } ] }, /** * Obtenir le groupe de ratio le plus proche pour l'écran */ getClosestRatioGroup(screenRatio) { // Ratios de référence const ratioGroups = { '16:9': 1.778, // 16/9 '16:10': 1.6, // 16/10 '21:9': 2.37, // 21/9 (2.333 à 2.388) '32:9': 3.556 // 32/9 }; let closest = '16:9'; let minDiff = Infinity; for (const [name, refRatio] of Object.entries(ratioGroups)) { const diff = Math.abs(screenRatio - refRatio); if (diff < minDiff) { minDiff = diff; closest = name; } } return closest; }, /** * Résolutions supportées par Boosteroid (whitelist serveur) * Ces résolutions sont garanties de fonctionner côté serveur */ SUPPORTED_RESOLUTIONS: [ // 16:9 Standard [1920, 1080], [2560, 1440], [3840, 2160], // 16:10 Widescreen [1920, 1200], [2560, 1600], [3840, 2400], // 21:9 Ultrawide [2560, 1080], [3440, 1440], [3840, 1600], // 32:9 Super Ultrawide [3840, 1080] ], /** * Vérifier si une résolution est supportée par Boosteroid */ isResolutionSupported(width, height) { return this.SUPPORTED_RESOLUTIONS.some(([w, h]) => w === width && h === height); }, /** * Générer la liste des résolutions sélectionnables * CRITIQUE: N'affiche QUE les résolutions >= native (upscale uniquement) */ getSelectableResolutions(screenWidth, screenHeight) { const screenRatio = screenWidth / screenHeight; const ratioGroup = this.getClosestRatioGroup(screenRatio); const gamingRes = this.GAMING_RESOLUTIONS[ratioGroup] || this.GAMING_RESOLUTIONS['16:9']; const nativePixels = screenWidth * screenHeight; const resolutions = []; // 1. Ajouter la résolution NATIVE de l'écran en premier resolutions.push({ w: screenWidth, h: screenHeight, label: 'Native', tier: 'native', isNative: true, ratioGroup: ratioGroup }); // 2. Ajouter UNIQUEMENT les résolutions gaming >= native (UPSCALE ONLY) gamingRes.forEach(res => { const resPixels = res.w * res.h; // CRITIQUE: Ignorer les résolutions plus petites que native if (resPixels < nativePixels) return; // Éviter les doublons avec la native if (res.w === screenWidth && res.h === screenHeight) return; resolutions.push({ w: res.w, h: res.h, label: res.label, tier: res.h >= 2160 ? 'ultra' : (res.h >= 1440 ? 'mid' : 'low'), isNative: false, ratioGroup: ratioGroup, isSupported: this.isResolutionSupported(res.w, res.h) }); }); // 3. Trier par nombre de pixels (du plus petit au plus grand) resolutions.sort((a, b) => (a.w * a.h) - (b.w * b.h)); // 4. Marquer la résolution recommandée (premier upscale disponible, préférer 1440p) const recommended = resolutions.find(r => !r.isNative && r.h >= 1440) || resolutions.find(r => !r.isNative); if (recommended) { recommended.isRecommended = true; } else { // Aucun upscale disponible, recommander native resolutions[0].isRecommended = true; } return resolutions; }, /** * Générer les résolutions supérieures basées sur le ratio détecté * Utilise maintenant la liste de résolutions gaming standards */ generateUpscaleResolutions(screenInfo) { const { width, height } = screenInfo; return this.getSelectableResolutions(width, height); }, /** * Obtenir les infos complètes de l'écran et les résolutions recommandées */ getScreenAnalysis() { // Vérifier le cache (TTL court pour éviter les valeurs obsolètes) const now = Date.now(); if (this._cache && (now - this._cacheTime) < this.CACHE_TTL) { return this._cache; } const screenInfo = this.detectAspectRatio(); const upscaleOptions = this.generateUpscaleResolutions(screenInfo); // Trouver la résolution recommandée (celle avec isRecommended = true) // ou fallback sur la native si disponible let recommended = upscaleOptions.find(r => r.isRecommended); if (!recommended) { recommended = upscaleOptions.find(r => r.isNative) || upscaleOptions[0]; } const result = { screen: screenInfo, resolutions: upscaleOptions, recommended: recommended, native: upscaleOptions.find(r => r.isNative), summary: `${screenInfo.width}x${screenInfo.height} (${screenInfo.ratioType} - ratio ${screenInfo.ratioExact})` }; // Mettre en cache this._cache = result; this._cacheTime = now; return result; }, /** * Générer le HTML des options de résolution pour un
${ICONS.monitor} ${SmartResolutionDetector.getScreenAnalysis().screen.width}x${SmartResolutionDetector.getScreenAnalysis().screen.height} ${SmartResolutionDetector.getScreenAnalysis().screen.ratioType}
`; // Insérer au bon endroit dans le menu if (insertAfter && insertAfter.parentNode) { // Insérer après le dernier menu_block (section Streaming) insertAfter.parentNode.insertBefore(section, insertAfter.nextSibling); console.log('[Optimizer+] UI insérée après la section Streaming'); } else { // Fallback: ajouter à la fin du menu menuElement.appendChild(section); console.log('[Optimizer+] UI ajoutée à la fin du menu'); } // Attacher les événements attachUIEvents(); // Initialiser le style des sliders (comme Boosteroid) initSliderStyles(); console.log('[Optimizer+] Interface injectée avec succès'); } // Fonction pour créer un slider personnalisé visuellement function createCustomSlider(inputElement) { // Créer le wrapper const wrapper = document.createElement('div'); wrapper.className = 'optimizer-slider'; // Créer les éléments visuels const track = document.createElement('div'); track.className = 'optimizer-slider-track'; const fill = document.createElement('div'); fill.className = 'optimizer-slider-fill'; const thumb = document.createElement('div'); thumb.className = 'optimizer-slider-thumb'; wrapper.appendChild(track); wrapper.appendChild(fill); wrapper.appendChild(thumb); // Insérer le wrapper avant l'input inputElement.parentNode.insertBefore(wrapper, inputElement); // Déplacer l'input dans le wrapper wrapper.appendChild(inputElement); // Fonction de mise à jour visuelle const updateVisual = () => { const min = parseFloat(inputElement.min) || 0; const max = parseFloat(inputElement.max) || 100; const value = parseFloat(inputElement.value) || 0; const percentage = ((value - min) / (max - min)) * 100; fill.style.width = `${percentage}%`; thumb.style.left = `${percentage}%`; }; // Initialiser updateVisual(); // Écouter les changements inputElement.addEventListener('input', updateVisual); return wrapper; } function initSliderStyles() { // Sélectionner tous les sliders de notre section const sliders = document.querySelectorAll('#optimizer-section input[type="range"]'); sliders.forEach(slider => { // Vérifier si déjà wrappé if (!slider.parentElement.classList.contains('optimizer-slider')) { createCustomSlider(slider); } }); } function attachUIEvents() { // Fonction de sauvegarde automatique (appelée à chaque changement) const autoSave = () => { Storage.set('config', CONFIG); }; // Résolution const resSelect = document.getElementById('optimizer-res-select'); if (resSelect) { resSelect.addEventListener('change', (e) => { const value = e.target.value; let width, height; let isAutoMode = false; if (value === 'auto') { // Mode auto - utilise la résolution native isAutoMode = true; const autoRes = SmartResolutionDetector.applyAutoResolution(); if (autoRes) { width = autoRes.w; height = autoRes.h; } else { const screen = SmartResolutionDetector.getScreenDimensions(); width = screen.width; height = screen.height; } } else { [width, height] = value.split('x').map(Number); } CONFIG.resolution.width = width; CONFIG.resolution.height = height; CONFIG.resolution.isAuto = isAutoMode; // v3.6.2: Pixel ratio intelligent selon résolution CONFIG.resolution.pixelRatio = width >= 3840 ? 2 : (width >= 2560 ? 1.5 : 1); // Mettre à jour l'affichage const resDisplay = document.getElementById('optimizer-resolution'); if (resDisplay) { resDisplay.textContent = isAutoMode ? `Auto: ${width}x${height}` : `${width}x${height}`; } // Réappliquer le hook de résolution hookResolution(); // Notification const aspectRatio = (width / height).toFixed(2); let ratioName = '16:9'; if (aspectRatio >= 3.4) ratioName = '32:9'; else if (aspectRatio >= 2.2) ratioName = '21:9'; else if (aspectRatio >= 1.75) ratioName = '16:9'; else if (aspectRatio >= 1.55) ratioName = '16:10'; const modeText = isAutoMode ? 'Auto' : ''; showNotification(`${modeText} Resolution: ${width}x${height} (${ratioName})`); autoSave(); }); } // Enhancer toggle const enhancerToggle = document.getElementById('optimizer-enhancer'); if (enhancerToggle) { enhancerToggle.addEventListener('change', (e) => { videoEnhancer.toggle(e.target.checked); autoSave(); }); } // Stream Interceptor toggle (opt-in) const streamInterceptorToggle = document.getElementById('optimizer-stream-interceptor'); if (streamInterceptorToggle) { streamInterceptorToggle.addEventListener('change', (e) => { CONFIG.streaming.interceptorEnabled = e.target.checked; if (e.target.checked) { StreamInterceptor.enable(); showNotification('Stream Interceptor enabled'); } else { StreamInterceptor.disable(); showNotification('Stream Interceptor disabled'); } autoSave(); }); } // v3.6.1 Filtres avancés toggle - Affiche/cache les détails const filtersToggle = document.getElementById('optimizer-filters-toggle'); if (filtersToggle) { filtersToggle.addEventListener('change', (e) => { videoEnhancer.toggleFilters(e.target.checked); const detailsSection = document.getElementById('optimizer-filters-details'); if (detailsSection) { detailsSection.style.display = e.target.checked ? 'block' : 'none'; } autoSave(); }); } // v3.6 Performance Mode toggle const perfModeToggle = document.getElementById('optimizer-performance-mode'); if (perfModeToggle) { perfModeToggle.addEventListener('change', (e) => { CONFIG.display.performanceMode = e.target.checked; if (e.target.checked) { // Mode Performance: désactiver les filtres lourds CONFIG.filters.clarity.enabled = false; CONFIG.filters.denoise.enabled = false; CONFIG.filters.deband.enabled = false; CONFIG.performance.gpuAcceleration = false; // Réappliquer les filtres légers uniquement videoEnhancer.updateFilterString(); videoEnhancer.applyFiltersToAllVideos(); showNotification('Performance Mode ON - FPS maximized'); } else { // Réactiver l'accélération GPU CONFIG.performance.gpuAcceleration = true; showNotification('Normal mode restored'); } autoSave(); }); } // Sharpness const sharpSlider = document.getElementById('optimizer-sharpness'); if (sharpSlider) { sharpSlider.addEventListener('input', (e) => { const value = parseInt(e.target.value) / 100; document.getElementById('optimizer-sharp-value').textContent = `${e.target.value}%`; videoEnhancer.updateSettings({ sharpness: value }); autoSave(); }); } // Contrast const contrastSlider = document.getElementById('optimizer-contrast'); if (contrastSlider) { contrastSlider.addEventListener('input', (e) => { const value = parseInt(e.target.value) / 100; document.getElementById('optimizer-contrast-value').textContent = `${e.target.value}%`; videoEnhancer.updateSettings({ contrast: value }); autoSave(); }); } // Saturation const satSlider = document.getElementById('optimizer-saturation'); if (satSlider) { satSlider.addEventListener('input', (e) => { const value = parseInt(e.target.value) / 100; document.getElementById('optimizer-sat-value').textContent = `${e.target.value}%`; videoEnhancer.updateSettings({ saturation: value }); autoSave(); }); } // Reset button - Réinitialisation instantanée const resetBtn = document.getElementById('optimizer-reset'); if (resetBtn) { resetBtn.addEventListener('click', () => { // Réinitialiser le CONFIG avec les valeurs par défaut CONFIG.resolution = { ...DEFAULT_CONFIG.resolution }; CONFIG.enhancer = { ...DEFAULT_CONFIG.enhancer }; CONFIG.filters = JSON.parse(JSON.stringify(DEFAULT_CONFIG.filters)); CONFIG.language = DEFAULT_CONFIG.language; // Sauvegarder le nouveau config localStorage.removeItem('optimizer_config'); Storage.set('config', CONFIG); // Mettre à jour VideoEnhancer immédiatement videoEnhancer.enabled = CONFIG.enhancer.enabled; videoEnhancer.filtersEnabled = CONFIG.filters.enabled; videoEnhancer.updateFilterString(); videoEnhancer.updateSVGFilters(); videoEnhancer.applyFiltersToAllVideos(); // Mettre à jour l'interface updateAllUIValues(); showNotification('[OK] ' + t('settingsReset').replace('[<<] ', '')); console.log('[Optimizer+] Reset effectué - valeurs par défaut restaurées'); }); } // Sélecteur de langue const langSelect = document.getElementById('optimizer-lang-select'); if (langSelect) { langSelect.addEventListener('change', (e) => { const newLang = e.target.value; CONFIG.language = newLang; // Mettre à jour la langue courante if (newLang === 'auto') { const browserLang = (navigator.language || navigator.userLanguage || 'en').substring(0, 2).toLowerCase(); currentLang = I18N.translations[browserLang] ? browserLang : 'en'; } else { currentLang = newLang; } autoSave(); showNotification(`${t('language')}: ${e.target.options[e.target.selectedIndex].text}`); // Recréer l'UI pour appliquer la nouvelle langue setTimeout(() => { const section = document.getElementById('optimizer-section'); if (section) { section.remove(); SessionState.isUIInjected = false; const menu = findOpenOptionsMenu(); if (menu) { createOptimizerUI(menu); SessionState.isUIInjected = true; } } }, 500); }); } // === FILTRES AVANCÉS === // Présets const presetBtns = document.querySelectorAll('.optimizer-preset-btn'); presetBtns.forEach(btn => { btn.addEventListener('click', (e) => { const preset = e.target.dataset.preset; videoEnhancer.applyPreset(preset); // Update UI presetBtns.forEach(b => b.classList.remove('active')); e.target.classList.add('active'); // Mettre à jour les sliders de l'interface updateFiltersUI(); autoSave(); showNotification(t('presetApplied', { name: FILTER_PRESETS[preset].name })); }); }); // Filter toggles individuels document.querySelectorAll('.optimizer-filter-checkbox').forEach(toggle => { toggle.addEventListener('change', (e) => { const filterName = e.target.dataset.filter; const isActive = e.target.checked; videoEnhancer.toggleFilter(filterName, isActive); // Passer en mode custom setPresetToCustom(); autoSave(); }); }); // USM sliders const usmAmount = document.getElementById('usm-amount'); if (usmAmount) { usmAmount.addEventListener('input', (e) => { const value = parseInt(e.target.value) / 100; document.getElementById('usm-title').textContent = `${e.target.value}%`; videoEnhancer.updateFilterSettings('usm', { amount: value }); setPresetToCustom(); autoSave(); }); } // CAS slider const casSharpness = document.getElementById('cas-sharpness'); if (casSharpness) { casSharpness.addEventListener('input', (e) => { const value = parseInt(e.target.value) / 100; document.getElementById('cas-title').textContent = `${e.target.value}%`; videoEnhancer.updateFilterSettings('cas', { sharpness: value }); setPresetToCustom(); autoSave(); }); } // Clarity slider const clarityAmount = document.getElementById('clarity-amount'); if (clarityAmount) { clarityAmount.addEventListener('input', (e) => { const value = parseInt(e.target.value) / 100; document.getElementById('clarity-title').textContent = `${e.target.value}%`; videoEnhancer.updateFilterSettings('clarity', { amount: value }); setPresetToCustom(); autoSave(); }); } // Denoise slider const denoiseStrength = document.getElementById('denoise-strength'); if (denoiseStrength) { denoiseStrength.addEventListener('input', (e) => { const value = parseInt(e.target.value) / 100; document.getElementById('denoise-title').textContent = `${e.target.value}%`; videoEnhancer.updateFilterSettings('denoise', { strength: value }); setPresetToCustom(); autoSave(); }); } // Vibrance slider const vibranceAmount = document.getElementById('vibrance-amount'); if (vibranceAmount) { vibranceAmount.addEventListener('input', (e) => { const value = parseInt(e.target.value) / 100; document.getElementById('vibrance-title').textContent = `${e.target.value}%`; videoEnhancer.updateFilterSettings('vibrance', { amount: value }); setPresetToCustom(); autoSave(); }); } } // Fonction pour mettre à jour TOUTE l'interface avec les valeurs actuelles du CONFIG function updateAllUIValues() { // Résolution const resSelect = document.getElementById('optimizer-res-select'); if (resSelect) { resSelect.value = `${CONFIG.resolution.width}x${CONFIG.resolution.height}`; } const resDisplay = document.getElementById('optimizer-resolution'); if (resDisplay) { resDisplay.textContent = `${CONFIG.resolution.width}x${CONFIG.resolution.height}`; } // Enhancer toggle const enhancerToggle = document.getElementById('optimizer-enhancer'); if (enhancerToggle) { enhancerToggle.checked = CONFIG.enhancer.enabled; } // Filtres toggle const filtersToggle = document.getElementById('optimizer-filters-toggle'); if (filtersToggle) { filtersToggle.checked = CONFIG.filters.enabled; } // Présets - mettre à jour le bouton actif const presetBtns = document.querySelectorAll('.optimizer-preset-btn'); presetBtns.forEach(btn => { btn.classList.remove('active'); if (btn.dataset.preset === CONFIG.filters.preset) { btn.classList.add('active'); } }); // Langue const langSelect = document.getElementById('optimizer-lang-select'); if (langSelect) { langSelect.value = CONFIG.language; } // Mettre à jour tous les sliders et toggles des filtres updateFiltersUI(); } // Fonction pour mettre à jour l'UI des filtres function updateFiltersUI() { // Enhancer de base const sharpSlider = document.getElementById('optimizer-sharpness'); if (sharpSlider) { sharpSlider.value = CONFIG.enhancer.sharpness * 100; document.getElementById('optimizer-sharp-value').textContent = `${Math.round(CONFIG.enhancer.sharpness * 100)}%`; } const contrastSlider = document.getElementById('optimizer-contrast'); if (contrastSlider) { contrastSlider.value = CONFIG.enhancer.contrast * 100; document.getElementById('optimizer-contrast-value').textContent = `${Math.round(CONFIG.enhancer.contrast * 100)}%`; } const satSlider = document.getElementById('optimizer-saturation'); if (satSlider) { satSlider.value = CONFIG.enhancer.saturation * 100; document.getElementById('optimizer-sat-value').textContent = `${Math.round(CONFIG.enhancer.saturation * 100)}%`; } // Filtres avancés const filters = CONFIG.filters; // USM const usmAmountEl = document.getElementById('usm-amount'); if (usmAmountEl) { usmAmountEl.value = filters.usm.amount * 100; document.getElementById('usm-title').textContent = `${Math.round(filters.usm.amount * 100)}%`; } // CAS const casSharpnessEl = document.getElementById('cas-sharpness'); if (casSharpnessEl) { casSharpnessEl.value = filters.cas.sharpness * 100; document.getElementById('cas-title').textContent = `${Math.round(filters.cas.sharpness * 100)}%`; } // Clarity const clarityAmountEl = document.getElementById('clarity-amount'); if (clarityAmountEl) { clarityAmountEl.value = filters.clarity.amount * 100; document.getElementById('clarity-title').textContent = `${Math.round(filters.clarity.amount * 100)}%`; } // Denoise const denoiseStrengthEl = document.getElementById('denoise-strength'); if (denoiseStrengthEl) { denoiseStrengthEl.value = filters.denoise.strength * 100; document.getElementById('denoise-title').textContent = `${Math.round(filters.denoise.strength * 100)}%`; } // Vibrance const vibranceAmountEl = document.getElementById('vibrance-amount'); if (vibranceAmountEl) { vibranceAmountEl.value = filters.vibrance.amount * 100; document.getElementById('vibrance-title').textContent = `${Math.round(filters.vibrance.amount * 100)}%`; } // Mettre à jour les toggles ['usm', 'cas', 'clarity', 'denoise', 'vibrance'].forEach(filterName => { const checkbox = document.querySelector(`.optimizer-filter-checkbox[data-filter="${filterName}"]`); if (checkbox) { checkbox.checked = filters[filterName].enabled; } }); // Mettre à jour le style visuel des sliders const sliders = document.querySelectorAll('#optimizer-section input[type="range"]'); sliders.forEach(slider => updateSliderStyle(slider)); } // Fonction pour passer en mode custom quand on modifie manuellement function setPresetToCustom() { CONFIG.filters.preset = 'custom'; // Mettre à jour les boutons de préset document.querySelectorAll('.optimizer-preset-btn').forEach(btn => { btn.classList.remove('active'); }); } function showDRMInfo() { let info = "=== Optimizer Plus - Info DRM ===\n\n"; info += `Resolution forcee: ${CONFIG.resolution.width}x${CONFIG.resolution.height}\n`; info += `Pixel Ratio: ${CONFIG.resolution.pixelRatio}x\n\n`; info += "Codecs actives:\n"; info += ` - AV1: ${CONFIG.codecs.forceAV1 ? '[OK]' : '[X]'}\n`; info += ` - HEVC: ${CONFIG.codecs.forceHEVC ? '[OK]' : '[X]'}\n`; info += ` - VP9: ${CONFIG.codecs.forceVP9 ? '[OK]' : '[X]'}\n\n`; info += `Bitrate max: ${CONFIG.streaming.maxBitrate / 1000000} Mbps\n\n`; info += "Enhancer:\n"; info += ` - Active: ${CONFIG.enhancer.enabled ? '[OK]' : '[X]'}\n`; info += ` - Nettete: ${Math.round(CONFIG.enhancer.sharpness * 100)}%\n`; info += ` - Contraste: ${Math.round(CONFIG.enhancer.contrast * 100)}%\n`; info += ` - Saturation: ${Math.round(CONFIG.enhancer.saturation * 100)}%\n`; // Vérifier les capacités DRM if (windowCtx.MSMediaKeys && windowCtx.MSMediaKeys.isTypeSupportedWithFeaturesOriginal) { info += "\nPlayReady DRM:\n"; const hwSupport = windowCtx.MSMediaKeys.isTypeSupportedWithFeaturesOriginal( "com.microsoft.playready.hardware", 'video/mp4; codecs="hev1,mp4a"; features="hdcp=2"' ) !== ''; info += ` - Hardware HDCP 2.2: ${hwSupport ? '[OK]' : '[X]'}\n`; } alert(info); } // =============================================================================== // VIDEO OBSERVER - Détection et amélioration des éléments vidéo // =============================================================================== function setupVideoObserver() { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeName === 'VIDEO') { handleVideoElement(node); } else if (node.querySelectorAll) { node.querySelectorAll('video').forEach(handleVideoElement); } }); }); }); observer.observe(document.documentElement, { childList: true, subtree: true }); // Traiter les vidéos existantes document.querySelectorAll('video').forEach(handleVideoElement); } function handleVideoElement(video) { console.log('[Optimizer+] Vidéo détectée'); // Appliquer l'enhancer videoEnhancer.applyToVideo(video); // Optimisations vidéo video.setAttribute('playsinline', ''); // Note: Le monitoring de frames a été désactivé pour performance // Utiliser le mode Performance dans les paramètres si nécessaire } // =============================================================================== // MENU COMMANDS - Commandes Tampermonkey // =============================================================================== function registerMenuCommands() { if (typeof GM_registerMenuCommand === 'undefined') return; GM_registerMenuCommand("Optimizer Plus - Parametres", () => { // Ouvrir le menu Boosteroid si possible const menuBtn = document.querySelector('[class*="menu"]'); if (menuBtn) menuBtn.click(); }); GM_registerMenuCommand("Info DRM & Codecs", showDRMInfo); GM_registerMenuCommand("Toggle AV1", () => { CONFIG.codecs.forceAV1 = !CONFIG.codecs.forceAV1; Storage.set('config', CONFIG); showNotification(`AV1: ${CONFIG.codecs.forceAV1 ? 'Active' : 'Desactive'}`); }); GM_registerMenuCommand("Toggle Enhancer", () => { videoEnhancer.toggle(!CONFIG.enhancer.enabled); showNotification(`Enhancer: ${CONFIG.enhancer.enabled ? 'Active' : 'Desactive'}`); }); GM_registerMenuCommand("Recharger avec parametres", () => { location.reload(); }); } // =============================================================================== // v3.6.3 DASHBOARD FLOATING WIDGET - Bouton flottant sur le dashboard // Permet de configurer la résolution avant de lancer un jeu // =============================================================================== function createDashboardWidget() { // Vérifier qu'on est bien sur le dashboard principal if (document.getElementById('optimizer-dashboard-widget')) { return; // Déjà créé } // Attendre que le DOM soit prêt et que le bouton chatbot soit chargé const waitForChatbot = () => { const chatbot = document.getElementById('botbutton'); if (chatbot) { injectWidget(); } else { // Réessayer après un délai setTimeout(waitForChatbot, 500); } }; // Créer le widget immédiatement, positionnement relatif au chatbot si présent const injectWidget = () => { const widget = document.createElement('div'); widget.id = 'optimizer-dashboard-widget'; // v3.7.2: Style inline pour éviter flash blanc au chargement widget.style.cssText = 'position:fixed;right:20px;bottom:180px;z-index:99998;background:transparent;opacity:0;visibility:hidden;pointer-events:none;transition:opacity 0.25s ease 0.05s;'; // Utiliser le Smart Resolution Detector pour générer les options const screenAnalysis = SmartResolutionDetector.getScreenAnalysis(); const resolutionOptions = SmartResolutionDetector.generateResolutionOptionsHTML( CONFIG.resolution.width, CONFIG.resolution.height, CONFIG.resolution.isAuto // v3.7.2: Passer le mode auto ); // Déterminer le status actuel (auto ou manuel) const currentResText = CONFIG.resolution.isAuto ? `Auto: ${CONFIG.resolution.width}x${CONFIG.resolution.height}` : `${CONFIG.resolution.width}x${CONFIG.resolution.height}`; widget.innerHTML = ` `; document.body.appendChild(widget); // v3.7.5: Empêcher le flash blanc en affichant le widget seulement une fois stylé const revealWidget = () => { widget.style.opacity = '1'; widget.style.visibility = 'visible'; widget.style.pointerEvents = 'auto'; }; if (typeof requestAnimationFrame === 'function') { requestAnimationFrame(() => requestAnimationFrame(revealWidget)); } else { setTimeout(revealWidget, 50); } console.log('[Optimizer+] [OK] Dashboard widget injecté dans le DOM'); // Attacher les événements avec plusieurs tentatives pour plus de robustesse const attachWithRetry = (attempt = 1) => { const btn = document.getElementById('opt-widget-toggle'); if (btn) { attachWidgetEvents(); console.log('[Optimizer+] [OK] Événements attachés (tentative ' + attempt + ')'); } else if (attempt < 5) { console.log('[Optimizer+] Bouton non trouvé, nouvelle tentative dans 100ms...'); setTimeout(() => attachWithRetry(attempt + 1), 100); } else { console.error('[Optimizer+] [X] Impossible de trouver le bouton après 5 tentatives'); } }; // Commencer après un délai setTimeout(() => attachWithRetry(), 50); console.log('[Optimizer+] [OK] Dashboard widget créé'); }; // Démarrer après un court délai pour laisser la page charger setTimeout(() => { injectWidget(); }, 800); } function attachWidgetEvents() { const widget = document.getElementById('optimizer-dashboard-widget'); const toggleBtn = document.getElementById('opt-widget-toggle'); const panel = document.getElementById('opt-widget-panel'); const resSelect = document.getElementById('opt-widget-resolution'); const reloadBtn = document.getElementById('opt-widget-reload'); const applyBtn = document.getElementById('opt-widget-apply'); const statusText = document.getElementById('opt-widget-status-text'); console.log('[Optimizer+] Attaching widget events...', { widget: !!widget, toggleBtn: !!toggleBtn, panel: !!panel, resSelect: !!resSelect }); if (!widget) { console.error('[Optimizer+] Widget container not found!'); return; } // Utiliser la délégation d'événements sur le widget conteneur widget.addEventListener('click', (e) => { const target = e.target; // Toggle button click if (target.id === 'opt-widget-toggle' || target.closest('#opt-widget-toggle')) { e.preventDefault(); e.stopPropagation(); console.log('[Optimizer+] Toggle button clicked via delegation'); if (panel) { const isOpen = panel.classList.contains('open'); if (isOpen) { // Fermer le panel panel.classList.remove('open'); panel.style.opacity = '0'; panel.style.visibility = 'hidden'; panel.style.transform = 'translateY(10px) scale(0.95)'; } else { // Ouvrir le panel panel.classList.add('open'); panel.style.opacity = '1'; panel.style.visibility = 'visible'; panel.style.transform = 'translateY(0) scale(1)'; } console.log('[Optimizer+] Panel state:', panel.classList.contains('open') ? 'OPEN' : 'CLOSED'); } return; } // Reload button click if (target.id === 'opt-widget-reload' || target.closest('#opt-widget-reload')) { e.preventDefault(); console.log('[Optimizer+] Reload button clicked'); location.reload(); return; } // Apply button click if (target.id === 'opt-widget-apply' || target.closest('#opt-widget-apply')) { e.preventDefault(); console.log('[Optimizer+] Apply button clicked'); Storage.set('config', CONFIG); hookResolution(); // Feedback visuel if (applyBtn) { applyBtn.innerHTML = ` Appliqué! `; applyBtn.style.background = '#22c55e'; setTimeout(() => { applyBtn.innerHTML = ` Appliquer `; applyBtn.style.background = ''; // Fermer le panel if (panel) { panel.classList.remove('open'); panel.style.opacity = '0'; panel.style.visibility = 'hidden'; panel.style.transform = 'translateY(10px) scale(0.95)'; } }, 1500); } console.log('[Optimizer+] [OK] Configuration sauvegardée'); return; } }); // Changement de résolution if (resSelect) { resSelect.addEventListener('change', (e) => { const value = e.target.value; let width, height, displayText; let isAutoMode = false; if (value === 'auto') { isAutoMode = true; // Mode auto-détection - utilise résolution NATIVE de l'écran const autoRes = SmartResolutionDetector.applyAutoResolution(); if (autoRes) { width = autoRes.w; height = autoRes.h; displayText = `Auto: ${width}x${height}`; } else { // Fallback const screen = SmartResolutionDetector.getScreenDimensions(); width = screen.width; height = screen.height; displayText = `Auto: ${width}x${height}`; } } else { // Résolution manuelle [width, height] = value.split('x').map(Number); displayText = `${width}x${height}`; } CONFIG.resolution.width = width; CONFIG.resolution.height = height; CONFIG.resolution.pixelRatio = width >= 3840 ? 2 : (width >= 2560 ? 1.5 : 1); CONFIG.resolution.isAuto = isAutoMode; // v3.7.2: Marquer le mode auto // Mettre à jour le status if (statusText) { statusText.textContent = displayText; } // Mettre à jour le tooltip du status dot const statusDot = document.querySelector('#optimizer-dashboard-widget .opt-status-dot'); if (statusDot) { statusDot.title = `Actif: ${displayText}`; } // Mettre à jour l'info écran si auto const screenValue = document.querySelector('.opt-screen-value'); if (screenValue && value === 'auto') { screenValue.classList.add('auto-active'); } else if (screenValue) { screenValue.classList.remove('auto-active'); } // Sauvegarder Storage.set('config', CONFIG); // Réappliquer le hook hookResolution(); console.log(`[Optimizer+] Résolution changée: ${displayText}`); }); } // Fermer le panel en cliquant ailleurs document.addEventListener('click', (e) => { if (panel && !widget.contains(e.target)) { panel.classList.remove('open'); panel.style.opacity = '0'; panel.style.visibility = 'hidden'; panel.style.transform = 'translateY(10px) scale(0.95)'; } }); // Fermer avec Escape document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && panel) { panel.classList.remove('open'); panel.style.opacity = '0'; panel.style.visibility = 'hidden'; panel.style.transform = 'translateY(10px) scale(0.95)'; } }); console.log('[Optimizer+] [OK] All widget events attached successfully'); } // =============================================================================== // INITIALISATION // =============================================================================== function init() { console.log('[Optimizer+] ======================================='); console.log('[Optimizer+] Boosteroid Optimizer Plus v3.7.2 by Derfog'); console.log('[Optimizer+] Device:', ENV_PROFILE.summary()); console.log('[Optimizer+] Filter Tier:', FilterState.currentTier); console.log('[Optimizer+] Resolution:', `${CONFIG.resolution.width}x${CONFIG.resolution.height}`); console.log('[Optimizer+] Bitrate:', `${Math.round(CONFIG.streaming.maxBitrate/1000000)}Mbps`); console.log('[Optimizer+] Low Latency:', CONFIG.performance.lowLatencyMode ? 'ON' : 'OFF'); console.log('[Optimizer+] ======================================='); // IMPORTANT: Les hooks techniques s'appliquent PARTOUT (même dashboard) // car ils préparent le navigateur pour le streaming hookResolution(); hookCodecs(); hookBitrate(); hookDRM(); hookPerformance(); // L'UI et les observers ne s'activent QUE si on n'est pas sur le dashboard if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', onDOMReady); } else { onDOMReady(); } } function onDOMReady() { // Injecter les styles (toujours, ils seront utilisés si on lance un jeu) ensureOptimizerTypography(); // Enregistrer les commandes menu Tampermonkey (toujours disponibles) registerMenuCommands(); // Déterminer le type de page const onStreaming = isStreamingPage(); const onDashboard = isDashboardPage(); console.log('[Optimizer+] Type de page - Streaming:', onStreaming, '| Dashboard:', onDashboard); // Si on est sur le dashboard ET pas sur streaming, afficher le widget flottant if (onDashboard && !onStreaming) { console.log('[Optimizer+] Dashboard détecté - Widget flottant activé'); console.log('[Optimizer+] L\'UI complète s\'activera automatiquement quand vous lancerez un jeu'); createDashboardWidget(); return; } // Créer les filtres SVG pour le sharpening videoEnhancer.createSVGFilters(); // v3.5: Initialiser les filtres sans flickering ZeroFlickerBootstrap.initializeFilters(); // Injecter le système d'UI intelligent injectUI(); // Observer les vidéos pour appliquer les filtres setupVideoObserver(); // v3.6.9 Log avec info écran const screenInfo = UltrawideSupport.getScreenInfo(); console.log('[Optimizer+] v3.6.9 Smart Resolution [OK]'); console.log(`[Optimizer+] Screen: ${screenInfo.width}x${screenInfo.height} (${screenInfo.type})`); // Raccourci ultrawide supprimé - utiliser le sélecteur de résolution // v3.6 Auto-détection ratio pour logging seulement if (CONFIG.display?.autoDetect !== false) { const ratio = parseFloat(screenInfo.ratio); if (ratio >= 2.0) { console.log('[Optimizer+] Écran large détecté - résolutions ultrawide disponibles'); } } } // Démarrer init(); })();