diff --git a/static/js/podcasts-web.js b/static/js/podcasts-web.js index 466abea..d06988e 100644 --- a/static/js/podcasts-web.js +++ b/static/js/podcasts-web.js @@ -1659,6 +1659,7 @@ }, podcastNext: () => next(), podcastToggle: () => (wantPlay ? pause() : resume()), + podcastPause: () => { if (wantPlay) pause(); }, podcastSeek: ms => seek(Number(ms)), podcastSkip: forward => skip(!!forward), podcastSpeed(speed) { diff --git a/static/js/sw.js b/static/js/sw.js index 9eb39fc..6ffb77c 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-v84'; +const CACHE = 'diora-v85'; const SHELL = [ '/static/css/app.css', '/static/js/app.js', diff --git a/static/podcasts/native-podcast.js b/static/podcasts/native-podcast.js index 4d63a72..f8d9920 100644 --- a/static/podcasts/native-podcast.js +++ b/static/podcasts/native-podcast.js @@ -1,4 +1,4 @@ -// Copied from diora-android 8ee6c2b 2026-10-04: web-native/static/native-podcast.js +// Copied from diora-android 1b935ff 2026-10-04: web-native/static/native-podcast.js // by tools/sync_podcast_ui.py. Do not edit here: change it in diora-android // and copy again. The browser host is static/js/podcasts-web.js. @@ -2859,6 +2859,10 @@ function renderAmbientMini() { const a = ambientInfo(); if (!mini || !a || !a.last) return false; + // An episode or the radio plays here: the bar is theirs, whatever came before. + const r = radioNow(); + if (!a.playing && ((player.playing && !player.remote) || (r && r.playing))) return false; + if (player.kind === 'podcast' && player.playing && !player.remote) return false; miniKind = 'ambient'; document.documentElement.classList.remove('nui-pod-playing'); document.documentElement.classList.add('nui-pod-mini-open'); @@ -3563,13 +3567,37 @@ label: speedLabel(s), action: () => (forPodcast ? N.podcastSpeedForPodcast(s) : N.podcastSpeed(s)), })).concat(forPodcast && own ? [{icon: '↺', label: 'Wie alle anderen Podcasts', action: () => N.podcastSpeedForPodcast(0)}] : []) - .concat(presetsEditItem())); + .concat(presetsEditItem(), silenceSpeedItems())); if (!N.podcastSpeedForPodcast || !player.feedId) { pick(false); return; } openSheet('Geschwindigkeit', [ {icon: '◎', label: 'Für alle Podcasts', hint: own ? null : speedLabel(player.speed || 1), action: () => pick(false)}, {icon: '◉', label: `Nur für „${plain(player.feed)}“`, hint: own ? speedLabel(player.speed || 1) : 'noch keine eigene', action: () => pick(true)}, - ].concat(presetsEditItem())); + ].concat(presetsEditItem(), silenceSpeedItems())); + } + + // „Stille überspringen“ where AntennaPod has it: in the speed menu. For all podcasts (this + // device), and for the playing one its own choice (server) — where the player can skip silence. + function silenceSpeedItems() { + const prefs = devicePrefs(); + if (!N.podcastSetPref || !('skip_silence' in prefs)) return []; + const device = !!prefs.skip_silence; + const live = player.feedId ? feeds().find(x => x.id === player.feedId) : null; + const mine = live && 'skip_silence' in live ? live.skip_silence : undefined; + const items = [{icon: device ? '☑' : '☐', label: 'Stille überspringen', + hint: (device ? 'an' : 'aus') + ' · alle Podcasts' + (mine === true ? ' · hier: immer' : mine === false ? ' · hier: nie' : ''), + action: () => { N.podcastSetPref('skip_silence', !device); toast(device ? 'Stille wird nicht mehr übersprungen' : 'Stille wird übersprungen'); }}]; + if (mine !== undefined && N.podcastFeedSettings) { + const title = plain(player.feed); + const save = v => N.podcastFeedSettings(player.feedId, JSON.stringify({skip_silence: v})); + items.push({icon: '∿', label: `Stille bei „${title}“`, hint: mine === true ? 'immer überspringen' : mine === false ? 'nie überspringen' : 'wie alle Podcasts', + action: () => openSheet(`Stille bei „${title}“ …`, [ + {icon: mine == null ? '●' : '○', label: 'Wie alle Podcasts', hint: device ? 'überspringen' : 'nicht überspringen', action: () => save(null)}, + {icon: mine === true ? '●' : '○', label: 'Immer überspringen', action: () => save(true)}, + {icon: mine === false ? '●' : '○', label: 'Nie überspringen', action: () => save(false)}, + ])}); + } + return items; } // ---- sleep timer (runs in the app, also with the screen off; episodes and the radio) ---- @@ -4365,7 +4393,9 @@ // Ambient starts (app.js): an episode playing here pauses. window.__dioraPausePlayer = function () { - if (player.kind === 'podcast' && player.playing && !player.remote && N.podcastToggle) N.podcastToggle(); + if (player.kind !== 'podcast' || !player.playing || player.remote) return; + // Pause, not toggle: Android's audio focus may have paused it a moment before. + if (N.podcastPause) N.podcastPause(); else if (N.podcastToggle) N.podcastToggle(); }; // The app loads this in ; the browser may load it after the page is parsed.