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:
1 parent
0b9493c147
commit
dad2b0b730
2 files changed
+31
-20
No files matched your search
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user