From cac8027fb3d39399d9c6e5e3c70614e1d15a2067 Mon Sep 17 00:00:00 2001 From: mrwnslz Date: Thu, 15 Oct 2026 09:01:57 +0200 Subject: [PATCH] =?UTF-8?q?Stille=20=C3=BCberspringen=20(Browser/Desktop):?= =?UTF-8?q?=20Pausen=20schnell=20durchspielen=20statt=20springen;=20sw=20v?= =?UTF-8?q?109?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ein Sprung im gestreamten Podcast wartet aufs Netz (hörbares Haken). Jetzt läuft eine Pause mit 4-facher Geschwindigkeit (höchstens 8x), je 0,12 s bleiben an beiden Enden; nur Pausen über 3 s werden noch übersprungen. Geprüft alle 40 ms statt mit timeupdate (alle 250 ms). Browser-Tab im Hintergrund (gedrosselte Timer): weiter mit Sprüngen, sonst liefe Sprache sekundenlang vierfach. Gemessen in der Desktop-App: Pausen 52,85–53,34 und 57,03–57,58 s schnell durchlaufen, kein Sprung. Co-Authored-By: Claude Opus 5.5 --- static/js/podcasts-web.js | 63 ++++++++++++++++++++++++++++++++++----- static/js/sw.js | 2 +- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/static/js/podcasts-web.js b/static/js/podcasts-web.js index 5de28d6..a129f86 100644 --- a/static/js/podcasts-web.js +++ b/static/js/podcasts-web.js @@ -974,16 +974,65 @@ return silence.spans; } - audio.addEventListener('timeupdate', () => { - if (!loaded || audio.paused || audio.seeking) return; + // A pause is run through fast rather than jumped over: a jump in a streamed + // file waits for the network again (a hitch you hear), a faster rate doesn't. + // A little of each pause stays at both ends; only a long one is still jumped. + // Checked every 40 ms while playing (timeupdate comes only every 250 ms, too + // late for a short pause). A browser tab in the background gets its timers + // slowed down to a second or more — speech at four times the speed for that + // long would be worse than a hitch, so there it jumps as before. + const SILENCE_RATE = 4; // × the podcast's own speed, inside a pause + const SILENCE_KEEP = 0.12; // s of each pause kept at either end + const SILENCE_JUMP = 3; // s: a pause longer than this is jumped over + let silenceFast = false; + let silenceTimer = null; + + // The end of the pause the player is in (less what stays of it), or null. + function silenceEndAt(t) { const spans = silenceSpans(); - if (!spans || !spans.length) return; - const t = audio.currentTime; + if (!spans) return null; for (const [start, end] of spans) { // in order - if (start > t) break; - if (t < end - 0.3) { try { audio.currentTime = end - 0.15; } catch (e) {} return; } + if (start + SILENCE_KEEP > t) return null; + if (t < end - SILENCE_KEEP) return end - SILENCE_KEEP; } - }); + return null; + } + + function silenceNormal() { + if (!silenceFast) return; + silenceFast = false; + audio.playbackRate = speedFor(curFeed()); + } + + function silenceTick() { + if (!loaded || audio.paused || audio.seeking) { silenceNormal(); return; } + const t = audio.currentTime; + const endAt = silenceEndAt(t); + if (endAt === null) { silenceNormal(); return; } + const timersFine = !document.hidden || !!window.dioraDesktop; // the desktop app never slows them + if (endAt - t > SILENCE_JUMP || !timersFine) { + silenceNormal(); + try { audio.currentTime = endAt; } catch (e) {} + } else { + const fast = Math.min(speedFor(curFeed()) * SILENCE_RATE, 8); + silenceFast = true; + if (audio.playbackRate !== fast) audio.playbackRate = fast; // also after applyKind reset it + } + } + + function silenceWatch() { + clearInterval(silenceTimer); + silenceTimer = null; + silenceNormal(); + if (loaded && !audio.paused && skipSilenceFor(curFeed())) silenceTimer = setInterval(silenceTick, 40); + } + audio.addEventListener('playing', silenceWatch); + audio.addEventListener('pause', silenceWatch); + audio.addEventListener('ended', silenceWatch); + audio.addEventListener('emptied', silenceWatch); + // Back from the background, or the setting changed: the timer may have to start. + document.addEventListener('visibilitychange', silenceWatch); + audio.addEventListener('timeupdate', () => { if (!silenceTimer && !audio.paused) silenceWatch(); else silenceTick(); }); // ---- radio and podcasts take turns --------------------------------------------------- diff --git a/static/js/sw.js b/static/js/sw.js index e4af648..efabfca 100644 --- a/static/js/sw.js +++ b/static/js/sw.js @@ -2,7 +2,7 @@ * diora service worker — caches the app shell for offline use. */ -const CACHE = 'diora-v108'; +const CACHE = 'diora-v109'; const SHELL = [ '/static/css/app.css', '/static/js/app/01-core.js',