Stille überspringen (Browser/Desktop): Pausen schnell durchspielen statt springen; sw v109
All checks were successful
Build and push Docker image / build (push) Successful in 16s
Test / test (push) Successful in 8m15s
Test / e2e (push) Successful in 1m53s

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 <noreply@anthropic.com>
This commit is contained in:
mrwnslz 2026-10-15 09:01:57 +02:00
parent 1c74e60224
commit cac8027fb3
2 changed files with 57 additions and 8 deletions

View file

@ -974,16 +974,65 @@
return silence.spans; return silence.spans;
} }
audio.addEventListener('timeupdate', () => { // A pause is run through fast rather than jumped over: a jump in a streamed
if (!loaded || audio.paused || audio.seeking) return; // 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(); const spans = silenceSpans();
if (!spans || !spans.length) return; if (!spans) return null;
const t = audio.currentTime;
for (const [start, end] of spans) { // in order for (const [start, end] of spans) { // in order
if (start > t) break; if (start + SILENCE_KEEP > t) return null;
if (t < end - 0.3) { try { audio.currentTime = end - 0.15; } catch (e) {} return; } 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 --------------------------------------------------- // ---- radio and podcasts take turns ---------------------------------------------------

View file

@ -2,7 +2,7 @@
* diora service worker — caches the app shell for offline use. * diora service worker — caches the app shell for offline use.
*/ */
const CACHE = 'diora-v108'; const CACHE = 'diora-v109';
const SHELL = [ const SHELL = [
'/static/css/app.css', '/static/css/app.css',
'/static/js/app/01-core.js', '/static/js/app/01-core.js',