Gap 1: Lock Screen seek bar drift — nowPlayingSyncTimer (5s timer that pushes elapsed time to MPNowPlayingInfoCenter) was created in playWithAVPlayer but never restarted after a background/foreground cycle. Now invalidated in suspendVisTimers() and recreated in resumeVisTimers(). The crossfade path benefits too — it never went through playWithAVPlayer so the timer was never created at all for crossfade sessions. Gap 2: Prefetcher re-downloads after restructure — AudioPreFetcher used isSongDownloaded(song.id) (exact ID match). After a Companion restructure changes IDs, songs already downloaded were re-fetched. Changed to isSongAvailableOffline(song) which falls back to title/artist/duration matching. Gap 3: Unbounded image disk cache — trimDiskCache() only ran on app launch. A long browsing session could push well past the 200MB limit. Now storeToDisk increments a write counter and triggers trim every 50 writes. The counter lives on ioQueue (serial) so no lock needed. Gap 4: Custom cover art in widget — WidgetBridge cached blur keyed by coverArtId. Custom covers don’t change the ID, so the bridge skipped the update. Now pushWidgetState() passes "custom_\(id)" as the key when AlbumCoverStore has a custom image. Same album’s songs still share the key → blur is reused, not redone. When custom is removed, key reverts to the bare ID → re-blurs with server art. |
||
|---|---|---|
| .. | ||
| Info.plist | ||
| NavidromeWidget.entitlements | ||
| NowPlayingWidget.swift | ||
| NowPlayingWidgetViews.swift | ||
| WidgetIntents.swift | ||