diff --git a/views/index.ejs b/views/index.ejs index c490160..6c275f1 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -45,6 +45,7 @@ .ctrl { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; background: #0A0A0A; border: 1px solid rgba(255,255,255,0.14); border-top: 0; padding: 12px 16px; margin-top: -1px; } .vu { display: flex; gap: 3px; align-items: flex-end; height: 30px; flex: none; } .vu span { width: 3px; height: 100%; background: var(--akzent); transform-origin: bottom; animation: fx-vu 0.7s infinite ease-in-out alternate; } + .vu.live span { animation: none; transform: scaleY(.15); transition: transform 60ms linear; } .now { flex: 1; min-width: 180px; } .now .lbl { font-size: 10px; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase; color: #6B6B6B; } .now .t { font-weight: 700; font-size: 15px; text-transform: uppercase; letter-spacing: -0.005em; margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } @@ -147,7 +148,50 @@ // Audio toggle var btnA = document.getElementById('btnAudio'); - btnA.addEventListener('click', function(){ video.muted = !video.muted; if(!video.muted && video.volume===0) video.volume=0.8; btnA.textContent = video.muted ? '🔇' : '🔊'; }); + btnA.addEventListener('click', function(){ video.muted = !video.muted; if(!video.muted && video.volume===0) video.volume=0.8; btnA.textContent = video.muted ? '🔇' : '🔊'; syncVu(); }); + + // --- pravi VU: Web Audio analizator na dejanskem zvoku streama --- + var vuEl = document.getElementById('vu'); + var vuBars = vuEl ? vuEl.querySelectorAll('span') : []; + var audioCtx = null, analyser = null, vuRaf = null, freqData = null; + function initAnalyser(){ + if (audioCtx || !window.AudioContext && !window.webkitAudioContext) return; + try { + audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + var src = audioCtx.createMediaElementSource(video); + analyser = audioCtx.createAnalyser(); + analyser.fftSize = 64; + analyser.smoothingTimeConstant = 0.75; + src.connect(analyser); + analyser.connect(audioCtx.destination); + freqData = new Uint8Array(analyser.frequencyBinCount); + } catch(e) { audioCtx = null; analyser = null; } + } + function vuLoop(){ + if (!analyser) return; + analyser.getByteFrequencyData(freqData); + var n = vuBars.length; // 7 stolpcev, razporejenih cez spekter (bas -> visine) + for (var i = 0; i < n; i++) { + var a = Math.floor(i * freqData.length / (n + 4)); + var b = Math.floor((i + 1) * freqData.length / (n + 4)); + var sum = 0; for (var j = a; j < b; j++) sum += freqData[j]; + var v = (sum / Math.max(1, b - a)) / 255; + var sc = Math.max(0.12, Math.min(1, Math.pow(v, 0.8) * 1.25)); + vuBars[i].style.transform = 'scaleY(' + sc.toFixed(3) + ')'; + } + vuRaf = requestAnimationFrame(vuLoop); + } + function syncVu(){ + if (!video.muted) { + initAnalyser(); + if (audioCtx && audioCtx.state === 'suspended') audioCtx.resume(); + if (analyser) { vuEl.classList.add('live'); if (!vuRaf) vuRaf = requestAnimationFrame(vuLoop); return; } + } + // mutirano ali analizator ni na voljo -> nazaj na organsko animacijo + vuEl.classList.remove('live'); + if (vuRaf) { cancelAnimationFrame(vuRaf); vuRaf = null; } + for (var i = 0; i < vuBars.length; i++) vuBars[i].style.transform = ''; + } // Fullscreen document.getElementById('btnFs').addEventListener('click', function(){ var f = document.getElementById('playerFrame');