fix(mobile): restore playback state + MediaSession stability
- _restoreLastPlayed: set audio.src + call updateMediaSession so play button works immediately and notification shows artwork on app open - visibilitychange: re-register all MediaSession handlers after screen unlock, fold/flip, or background return (fixes vanishing notification) - Stall watchdog (3s interval): detects frozen audio in background and calls play() to recover; also catches missed 'ended' events so auto-advance works reliably when screen is off - stalled/error listeners: attempt audio reload on network hiccup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
74743a81e6
commit
e31ddec843
2 files changed
+43
-1
No files matched your search
+39
-1
@@ -8,7 +8,7 @@ import {
|
|||||||
initProgressScrub, updateProgress, updatePreviewProgress, stopPreview,
|
initProgressScrub, updateProgress, updatePreviewProgress, stopPreview,
|
||||||
toggleRadioMode, starCurrent, downloadCurrent, renderQueue, playPreview,
|
toggleRadioMode, starCurrent, downloadCurrent, renderQueue, playPreview,
|
||||||
toggleStar, dlSongLocal, _applyRadioBtn, _restoreLastPlayed, setPlayIcon,
|
toggleStar, dlSongLocal, _applyRadioBtn, _restoreLastPlayed, setPlayIcon,
|
||||||
npToggleStar,
|
npToggleStar, updateMediaSession,
|
||||||
} from './player.js';
|
} from './player.js';
|
||||||
import {
|
import {
|
||||||
navigate, _navGoBack, _navLoad, loadHomeData, onLocalSearch, onMobileSearch,
|
navigate, _navGoBack, _navLoad, loadHomeData, onLocalSearch, onMobileSearch,
|
||||||
@@ -106,8 +106,46 @@ window.addEventListener('load', () => {
|
|||||||
});
|
});
|
||||||
S.previewAudio.addEventListener('timeupdate', updatePreviewProgress);
|
S.previewAudio.addEventListener('timeupdate', updatePreviewProgress);
|
||||||
S.previewAudio.addEventListener('ended', stopPreview);
|
S.previewAudio.addEventListener('ended', stopPreview);
|
||||||
|
|
||||||
|
// Recovery: try to resume if audio stalls (network hiccup, background throttle)
|
||||||
|
S.audio.addEventListener('stalled', () => { if(!S.audio.paused) S.audio.load(); });
|
||||||
|
S.audio.addEventListener('waiting', () => { /* let browser handle buffering */ });
|
||||||
|
S.audio.addEventListener('error', () => {
|
||||||
|
if(!S.audio.paused && S.queue[S.qIdx]) {
|
||||||
|
const t = setTimeout(() => { S.audio.load(); S.audio.play().catch(()=>{}); }, 2000);
|
||||||
|
S.audio.addEventListener('play', () => clearTimeout(t), { once: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
S.audio.volume = 0.8;
|
S.audio.volume = 0.8;
|
||||||
|
|
||||||
|
// Re-register MediaSession handlers when returning from background/screen-off/flip
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if(document.visibilityState !== 'visible') return;
|
||||||
|
const s = S.queue[S.qIdx];
|
||||||
|
if(!s) return;
|
||||||
|
updateMediaSession(s);
|
||||||
|
if(navigator.mediaSession)
|
||||||
|
navigator.mediaSession.playbackState = S.audio.paused ? 'paused' : 'playing';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Watchdog: detect when audio silently stalled in background (ended event not fired, or frozen)
|
||||||
|
let _wdLast = 0, _wdStalledAt = 0;
|
||||||
|
setInterval(() => {
|
||||||
|
if(!S.audio.src || S.audio.paused) { _wdStalledAt = 0; _wdLast = 0; return; }
|
||||||
|
const dur = S.audio.duration;
|
||||||
|
const cur = S.audio.currentTime;
|
||||||
|
// ended but event didn't fire (background throttling)
|
||||||
|
if(dur > 0 && cur >= dur - 0.5) { onEnded(); _wdStalledAt = 0; _wdLast = 0; return; }
|
||||||
|
if(cur !== _wdLast) { _wdStalledAt = 0; _wdLast = cur; return; }
|
||||||
|
if(!_wdStalledAt) { _wdStalledAt = Date.now(); return; }
|
||||||
|
// frozen for >8s → try to resume
|
||||||
|
if(Date.now() - _wdStalledAt > 8000) {
|
||||||
|
_wdStalledAt = 0;
|
||||||
|
S.audio.play().catch(() => {});
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
injectPWA();
|
injectPWA();
|
||||||
initProgressScrub();
|
initProgressScrub();
|
||||||
|
|
||||||
|
|||||||
@@ -353,5 +353,9 @@ export function _restoreLastPlayed() {
|
|||||||
art.innerHTML = cv ? `<img src="${cv}" style="width:100%;height:100%;object-fit:cover">` : '<div class="no-art">♪</div>';
|
art.innerHTML = cv ? `<img src="${cv}" style="width:100%;height:100%;object-fit:cover">` : '<div class="no-art">♪</div>';
|
||||||
updateStarBtn();
|
updateStarBtn();
|
||||||
document.title = `${s.title} — NovaSonic`;
|
document.title = `${s.title} — NovaSonic`;
|
||||||
|
// set src so the play button works immediately without needing to re-select the song
|
||||||
|
S.audio.src = streamUrl(s.id);
|
||||||
|
S.audio.load();
|
||||||
|
updateMediaSession(s);
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user