fix: Android lock screen notification + radio mode + SW auto-update reliability

- Set mediaSession.playbackState='playing'/'paused' so Android keeps the
  notification alive when the screen sleeps
- Provide 96x96/300x300/512x512 artwork sizes for Android notification views
- playQueue() now calls fillRadioQueue() immediately when radio mode is on,
  so the infinite queue works without toggling radio off/on
- SW: add GET_VERSION/VERSION message handler for version-query fallback
- SW registration: updateViaCache:'none' prevents browser HTTP-caching sw.js
- Bump cache to ns-v30

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-04-26 01:02:56 +02:00
1 parent 0b9493c147
commit dad2b0b730
2 files changed
+31 -20

No files matched your search

+24 -19
View File
@@ -116,8 +116,8 @@ window.addEventListener('load',()=>{
document.getElementById('srv-pass').addEventListener('keydown',e=>{if(e.key==='Enter')doConnect()});
audio.addEventListener('timeupdate',updateProgress);
audio.addEventListener('ended',onEnded);
audio.addEventListener('play',()=>{setPlayIcon(true);document.getElementById('player-art').classList.add('spinning')});
audio.addEventListener('pause',()=>{setPlayIcon(false);document.getElementById('player-art').classList.remove('spinning')});
audio.addEventListener('play',()=>{setPlayIcon(true);document.getElementById('player-art').classList.add('spinning');if(navigator.mediaSession)navigator.mediaSession.playbackState='playing';});
audio.addEventListener('pause',()=>{setPlayIcon(false);document.getElementById('player-art').classList.remove('spinning');if(navigator.mediaSession)navigator.mediaSession.playbackState='paused';});
previewAudio.addEventListener('timeupdate',updatePreviewProgress);
previewAudio.addEventListener('ended',stopPreview);
audio.volume=0.8;
@@ -975,7 +975,7 @@ function dlSongLocal(s){
function downloadCurrent(){if(qIdx>=0&&queue[qIdx])dlSongLocal(queue[qIdx])}
// ─── PLAYER ───
function playQueue(songs,startIdx){queue=songs.map(s=>({...s}));qIdx=startIdx;playCurrent();renderQueue()}
function playQueue(songs,startIdx){queue=songs.map(s=>({...s}));qIdx=startIdx;playCurrent();renderQueue();if(radioMode)fillRadioQueue();}
function playCurrent(){
if(qIdx<0||qIdx>=queue.length)return;
const s=queue[qIdx];
@@ -1025,7 +1025,8 @@ function _syncNpDirect(s){
}
function updateMediaSession(s){
if(!navigator.mediaSession)return;
navigator.mediaSession.metadata=new MediaMetadata({title:s.title,artist:s.artist||'',album:s.album||'',artwork:s.coverArt?[{src:coverUrl(s.coverArt,300),sizes:'300x300'}]:[]});
const artwork=s.coverArt?[{src:coverUrl(s.coverArt,96),sizes:'96x96',type:'image/jpeg'},{src:coverUrl(s.coverArt,300),sizes:'300x300',type:'image/jpeg'},{src:coverUrl(s.coverArt,512),sizes:'512x512',type:'image/jpeg'}]:[];
navigator.mediaSession.metadata=new MediaMetadata({title:s.title,artist:s.artist||'',album:s.album||'',artwork});
navigator.mediaSession.setActionHandler('play',togglePlay);
navigator.mediaSession.setActionHandler('pause',togglePlay);
navigator.mediaSession.setActionHandler('previoustrack',prevSong);
@@ -1330,24 +1331,28 @@ async function loadArtists(){
// ─── PWA ───
function injectPWA(){
if (!('serviceWorker' in navigator)) return;
navigator.serviceWorker.register('sw.js').then(reg => {
// Force-check for SW update every time the app opens
reg.update().catch(() => {});
// New SW found while page is open (e.g. user left tab open overnight)
reg.addEventListener('updatefound', () => {
const sw = reg.installing;
if (!sw) return;
sw.addEventListener('statechange', () => {
// 'installed' + existing controller = update ready but waiting for skipWaiting
// With skipWaiting() this fires briefly; the SW_UPDATED message handles the banner
});
});
}).catch(() => {});
// SW posts SW_UPDATED after it activates and claims this client
// Listen before registering so we never miss a message
navigator.serviceWorker.addEventListener('message', e => {
if (e.data?.type === 'SW_UPDATED') showUpdateBanner(e.data.version);
if (e.data?.type === 'SW_UPDATED') {
localStorage.setItem('sw-version', e.data.version);
showUpdateBanner(e.data.version);
}
// Version query response — covers the race where SW activated before this listener
if (e.data?.type === 'VERSION') {
const prev = localStorage.getItem('sw-version');
if (prev && prev !== e.data.version) showUpdateBanner(e.data.version);
localStorage.setItem('sw-version', e.data.version);
}
});
// updateViaCache:'none' prevents the browser HTTP cache from hiding SW file changes
navigator.serviceWorker.register('sw.js', { updateViaCache: 'none' }).then(reg => {
reg.update().catch(() => {});
// Query active SW version right now to catch updates that happened before page JS ran
const ctrl = navigator.serviceWorker.controller;
if (ctrl) ctrl.postMessage({ type: 'GET_VERSION' });
}).catch(() => {});
}
function showUpdateBanner(version) {