Compare commits

..

2 commits

Author SHA1 Message Date
1533497409 Web: der Sleep-Timer zählt und stoppt auch beim Radio
All checks were successful
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Successful in 5m17s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:16:24 +02:00
7c87343e66 Web: ein Player für Radio und Podcasts
Some checks failed
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Has been cancelled
Die Podcast-Leiste und der Vollbild-Player zeigen auch den Sender (Logo,
„● Live“, Stopp behält den Sender, speichern, Sleep-Timer, Sender
wechseln); die Radio-Leiste behält nur Lautstärke, Votes, Fokusmodus und
Timer. Podcast-Oberfläche aus diora-android e6761d0, sw v55.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:12:59 +02:00
4 changed files with 64 additions and 4 deletions

View file

@ -453,6 +453,7 @@
this.leftMs = 0; this.leftMs = 0;
setFade(1); setFade(1);
pause(); pause();
stopRadio(); // the radio sleeps too (it plays in app.js's element)
return; return;
} }
if (this.leftMs < FADE_MS) setFade(Math.max(this.leftMs / FADE_MS, 0.1)); if (this.leftMs < FADE_MS) setFade(Math.max(this.leftMs / FADE_MS, 0.1));
@ -758,7 +759,8 @@
if (playing && Date.now() - lastSaved > 10000) saveNow(id, true); if (playing && Date.now() - lastSaved > 10000) saveNow(id, true);
if (playing) skipOutro(id); if (playing) skipOutro(id);
} }
sleep.tick(playing); // The sleep timer counts while either plays (radio and podcasts share the player).
sleep.tick(playing || radioPlaying());
if (playing || sleep.leftMs > 0) report(); if (playing || sleep.leftMs > 0) report();
}, 1000); }, 1000);
@ -791,10 +793,62 @@
const playStation = window.playStation; const playStation = window.playStation;
window.playStation = function () { window.playStation = function () {
putAway(); putAway();
return playStation.apply(this, arguments); const r = playStation.apply(this, arguments);
radioChanged();
return r;
}; };
} }
// ---- the radio in the same player (in the app native-radio.js does this) ----------
// The podcast page's bar and full player show the station too; app.js keeps
// playing it, its own bar keeps only volume, votes, focus mode and timer.
function radioChanged() {
if (window.__dioraRadioChanged) window.__dioraRadioChanged();
}
function radioSaved(url) {
return [...document.querySelectorAll('#saved-tbody tr[data-url]')].some(r => r.dataset.url === url);
}
function radioLogo(st) {
const all = [].concat(typeof INITIAL_SAVED !== 'undefined' ? INITIAL_SAVED : [],
typeof INITIAL_FEATURED !== 'undefined' ? INITIAL_FEATURED : []);
const s = all.find(x => x && x.url === st.url);
return (s && s.favicon_url) || '';
}
const radioPlaying = () => typeof isPlaying !== 'undefined' && !!isPlaying;
window.__dioraRadioNow = function () {
const st = typeof currentStation !== 'undefined' ? currentStation : null;
if (!st) return null;
return {
name: st.name || '',
logo: radioLogo(st),
track: typeof currentTrack !== 'undefined' ? currentTrack : '',
playing: radioPlaying(),
buffering: false,
saved: radioSaved(st.url),
};
};
// Stop keeps the station (like a paused episode): the bar stays with ▶.
window.__dioraRadioToggle = () => (radioPlaying() ? stopPlayback(false) : togglePlayStop());
window.__dioraRadioSave = () => saveCurrentStation();
for (const name of ['stopPlayback', 'updateNowPlayingUI', 'saveCurrentStation']) {
const fn = window[name];
if (typeof fn !== 'function') continue;
window[name] = function () {
const r = fn.apply(this, arguments);
if (r && typeof r.then === 'function') r.then(radioChanged, radioChanged);
else radioChanged();
return r;
};
}
const savedRows = document.getElementById('saved-tbody');
if (savedRows) new MutationObserver(radioChanged).observe(savedRows, {childList: true});
// ---- what the page and the lock screen see --------------------------------------------- // ---- what the page and the lock screen see ---------------------------------------------
const decoder = document.createElement('textarea'); const decoder = document.createElement('textarea');

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-v54'; const CACHE = 'diora-v55';
const SHELL = [ const SHELL = [
'/static/css/app.css', '/static/css/app.css',
'/static/js/app.js', '/static/js/app.js',

View file

@ -1,4 +1,4 @@
/* Copied from diora-android 6c2b071 2026-10-02: the podcast parts of web-native/static/native.css /* Copied from diora-android e6761d0 2026-10-02: the podcast parts of web-native/static/native.css
(bottom sheet, podcasts) by tools/sync_podcast_ui.py. Do not edit here. (bottom sheet, podcasts) by tools/sync_podcast_ui.py. Do not edit here.
The web's own additions are in podcasts-web.css. */ The web's own additions are in podcasts-web.css. */
@ -291,6 +291,7 @@ html.nui-radio.nui-pod-mini-open body {
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
#nui-pod-mini[hidden] { display: none; } #nui-pod-mini[hidden] { display: none; }
#nui-pod-mini [hidden] { display: none !important; }
.nui-pod-mini-bar { position: absolute; left: 0; right: 0; top: -1px; height: 2px; background: #2a2a2a; } .nui-pod-mini-bar { position: absolute; left: 0; right: 0; top: -1px; height: 2px; background: #2a2a2a; }
.nui-pod-mini-bar span { display: block; height: 100%; background: var(--accent); } .nui-pod-mini-bar span { display: block; height: 100%; background: var(--accent); }
.nui-pod-skip { font-size: 20px; } .nui-pod-skip { font-size: 20px; }

View file

@ -55,6 +55,11 @@ html.dpw #nui-dock {
margin: 0 auto; margin: 0 auto;
} }
html.dpw body.dnd-mode #nui-dock { display: none; } html.dpw body.dnd-mode #nui-dock { display: none; }
/* One player for both: what plays (and play / stop / save) is in the bar
above; the radio bar keeps volume, votes, focus mode and the timer. */
html.dpw.nui-one-player #now-playing-bar .now-playing-info,
html.dpw.nui-one-player #play-stop-btn,
html.dpw.nui-one-player #save-station-btn { display: none !important; }
html.dpw #nui-pod-mini { html.dpw #nui-pod-mini {
background: var(--dpw-surface); background: var(--dpw-surface);
border: 1px solid var(--border); border: 1px solid var(--border);