ytdl-sub Integration Guide¶
sub2pod converts ytdl-sub output directories into podcast RSS feeds. This guide explains how to configure ytdl-sub to produce output compatible with sub2pod.
Table of Contents¶
- Output format contract
- Configuring nfo_tags for per-episode metadata
- Configuring episode thumbnails
- Configuring channel artwork
- Complete example configuration
- Running sub2pod: post_process hook vs cron job
- Opus MIME type caveat and re-encode workaround
Output format contract¶
sub2pod expects each channel directory to contain the following files:
| File | Required | Description |
|---|---|---|
tvshow.nfo |
Yes | Kodi XML with <title> and optionally <genre> |
poster.jpg or fanart.jpg |
No | Channel artwork (used as <itunes:image>) |
YYYY-MM-DD - <title>.<ext> |
Yes | Date-prefixed audio files (.opus, .mp3, .m4a) |
YYYY-MM-DD - <title>.nfo |
No | Per-episode Kodi XML sidecar (see fields below) |
YYYY-MM-DD - <title>-thumb.jpg |
No | Per-episode thumbnail (currently reserved for future use) |
Episode NFO fields read by sub2pod¶
| NFO element | sub2pod field | RSS element |
|---|---|---|
<title> |
EpisodeInfo.title |
<item><title> |
<plot> or <description> |
EpisodeInfo.plot |
<item><description> |
<aired> or <upload_date> |
EpisodeInfo.aired |
<item><pubDate> |
<runtime> |
EpisodeInfo.runtime |
<itunes:duration> |
<webpage_url> |
EpisodeInfo.original_url |
<item><link> |
When no .nfo sidecar is present, sub2pod falls back to parsing the date and
title from the filename pattern YYYY-MM-DD - <title>.<ext>.
Configuring nfo_tags for per-episode metadata¶
The nfo_tags output type in ytdl-sub produces Kodi-compatible NFO sidecar files.
Configure it to emit the fields sub2pod needs.
Required ytdl-sub variables¶
Use these ytdl-sub source variables in your NFO tags:
nfo_tags:
nfo_name: "{upload_date_standardized} - {title}.nfo"
nfo_root: "episodedetails"
tags:
title: "{title}"
description: "{description}"
upload_date: "{upload_date_standardized}"
webpage_url: "{webpage_url}"
| Variable | Maps to | Notes |
|---|---|---|
{title} |
<title> |
Video title from the source |
{description} |
<description> |
Full video description (show notes) |
{upload_date_standardized} |
<upload_date> |
Date in YYYY-MM-DD format |
{webpage_url} |
<webpage_url> |
URL of the original source page |
Optional: runtime¶
If your source provides duration metadata (e.g. YouTube), include it:
Note: The
<runtime>field in Kodi NFO files is measured in minutes. ytdl-sub's{duration}variable provides seconds. To use it, divide by 60:<runtime>{duration_seconds / 60}</runtime>(if your ytdl-sub version supports arithmetic expressions in tags). Otherwise, omit<runtime>— it is optional and sub2pod will still produce valid feeds without it.
Optional: genre¶
Include genre in tvshow.nfo for iTunes category support:
nfo_tags:
nfo_name: "tvshow.nfo"
nfo_root: "tvshow"
tags:
title: "{tv_show_name}"
genre: "Technology"
Configuring episode thumbnails¶
Use ytdl-sub's episode_thumbnails output type to produce per-episode JPEG files
compatible with sub2pod's naming convention:
presets:
podcast:
output_options:
output_directory: "/path/to/podcast/{tv_show_name}"
file_name: "{upload_date_standardized} - {title}.{ext}"
episode_thumbnails:
thumbnail_name: "{upload_date_standardized} - {title}-thumb.jpg"
Note: Episode thumbnails are currently reserved for future use in sub2pod. The RSS feed includes only channel-level artwork (
<itunes:image>) viaposter.jpgorfanart.jpg. Generating thumbnails now ensures your directories are forward-compatible when per-episode artwork support is added.
Configuring channel artwork¶
ytdl-sub can download channel artwork. Ensure it writes poster.jpg:
presets:
podcast:
output_options:
output_directory: "/path/to/podcast/{tv_show_name}"
# Download channel thumbnail as poster.jpg
# (exact configuration depends on your ytdl-sub version and source plugin)
If ytdl-sub's channel thumbnail output produces a different filename, rename it
manually or symlink it to poster.jpg.
Complete example configuration¶
Below is a complete ytdl-sub config.yaml for a YouTube channel converted to
a sub2pod-compatible podcast directory:
configuration:
working_directory: ".ytdl-sub"
presets:
podcast:
# --- YouTube source ---
youtube:
channel_url: "https://www.youtube.com/@Level1Techs"
download_strategy: "audio_only"
# --- Output file naming ---
output_options:
output_directory: "/home/NAS/ytdl-sub/mark/podcast/{tv_show_name}"
file_name: "{upload_date_standardized} - {title}.{ext}"
# --- Episode NFO sidecar ---
nfo_tags:
nfo_name: "{upload_date_standardized} - {title}.nfo"
nfo_root: "episodedetails"
tags:
title: "{title}"
description: "{description}"
upload_date: "{upload_date_standardized}"
webpage_url: "{webpage_url}"
# --- Episode thumbnails ---
episode_thumbnails:
thumbnail_name: "{upload_date_standardized} - {title}-thumb.jpg"
# --- Show-level NFO ---
nfo_tags:
nfo_name: "tvshow.nfo"
nfo_root: "tvshow"
tags:
title: "{tv_show_name}"
genre: "Technology"
# --- Overrides ---
overrides:
tv_show_name: "Links with Friends"
Important: The
tv_show_nameoverride must match your desired channel directory name. sub2pod uses the directory name to construct enclosure URLs.
Running sub2pod: post_process hook vs cron job¶
sub2pod is a separate CLI tool. You need to decide when to run it relative to ytdl-sub.
Option A: ytdl-sub post_process hook (recommended for simplicity)¶
ytdl-sub supports post_process scripts
that run after each subscription completes. This keeps your feeds up-to-date
immediately after new content is downloaded.
presets:
podcast:
# ... other configuration ...
post_process:
- command: "sub2pod"
args:
- "/home/NAS/ytdl-sub/mark/podcast"
- "https://podcast.example.com"
Pros: - Feeds update automatically after each ytdl-sub run - No separate scheduling needed - OPML index regenerated on every run
Cons: - sub2pod runs even if no new episodes were downloaded (though it's fast) - Requires sub2pod to be installed in the same environment as ytdl-sub - Errors in sub2pod may obscure ytdl-sub exit status
Option B: Standalone cron job (recommended for production)¶
Run sub2pod on a schedule independent of ytdl-sub. This decouples download from feed generation and is easier to debug.
# Run sub2pod every 6 hours
0 */6 * * * /usr/local/bin/sub2pod /home/NAS/ytdl-sub/mark/podcast https://podcast.example.com
Pros: - ytdl-sub and sub2pod run independently; failures in one don't affect the other - Predictable feed update intervals - Easier to test and debug
Cons: - Feeds may be stale for up to the cron interval after new downloads - Needs separate scheduling configuration
Recommendation¶
Start with the cron job approach. It is simpler to set up, easier to debug, and avoids coupling between the download and feed-generation steps. If you need immediate feed updates after download, switch to the post_process hook later.
Opus MIME type caveat and re-encode workaround¶
The problem¶
ytdl-sub's audio_only download strategy often produces .opus files (Opus
audio in an Ogg container). sub2pod emits the correct MIME type for this format:
However, Apple Podcasts does not support Opus audio. Episodes encoded as Opus will not play in Apple Podcasts, iTunes, or other Apple ecosystem clients.
Affected clients¶
| Client | Opus support |
|---|---|
| Apple Podcasts / iTunes | ❌ Not supported |
| Overcast | ❌ Not supported |
| Pocket Casts | ✅ Supported |
| AntennaPod (Android) | ✅ Supported |
| Podcast Addict (Android) | ✅ Supported |
| VLC (desktop) | ✅ Supported |
Workaround: Re-encode to MP3¶
Configure ytdl-sub to download or re-encode to MP3 instead of Opus:
presets:
podcast:
youtube:
channel_url: "https://www.youtube.com/@Level1Techs"
download_strategy: "audio_only"
# Request MP3 format from yt-dlp
ytdl_options:
postprocessors:
- key: "FFmpegExtractAudio"
preferredcodec: "mp3"
preferredquality: "192"
This tells yt-dlp (which ytdl-sub uses internally) to extract audio and encode
it as MP3 at 192 kbps. sub2pod will then emit audio/mpeg as the enclosure MIME
type, which is universally supported.
Workaround: Re-encode to M4A (AAC)¶
For lower bitrates at equivalent quality, use AAC in an MP4 container:
presets:
podcast:
ytdl_options:
postprocessors:
- key: "FFmpegExtractAudio"
preferredcodec: "m4a"
preferredquality: "128"
sub2pod emits audio/mp4 for .m4a files, which is also broadly supported.
If you keep Opus¶
If you are targeting Android clients or accept the compatibility trade-off:
- Test your feed in your target podcast clients before publishing.
- Be aware that the file extension
.opuswith MIME typeaudio/ogg; codecs=opusis technically correct per RFC 5334 and RFC 7845, but not all feed validators and podcast clients handle it correctly. - Consider providing an alternate feed with MP3-encoded episodes for iOS users.