[PR #4806] Add support for custom episode cover art for podcasts #4352

Open
opened 2026-04-25 00:19:23 +02:00 by adam · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/advplyr/audiobookshelf/pull/4806
Author: @mfcar
Created: 11/6/2025
Status: 🔄 Open

Base: masterHead: mf/episodeCoverArt


📝 Commits (1)

  • f703fb6 Add support to custom episode cover art

📊 Changes

16 files changed (+446 additions, -20 deletions)

View changed files

📝 client/components/app/MediaPlayerContainer.vue (+8 -2)
📝 client/components/cards/LazyBookCard.vue (+5 -2)
📝 client/components/covers/BookCover.vue (+6 -1)
📝 client/components/modals/podcast/ViewEpisode.vue (+7 -1)
📝 client/pages/library/_library/podcast/latest.vue (+11 -4)
📝 server/Auth.js (+5 -1)
📝 server/controllers/PodcastController.js (+29 -0)
📝 server/managers/CacheManager.js (+60 -1)
📝 server/managers/CoverManager.js (+84 -0)
📝 server/managers/PodcastManager.js (+23 -0)
server/migrations/v2.30.1-episode-cover-support.js (+133 -0)
📝 server/models/FeedEpisode.js (+17 -4)
📝 server/models/PodcastEpisode.js (+28 -3)
📝 server/routers/ApiRouter.js (+1 -0)
📝 server/scanner/PodcastScanner.js (+21 -0)
📝 server/utils/podcastUtils.js (+8 -1)

📄 Description

Brief summary

This PR adds the feature to download episode art for specific episodes. It first checks for the <itunes:image> tag, and if it doesn’t exist, it attempts to extract the image from the audio file.

Which issue is fixed?

Fixes #1573

In-depth Description

This PR updates the Server to attempt to download the episode art from the itunes:image tag in the feed. If that’s not possible, it tries to extract the image directly from the audio file.

If an episode doesn’t have a custom cover, the podcast’s main cover will continue to be used.
Since the client is being rewritten, I’ve kept UI changes to a minimum. The updates were applied to the following pages:

  • Latest Episodes page
  • Library home page (Continue Listening shelf)
  • Miniplayer
  • Episode details modal

Once the new UI is ready, I can move these changes there.

Two new columns have been added via migration (sorry, I set a future version for the migration):

  • podcastEpisodes.coverPath — stores the local file path
  • podcastEpisodes.imageURL — stores the RSS feed image URL
  • feedEpisodes.episodeCoverURL — used for RSS feed generation

The episode art are saved on: metadata/episodes/<EPISODE_ID>/cover.jpg

I try to replicate in the Cache Manager the same behavior used for caching podcast covers.
Also, I added the podcast cover URL to the exception list in the Auth.js file so that it doesn’t require authentication.

If this PR is integrated, I can create a new PR to add this feature to the mobile app as well.

Also, a new endpoint to return the Episode Art was created: /podcasts/:id/episode/:episodeId/cover, this can be used by other application like ShelfPlayer: https://github.com/rasmuslos/ShelfPlayer/issues/175

How have you tested this?

Yes, I tested with two podcasts that have different cover art for specific episodes:

Add these podcasts to your library, download a few episodes, and check them on the Latest Episodes page.

Screenshots

Library Home

Screenshot 2025-11-06 at 18 41 09

Latest

Screenshot 2025-11-06 at 18 42 41

Episode Modal

Screenshot 2025-11-06 at 18 42 54

Demo

https://github.com/user-attachments/assets/2d0ec6e4-b09d-4317-b4fe-61838f3b7a17


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/advplyr/audiobookshelf/pull/4806 **Author:** [@mfcar](https://github.com/mfcar) **Created:** 11/6/2025 **Status:** 🔄 Open **Base:** `master` ← **Head:** `mf/episodeCoverArt` --- ### 📝 Commits (1) - [`f703fb6`](https://github.com/advplyr/audiobookshelf/commit/f703fb60dab3348df7ea3743f76346653e4ccb9d) Add support to custom episode cover art ### 📊 Changes **16 files changed** (+446 additions, -20 deletions) <details> <summary>View changed files</summary> 📝 `client/components/app/MediaPlayerContainer.vue` (+8 -2) 📝 `client/components/cards/LazyBookCard.vue` (+5 -2) 📝 `client/components/covers/BookCover.vue` (+6 -1) 📝 `client/components/modals/podcast/ViewEpisode.vue` (+7 -1) 📝 `client/pages/library/_library/podcast/latest.vue` (+11 -4) 📝 `server/Auth.js` (+5 -1) 📝 `server/controllers/PodcastController.js` (+29 -0) 📝 `server/managers/CacheManager.js` (+60 -1) 📝 `server/managers/CoverManager.js` (+84 -0) 📝 `server/managers/PodcastManager.js` (+23 -0) ➕ `server/migrations/v2.30.1-episode-cover-support.js` (+133 -0) 📝 `server/models/FeedEpisode.js` (+17 -4) 📝 `server/models/PodcastEpisode.js` (+28 -3) 📝 `server/routers/ApiRouter.js` (+1 -0) 📝 `server/scanner/PodcastScanner.js` (+21 -0) 📝 `server/utils/podcastUtils.js` (+8 -1) </details> ### 📄 Description ## Brief summary <!-- Please provide a brief summary of what your PR attempts to achieve. --> This PR adds the feature to download [episode art](https://podcasters.apple.com/support/5516-episode-art-template) for specific episodes. It first checks for the `<itunes:image>` tag, and if it doesn’t exist, it attempts to extract the image from the audio file. ## Which issue is fixed? <!-- Which issue number does this PR fix? Ex: "Fixes #1234" --> Fixes #1573 ## In-depth Description <!-- Describe your solution in more depth. How does it work? Why is this the best solution? Does it solve a problem that affects multiple users or is this an edge case for your setup? --> This PR updates the Server to attempt to download the episode art from the <itunes:image> tag in the feed. If that’s not possible, it tries to extract the image directly from the audio file. If an episode doesn’t have a custom cover, the podcast’s main cover will continue to be used. Since the client is being rewritten, I’ve kept UI changes to a minimum. The updates were applied to the following pages: - Latest Episodes page - Library home page (Continue Listening shelf) - Miniplayer - Episode details modal Once the new UI is ready, I can move these changes there. Two new columns have been added via migration (sorry, I set a future version for the migration): - podcastEpisodes.coverPath — stores the local file path - podcastEpisodes.imageURL — stores the RSS feed image URL - feedEpisodes.episodeCoverURL — used for RSS feed generation The episode art are saved on: `metadata/episodes/<EPISODE_ID>/cover.jpg` I try to replicate in the Cache Manager the same behavior used for caching podcast covers. Also, I added the podcast cover URL to the exception list in the Auth.js file so that it doesn’t require authentication. If this PR is integrated, I can create a new PR to add this feature to the mobile app as well. Also, a new endpoint to return the Episode Art was created: `/podcasts/:id/episode/:episodeId/cover`, this can be used by other application like ShelfPlayer: https://github.com/rasmuslos/ShelfPlayer/issues/175 ## How have you tested this? <!-- Please describe in detail with reproducible steps how you tested your changes. --> Yes, I tested with two podcasts that have different cover art for specific episodes: - In the Dark: https://publicfeeds.net/f/5770/in-the-dark - Vortex: https://anchor.fm/s/10a0d0948/podcast/rss Add these podcasts to your library, download a few episodes, and check them on the Latest Episodes page. ## Screenshots ### Library Home <img width="1318" height="815" alt="Screenshot 2025-11-06 at 18 41 09" src="https://github.com/user-attachments/assets/00440ce6-6ba1-4ce3-9685-1078e86cf094" /> --- ### Latest <img width="788" height="581" alt="Screenshot 2025-11-06 at 18 42 41" src="https://github.com/user-attachments/assets/d39e10f7-324c-4904-986e-313186285631" /> --- ### Episode Modal <img width="615" height="527" alt="Screenshot 2025-11-06 at 18 42 54" src="https://github.com/user-attachments/assets/dc0e32ed-5efe-4b82-8665-4747c5d4d648" /> --- ## Demo https://github.com/user-attachments/assets/2d0ec6e4-b09d-4317-b4fe-61838f3b7a17 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
adam added the pull-request label 2026-04-25 00:19:23 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/audiobookshelf#4352