Deploy YTPTube (yt-dlp web UI) via its official Docker image
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Paul Provost 488bbf7ca6 Add ytptube_browser_control_enabled (YTP_BROWSER_CONTROL_ENABLED)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 19:24:18 -04:00
defaults Add ytptube_browser_control_enabled (YTP_BROWSER_CONTROL_ENABLED) 2026-10-22 19:24:18 -04:00
meta Initial ytptube role, modelled on the slskd role 2026-10-22 10:56:19 -04:00
tasks Add ytptube_browser_control_enabled (YTP_BROWSER_CONTROL_ENABLED) 2026-10-22 19:24:18 -04:00
README.md Add ytptube_browser_control_enabled (YTP_BROWSER_CONTROL_ENABLED) 2026-10-22 19:24:18 -04:00

ytptube

Deploy YTPTube, a web UI and download manager for yt-dlp, via its official ghcr.io/arabcoders/ytptube Docker image.

Requirements

  • community.docker collection (needs the python3-docker apt package's bindings - this role installs it).

Role Variables

Variable Default Description
ytptube_config_dir ~/local/ytptube/config Host directory mounted at /config (database, presets, tasks, cookies, logs). Created by this role.
ytptube_downloads_dir ~/local/ytptube/downloads Host directory mounted at /downloads; finished files in files/, in-progress in tmp/. Created by this role.
ytptube_image ghcr.io/arabcoders/ytptube:latest Docker image.
ytptube_uid / ytptube_gid 1000 User the container runs as (docker --user; not a linuxserver.io image).
ytptube_tz Etc/UTC Container timezone.
ytptube_default_preset default Preset preselected for new downloads (YTP_DEFAULT_PRESET). Must name an existing preset.
ytptube_presets [] Presets created/replaced through the API, matched by name (see below).
ytptube_notifications [] Notification targets created/replaced through the API, matched by name (see below).
ytptube_api_key "" API key (ytp_...), required when ytptube_presets or ytptube_notifications is set.
ytptube_browser_control_enabled false Enables rename/move/delete in the web UI's Files section (YTP_BROWSER_CONTROL_ENABLED).
ytptube_http_port 8081 Host port for the web UI (container port is always 8081).

Configuration

Only the download/temp paths, default preset, file controls and timezone are set by this role (as YTP_* env vars). Everything else - presets, scheduled tasks, conditions, notifications - is configured from the web UI and stored under /config. Other YTP_* settings are listed in upstream's FAQ.

Presets

Each ytptube_presets item has a name and any of description, folder, template, cookies, cli (omitted fields are ""). A missing preset is created, one whose fields differ is replaced with PUT; presets not in the list (UI-made or built-in) are never touched or deleted. Use the name as YTPTube stores it - lowercase, spaces as _ - or the role fails rather than creating a duplicate every run. This talks to the API, so it needs ytptube_api_key, which only exists once the first account has been created in the web UI: on a fresh install, leave ytptube_presets empty for the first run.

ytptube_default_preset: 720p_mp4
ytptube_presets:
  - name: 720p_mp4
    description: Best MP4 video + M4A audio, up to 720p.
    cli: |-
      --socket-timeout 30 --download-archive %(archive_file)s
      --format 'bestvideo[height<=720][ext=mp4]+bestaudio[ext=m4a]/mp4[height<=720]/mp4'

Notifications

ytptube_notifications works the same way: a target is created if missing and replaced with PUT if any given field differs; unlisted targets are left alone. Fields: name, "on" (event names, [] = all; quote it - a bare YAML on: key is the boolean true), presets ([] = all), enabled (default true) and request (url, plus type json/form, method POST/PUT, data_key, headers for plain webhooks). A request.url that doesn't start with http is sent through Apprise - e.g. ntfy with an access token:

ytptube_notifications:
  - name: ntfy
    "on": [item_completed, log_error, task_error]
    request:
      url: "ntfys://{{ vault_ntfy_token }}@ntfy.example.com/ytdlp"

The URL usually carries a secret, so these tasks are no_log.

The upstream compose file's optional Chromium/CDP proxy and FlareSolverr sidecars are not deployed.

Queueing a URL

The easy way is ytptube-add, which wraps the calls below (ytptube-add URL, ytptube-add -j -p audio_m4a URL from the WAN) and documents an iOS share-sheet shortcut. The underlying API:

Besides the web UI (and its browser extensions/bookmarklet), URLs can be queued with the API, authenticated with an API key (Settings -> API keys). POST /api/history takes a JSON object (or an array of them) with url and optionally preset, folder, template, cli and auto_start (default true); without preset, the item uses ytptube_default_preset. It answers 202 with {"status": "accepted", ...}.

curl -sS -H "Authorization: Bearer $YTPTUBE_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://www.youtube.com/watch?v=...", "preset": "audio_m4a"}' \
  "$YTPTUBE_URL/api/history"

$YTPTUBE_URL depends on where you are. In the sulaco homelab (API key in pass personal/apikeys/ytptube/sulaco):

  • LAN or VPN: https://ytdlp.home.provost.one (tightbeam's Caddy, local-CA certificate - the client must trust that CA, or add -k), or http://sulaco.local:8571 directly.

  • WAN, through the jumphost ssh.home.provost.one (tightbeam): Caddy only answers LAN/VPN clients, so tunnel to sulaco's port through SSH and use http://localhost:8571:

    # The tunnel stays up for 10s, or until the last connection through it closes.
    ssh -f -o ExitOnForwardFailure=yes -L 8571:192.168.2.245:8571 ssh.home.provost.one sleep 10
    YTPTUBE_URL=http://localhost:8571
    # ...then the curl above
    

    The same tunnel (with -N instead of -f ... sleep 10, left running) also serves the web UI at http://localhost:8571. If 8571 is taken locally, pick another local port: -L 18571:192.168.2.245:8571.

For a quick one-off without a JSON body there is also GET /api/history/add?url=<url-encoded>&preset=<name> (same Authorization header).

Example Playbook

- hosts: sulaco
  vars:
    ansible_python_interpreter: /usr/bin/python3
  roles:
    - role: ytptube
      ytptube_config_dir: /mnt/disks/storage3/ytptube/config
      ytptube_downloads_dir: /mnt/disks/storage3/ytptube/downloads

Notes

  • On first visit, YTPTube asks you to create a local account. Do that before exposing the port anywhere: authenticated users are instance administrators and can pass arbitrary yt-dlp options, including ones that execute commands.
  • Uses ansible_python_interpreter: /usr/bin/python3 in the example above for the same reason the slskd/sonarr roles do: Linuxbrew's Python (if present, earlier in PATH) doesn't have python3-docker's bindings.