Skip to content

alfdav/music-dl

Repository files navigation

music-dl

Your Tidal library, in your browser. Download, manage, and play — all from one place.

License Python 3.12+

Using an AI assistant? Paste the block below into Claude Code, Codex, Cursor, or any LLM tool.

LLM Quick Reference
music-dl — local-first Tidal music manager. Downloads lossless/hi-res tracks,
manages a local library (any drive or NAS), plays everything in a browser GUI.
macOS, Linux, and Windows 10/11. Free and open-source.

INSTALL (macOS/Linux):
  curl -fsSL https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install.sh | bash
INSTALL (Windows 10/11):
  irm https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install.ps1 | iex
INSTALL (Headless/NAS):
  curl -fsSL https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install-docker.sh | bash
INTERNAL LATEST (macOS):
  curl -fsSL https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install-macos-local.sh | bash
INTERNAL LATEST (Windows 10/11):
  irm https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install-windows-local.ps1 | iex

DEV:   cd tidaldl-py && uv sync && music-dl gui     # http://localhost:8765
TEST:  cd tidaldl-py && uv run --extra test pytest
BUILD: cd tidaldl-py && uv sync && uv pip install pyinstaller && bun install && bunx tauri build --bundles dmg

STACK: Python 3.12+, FastAPI, vanilla JS, Tauri v2, Bun/discord.js for the optional bot.
REPO:  monorepo — Python app under tidaldl-py/, Discord bot under apps/discord-bot/.

KEY PATHS:
  DESIGN.md                         — agent-readable design tokens and visual identity contract
  tidaldl-py/docs/design-system.md  — detailed UI component/layout/animation rules
  tidal_dl/gui/static/{app.js,style.css,index.html} — frontend (no framework)
  tidal_dl/gui/__init__.py    — FastAPI app factory
  tidal_dl/gui/api/           — all API routes
  tidal_dl/gui/security.py    — CSRF, path validation, host validation
  src-tauri/src/lib.rs        — Tauri sidecar spawn + health poll
  apps/discord-bot/           — optional private Discord voice bot

RULES:
  - Audio: direct <audio src="..."> only. NO Web Audio API. Non-negotiable.
  - Design: read DESIGN.md before UI work; keep it aligned with design-system.md and style.css.
  - Security: localhost-only, CSRF on writes, path validation on file ops.
  - Tooling: uv over pip, bun over npm.

Home

What is this?

A local-first music manager that connects to your Tidal account. Search the catalog, download tracks in lossless or hi-res quality, browse your local collection, and play everything directly in the browser. Your files, your NAS, your rules.

A setup wizard walks you through Tidal login and library configuration on first launch — no config files to edit.

The GUI can also start and recover the Tidal OAuth flow itself from the browser. Use music-dl login only if you want to authenticate from the terminal for CLI-first workflows.

Install

Using an AI coding agent? Expand the LLM Quick Reference at the top and paste it into your agent.

Desktop: macOS / Linux

Copy this into Terminal:

curl -fsSL https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install.sh | bash

What it does:

  • macOS Apple Silicon: downloads the latest .dmg, verifies the GitHub release checksum, installs to /Applications, strips quarantine, then opens music-dl.app.
  • Linux x86_64: downloads the latest .AppImage, verifies the GitHub release checksum, installs it as ~/.local/bin/music-dl.

If macOS reports a DMG mount failure, rerun this current command first. The installer keeps progress output separate from the verified DMG path passed to hdiutil.

Desktop: Windows 10/11

Copy this into PowerShell:

irm https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install.ps1 | iex

Downloads the latest unsigned .msi, verifies the GitHub release checksum, then starts the Windows installer. SmartScreen warnings are expected for early unsigned builds. WSL is not required.

Headless / NAS / Docker

Copy this into Terminal:

curl -fsSL https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install-docker.sh | bash

Builds and starts the Docker Compose GUI at http://localhost:8765. Use this for Linux servers, NAS boxes, or machines where you do not want desktop packaging.

macOS: Build From Source

If you prefer to build locally, copy this into Terminal:

curl -fsSL https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install-macos-local.sh | bash

On success, it installs music-dl.app to /Applications/music-dl.app. Requires Xcode Command Line Tools, Rust, uv, and Bun.

Internal Latest From Master

Use these on our own machines when master has newer commits than the latest GitHub release and we do not want to cut binaries.

No local build tools, rolling edge channel:

curl -fsSL https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install.sh | MUSIC_DL_RELEASE_TAG=edge bash
$env:MUSIC_DL_RELEASE_TAG = "edge"
irm https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install.ps1 | iex
Remove-Item Env:MUSIC_DL_RELEASE_TAG

These install the latest rolling edge artifact. Edge builds are produced automatically from master, replace the previous edge release assets, and point the app updater at the same edge manifest.

Build locally from source:

curl -fsSL https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install-macos-local.sh | bash

Windows 10/11:

irm https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install-windows-local.ps1 | iex

Both installers clone or refresh the source checkout, build locally, and install the app. They require normal build tools. Source installers use SSH Git by default (git@github.com:alfdav/music-dl.git), so your machine needs GitHub SSH access.

Manual Build

See Building the Desktop App for the full prerequisite list and platform-specific commands. The short version for macOS:

cd tidaldl-py
uv sync && uv pip install pyinstaller
bun install
bunx tauri build --bundles dmg
# Output: src-tauri/target/release/bundle/dmg/

Updating

  • macOS/Linux desktop: rerun the same install.sh command.
  • Windows: rerun the same PowerShell command and follow the MSI installer.
  • Headless/Docker: rerun the same install-docker.sh command.
  • macOS source build: rerun the same install-macos-local.sh command.

These same install one-liners appear in every release's notes. Canonical source: docs/release/install-instructions.md — edit there and both README and release notes stay in sync.

CLI / uv

Requires Python 3.12+ and ffmpeg.

uv tool install --from git+https://github.com/alfdav/music-dl.git#subdirectory=tidaldl-py music-dl
music-dl gui

Your browser opens automatically. The wizard handles the rest.


Screenshots

Library — browse by artist with quality badges and instant search

Library

Search — find tracks on Tidal, see what you already own, download in one click

Search


Features

  • Library browser — your local collection organized by artist or album with page-sized/cached loading, a dedicated Recently Added category, album art, quality badges (24-bit, lossless, MQA), and instant search
  • Home dashboard — recent additions, recently played, top artists, genres, repeat listening stats, and Continue Listening resume
  • Tidal search & download — search the full Tidal catalog, see which tracks you already own, download what you're missing
  • Quality upgrades — re-download existing tracks at higher quality without duplicates
  • Duplicate cleanup — ISRC-based deduplication finds exact copies across your collection
  • In-browser playback — play anything in your library, bit-perfect to your DAC, with persisted queue, volume, repeat/shuffle preferences, keyboard shortcuts, and queue actions
  • Waveform visualizer — pre-computed amplitude data drives a ripple animation from the playhead, zero audio post-processing
  • Playlist sync — point it at a Tidal playlist and it downloads only the tracks you don't have
  • Favorites — mark tracks you love, access them from one place
  • Local lyrics — synced .lrc sidecars and embedded tag fallback, rendered in the player with no network lookups. See tidaldl-py/docs/local-lyrics.md.
  • Setup wizard — first-run experience that walks you through Tidal login and library paths
  • Discord bot (optional) — single-user, single-guild companion that streams and downloads from your library over Discord voice. Configure it and manage the bot service from the GUI's DJAI view, then use the auto-posted Discord remote panel for search, playlists, playback controls, and repeat. See apps/discord-bot/README.md and tidaldl-py/docs/bot-onboarding.md.

CLI

The GUI is the main experience, but everything works from the terminal too:

music-dl gui                    # launch the web UI
music-dl dl <URL>               # download a track, album, or playlist
music-dl dl <URL> <URL> ...     # download multiple URLs
music-dl dl --list urls.txt     # download URLs from a file, one per line
music-dl dl <URL> --output ~/x  # one-off output directory override
music-dl cfg                    # view/edit settings
music-dl login                  # authenticate with Tidal from the terminal
music-dl logout                 # clear stored Tidal credentials
music-dl sync                   # sync library database
music-dl import <file>          # import a playlist from CSV/JSON
music-dl isrc-tag <path>        # write ISRC tags to local audio files
music-dl source show            # inspect Hi-Fi API/OAuth download source settings
music-dl scan add <PATH>        # add and scan a local library directory
music-dl dl_fav tracks --since 2026-01-01  # download favorite tracks incrementally
music-dl gui --setup-bot        # terminal fallback for Discord bot onboarding

Run music-dl --help for the full list.

Configuration

Settings are managed from the in-app Settings page. The config file lives at ~/.config/music-dl/settings.json.

Setting Default What it does
download_base_path ~/download Where downloaded files go
scan_paths "" Comma-separated local library roots
quality_audio HI_RES_LOSSLESS Preferred audio quality
skip_existing true Skip tracks you already have
skip_duplicate_isrc true Skip tracks with matching ISRC codes
download_source hifi_api Preferred stream source
download_source_fallback true Fall back to OAuth when the preferred source fails

Architecture

graph TD
    CLI["CLI · Typer<br/><code>cli.py</code>"] --> Core
    GUI["GUI · FastAPI<br/><code>gui/</code>"] --> Core
    Bot["Discord bot · Bun/discord.js<br/><code>apps/discord-bot</code>"] --> BotAPI["Bot API<br/><code>/api/bot/*</code>"]
    BotAPI --> Core
    Core["config.py<br/>Settings · Tidal"] --> DB["library_db.py<br/>SQLite + WAL"]
    Core --> DL["download.py<br/>Download class"]
    Tidal["Tidal API<br/>tidalapi"] --> DL
    DL --> Tag["mutagen<br/>tagging"]
Loading

CLI, GUI, and the optional bot share the same backend core. CLI and GUI use the same singletons (Settings, Tidal, LibraryDB). The Discord bot stays thin: slash commands, queue state, and Discord voice transport live in Bun; source resolution, playable URLs, downloads, and auth stay in music-dl. The <audio> element plays files directly from source — no Web Audio API, no processing.

For deep dives, see:

  • Backend Reference — API routes, DB schema, download pipeline, middleware, security model
  • DESIGN.md — agent-readable design tokens and visual identity contract
  • Design System — detailed UI component patterns, layout, and animation rules
  • Docker Guide — detailed Docker usage, mounts, CLI commands, headless/cron

Environment Variables

Variable Default What it does
MUSIC_DL_CONFIG_DIR ~/.config/music-dl Config/credentials directory
MUSIC_DL_BIND_ALL (unset) Set to 1 to bind server to 0.0.0.0 (Docker sets this automatically)
MUSIC_DL_HOST 127.0.0.1 Docker compose host binding. Set to 0.0.0.0 for LAN access
MUSIC_DL_PORT 8765 Docker compose port mapping
MUSIC_DL_CONFIG ~/.config/music-dl Docker compose config volume source
MUSIC_DL_DOWNLOADS ~/Music Docker compose downloads volume source
MUSIC_DL_BOT_ENV_PATH <config-dir>/discord-bot.env Optional Discord bot env-file override
MUSIC_DL_BOT_TOKEN_PATH <config-dir>/bot-shared-token Optional backend shared-token file override
MUSIC_DL_BOT_PATH auto-detected repo path Optional path to apps/discord-bot for music-dl gui --setup-bot
MUSIC_DL_BOT_TOKEN (unset) Optional env override for bot/backend bearer auth

Development

git clone git@github.com:alfdav/music-dl.git
cd music-dl/tidaldl-py
uv sync
music-dl gui

Run the Python test suite:

uv run --extra test pytest

Run the Discord bot checks:

cd apps/discord-bot
bun test
bun run typecheck

Run the release smoke coverage from the repository root:

uv run --project tidaldl-py --extra test pytest \
  tidaldl-py/tests/test_gui_command.py \
  tidaldl-py/tests/test_gui_api.py \
  tidaldl-py/tests/test_setup.py \
  tidaldl-py/tests/test_token_refresh.py \
  tidaldl-py/tests/test_public_branding.py \
  tidaldl-py/tests/test_packaging.py
uv build --project tidaldl-py
docker build -f docker/Dockerfile -t music-dl .

Building the Desktop App

Prerequisites: Rust, Bun, Python 3.12+, and platform-specific dependencies.

macOS:

# Xcode CLI tools (if not installed)
xcode-select --install

Linux (Ubuntu/Debian):

sudo apt install libwebkit2gtk-4.1-dev libayatana-appindicator3-dev \
  librsvg2-dev patchelf libgtk-3-dev ffmpeg

Windows 10/11:

  • WebView2 Runtime (normally already installed on Windows 10/11)
  • Microsoft C++ Build Tools / Visual Studio Build Tools
  • WiX requirements used by Tauri MSI builds

Build:

cd tidaldl-py
uv sync && uv pip install pyinstaller
bun install
# Linux:
bunx tauri build          # outputs .AppImage + .deb
# macOS (produces .app + .dmg):
bunx tauri build --bundles dmg
# Output: src-tauri/target/release/bundle/

The build process: PyInstaller compiles the Python backend into a standalone sidecar binary → Tauri wraps it with a native window → outputs .app/.dmg (macOS), .AppImage/.deb (Linux), or .msi (Windows).

For Windows local builds, build and rename the PyInstaller sidecar before running Tauri, then use the CI config override so Tauri does not run the default Unix beforeBuildCommand:

cd tidaldl-py
uv sync --extra build
bun install
$TargetTriple = rustc --print host-tuple
uv run pyinstaller --clean --distpath src-tauri/binaries --workpath build/pyinstaller --noconfirm build/pyinstaller/music-dl-server.spec
Move-Item -Force "src-tauri/binaries/music-dl-server.exe" "src-tauri/binaries/music-dl-server-$TargetTriple.exe"
bunx tauri build --target $TargetTriple --bundles msi --config src-tauri/tauri.ci.conf.json

The one-command internal Windows source installer runs that same flow:

irm https://raw.githubusercontent.com/alfdav/music-dl/master/scripts/install-windows-local.ps1 | iex

The desktop app and browser mode share the same local web UI. Tauri starts or reuses the localhost daemon, then opens the same route the browser would use. Desktop protocol links such as music-dl://open#search open supported internal views in the app.

Linux, macOS, and Windows releases are published via GitHub Actions. The macOS app is not notarized (no Apple Developer ID). The scripts/install.sh one-liner verifies the GitHub release checksum and strips the quarantine xattr so Gatekeeper doesn't fire. If you download a DMG through Safari instead, macOS will set the quarantine bit and you'll need a one-time right-click → Open bypass on first launch. Windows MSI builds are unsigned, so SmartScreen may warn on first install.

Windows smoke test before marking a release supported:

  1. Install the MSI.
  2. Launch music-dl.
  3. Complete or recover Tidal authentication.
  4. Choose a local library/download path.
  5. Search for one track.
  6. Download one track.
  7. Play that track.
  8. Quit and reopen the app.
  9. Confirm settings, auth, and library state persist.

See CONTRIBUTING.md for the full development workflow.

Security

The GUI binds to localhost only — it is not accessible from other machines. CSRF protection is enabled for all write operations. The Docker image runs as a non-root user (UID 1000) and binds to localhost on the host side by default.

Do not expose port 8765 to untrusted networks without adding your own authentication layer.

License

Apache-2.0. See LICENSE.

Disclaimer

Personal project for educational purposes and private use. Not affiliated with or endorsed by TIDAL. A valid TIDAL subscription is required. Downloaded files are for personal offline use in accordance with your subscription terms. You are responsible for compliance with applicable laws and TIDAL's Terms of Service.

Credits

Built on yaronzz/Tidal-Media-Downloader and tidal-dl-ng. Powered by tidalapi, mutagen, FastAPI, Rich, and Typer.

About

CLI tool for downloading music from Tidal

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors