Table of Contents
- English subtitles & chapter marks, permissions, modification timestamp
- Embed metadata and a cover image in video downloads
- Automatically marking MeTube downloads with SponsorBlock chapters
- Notifications on download completion
- Using --parse-metadata (MetadataParser) from JSON
- Download video and extract audio in one go
- What MeTube already sets for you
- Converting a yt-dlp command line into JSON
Here you can find various useful configurations that can be set via the YTDL_OPTIONS and YTDL_OPTIONS_FILE environment variables.
English subtitles & chapter marks, permissions, modification timestamp
Embeds English subtitles and chapter markers (for videos that have them), changes the permissions on the downloaded video, and sets the file modification timestamp to the date of when it was downloaded.
environment:
- 'YTDL_OPTIONS={"writesubtitles":true,"subtitleslangs":["en","-live_chat"],"updatetime":false,"postprocessors":[{"key":"Exec","exec_cmd":"chmod 0664","when":"after_move"},{"key":"FFmpegEmbedSubtitle","already_have_subtitle":false},{"key":"FFmpegMetadata","add_chapters":true}]}'
Embed metadata and a cover image in video downloads
Contributed by @PikuZheng.
NOTE: May cause errors for non-Youtube videos.
environment:
- 'YTDL_OPTIONS={ "ignoreerrors":true, "writethumbnail":true, "postprocessors":[ {"key":"FFmpegMetadata","add_metadata":true,"add_chapters":true,"add_infojson":"if_exists"}, {"key":"EmbedThumbnail"}]}'
⚠️ Do not apply this to audio downloads. Picking Audio in the UI already embeds a cover image, and the
writethumbnailabove switches that off — see What MeTube already sets for you. To use this for video only, define it as a named preset viaYTDL_OPTIONS_PRESETSinstead of setting it globally.
Automatically marking MeTube downloads with SponsorBlock chapters
Contributed by @loomweaver.
Mimics the command yt-dlp --sponsorblock-mark sponsor,selfpromo,intro,outro,poi_highlight.
NOTE: due to the complex usage of quotes here (both " and ' are used), it's advisable to put it in a file and point YTDL_OPTIONS_FILE to it, rather than trying to configure it via YTDL_OPTIONS.
{"postprocessors": [{"api":"https://sponsor.ajay.app/","categories": ["intro",
"outro",
"poi_highlight",
"selfpromo",
"sponsor"],
"key": "SponsorBlock",
"when": "after_filter"},
{"force_keyframes":false,
"key": "ModifyChapters",
"remove_chapters_patterns": [],
"remove_ranges": [],
"remove_sponsor_segments": [],
"sponsorblock_chapter_title": "'[SponsorBlock]: ''%(category_names)l'"},
{"add_chapters": true,
"add_infojson": "none",
"add_metadata": false,
"key": "FFmpegMetadata"}]}
Notifications on download completion
The image includes curl, and yt-dlp's Exec postprocessor runs a command
after each successful download — enough for ntfy, Gotify, Pushover, Telegram,
webhooks, etc.:
YTDL_OPTIONS={"postprocessors":[{"key":"Exec","exec_cmd":"curl -s -d 'Downloaded: ' -d %(title)q https://ntfy.sh/YOUR_TOPIC","when":"after_move"}]}
exec_cmd supports the full yt-dlp output-template syntax (%(title)q,
%(filepath)q, ...). To make notifications opt-in per download instead of
global, define it as a named preset via YTDL_OPTIONS_PRESETS.
Note: postprocessors only run on success — there is currently no hook for failed downloads.
Using --parse-metadata (MetadataParser) from JSON
yt-dlp's --parse-metadata normally builds Python tuples that JSON can't
express. Use the MetadataFromField postprocessor instead — it takes plain
"FROM:TO" strings and behaves identically. For example, to clear the
description (the equivalent of --parse-metadata ":(?P<meta_description>)"):
YTDL_OPTIONS={"postprocessors":[{"key":"MetadataFromField","formats":[":(?P<meta_description>)"],"when":"pre_process"}]}
Each entry in formats is one FROM:TO expression, exactly as you would
write it on the yt-dlp command line.
Download video and extract audio in one go
keepvideo plus FFmpegExtractAudio gives you both the video file and an
audio file from a single download:
YTDL_OPTIONS={"keepvideo":true,"postprocessors":[{"key":"FFmpegExtractAudio","preferredcodec":"mp3"}]}
To convert the video container instead (e.g. everything as MKV):
YTDL_OPTIONS={"postprocessors":[{"key":"FFmpegVideoConvertor","preferedformat":"mkv"}]}
(Note: yt-dlp's option is really spelled preferedformat, with one r.)
Define these as named presets via YTDL_OPTIONS_PRESETS to pick them
per-download in the UI instead of applying them globally.
What MeTube already sets for you
MeTube builds part of the yt-dlp configuration itself from what you pick in the
UI, then merges your YTDL_OPTIONS on top. Options it already handles are best
left out of YTDL_OPTIONS — setting them again is at best redundant, and in one
case actively harmful.
Picking Audio (any format except wav) makes MeTube add all of this:
| Option | Purpose |
|---|---|
writethumbnail |
download the cover image |
FFmpegExtractAudio |
extract to your chosen format and quality |
FFmpegThumbnailsConvertor |
convert the cover to jpg before embedding |
FFmpegMetadata |
write tags |
EmbedThumbnail |
embed the cover |
Picking Video adds no postprocessors at all, which is why the metadata recipe above is useful there and unnecessary for audio.
⚠️ The
writethumbnailtrap. MeTube adds that audio chain only whenwritethumbnailis absent from your options. The intent was to let you turn cover art off with"writethumbnail": false, but the check looks at whether the key is present rather than at its value — so setting it totrueswitches the chain off just the same:Audio + mp3, no YTDL_OPTIONS -> ExtractAudio, ThumbnailsConvertor, Metadata, EmbedThumbnail Audio + mp3, "writethumbnail": true -> ExtractAudioWithout
FFmpegThumbnailsConvertorthe cover stays in its original format (webp, from YouTube). Embedding that into an mp3 fails with "Supported filetypes for thumbnail embedding are: mp3...", or leaves the image sitting next to the audio file as a separate.webp. If cover art has stopped working on your audio downloads, this is almost always why — removewritethumbnailfromYTDL_OPTIONS.
Converting a yt-dlp command line into JSON
yt-dlp ships devscripts/cli_to_api.py,
which turns command-line flags into the API options that YTDL_OPTIONS expects.
It is the right starting point, but do not paste its output in wholesale — it
emits everything, including the keys MeTube manages from the UI. Delete those and
keep only what remains.
For example, a square-cropped cover image:
yt-dlp -x --audio-format mp3 --embed-thumbnail --convert-thumbnail jpg \
--ppa "EmbedThumbnail+ffmpeg_o:-c:v mjpeg -vf crop=\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\""
The converter turns that into a block containing format, writethumbnail,
final_ext, FFmpegExtractAudio, FFmpegThumbnailsConvertor and
EmbedThumbnail — every one of which MeTube already does when you pick Audio +
mp3, and one of which (writethumbnail) would break it. The only part MeTube
does not provide is the postprocessor argument, so that is all you need:
{
"postprocessor_args": {
"embedthumbnail+ffmpeg_o": [
"-c:v", "mjpeg",
"-vf", "crop='if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'"
]
}
}
The --ppa prefix becomes the lowercased key embedthumbnail+ffmpeg_o, and the
arguments become a list rather than a single string, which removes the shell
quoting problem entirely.