Podcasts: diora desktop kann den Browser-Host mitbenutzen (DIORA_PODCAST_HOST), sw v68
Setzt eine Seite window.DIORA_PODCAST_HOST, arbeitet podcasts-web.js trotz vorhandenem DioraNative als ihr Podcast-Host: - dock: 'existing' nimmt native-radio.js' #nui-dock statt einer Leiste in der Web-Radioleiste; die Podcast-Seite lädt erst nach DOMContentLoaded - styles: 'ui-only' nur podcast-ui.css, ohne das Browser-Layout (html.dpw) - radio: false die Radio-Hooks (__dioraRadioNow …) kommen von native-radio.js; der Sender legt die Folge weiterhin beiseite, Radio-Stopp meldet der Sync weiter - deviceName/deviceKind wie der gemeinsame Player das Gerät führt Ohne die Variable ändert sich im Browser nichts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
3e67d324c7
commit
461b42a5bf
2 changed files with 73 additions and 52 deletions
|
|
@ -23,17 +23,27 @@
|
||||||
* Radio and podcasts never play at the same time: starting an episode stops
|
* Radio and podcasts never play at the same time: starting an episode stops
|
||||||
* the radio (app.js stopPlayback), starting a station puts the episode away.
|
* the radio (app.js stopPlayback), starting a station puts the episode away.
|
||||||
*
|
*
|
||||||
* In the app (and the desktop app) window.DioraNative exists and this file
|
* In the app window.DioraNative exists and this file does nothing. diora
|
||||||
* does nothing. Written without ?. and ?? so old WebViews can still parse it.
|
* desktop has a DioraNative too (books, radio) but no podcast part of its own:
|
||||||
|
* it sets window.DIORA_PODCAST_HOST and this file is its podcast host, fitted
|
||||||
|
* into the phone layout it already shows:
|
||||||
|
* dock: 'existing' use native-radio.js's #nui-dock instead of a bar in the web radio bar
|
||||||
|
* styles: 'ui-only' only the podcast page's own CSS, not the browser layout
|
||||||
|
* radio: false native-radio.js provides the radio hooks (__dioraRadioNow …)
|
||||||
|
* deviceName, deviceKind how the shared player lists this device
|
||||||
|
* Written without ?. and ?? so old WebViews can still parse it.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
if (window.DioraNative) return;
|
const HOST = window.DIORA_PODCAST_HOST || null;
|
||||||
|
if (window.DioraNative && !HOST) return;
|
||||||
if (typeof IS_AUTHENTICATED === 'undefined' || !IS_AUTHENTICATED) return;
|
if (typeof IS_AUTHENTICATED === 'undefined' || !IS_AUTHENTICATED) return;
|
||||||
if (!document.getElementById('tab-podcasts')) return;
|
if (!document.getElementById('tab-podcasts')) return;
|
||||||
|
const OPT = Object.assign({dock: 'bar', styles: 'web', radio: true, deviceName: '', deviceKind: 'web'}, HOST || {});
|
||||||
|
|
||||||
const UI_SCRIPT = '/static/podcasts/native-podcast.js';
|
const UI_SCRIPT = '/static/podcasts/native-podcast.js';
|
||||||
const UI_STYLES = ['/static/podcasts/podcast-ui.css', '/static/podcasts/podcasts-web.css'];
|
const UI_STYLES = OPT.styles === 'ui-only' ? ['/static/podcasts/podcast-ui.css']
|
||||||
|
: ['/static/podcasts/podcast-ui.css', '/static/podcasts/podcasts-web.css'];
|
||||||
|
|
||||||
// ---- storage ------------------------------------------------------------------
|
// ---- storage ------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -890,6 +900,8 @@
|
||||||
|
|
||||||
const radioPlaying = () => typeof isPlaying !== 'undefined' && !!isPlaying;
|
const radioPlaying = () => typeof isPlaying !== 'undefined' && !!isPlaying;
|
||||||
|
|
||||||
|
// diora desktop: native-radio.js already shows the radio in the shared bar.
|
||||||
|
if (OPT.radio) {
|
||||||
window.__dioraRadioNow = function () {
|
window.__dioraRadioNow = function () {
|
||||||
const st = typeof currentStation !== 'undefined' ? currentStation : null;
|
const st = typeof currentStation !== 'undefined' ? currentStation : null;
|
||||||
if (!st) return null;
|
if (!st) return null;
|
||||||
|
|
@ -916,6 +928,7 @@
|
||||||
return r;
|
return r;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// The radio stopping is news for the shared player too.
|
// The radio stopping is news for the shared player too.
|
||||||
if (typeof window.stopPlayback === 'function') {
|
if (typeof window.stopPlayback === 'function') {
|
||||||
const stopPlayback = window.stopPlayback;
|
const stopPlayback = window.stopPlayback;
|
||||||
|
|
@ -931,7 +944,7 @@
|
||||||
|
|
||||||
// Player sync: the station another device played last becomes the current
|
// Player sync: the station another device played last becomes the current
|
||||||
// one here, without playing (the bar shows it with ▶).
|
// one here, without playing (the bar shows it with ▶).
|
||||||
window.__dioraAdoptStation = function (st) {
|
if (OPT.radio) window.__dioraAdoptStation = function (st) {
|
||||||
if (!st || !st.url || radioPlaying() || typeof currentStation === 'undefined') return;
|
if (!st || !st.url || radioPlaying() || typeof currentStation === 'undefined') return;
|
||||||
currentStation = {url: st.url, name: st.name || '', id: st.id || null};
|
currentStation = {url: st.url, name: st.name || '', id: st.id || null};
|
||||||
currentTrack = '';
|
currentTrack = '';
|
||||||
|
|
@ -964,7 +977,7 @@
|
||||||
let id = '';
|
let id = '';
|
||||||
try { id = localStorage.getItem('diora_device_id') || ''; } catch (e) {}
|
try { id = localStorage.getItem('diora_device_id') || ''; } catch (e) {}
|
||||||
if (!id) {
|
if (!id) {
|
||||||
id = 'web-' + Array.from(crypto.getRandomValues(new Uint8Array(12)), b => b.toString(16).padStart(2, '0')).join('');
|
id = OPT.deviceKind + '-' + Array.from(crypto.getRandomValues(new Uint8Array(12)), b => b.toString(16).padStart(2, '0')).join('');
|
||||||
try { localStorage.setItem('diora_device_id', id); } catch (e) {}
|
try { localStorage.setItem('diora_device_id', id); } catch (e) {}
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
|
|
@ -981,7 +994,7 @@
|
||||||
|
|
||||||
const DEVICE_ID = deviceId();
|
const DEVICE_ID = deviceId();
|
||||||
// Short, it stands in the bar ("läuft auf Chrome · Windows").
|
// Short, it stands in the bar ("läuft auf Chrome · Windows").
|
||||||
const DEVICE_NAME = browserName();
|
const DEVICE_NAME = OPT.deviceName || browserName();
|
||||||
|
|
||||||
const sync = {
|
const sync = {
|
||||||
state: null, // the server's state, as last taken in
|
state: null, // the server's state, as last taken in
|
||||||
|
|
@ -1362,7 +1375,7 @@
|
||||||
clearTimeout(retryTimer);
|
clearTimeout(retryTimer);
|
||||||
if (source) source.close();
|
if (source) source.close();
|
||||||
const seen = Math.max(sync.state ? sync.state.seq : -1, sync.later ? sync.later.seq : -1);
|
const seen = Math.max(sync.state ? sync.state.seq : -1, sync.later ? sync.later.seq : -1);
|
||||||
const q = new URLSearchParams({since: String(seen), device_id: DEVICE_ID, device_name: DEVICE_NAME, device_kind: 'web'});
|
const q = new URLSearchParams({since: String(seen), device_id: DEVICE_ID, device_name: DEVICE_NAME, device_kind: OPT.deviceKind});
|
||||||
source = new EventSource('/podcasts/player/stream/?' + q);
|
source = new EventSource('/podcasts/player/stream/?' + q);
|
||||||
source.onopen = () => { retryMs = 1000; };
|
source.onopen = () => { retryMs = 1000; };
|
||||||
source.onmessage = e => heard(parse(e.data, null));
|
source.onmessage = e => heard(parse(e.data, null));
|
||||||
|
|
@ -1603,7 +1616,8 @@
|
||||||
|
|
||||||
// ---- page set-up -------------------------------------------------------------------------
|
// ---- page set-up -------------------------------------------------------------------------
|
||||||
|
|
||||||
document.documentElement.classList.add('dpw');
|
// The browser layout (podcasts-web.css hangs off html.dpw); diora desktop keeps the phone's.
|
||||||
|
if (OPT.styles !== 'ui-only') document.documentElement.classList.add('dpw');
|
||||||
|
|
||||||
for (const href of UI_STYLES) {
|
for (const href of UI_STYLES) {
|
||||||
const link = document.createElement('link');
|
const link = document.createElement('link');
|
||||||
|
|
@ -1615,6 +1629,8 @@
|
||||||
// The dock the page puts its player bar in (native-radio.js builds it in the app):
|
// The dock the page puts its player bar in (native-radio.js builds it in the app):
|
||||||
// here it is part of the radio bar at the bottom, in place of its "now playing",
|
// here it is part of the radio bar at the bottom, in place of its "now playing",
|
||||||
// so radio and podcasts share one bar.
|
// so radio and podcasts share one bar.
|
||||||
|
// diora desktop: native-radio.js builds the dock (on DOMContentLoaded) with the tab bar.
|
||||||
|
if (OPT.dock !== 'existing') {
|
||||||
const dock = document.createElement('div');
|
const dock = document.createElement('div');
|
||||||
dock.id = 'nui-dock';
|
dock.id = 'nui-dock';
|
||||||
const bar = document.getElementById('now-playing-bar');
|
const bar = document.getElementById('now-playing-bar');
|
||||||
|
|
@ -1633,6 +1649,7 @@
|
||||||
placeDock();
|
placeDock();
|
||||||
if (bar && window.ResizeObserver) new ResizeObserver(placeDock).observe(bar);
|
if (bar && window.ResizeObserver) new ResizeObserver(placeDock).observe(bar);
|
||||||
window.addEventListener('resize', placeDock);
|
window.addEventListener('resize', placeDock);
|
||||||
|
}
|
||||||
|
|
||||||
// Escape closes what the podcast page has open: a sheet, the player, notes,
|
// Escape closes what the podcast page has open: a sheet, the player, notes,
|
||||||
// a podcast, choosing several (its back handler) — never the tab itself.
|
// a podcast, choosing several (its back handler) — never the tab itself.
|
||||||
|
|
@ -1696,8 +1713,12 @@
|
||||||
if (typeof _currentTopTab === 'undefined' || _currentTopTab !== tab) showTab(tab);
|
if (typeof _currentTopTab === 'undefined' || _currentTopTab !== tab) showTab(tab);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// The page (copied from the app) comes last, once all of the above is in place.
|
// The page (copied from the app) comes last, once all of the above is in place —
|
||||||
|
// with an existing dock, also after the page's own DOMContentLoaded work built it.
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = UI_SCRIPT;
|
script.src = UI_SCRIPT;
|
||||||
document.body.appendChild(script);
|
if (OPT.dock !== 'existing') document.body.appendChild(script);
|
||||||
|
else if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', () => setTimeout(() => document.body.appendChild(script), 0));
|
||||||
|
} else setTimeout(() => document.body.appendChild(script), 0);
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -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-v67';
|
const CACHE = 'diora-v68';
|
||||||
const SHELL = [
|
const SHELL = [
|
||||||
'/static/css/app.css',
|
'/static/css/app.css',
|
||||||
'/static/js/app.js',
|
'/static/js/app.js',
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue