fix(cover+downloads): channel art parsing, episode image fallback, local playback

The Fifth Column (and any feed added by URL) had NO coverUrl — the RSS
parser never captured channel artwork, so Now Playing had nothing to show.
- rss-parser: parseChannelCoverUrl (<itunes:image href> / RSS2 <image><url>);
  parseRSSFeed sets it on the Podcast.
- feed store: fetchEpisodes returns the channel cover; subscribe + both
  refresh paths backfill coverUrl when missing (no second fetch).
- useAudio + CLI --play: cover resolves feed.podcast.coverUrl ?? episode.imageUrl,
  so episodes without channel art still get their own image.
- useAudio play/load/switchBackend: prefer the downloaded file
  (getDownloadedFilePath) over the stream URL — downloaded episodes now
  play from disk.
Verified: Fifth Column episode, cold cache -> cover-art-files set at load
-> albumart track present.
This commit is contained in:
2026-08-12 10:52:54 -04:00
parent 0b4a551744
commit 13664c3cec
4 changed files with 72 additions and 23 deletions

View File

@@ -186,9 +186,13 @@ async function handlePlay(feeds: Feed[], arg: string): Promise<void> {
const backend = createAudioBackend()
if (episodeResult.audioUrl) {
// Stage the podcast cover so the system Now Playing shows
// artwork (mpv --cover-art-files), like the UI path does.
const coverArtPath = feedResult.podcast.coverUrl
? await fetchCoverArt(feedResult.podcast.coverUrl)
// artwork (mpv --cover-art-files), like the UI path does. Falls
// back to the episode's own image when the feed has no channel
// cover (URL-added feeds).
const coverUrl =
feedResult.podcast.coverUrl ?? episodeResult.imageUrl;
const coverArtPath = coverUrl
? await fetchCoverArt(coverUrl)
: null
await backend.play(episodeResult.audioUrl, {
mediaTitle: `${feedResult.podcast.title}${episodeResult.title}`,