feat: wire NSBridge fully + HD artwork + disable watchdog in wrapper mode

player.js
- Replace `_native()` function with `const HAS_BRIDGE` (evaluated once at
  module load so bridge detection is rock-solid)
- Add `buildTrackList()` — maps S.queue to NSBridge Track array with 1024px
  artworkUrl so lock-screen cover is crisp on high-DPR displays
- Add `_pushQueueIfBridge()` — called by `fillRadioQueue()` so radio top-ups
  are pushed to ExoPlayer in real time (fixes autoplay gap after radio refill)
- `playQueue`: route through `NSBridge.setQueue` when bridge present; old
  `S.audio` path retained for browser/PWA fallback
- `playCurrent`: `NSBridge.seekToIndex + play` in bridge mode
- `nextSong / prevSong / togglePlay / onEnded`: delegate to bridge, skip local
  S.qIdx manipulation (native sends onTrackChanged callback instead)
- `seekTo / _initBarScrub`: use `_lastDurationMs` for ms-accurate seeks
- `onTrackChanged`: guard with `p.index !== S.qIdx` to avoid redundant redraws
- `updateMediaSession`: artwork sizes 96/300/512 → 256/512/1024
- Player-bar cover: 100 → 256px; NP cover/bg: 400 → 1024px;
  queue items: 60 → 128px; preload hint sizes: 96/300/512 → 256/512/1024

main.js
- Wrap watchdog setInterval in `typeof window.NSBridge === 'undefined'` guard;
  the foreground service keeps ExoPlayer alive — JS watchdog not needed there

views.js
- Album detail / playlist detail covers: 300 → 512px
- Playlist grid cards: 200 → 512px
- Search result song cards: 200 → 256px
- Artist top-songs / all-songs thumbnails: 60 → 128px
- Partial album art in _renderArtistAlbums: 300 → 512px

components.js
- makeAlbumCard: 200 → 512px
- makeSongItem: 60 → 128px

nowplaying.js
- NP queue items: 60 → 128px
- NP action sheet thumb: 80 → 256px

sw.js
- Bump CACHE to ns-v41 so stale small-cover entries are evicted on next load

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-05-09 10:33:43 +02:00
1 parent 0ee2a62adb
commit 76ecfe2268
6 files changed
+144 -113

No files matched your search

+19 -16
View File
@@ -182,22 +182,25 @@ window.addEventListener('load', () => {
// pageshow fires more reliably than visibilitychange on iOS PWA and Samsung
window.addEventListener('pageshow', e => { if(e.persisted) _onForeground(); });
// 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);
// Watchdog: detect when audio silently stalled in background (ended event not fired, or frozen).
// Disabled with Bridge — the foreground service keeps ExoPlayer alive independently.
if (typeof window.NSBridge === 'undefined') {
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();
initProgressScrub();