fix(cover): art reaches Now Playing on first play (restore + cold cache)

Two gaps left cover art missing for the most common play paths:
- Boot-restore preloaded the episode with cachedCoverPath??undefined racing
  the fire-and-forget prefetch; a cold preload + fast-path play re-applied
  art via video-add, which produces a NON-albumart track (verified) that
  mpv's Now Playing artwork logic ignores. load() now awaits the bounded
  fetch (covers ~300ms, 8s cap) so the cover is present at load-time.
- Removed the dead video-add re-apply (fast path + addCoverArt method +
  interface) and the play() late-add fallback; a cold-cache play now
  prefetches for next time instead.
- FeedPage prefetches covers for the focus window (single-flight, cached)
  so ordinary plays land on a warm cache.
Verified: preload with awaited cover -> albumart track present (2 tracks).
This commit is contained in:
2026-08-12 10:22:49 -04:00
parent deac6081ca
commit bd7d988741
3 changed files with 28 additions and 29 deletions

View File

@@ -60,7 +60,6 @@ export interface AudioBackend {
* (mpv `video-add`). Lets play() start without waiting on art; the
* Now Playing artwork pops in when the download lands.
*/
addCoverArt(path: string): Promise<void>;
pause(): Promise<void>;
resume(): Promise<void>;
stop(): Promise<void>;
@@ -561,12 +560,6 @@ export class MpvBackend implements AudioBackend {
Math.round((opts?.volume ?? 1) * 100),
]);
await this.send(["set_property", "speed", opts?.speed ?? 1]);
if (opts?.coverArtPath) {
// File is already loaded: cover-art-files only applies at
// load, so add the art as a runtime albumart track instead.
await this.send(["set_property", "cover-art-files", opts.coverArtPath]);
await this.send(["video-add", opts.coverArtPath]);
}
if (opts?.mediaTitle) {
await this.send(["set_property", "force-media-title", opts.mediaTitle]);
}
@@ -591,14 +584,6 @@ export class MpvBackend implements AudioBackend {
});
}
async addCoverArt(path: string): Promise<void> {
if (!this._loadedUrl) return;
// Keep the property pointing at the latest art too, so a subsequent
// loadfile of the same episode carries it.
await this.send(["set_property", "cover-art-files", path]);
await this.send(["video-add", path]);
}
async pause(): Promise<void> {
await this.send(["set_property", "pause", true]);
this._intentPlaying = false;
@@ -716,7 +701,6 @@ class NoopBackend implements AudioBackend {
readonly name: BackendName = "none";
async play(): Promise<void> {}
async preload(): Promise<void> {}
async addCoverArt(): Promise<void> {}
async pause(): Promise<void> {}
async resume(): Promise<void> {}
async stop(): Promise<void> {}