[Bug]: Incorrect enclosure type for video files in podcast RSS feed #1445

Open
opened 2026-04-24 23:45:48 +02:00 by adam · 2 comments
Owner

Originally created by @mchill on GitHub (Oct 8, 2023).

Describe the issue

When you put an mp4 file in a podcast directory and open the RSS feed, the generated item for that file has an enclosure with type="audio/mp4". It should be type="video/mp4" instead. Some podcast apps like Podcast Addict support video playback, but will force audio playback of video files when the type is audio/*.

Steps to reproduce the issue

  1. Create a podcast library
  2. Create a folder in the library and add an mp4 file to it
  3. Scan the library to detect the test podcast
  4. Open the RSS feed
  5. See that the enclosure has type="audio/mp4"

Audiobookshelf version

v2.4.4

How are you running audiobookshelf?

Docker

Originally created by @mchill on GitHub (Oct 8, 2023). ### Describe the issue When you put an mp4 file in a podcast directory and open the RSS feed, the generated item for that file has an enclosure with `type="audio/mp4"`. It should be `type="video/mp4"` instead. Some podcast apps like Podcast Addict support video playback, but will force audio playback of video files when the type is `audio/*`. ### Steps to reproduce the issue 1. Create a podcast library 2. Create a folder in the library and add an mp4 file to it 3. Scan the library to detect the test podcast 4. Open the RSS feed 5. See that the enclosure has `type="audio/mp4"` ### Audiobookshelf version v2.4.4 ### How are you running audiobookshelf? Docker
adam added the bug label 2026-04-24 23:45:48 +02:00
Author
Owner

@AdrianEdelen commented on GitHub (Feb 6, 2025):

as a POC i changed this:
https://github.com/advplyr/audiobookshelf/blob/5e5a604d0332f07c18ac73b45fe8b3681b9a05cc/server/models/FeedEpisode.js#L67

to this:

enclosureType: episode.enclosureType,

since we don't actually lose the proper enclosure type when creating a podcastEpisode and it works. I was able to create the feed through ABS and then add the feed to pocket casts (as I would with any other feed) and play the video. additionally the podcast plays as just audio on the web player (I haven't tested on the android app yet.)

I will continue testing but it seems promising so far.

[pictures removed because they were just large phone screenshots showing the video podcast playing]

I understand this isn't exactly the same scenario, however it is a step in the right direction for supporting video feeds in general.

@AdrianEdelen commented on GitHub (Feb 6, 2025): as a POC i changed this: https://github.com/advplyr/audiobookshelf/blob/5e5a604d0332f07c18ac73b45fe8b3681b9a05cc/server/models/FeedEpisode.js#L67 to this: ``` js enclosureType: episode.enclosureType, ``` since we don't actually lose the proper enclosure type when creating a `podcastEpisode` and it works. I was able to create the feed through ABS and then add the feed to pocket casts (as I would with any other feed) and play the video. additionally the podcast plays as just audio on the web player (I haven't tested on the android app yet.) I will continue testing but it seems promising so far. [pictures removed because they were just large phone screenshots showing the video podcast playing] I understand this isn't exactly the same scenario, however it is a step in the right direction for supporting video feeds in general.
Author
Owner

@AdrianEdelen commented on GitHub (Feb 6, 2025):

Investigating into getting video information as uploaded files. It looks like embedded cover art is one video stream. currently ABS just assumes the video stream is cover art if it exists.

so to start we can take a look in setData in MediaProbeData.js and see that video stream is getting nulled out if there is a video stream codec.

https://github.com/advplyr/audiobookshelf/blob/5e5a604d0332f07c18ac73b45fe8b3681b9a05cc/server/scanner/MediaProbeData.js#L46C5-L52C80
(not sure why this isn't embedding properly)

next I see that we only actually keep one video stream when cleaning the ffmpeg probe output, which in the case of video files that also have an embedded cover would result in only the first index being used as a cover art and the rest of the video file information being lost.

Since at least for a basic implementation, all we need is to know whether or not there is a video stream (and that it actually is video and not a cover art) I think we can do the following high level changes:

  1. keep all video streams when processing the ffmpeg probe.
  2. modify MediaProbeData.setData() to check the video streams for specific codecs, setting embeddedCoverArt and VideoStream based on codec type.

something like

    const COVER_ART_CODECS = new Set(["mjpeg", "png", "bmp", "jpeg"]);
    const VIDEO_STREAM_CODECS = new Set(["h264", "hevc", "vp8", "vp9", "av1", "mpeg4"]);

    this.embeddedCoverArt = COVER_ART_CODECS.has(data.video_stream?.codec) ? data.video_stream : null;
    this.videoStream = VIDEO_STREAM_CODECS.has(data.video_stream?.codec) ? data.video_stream : null;

(I didn't consider here the case where there is cover art and a video stream, we would need to store all streams in the probe and then check them all, not a big difference)

then later on when creating the audioFile we add a property such as hasVideo which can be set in AudioFile.setDataFromProbe, which will then be used in scanNewPodcastLibraryItem and so on, to eventually set the enclosure tag for the podcast episode (which is null for uploaded files audio and video)

considerations:

  • Side effects - need to ensure that changing the behavior of embeddedCoverArt and VideoStreams does not cause issues elsewhere.
  • updating codecs - potentially need to maintain a list or update the valid codecs.
  • cover art extractor - will this affect cover art extraction (i don't think so)
  • minor UI changes, when in the library files component, the file type will still be listed as audio, which isn't techhnically true. However for someone using the ABS client, it may as well be. which leads to:
  • communication to end user, how to demonstrate that video podcasts are supported by the server but not the client.

Image

I am working on the POC for this that is a little more substantial than just commenting out and changing random stuff.

@AdrianEdelen commented on GitHub (Feb 6, 2025): Investigating into getting video information as uploaded files. It looks like embedded cover art is one video stream. currently ABS just assumes the video stream is cover art if it exists. so to start we can take a look in setData in MediaProbeData.js and see that video stream is getting nulled out if there is a video stream codec. https://github.com/advplyr/audiobookshelf/blob/5e5a604d0332f07c18ac73b45fe8b3681b9a05cc/server/scanner/MediaProbeData.js#L46C5-L52C80 (not sure why this isn't embedding properly) next I see that we only actually keep one video stream when cleaning the ffmpeg probe output, which in the case of video files that also have an embedded cover would result in only the first index being used as a cover art and the rest of the video file information being lost. Since at least for a basic implementation, all we need is to know whether or not there is a video stream (and that it actually is video and not a cover art) I think we can do the following high level changes: 1. keep all video streams when processing the ffmpeg probe. 2. modify `MediaProbeData.setData()` to check the video streams for specific codecs, setting embeddedCoverArt and VideoStream based on codec type. something like ``` js const COVER_ART_CODECS = new Set(["mjpeg", "png", "bmp", "jpeg"]); const VIDEO_STREAM_CODECS = new Set(["h264", "hevc", "vp8", "vp9", "av1", "mpeg4"]); this.embeddedCoverArt = COVER_ART_CODECS.has(data.video_stream?.codec) ? data.video_stream : null; this.videoStream = VIDEO_STREAM_CODECS.has(data.video_stream?.codec) ? data.video_stream : null; ``` (I didn't consider here the case where there is cover art and a video stream, we would need to store all streams in the probe and then check them all, not a big difference) then later on when creating the audioFile we add a property such as `hasVideo` which can be set in AudioFile.setDataFromProbe, which will then be used in `scanNewPodcastLibraryItem` and so on, to eventually set the enclosure tag for the podcast episode (which is null for uploaded files audio and video) considerations: - Side effects - need to ensure that changing the behavior of embeddedCoverArt and VideoStreams does not cause issues elsewhere. - updating codecs - potentially need to maintain a list or update the valid codecs. - cover art extractor - will this affect cover art extraction (i don't think so) - minor UI changes, when in the library files component, the file type will still be listed as audio, which isn't techhnically true. However for someone using the ABS client, it may as well be. which leads to: - communication to end user, how to demonstrate that video podcasts are supported by the server but not the client. ![Image](https://github.com/user-attachments/assets/c510320a-4196-4a31-94f9-86c7b5547645) I am working on the POC for this that is a little more substantial than just commenting out and changing random stuff.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/audiobookshelf#1445