ytdl-sub is command-line tool written in Python for downloading videos from YouTube and other video sites using the yt-dlp library. An especially interesting feature is its ability to prepare metadata for media players, such as Kodi, Jellyfin, Plex or Emby.

In this article, we will be going over how to use Docker and Docker Compose to run ytdl-sub on your system.

0. Prerequisites Link to heading

1. Prepare the environment Link to heading

Create a new directory for the project and change into it:

mkdir -vp /opt/containers/ytdl-sub
cd /opt/containers/ytdl-sub

This will create a new directory called /opt/containers/ytdl-sub, as well as any parent directories that do not exist.

You can also use a different directory if you prefer, but make sure to replace /opt/containers/ytdl-sub with the correct path in the following steps.

2. Create the Docker Compose file Link to heading

Create a new file called docker-compose.yml using your favorite command-line editor:

editor docker-compose.yml # you can also use nano, vim, etc. directly

and paste the following contents into it:

version: "3.8" # no longer needed with Docker Compose 2.0

services:
  ytdl-sub:
    image: ghcr.io/jmbannon/ytdl-sub:latest
    container_name: ytdl-sub # optional
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Berlin # set this to your timezone
      - DOCKER_MODS=linuxserver/mods:universal-cron
    volumes:
      - ./config:/config
      - <path/to/tv_shows>:/tv_shows # optional
      - <path/to/movies>:/movies # optional
      - <path/to/music_videos>:/music_videos # optional
      - <path/to/music>:/music # optional
    restart: unless-stopped

The PUID and PGID environment variables can be used to set the user and group ID of the files created by the container. You should set these, as you likely want to access the files from outside the container, i.e. from your media player. Assuming that your media player is also running as user 1000 and group 1000, you can set these variables to 1000 as shown above.

The TZ environment variable can be used to set the timezone of the container, which you should set to the system timezone of your host machine. A list of valid timezones can be found here.

Be sure to replace the following values:

  • <path/to/tv_shows>: The path to the directory where you want to store TV shows.
  • <path/to/movies>: The path to the directory where you want to store movies.
  • <path/to/music_videos>: The path to the directory where you want to store music videos.
  • <path/to/music>: The path to the directory where you want to store music.

If you do not want to download a certain type of media, you can remove the corresponding volume from the volumes section.

2.1 (Optional) Configure Hardware Acceleration Link to heading

With the above configuration, downloaded videos are transcoded using software rendering. If you are able to use hardware acceleration, you can configure the container to use either your onboard graphics or a dedicated graphics card.

2.1.1 CPU Pass-through Link to heading

If you are using a CPU with integrated graphics, you can use the following configuration:

# ...
services:
  ytdl-sub:
    devices:
      - /dev/dri:/dev/dri
# ...

2.1.2 GPU Pass-through (NVIDIA) Link to heading

If you are using an NVIDIA graphics card, you can use the following configuration:

# ...
services:
  ytdl-sub:
    environment:
      # ...
      - NVIDIA_VISIBLE_DEVICES=all
      - NVIDIA_DRIVER_CAPABILITIES=all
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]
# ...

2.2 Full Example Link to heading

version: "3.8"

services:
  ytdl-sub:
    image: ghcr.io/jmbannon/ytdl-sub:ubuntu-latest
    container_name: ytdl-sub
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Berlin
      - DOCKER_MODS=linuxserver/mods:universal-cron
      - NVIDIA_DRIVER_CAPABILITIES=all # Nvidia ENV args
      - NVIDIA_VISIBLE_DEVICES=all
    volumes:
      - ./config:/config
      - /media/your_drive/tv_shows:/tv_shows # optional
      - /media/your_drive/moves:/movies # optional
      - /media/your_drive/music_videos:/music_videos # optional
      - /media/your_drive/music:/music # optional
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu] # GPU passthrough
    restart: unless-stopped

3. Create the configuration file Link to heading

Using the configuration file, we can tell ytdl-sub

  • how to name the downloaded files,
  • organize them into directories,
  • what metadata to include,
  • additional options for the downloader, such as
    • whether to download subtitles,
    • or whether they should be embedded into the video file,
    • as well as applying SponsorBlock to the downloaded videos.

ytdl-sub will create a default configuration file for you after the first run, which you can then edit to your liking. If you want it to create the file for you, you can skip ahead to 4. Run ytdl-sub. After the first run, you can find the configuration file at config/config.yml, which you should edit according to your needs. After that, you should set up your subscriptions.

Create a new directory called config, which will hold the configuration and your subscriptions:

mkdir config
# config/config.yml

# This config uses prebuilt presets included with ytdl-sub to download and format
# channels from YouTube or other sites supported by yt-dlp into a TV show for
# your favorite player. The directory format will look something like
#
#   /tv_shows
#       /Season 2021
#           s2021.e0317 - Pattys Day Video-thumb.jpg
#           s2021.e0317 - Pattys Day Video.mp4
#           s2021.e0317 - Pattys Day Video.nfo
#       /Season 2022
#           s2022.e1225 - Merry Christmas-thumb.jpg
#           s2022.e1225 - Merry Christmas.mp4
#           s2022.e1225 - Merry Christmas.nfo
#       poster.jpg
#       fanart.jpg
#       tvshow.nfo
#
# The idea is to use dates as numerics to represent season and episode numbers.
configuration:
  working_directory: ".ytdl-sub-downloads"

presets:
  # Your main TV show preset - all your tv show subscriptions will use this.
  tv_show:
    preset:
      # Choose one of the following player types:
      # - "kodi_tv_show_by_date"
      - "jellyfin_tv_show_by_date"
      # - "plex_tv_show_by_date"

      # - "kodi_tv_show_by_date" # replace with desired player type

      # Choose one of the following season/episode formats:
      # - "season_by_year__episode_by_month_day"
      # - "season_by_year_month__episode_by_day"
      # - "season_by_year__episode_by_month_day_reversed"
      # - "season_by_year__episode_by_download_index"

      - "season_by_year__episode_by_month_day" # replace with desired season/episode format

      # Include any of the presets listed below in your 'main preset' if you want
      # it applied to every TV show. Or, use them on the individual subscriptions.
      # - "only_recent_videos"
      - "add_subtitles"
      # - 'sponsorblock'
      # - "include_info_json"

    # To download age-restricted videos, you will need to uncomment and set your cookie
    # file here as a ytdl parameter. For more info, see
    # https://ytdl-sub.readthedocs.io/en/latest/faq.html#download-age-restricted-youtube-videos
    #
    # ytdl_options:
    #   cookiefile: "/config/cookie_file.txt"  # replace with actual cookie file path

    overrides:
      tv_show_directory: "/tv_shows" # replace with path to tv show directory
      # Fields in the prebuilt preset that can be changed:
      #
      # episode_title: "{upload_date_standardized} - {title}"
      # episode_plot: "{webpage_url}"  # source variable for the video description is {description}

  ####################################################################################################

  # Preset to only download and keep recent videos
  only_recent_videos:
    # Only download videos within the download_range
    date_range:
      after: "today-{download_range}"

    # Stops fetching metadata if the video is out of range (saves time + bandwidth)
    ytdl_options:
      break_on_reject: True

    # Deletes any videos older than download_range. WARNING: do not use
    # "season_by_year__episode_by_download_index" if you plan to delete older videos
    output_options:
      keep_files_after: "today-{download_range}"

    # Set the duration of download_range, defaults to 2 months
    overrides:
      download_range: "2months"

  ####################################################################################################

  # Preset to download subtitles (either by file or embedded)
  add_subtitles:
    subtitles:
      # Embed subtitles into the video
      embed_subtitles: True
      # And/or download them as a file. Uncomment to download as file:
      # subtitles_name: "{episode_file_path}.{lang}.{subtitles_ext}"
      # subtitles_type: "srt"

      languages: "en" # supports list of multiple languages
      allow_auto_generated_subtitles: True # allow auto subtitles

  ####################################################################################################

  # Preset to cut sponsor segments from videos
  sponsorblock:
    # If you download using cron, it is wise to add a delay before downloading ad-filled content to
    # give folks time to submit sponsor segments. Uncomment to wait 2 days before download a video.
    # date_range:
    #  before: "today-2days"

    chapters:
      # Remove all of these sponsorblock categories
      sponsorblock_categories:
        - "intro"
        - "outro"
        - "selfpromo"
        - "preview"
        - "interaction"
        - "sponsor"
        - "music_offtopic"
      remove_sponsorblock_categories: "all"
      force_key_frames: False

  ####################################################################################################

  # Preset for the hoarders who want to also save the info.json file
  include_info_json:
    output_options:
      info_json_name: "{episode_file_path}.{info_json_ext}"

  playlist:
    download:
      download_strategy: "url"
      url: "{url}"
    output_options:
      output_directory: "/tv_shows/{playlist_name}"
      file_name: "{playlist_name}.{title}.{ext}"
    # overrides:
    #   output_directory: "/path/to/ytdl-sub-videos"

The tv_show preset is the main preset that you should use for regular YouTube Videos and TV Shows. It will download the video, metadata, and subtitles, and organize them into a directory structure that is compatible with the media player of your choice (Jellyfin is selected in the above configuration).

Keep in mind that this preset will download ALL videos from the channel you subscribe to. If you want to only download recent videos, you can use the only_recent_videos preset, which will only download videos within the download_range (defaults to the last 2 months).

4. Create your subscriptions Link to heading

# config/subscriptions.yml

# The name of our subscription. Let us create one to download
# ALL of Rick A's videos
rick_a:
  # Inherit our `tv_show` preset we made above
  preset:
    - "tv_show"

  # Set override variables to set the channel URL and the
  # name we want to give the TV show.
  overrides:
    tv_show_name: "Rick A"
    url: "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw"

# Let us make another subscription that will only download Rick A's
# video's in the last 2 weeks.
rick_a_recent:
  # Inherit our `tv_show` AND `only_recent_videos` preset
  # Bottom-most presets take precedence.
  preset:
    - "tv_show"
    - "only_recent_videos"

  # Set override variables for this subscription. Modify the
  # `download_range` to only download and keep 2 weeks' worth
  # of videos.
  overrides:
    tv_show_name: "Rick A"
    url: "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw"
    download_range: "2weeks"

5. Run ytdl-sub Link to heading

Before running ytdl-sub, make sure to set the right permissions on your config and media directories. They should be owned by the user and group you configured in the docker-compose.yml file.

sudo chown -R 1000:1000 config path/to/tv_shows path/to/movies path/to/music_videos path/to/music

Then run ytdl-sub with docker-compose as a one-off job:

docker compose run --rm ytdl-sub bash -c "cd /config && ytdl-sub sub subscriptions.yml"

6. Set up a cron job Link to heading

If you want to download videos automatically, you can set up a cron job to run ytdl-sub periodically. Either on the host system, that runs a one-off job, or in the container itself.

6.1 Running cron on the host system Link to heading

For example, to run ytdl-sub every 6 hours, you can add the following to your host’s crontab:

0 */6 * * * bash -c 'cd /opt/containers/ytdl-sub && docker compose run --rm ytdl-sub bash -c "cd /config && ytdl-sub sub subscriptions.yml"'

or, you can leave the container running and run ytdl-sub from within the container:

6.2 Running cron in the container Link to heading

This will require you to place a script in the container’s /config directory, which we’ve mounted as a volume in the docker-compose.yml file. The script will be run by the container user’s crontab.

First, start the container:

docker compose up -d

Then edit the container user’s crontab by logging in as the root user:

docker exec -it ytdl-sub /bin/bash

and then running:

echo "  0     */6     *       *       *       /config/run_cron" >> /config/crontabs/abc

Which will add a cron job to run ytdl-sub every 6 hours to the container user’s (abc) crontab.

Then assemble the script that will be run by cron:

echo '#!/bin/bash' > /config/run_cron
echo "echo 'Cron started, running ytdl-sub...'" >> /config/run_cron
echo "ytdl-sub --config=/config/config.yaml sub /config/subscriptions.yaml" >> /config/run_cron
chmod +x /config/run_cron  # make the script executable

Then exit the container and restart it:

docker compose restart ytdl-sub

7. Advanced Usage Link to heading

7.1 Downloading age-restricted videos Link to heading

To download age-restricted videos, you will need to give ytdl-sub a valid YouTube login cookie. To do this, you will need to log into YouTube in your browser, open the developer tools, and copy the cookie from the browser. For more details, see the ytdl-sub docs.

7.2 Downloading from other sites Link to heading

With ytdl-sub, you can do more than just download YouTube videos. You can download videos, music, podcasts and playlists from a variety of sites.

For a full list of supported sites, see the yt-dlp docs.

8. Sources Link to heading