MeTube: A selfhosted WebUI for yt-dlp
Most people should have heard of youtube-dl and the legal battles around it. As the content industry only saw it as a tool for piracy. And yes, while many may use it solely for that there is also the big group of people who want to download single TV news articles (videos) or documentaries produced by public-service broadcasting companies such as ARTE, ARD, ZDF - to name a few German ones. youtube-dl was sued into oblivion, but as it was OpenSource other forks were created with yt-dlp being the currently active one.
yt-dlp however is not just the only program offering this kind of functionality. As such there is the program MediathekView which gathers all senders program information and allows for the easy download of all content. Why MediathekView isn't sued? It only allows the download of content from the online Mediathek of public broadcasting companies, such as: ARD, ZDF, Arte, 3Sat, SWR, BR, MDR, NDR, WDR, HR, RBB, ORF and SRF. Which are all public broadcasting TV senders from Germany, Austria and Switzerland. Hence no problems with 3rd party rights do exist.
But... MediathekView is a local application and I like to have a simple web-frontend useable from any device. Introducing MeTube it's a web-frontend build around yt-dlp and provided as a Docker container. The WebUI itself is minimalistic but does its job.
In my environment MeTube is configured to save videos on a share on my NAS. Storing the videos in the correct folder automatically.
Mounting the CIFS-Share is done via the following line in /etc/fstab:
root@portainer:~# cat /etc/fstab
# /etc/fstab: static file system information.
[...]
# Metube Mount
//ip.ip.ip.ip/video/yt-dlp /mnt/yt-dlp cifs rw,vers=3.0,credentials=/root/.fileserver_smbcredentials,dir_mode=0775,file_mode=0775,uid=1002,gid=1002
Then we use that local mount for the /downloads folder of the Docker container. After the first start I learned I additionally need to explicitly store the TEMP_DIR and STATE_DIR on local volumes on the Docker container host itself. After that I fixed the healthcheck as it is hardcoded for HTTP.
services:
metube:
image: ghcr.io/alexta69/metube
container_name: metube
restart: unless-stopped
ports:
- "8081:8081"
volumes:
# On CIFS-Share, mounted via /etc/fstab
- /mnt/yt-dlp:/downloads
# HTTPS
- /opt/docker/certs/portainer.lan.crt:/ssl/crt.pem
- /opt/docker/certs/portainer.lan.key:/ssl/key.pem
# Local volumes to make CIFS-Share work
- /opt/docker/metube/temp:/temporary
- /opt/docker/metube/state:/state
environment:
- PUID=1002
- PGID=1002
# HTTPS
- HTTPS=true
- CERTFILE=/ssl/crt.pem
- KEYFILE=/ssl/key.pem
# Needed as our /downloads folder is located on a CIFS-Share
- TEMP_DIR=/temporary
- STATE_DIR=/state
# Downloaded files are deleted on the server, when they are trashed from the "Completed" section of the UI
# - Will delete files from /download! This is not to clear some kind of cache
#- DELETE_FILE_ON_TRASHCAN=true
#
# yt-dlp options
# Download best video (not higher than 1080p) & audio in german language, if no german is available use english, else use default
# Note 1: Not every language has a separate "audio only" track, this is why we use best[language...], as ba[language=...] only matches "audio only" tracks
# Note 2: yt-dlp can't reliably identify the automatically generated audiotracks as these are not clearly listed as such in the metadata
# format-sort res:1080 means: Not higher than 1080p
# ^=de means we also take de-DE or de-AT, similiar for ^=en meaning en-UK, en-US, etc.
- 'YTDL_OPTIONS={ "format-sort": "res:1080", "format": "best[language^=de]/best[language^=en]/b", "merge_output_format": "mp4" }'
# Internal container healthcheck is hardcoded for HTTP
healthcheck:
test: ["CMD-SHELL", "curl -fsS --insecure -m 2 https://localhost:8081/ || exit 1"]
interval: 60s
timeout: 10s
retries: 3
start_period: 10s
The only feature I currently miss is that I can't select the audio track which I do want to download along with the video. However having such options would only solve one part of the problem. As the other part would be that each site would need to clearly state which audio track contains which language. And that isn't even reliably done on YouTube.
Hence I help myself with passing some options to yt-dlp via YTDL_OPTIONS. My config 'YTDL_OPTIONS={ "format-sort": "res:1080", "format": "best[language^=de]/best[language^=en]/b", "merge_output_format": "mp4" }' means: Sort the videos based on the resolution, set 1080p as the highest (best). Videos in higher resolution won't be considered for download. Based on that we download the best video/audio available in German and if nothing is available in German we use English. If that isn't available too, we just use the default.
As stated in the comments of the Dockerfile the problem is that not every language has an audio only track. Only on those a bestaudio filter like ba[language^=en] would work. I encountered too many videos where the language was part of the video track, therefore I switched to just use best[language^=...]. This works more reliable from my experience. And yes, these settings overwrite everything you select in the web-frontend.
Nonetheless for the edge cases being able to define settings for a single download would be nice.
Last tips
If you encounter any problem when downloading a video with yt-dlp make sure you have a JavaScript runtime installed (MeTube uses deno). You either need to specify the path to it in the config file or on the command line via: yt-dlp --js-runtimes deno:/path/to/deno -F URL as only then a -F will show you all available formats. Else some can be missing.
Secondly, use -s to simulate a run when testing your config. Like for my config above: yt-dlp --js-runtimes deno:C:\Users\Khark\.deno\bin\deno.exe -s -S res:1080 -f "best[language^=de]/best[language^=en]/b" URL
Also pay attention to the log line that stated which video and audio track are being downloaded: [info] YouTube-VideoID: Downloading 1 format(s): 96-5. This number corresponds directly to the values you can retrieve with -F. Generally when your filter isn't working it mostly downloads the default video and audio track.
Generally I can recommend reading the yt-dlp readme on Format Selection.
(Click to enlarge)
(Click to enlarge)
Huh? What? I use all kinds of strange ports in my home network and never got that error message.