您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Boosts quiet sounds, compresses loud peaks.
当前为
// ==UserScript== // @name Kick & YouTube Audio Maximizer // @name:tr Kick & YouTube Ses Dengeleyici // @description:tr Sessiz sesleri yükseltir, yüksek sesleri bastırır. // @description Boosts quiet sounds, compresses loud peaks. // @namespace http://tampermonkey.net/ // @version 1.6 // @author baris // @match *://*.kick.com/* // @match *://kick.com/* // @match *://*.youtube.com/* // @match *://youtube.com/* // @match *://youtu.be/* // @grant none // @license GNU GPLv3 // ==/UserScript== (() => { // --- DEĞİŞİKLİK 1: Arayüz ve Ses Bağlamını global değişkenlerde tutalım --- // Bu değişkenler sayesinde UI ve AudioContext sadece bir kez oluşturulacak. let ui = null; let audioCtx = null; // Yeni video yüklendiğinde sadece bir kez init et async function initVideo() { const video = document.querySelector('video'); // Eğer video yoksa veya bu videoya zaten işlem uygulandıysa çık if (!video || video.dataset.maxApplied) return; video.dataset.maxApplied = '1'; // --- DEĞİŞİKLİK 2: AudioContext'i sadece bir kez oluştur --- if (!audioCtx) { audioCtx = new AudioContext(); } // Kullanıcı etkileşimi olmadan ses başlamazsa, devam ettirmek için dinleyici ekle if (audioCtx.state === 'suspended') { const resume = () => { audioCtx.resume(); window.removeEventListener('click', resume); }; window.addEventListener('click', resume); } const src = audioCtx.createMediaElementSource(video); const comp = audioCtx.createDynamicsCompressor(); comp.threshold.value = -50; comp.knee.value = 30; comp.ratio.value = 20; comp.attack.value = 0.002; comp.release.value = 0.10; const gain = audioCtx.createGain(); gain.gain.value = 2.5; const ana = audioCtx.createAnalyser(); ana.fftSize = 512; const buf = new Uint8Array(ana.fftSize); src.connect(comp); comp.connect(gain); gain.connect(ana); ana.connect(audioCtx.destination); // --- DEĞİŞİKLİK 3: Arayüzü oluştur veya mevcut olanı getir --- // Arayüz daha önce oluşturulmadıysa createUI ile oluşturulacak. const g = await createUI(); const W = g.canvas.width, H = g.canvas.height; let smooth = 0, peak = 0, peakT = 0; const lerp = (a, b, f) => a + (b - a) * f; (function draw() { // Eğer video elementi sayfadan kaldırıldıysa (örneğin başka sayfaya geçildi), // çizim döngüsünü durdur. Bu, gereksiz işlemci kullanımını önler. if (!video.isConnected) { return; } requestAnimationFrame(draw); ana.getByteTimeDomainData(buf); const rms = Math.sqrt(buf.reduce((s, v) => s + ((v - 128) / 128) ** 2, 0) / buf.length); const level = Math.min(rms * gain.gain.value, 1); smooth = lerp(smooth, level, 0.18); const now = performance.now(); if (smooth > peak || now - peakT > 2000) { peak = smooth; peakT = now; } if (now - peakT > 2000) peak = lerp(peak, smooth, 0.05); const hue = lerp(220, 0, smooth); g.clearRect(0, 0, W, H); g.fillStyle = `hsl(${hue} 80% 50%)`; g.fillRect(0, 0, smooth * W, H); g.fillStyle = '#fff'; g.fillRect(peak * W - 1, 0, 2, H); })(); } // --- DEĞİŞİKLİK 4: Arayüzü "get-or-create" (varsa getir, yoksa oluştur) mantığıyla yeniden yapılandır --- // Bu fonksiyon artık UI'yi sadece bir kez oluşturur ve her zaman aynı context'i döndürür. function createUI() { return new Promise(res => { // Eğer UI zaten oluşturulmuşsa, mevcut context'i hemen döndür. if (ui) { return res(ui.g); } const waitDOM = () => document.body ? Promise.resolve() : new Promise(inner => { const id = setInterval(() => { if (document.body) { clearInterval(id); inner(); } }, 200); }); waitDOM().then(() => { const wrap = Object.assign(document.createElement('div'), { style: ` position:fixed; bottom:80px; left:20px; z-index:2147483647; background:rgba(0,0,0,0.6); padding:6px 8px; border-radius:8px; pointer-events:none; `.replace(/\s+/g, ' ') }); wrap.innerHTML = ` <div style="color:#fff;font-family:monospace;margin-bottom:4px"> 🔊 Maximizer by Barış </div>`; const canvas = Object.assign(document.createElement('canvas'), { width:300, height:20, style:'background:#222;border-radius:6px;display:block;' }); wrap.appendChild(canvas); document.body.appendChild(wrap); // Oluşturulan UI elemanlarını ve context'i global değişkende sakla ui = { wrap: wrap, canvas: canvas, g: canvas.getContext('2d') }; // Promise'i çözerek context'i döndür res(ui.g); }); }); } // Başlangıç ve dinleyiciler (async () => { // Sayfa ilk yüklendiğinde dene await initVideo(); // YouTube/Kick SPA navigasyonunda yeniden init et let lastUrl = location.href; const observerCallback = () => { if (location.href !== lastUrl) { lastUrl = location.href; // Sayfa geçişlerinde videonun yüklenmesi için kısa bir gecikme eklemek faydalıdır setTimeout(initVideo, 500); } }; // URL değişimlerini hem aralıkla hem de MutationObserver ile takip etmek daha sağlamdır. setInterval(observerCallback, 1000); // DOM değişimlerinde video yeniden yüklendiyse init et (örneğin, video oynatıcı ilk kez yüklendiğinde) new MutationObserver(initVideo).observe(document.body, { childList: true, subtree: true }); })(); })();