Selfhosted Tidal DL with ReactJS UI
  • TypeScript 94.4%
  • JavaScript 3.8%
  • Dockerfile 0.6%
  • Shell 0.4%
  • Makefile 0.4%
  • Other 0.3%
Find a file
2026-06-04 19:09:48 -05:00
.github ci: fork workflows 2026-05-30 19:57:10 -05:00
api feat(lidarr): make explicit tags opt-in as they seem to cause parsing problems 2026-06-04 19:09:48 -05:00
app chore(deps): bump the root-dependencies group across 1 directory with 7 updates 2026-05-24 01:59:42 +00:00
docker feat(api): update tiddl to 3.4.3 (#787) 2026-05-10 22:29:33 +02:00
docs feat(lidarr): make explicit tags opt-in as they seem to cause parsing problems 2026-06-04 19:09:48 -05:00
e2e feat(lidarr): make explicit tags opt-in as they seem to cause parsing problems 2026-06-04 19:09:48 -05:00
.dockerignore feat(e2e): remove mock + docker isolation (#493) 2025-11-25 01:58:01 +01:00
.env fix(front): Expand discography album when processing (#755) 2026-04-28 18:18:58 +02:00
.gitignore feat(api): use download_path as final destination (#586) 2026-01-13 19:31:38 +01:00
CHANGELOG.md feat(front): fix player drawer and autoclose (#791) 2026-05-12 08:30:29 +02:00
compose.yml feat(lidarr): make explicit tags opt-in as they seem to cause parsing problems 2026-06-04 19:09:48 -05:00
eslint.config.mjs fix ci 2026-04-19 21:13:34 +00:00
Makefile Feat/enhance token renew (#611) 2026-01-23 23:01:07 +01:00
package.json chore(deps): bump the root-dependencies group across 1 directory with 7 updates 2026-05-24 01:59:42 +00:00
README.md feat(lidarr): make explicit tags opt-in as they seem to cause parsing problems 2026-06-04 19:09:48 -05:00
yarn.lock chore(deps): bump tmp from 0.2.5 to 0.2.7 2026-05-28 03:32:33 +00:00

Self-hosted Tidal Media Downloader Docker Image

Tidarr is a Docker image that provides a web interface to download up to 24-bit 192.0 kHz media (tracks, albums, playlists, music videos) from Tidal using Tiddl python binary. Format on the fly with Beets, automatically update your Plex library, push notifications, or use it as a Lidarr provider.

GitHub Stars GitHub Release GitHub Release Dependabot Playwright CI Docker build Docker Pulls Docker image size Buy Me A Coffee

Table of Contents


Warning

Disclaimer

  • Need an official (shared ?) Tidal account
  • For educational purposes and personal use only
  • Do not forget to support your local artists (merch, live, ...) 🙏❤️

FEATURES

Main

  • Downloadable media : tracks, albums, playlists, mixes, music videos
  • Max quality : FLAC, 24 bit 192.0 kHz (if available)
  • Tidal trends content
  • User playlists, mixes, favorites
  • Search by keywords
  • Search by url : artist url, album url, playlist url, track url, mix url
  • Download covers
  • Admin password
  • Lidarr integration
  • OpenID Connect (OIDC) authentication support
  • M3U file for playlist with customizable path
  • Watch and sync playlists, mixes, favorites and artists with cron
  • Skip download if track exists
  • Custom CSS
  • Docker platforms:linux/amd64 and linux/arm64

Service integration

  • Beets.io - Tag releases
  • Gotify - Push notifications
  • Ntfy - Push notifications
  • Apprise API - Push notifications
  • Plex - Library update, search item button (album, track artiste)
  • Jellyfin - Library update, search item button (album, track artiste)
  • Navidrome - Library update, search item button (album, track artiste)
  • Lidarr - [BETA] Use Tidarr as usenet indexer (download provider)
  • Webhook push over - Push notifications using webhook (MatterMost)

Companion app

Technicals

  • Server-side download list processing
  • UI built with ReactJS + ExpressJS API
  • Self-hostable with Docker (linux/amd64 and linux/arm64)
  • Download Tidal content with Tiddl (3.4.3)

Advanced Integration & Automation

  • REST API - Integrate Tidarr with external applications and automation tools
  • Custom scripts - Execute your own shell scripts during post-processing pipeline (after download, before moving to library)

GETTING STARTED

Example docker-compose.yml :

services:
  tidarr:
    image: cstaelen/tidarr
    container_name: "tidarr"
    ports:
      - 8484:8484
    volumes:
      - /any/folder/to/tidarr/config:/shared
      - /any/folder/to/library:/music
    restart: "unless-stopped"

or

docker run  \
		--rm \
		--name tidarr \
		-p 8484:8484 \
		-v /any/folder/to/tidarr/config:/shared \
		-v /any/folder/to/library:/music \
    cstaelen/tidarr:latest

Tip

Separate processing drive

Tidarr uses /shared/.processing/ as a temporary folder during downloads. On setups where /shared is on a small drive (e.g. SSD config drive), large downloads (like discographies) can fill it up. You can remap the processing folder to a separate drive:

volumes:
  - ...
  - /path/to/media-drive/processing:/shared/.processing  # Separate drive for temp downloads

TIDAL AUTHENTICATION

(if no tiddl.json file provided) :

Authorize your device using the UI token dialog

or

docker compose exec -it -e tidarr tiddl auth login

or

docker exec -it -e tidarr tiddl auth

OPTIONS

Download settings

⚠️ Beware to set the right template path

To set your download options you can :

  • use the UI configuration editor in settings dialog
  • edit toml file /your/docker/path/to/tidarr/config/.tiddl/config.toml.

Tiddl config options

Tiddl path templating

PUID PGID UMASK

environment:
  - ...
  - PUID=1234
  - PGID=123
  - UMASK=0022

Listening port

Default listening port is 8484 but you can override it using :

environment:
  - ...
  - PORT=9999

Password protection

If not set, no password is required to access the app.

environment:
  - ...
  - ADMIN_PASSWORD=<string> # if not set, no password are required to access

OpenID Connect (OIDC) Authentication

Tidarr supports OIDC authentication for integration with identity providers like Keycloak, PocketID, Authentik, etc.

When OIDC is configured, the login page will display a "Login with OpenID" button instead of the password field.

environment:
  - ...
  - OIDC_ISSUER=https://your-oidc-provider.com
  - OIDC_CLIENT_ID=tidarr
  - OIDC_CLIENT_SECRET=your-client-secret
  - OIDC_REDIRECT_URI=https://your-tidarr-domain.com/api/auth/oidc/callback

Note

OIDC Configuration

  • OIDC_ISSUER: The URL of your OpenID Connect provider
  • OIDC_CLIENT_ID: The client ID registered in your OIDC provider
  • OIDC_CLIENT_SECRET: The client secret for your application
  • OIDC_REDIRECT_URI: The callback URL (must match the one configured in your OIDC provider)
  • OIDC authentication takes precedence over password authentication if both are configured
  • JWT tokens are valid for 12 hours after successful authentication

Lock quality selector

Force use of tiddl.json quality value and disable quality selector in app

environment:
  - ...
  - LOCK_QUALITY=true

Playlist options

M3U track base path

Default base path used in .m3u : ./ You can custom base path used by track path in .m3u file :

environment:
  - ...
  - M3U_BASEPATH_FILE="../../"

Playlist Albums

Automatically download complete albums for all tracks in a playlist. When enabled, Tidarr will extract unique album IDs from each track in the playlist and add them to the download queue.

environment:
  - ...
  - PLAYLIST_ALBUMS=true

Note

This feature processes playlists and mixes after the playlist download completes. Albums are added to the queue automatically, eliminating the need to manually download each album. Duplicates are avoided by tracking unique album IDs + Tiddl "skip existing" feature.

Discography download mode

By default, Tidarr expands an artist download into individual album queue items (one item per album). This allows better control, error isolation, and retry per album.

To download an entire discography as a single tiddl job instead:

environment:
  - ...
  - ARTIST_SINGLE_DOWNLOAD=true

Note

When ARTIST_SINGLE_DOWNLOAD=true, the artist is passed directly to tiddl as a single download job. This is faster but provides less granular error handling — if one album fails, the entire job may be affected.

Rate-limited download mode

Throttle downloads to avoid being rate-limited by Tidal: automatically pause the queue after N items have been downloaded, and optionally resume it after a delay.

environment:
  - ...
  - DOWNLOAD_BATCH_SIZE=10   # Pause queue after 10 completed downloads
  - DOWNLOAD_BATCH_DELAY=60  # Auto-resume after 60 minutes

Note

DOWNLOAD_BATCH_SIZE and DOWNLOAD_BATCH_DELAY can be used independently:

  • DOWNLOAD_BATCH_SIZE alone: queue auto-pauses after N downloads, resume manually via the UI
  • Both together: fully automated rate-limited downloading (e.g. 10 albums every hour)

Sync playlists and mixes

Default value is daily sync at 3 am (0 3 * * *). You can set a custom cron expression using SYNC_CRON_EXPRESSION env var.

To run task at midnight (00:00) every Monday :

environment:
  - ...
  - SYNC_CRON_EXPRESSION="0 0 * * 1"

* Syntax:

  • Minute (0 - 59)
  • Hour (0 - 23)
  • Day of the month (1 - 31)
  • Month (1 - 12)
  • Day of the week (0 - 7) (Sunday is both 0 and 7)

Custom CSS

You can customize Tidarr's appearance using the UI in settings dialog, or by editing the custom.css file. This file is automatically created in your config folder on first launch.

File location: /your/docker/path/to/tidarr/config/custom.css

Download History

Track your downloaded items with the history feature. When enabled, Tidarr will maintain a list of all downloaded content and mark items as already downloaded in the UI.

environment:
  - ...
  - ENABLE_HISTORY=true

Features:

  • Persistent download tracking across restarts
  • Visual indicators for already downloaded items (green checkmark)
  • Manual history clearing available in settings dialog

Replay Gain

Enable automatic Replay Gain analysis for your music library. When activated, Tidarr will scan audio files and add loudness normalization metadata using FFmpeg and rsgain.

environment:
  - ...
  - REPLAY_GAIN=true

Note

Replay Gain scanning happens after Beets tagging (if enabled) and before moving files to your library. The process adds minimal overhead to downloads while ensuring consistent playback volume across your music collection.

SERVICES

Beets

Add to your docker-compose file in environment: section :

environment:
  - ...
  - ENABLE_BEETS=true

Beets options in </mounted/config/folder/>beets-config.yml:

Note

Beets is locked to version 2.5.1

Starting from 2.6.0, beets added numba as an unconditional dependency, which requires llvmlite to compile from source. llvmlite has no pre-built wheels for Alpine Linux (musl libc), making installation fail on both amd64 and arm64. Upgrading beets requires switching the base image to a glibc-based distribution (e.g. Debian).

Plex integration

You can active:

  • Plex scan after download
  • Plex search button on artist, album and track pages

Add to your docker-compose file in environment: section :

environment:
  - ...
  - PLEX_URL=<url|ip:port>
  - PLEX_PUBLIC_URL=<public_url>  # optional, for frontend links (if different from internal URL)
  - PLEX_LIBRARY=<music_library_id>
  - PLEX_TOKEN=<x-plex-token>
  # Plex path to the library root
  - PLEX_PATH=/path/to/music/library

Note

All Plex API queries are proxied through the Tidarr backend to avoid CORS issues and keep your Plex token secure. The search button displays real-time result counts (artists, albums, tracks) from your Plex library.

Doc : https://www.plexopedia.com/plex-media-server/api/library/scan-partial/

Jellyfin integration

You can active:

  • Jellyfin scan after download

Add to your docker-compose file in environment: section :

environment:
  - ...
  - JELLYFIN_URL=<url|ip:port>
  - JELLYFIN_PUBLIC_URL=<public_url>  # optional, for frontend links (if different from internal URL)
  - JELLYFIN_API_KEY=<X-Emby-Token>
  • Jellyfin API Key : your Jellyfin API Key : Go to Dashboard -> API Keys

Note

All Jellyfin API queries are proxied through the Tidarr backend to avoid CORS issues and keep your Jellyfin API Key secure. The search button displays real-time result counts (artists, albums, tracks, videos) from your Jellyfin library.

Navidrome integration

You can activate:

  • Navidrome scan after download
  • Navidrome search button on artist, album and track pages

Add to your docker-compose file in environment: section :

environment:
  - ...
  - NAVIDROME_URL=http://navidrome.url
  - NAVIDROME_PUBLIC_URL=https://navidrome.public.url  # optional, for frontend links (if different from internal URL)
  - NAVIDROME_USER=navidrome_user
  - NAVIDROME_PASSWORD=navidrome_password

Note

All Navidrome API queries are proxied through the Tidarr backend to avoid CORS issues and keep your credentials secure. The search button displays real-time result counts (artists, albums, tracks) from your Navidrome library using the Subsonic API. Library scan is triggered automatically after each download.

Gotify

Add to your docker-compose file in environment: section :

environment:
  - ...
  - GOTIFY_URL=<url|ip:port>
  - GOTIFY_TOKEN=<gotify_app_token>

Ntfy

Add to your docker-compose file in environment: section:

environment:
  - ...
  - NTFY_URL=<url|ip:port>
  - NTFY_TOPIC=<ntfy_topic>
  - NTFY_TOKEN=<ntfy_token_security> # optional if it is not public
  - NTFY_PRIORITY=<ntfy_priority> # optional (default=3)

Apprise API

Add to your docker-compose file in environment: section :

environment:
  - ...
  - APPRISE_API_ENDPOINT=http://{apprise_api_url}:{port}/notify/{config_id}
  - APPRISE_API_TAG=tidarr # optional

If no tag is defined, default tag value will be "all".

Webhook push over

Many push over services can be used as an URL to curl with a payload. Example with MatterMost :

curl -i -X POST -H 'Content-Type: application/json' -d '{"text": "Hello, this is some text\nThis is more text. 🎉"}' https://your-mattermost-server.com/hooks/xxx-generatedkey-xxx

You can set URL in Tidarr env vars

environment:
  - ...
  - PUSH_OVER_URL=https://your-mattermost-server.com/hooks/xxx-generatedkey-xxx

It should also works with other services using the same payload format {"text": "..."}.

Lidarr connector

Tidarr can be integrated with Lidarr as both a Newznab indexer and a SABnzbd download client. This allows you to leverage Lidarr's powerful library management while using Tidarr for high-quality music downloads from Tidal.

What you can do:

  • Automatically search for albums in Tidal via Lidarr
  • Trigger downloads directly from Lidarr
  • Manage queue from Lidarr
  • Use Lidarr post processing
  • Manage your music library with Lidarr's metadata matching

Lidarr searches request 20 albums per outbound Tidal search by default. Set LIDARR_TIDAL_SEARCH_LIMIT to override the per-request limit; values are capped at 100 and 0 is interpreted as a request for maximum. If fallback searches run, each request uses the same configured limit and merged results may exceed the per-request value.

Lidarr search results are filtered with Tidal quality hints when available. Tidarr only advertises [FLAC 24bit] when Tidal reports hi-res hints for the album and every track in the album list; final downloads still depend on the stream Tiddl can retrieve from Tidal. Set LIDARR_DISABLE_MAX_RESULTS=true to suppress [FLAC 24bit] results while still returning regular [FLAC] results from Lidarr's lossless category, which is useful especially while oskvr37/tiddl #333 is open.

Set LIDARR_EXPLICIT_TAGS=true to include [EXPLICIT] in explicit album results returned to Lidarr. The tag is omitted by default, and any value other than trimmed, case-insensitive true is treated as disabled.

Note

Quick Setup

Step 1: Configure shared volumes between Tidarr and Lidarr

services:
  tidarr:
    volumes:
      - ...
      - /path/to/lidarr/downloads:/shared/nzb_downloads  # Shared download location

  lidarr:
    volumes:
      - ....
      - /path/to/lidarr/downloads:/downloads             # Same physical folder

Step 2: Add Tidarr as Indexer (Lidarr settings → Indexers)

Step 3: Add Tidarr as Download Client (Lidarr settings → Download Clients)

Notes:

  • The shared download folder allows Tidarr to download files that Lidarr can then import

📖 Complete Setup Guide - Detailed configuration, troubleshooting, and advanced topics

ADVANCED

Custom Processing Scripts

Tidarr supports two custom shell scripts during the post-processing pipeline:

  • custom-script.sh - Runs before files are moved to the library
  • custom-post-script.sh - Runs after files are moved to the library

You can also install additional Python packages by placing a requirements.txt file in your root config folder.

Note

Interact with Tidarr download process

  1. Create shell scripts in your config folder (the mounted shared/ volume)
  2. Scripts will be automatically detected and executed during post-processing
  3. Use custom-post-script.sh to move playlists to a separate folder, sync to external storage, etc.

📖 View complete documentation

NO DOWNLOAD

If you want to use Tidarr only as UI and not download files, you can set NO_DOWNLOAD=true in the environment variables.

This way you can use Tidarr to manage your download history, watchlist, and keep benefits of json DB (sync_list.json, queue.json) to manage download via custom scripts.

Queue items are set to no_download status and never processed automatically. You can still trigger a one-off download for any individual item directly from the queue UI using the single download button.

Note

Unecessary configurations

In NO_DOWNLOAD mode those configurations are unecessary:

  • Docker library volume can be omit
  • .tiddl/config.toml has no effect

API Documentation

If you want to interact with Tidarr from other applications (scripts, external services, automations), you can use the Express API.

Note

Integration with other applications

Tidarr's REST API allows you to:

  • Secure API requests using X-API-KEY header (available in configuration dialog)
  • Add downloads (albums, tracks, playlists, etc.)
  • Manage the queue (pause, resume, delete)
  • Synchronize playlists
  • Manage Tidal authentication
  • Customize configuration

📖 View complete API documentation

User requests

As I'm the only maintainer for now, user requested features can take time.

  1. Feel free to create an issue with enhancement or bug tag.
  2. Be my guest, fork and dev !

DONATE

If you would like to support this project, please do not hesitate to make a donation. It contributes a lot to motivation, gives me the energy to continue maintaining the project and adding the features requested by the users :)

Buy Me A Coffee

DEVELOP

Want more features and/or contribute ? Be my guest, fork and dev <3

Check docker environment variables in compose.yml before running :

make dev

Open http://localhost:3000 with your browser to see the result.

Multi-platform Docker builds

The docker-build Makefile target now relies on Docker Buildx so you can produce images for several architectures in one command.

  • Build a multi-platform image (default: linux/amd64 and linux/arm64):
make docker-build IMAGE_TAG=latest BUILD_VERSION=1.2.3
  • Build a custom platform image or local image for your host (example linux/arm64):
make docker-build PLATFORMS=linux/arm64 IMAGE_TAG=dev BUILD_VERSION=0.0.0-dev

Run tests :

make testing-build
make testing-run

PURPOSES

  • Renovate old torrent dl media library with full FLAC
  • Just for coding
  • Just for fun

RESOURCES