downtify

downtify

Docker app from athuull's Repository

Overview

Downtify is an open-source music downloader backend powered by FastAPI, yt-dlp, and ffmpeg. It provides an automated download engine and queue management for companion clients like Hera.


Downtify

Self-hosted music downloader. Paste a Spotify link, get a perfectly tagged audio file — no API keys, no account, no hassle.

https://github.com/user-attachments/assets/9711efe8-a960-4e1a-8d55-e0d1c20208f7


✨ What is Downtify?

Downtify is a self-hosted web app that downloads music from Spotify — without touching the Spotify API, without needing an account, and without any Premium subscription. Just drop a link and get a fully-tagged audio file.

It resolves track metadata directly from Spotify's public embed pages, finds the best audio match on YouTube Music, downloads it with yt-dlp, converts it with ffmpeg, and embeds album art + all metadata with mutagen. The entire pipeline runs inside a single Docker container.


🚀 Features

Feature Details
🎵 Tracks, albums & playlists Any Spotify link works — single track, full album, or entire playlist
👁️ Playlist & Artist Watch Watch Spotify or YouTube Music playlists and artists — new songs and new releases download automatically
🎨 Rich metadata Album art, title, artist, album, year — all embedded in every file
🎚️ Multiple formats MP3 · FLAC · M4A · OGG · OPUS
🔎 Free-text search Search YouTube Music directly — no Spotify link needed
📥 CSV library import Import a library export from Soundiiz, TuneMyMusic or Exportify and queue the whole thing
🔑 Zero credentials No Spotify API key, no account, no Premium required
🔔 Real-time progress Live download progress via WebSocket — no page reload needed
🐳 One Docker command Up and running in under a minute
🏠 Home server platforms Available on Umbrel, CasaOS and HomeDock
🎧 Built-in player Play your downloaded music straight from the web UI — progress bar, shuffle, repeat, volume
🌍 Multi-language UI English (default) plus 6 more languages — easy to add more
📱 Installable (PWA) Add Downtify to your iOS or Android home screen — launches full-screen, no browser chrome
🍪 Cookie upload Upload a YouTube cookies.txt from the settings screen to download explicit/age-restricted tracks — no bind mounts, works on Windows

🚀 Quick Start

docker run -d -p 8000:8000 --name downtify \
  -v /path/to/downloads:/downloads \
  -v downtify_data:/data \
  ghcr.io/henriquesebastiao/downtify

Open http://localhost:8000, paste a Spotify link, and hit download.

Change /path/to/downloads to wherever you want your music saved.

Docker Compose

services:
  downtify:
    container_name: downtify
    image: ghcr.io/henriquesebastiao/downtify:latest
    ports:
      - '8000:8000'
    volumes:
      - ./downloads:/downloads
      - downtify_data:/data
    restart: unless-stopped

volumes:
  downtify_data:

Need a custom port? Use the DOWNTIFY_PORT environment variable:

ports:
  - '8000:30321'
environment:
  - DOWNTIFY_PORT=30321

🏠 One-Click Install on Home Servers

Platform Link
☂️ Umbrel Install on Umbrel
🏠 CasaOS Install on CasaOS
⚓ HomeDock OS Install on HomeDock

⚙️ How It Works

Downtify's download pipeline has three stages:

Spotify embed page  →  YouTube Music search  →  yt-dlp + ffmpeg + mutagen
   (metadata)             (audio match)            (download & tag)
  1. Metadata — Track, album and playlist links are resolved by scraping the public open.spotify.com/embed pages. No Spotify credentials of any kind are required.
  2. Audio matchytmusicapi searches YouTube Music for the track and picks the best result by comparing audio duration. Free-text searches skip the Spotify step entirely.
  3. Download & tagyt-dlp downloads the audio and ffmpeg converts it to your chosen format. mutagen embeds title, artist, album, year and cover art into the file.

👁️ Playlist Monitor

The Playlist Monitor lets Downtify watch your favorite Spotify and YouTube Music playlists — and the artists you follow — and automatically download new songs and new releases, hands-free.

How to use it:

  1. Click the eye icon (👁) in the navigation bar
  2. Paste a Spotify or YouTube Music playlist URL, or an artist URL (Spotify, or YouTube Music such as music.youtube.com/@artist) to watch everything they release
  3. Choose how often Downtify should check for new tracks (every 15 min up to once a month)
  4. Click Watch

From that point on, whenever a new song appears in the playlist, Downtify will detect and download it on the next scheduled check. Tracks that were already in the playlist when you added it are skipped — only new additions are downloaded.

Adding an artist works the same way: Downtify downloads their discography and then watches for new releases, so you don't need a dedicated playlist per artist. Each watch shows a Spotify or YouTube Music badge. You can pause, resume, force an immediate check, or stop any watch at any time from the same page.

YouTube Music playlists are handy for songs that aren't on Spotify: each track is downloaded from the exact video in the playlist.

Playlists checked daily or less often can be pinned to a specific hour (e.g. always sync overnight at 3 AM) with the DOWNTIFY_MONITOR_SYNC_TIME and TZ environment variables, and can be sorted by date added, title, frequency, track count, or status. See Playlist Monitor in the full docs for details.


🎛️ Download Settings

Access the settings panel (⚙️ icon) to configure:

Setting Options
Output format MP3 · FLAC · M4A · OGG · OPUS
Bitrate 128 · 192 · 256 · 320 kbps (ignored for FLAC)
Audio provider YouTube Music
Organize by artist Off (default) · On
Parallel downloads 1–30 concurrent downloads (default 3)
Delay between downloads 0–300 seconds (default 0 = off)
Download cover art On (default) · Off
Cover art resolution 300–1200px (default 600)
Overwrite existing files On (default) · Off

Delay between downloads waits a configurable number of seconds between songs instead of firing requests back-to-back — combined with a lower Parallel downloads value, it's the main tool for avoiding YouTube rate limits on large unattended downloads.

Cover art resolution raises the size Downtify requests for YouTube Music-sourced cover art — handy if you feed your library into a media server like Plex that shows higher-resolution artwork than the 600px default. Turn Download cover art off entirely to skip fetching artwork — smaller, faster downloads.

Turn Overwrite existing files off to save bandwidth: a song that's already anywhere in your download folder (library root, another playlist's folder, an artist/album folder) isn't downloaded again. Useful when the same track is in several playlists or already in your library.

See Download Settings for the full reference.

📁 Organize by artist

When Settings → File organization → Organize by artist is enabled, every downloaded track is saved inside a subfolder named after the track's primary artist:

<downloads>/
  The Night Owls/
    The Night Owls - Do I Still Recall.mp3
    The Night Owls - R U Awake.mp3
  Tame Impala/
    Tame Impala - The Less I Know The Better.mp3

This applies to all downloads — single tracks, albums and playlists alike. Playlist tracks are saved in their artist's folder instead of a playlist folder, which makes the library compatible with media apps (like Jellyfin, Navidrome, Plex and Beets) that expect an Artist/Song.ext folder structure.

When the setting is off (default), the existing behaviour is preserved: single tracks go directly into the root of the downloads folder, and playlist tracks go into a per-playlist subfolder.

M3U files and playlists — If you download a Spotify playlist with both Organize by artist and Generate M3U enabled, the M3U file is placed in <downloads>/Playlists/<playlist-name>.m3u (rather than inside the playlist subfolder) because the tracks are now spread across multiple artist folders. The relative paths inside the M3U still resolve correctly regardless of where you mount the library.


📦 What Spotify links are supported?

Link type Supported
Spotify track
Spotify album
Spotify playlist
YouTube Music playlist
YouTube Music search (free text)
Direct YouTube link

📥 Import a library CSV

Already exported your library from Soundiiz, TuneMyMusic or Exportify? Click "Import a library CSV" below the search box on the home page and pick the file — Downtify reads its Title/Artist columns and queues every track for download. See Library Import in the full docs for supported columns, limits, and rate-limiting tips.


📃 M3U playlist export

Downtify writes a standard EXTM3U file alongside your audio whenever a playlist gets downloaded — both for manual playlist paste-downloads and for Playlist Monitor sweeps that fetched at least one new track:

<downloads>/Playlists/<playlist-name>.m3u

The behaviour is governed by a single toggle in Settings → Playlists → Generate M3U file for playlists (on by default). Flip it off if you'd rather not produce M3Us at all; the rest of the download flow is unchanged.

Tracks that failed to download or had no YouTube Music match are skipped (and logged). The M3U is regenerated fresh on every run, so re-pasting the same playlist URL — or letting the Monitor add new tracks over time — always produces a complete, in-order file.

Track paths inside the M3U are written relative to the M3U file itself, so the same file works whether it's read from inside Downtify (where the library is mounted at /downloads) or from another consumer that mounts the same library at a different root — e.g. Jellyfin under /nas/music. Just point your media server at the same library mount and the playlist will appear as a single unit instead of a pile of loose files.


[!WARNING] Users are responsible for their actions and any legal consequences. Downtify does not support unauthorized downloading of copyrighted material and takes no responsibility for user actions.


🎧 Built-in Player

Downtify ships with a clean web player so you don't need a separate app to listen to what you've downloaded. Open the headphones icon (🎧) in the navigation bar — or hit the play button next to any file in the Library — and Downtify will load every audio file from your downloads folder into a queue.

What's included:

  • Big now-playing card with embedded album art and a progress bar (click or drag to seek)
  • Play / pause / previous / next
  • Shuffle with a stable random order across the whole queue
  • Repeat modes: off → all → one
  • Volume slider with mute toggle (volume is remembered between sessions)
  • Side queue listing every track in your library, each one with its own thumbnail and the currently playing one highlighted
  • Playing from selector — play just one downloaded playlist, artist or album instead of your whole library queued at once. See Built-in Player in the full docs.

The player parses Artist - Title.ext filenames so the now-playing card shows artist and title nicely, and pulls the cover art directly from the audio file's embedded tags (the same artwork Downtify wrote at download time). Playback uses your browser's native HTML5 audio element — no extra dependencies, no extra processes.

The Library page has the same playlist/artist/album filter to narrow down the file list before playing, re-downloading or deleting a track.


📱 Install as an App (PWA)

Downtify's web UI can be installed to your phone's home screen — on iOS (Safari: Share → Add to Home Screen) and Android (Chrome: ⋮ → Add to Home screen) — and launches full-screen with the Downtify icon, no browser address bar. See Install as an App in the full docs.


🌍 Internationalization

Downtify's UI is fully translatable. The default language is English, with Spanish, Brazilian Portuguese, French, Turkish, Greek and Hungarian included out of the box. You can switch languages from Settings → Language; your choice is saved in the browser's localStorage and applied instantly without a reload.

Contributing translations

Adding a new language is a small, three-step change — no build tooling beyond the existing Vite setup is required.

  1. Copy the English file as a starting point. Locale files live in frontend/src/i18n/locales/. Each file exports a single object whose keys match the structure of en.js exactly. Pick an IETF language tag for the file name (e.g. de.js, it.js, ja.js, pt-PT.js).

    cp frontend/src/i18n/locales/en.js frontend/src/i18n/locales/de.js
    
  2. Translate the values. Keep the keys, the placeholder tokens (e.g. {count}, {name}, {file}) and the overall shape unchanged — only the strings on the right-hand side should change. Update the language.name field at the top of the file to the native name of the language ("Français", "Deutsch", "Italiano"…) — this is the label that appears in the language picker.

  3. Register the locale in frontend/src/i18n/index.js:

    import de from './locales/de.js'
    
    export const AVAILABLE_LOCALES = [
      { code: 'en', name: 'English', messages: en },
      { code: 'es', name: 'Español', messages: es },
      { code: 'pt-BR', name: 'Português (BR)', messages: ptBR },
      { code: 'fr', name: 'Français', messages: fr },
      { code: 'tr', name: 'Türkçe', messages: tr },
      { code: 'el', name: 'Ελληνικά', messages: el },
      { code: 'hu', name: 'Magyar', messages: hu },
      { code: 'de', name: 'Deutsch', messages: de }, // new entry
    ]
    

That's it. Rebuild the frontend (cd frontend && npm run build) — your language will show up in Settings → Language automatically.

Tips for translators:

  • Missing keys fall back to English, so partial translations still ship. You can submit a PR with only the strings you're confident about.
  • Placeholder tokens like {count} or {file} must be left as-is — they're substituted at runtime.
  • Keep strings concise: the UI is laid out tightly and very long translations may wrap awkwardly. If you need to rephrase to fit, that's fine.
  • After translating, run npm run dev from frontend/ and click through every page in your language to spot anything that overflows or reads oddly in context.

Pull requests with new translations are very welcome — just open a PR against main.


🩹 Troubleshooting

Most download problems have the same root cause: YouTube wants a signed-in session. Upload a cookies.txt in Settings (⚙️) → YouTube cookies and they usually go away.

Problem Likely cause Fix
A song with explicit content won't download (Sign in to confirm your age) YouTube only serves age-restricted tracks to a signed-in adult account Upload a cookies.txt in Settings → YouTube cookies
Downloads fail, hang or say the format is unavailable YouTube is challenging/rate-limiting the requests — common on a VPS or VPN Upload a cookies.txt; then raise Delay between downloads and lower Parallel downloads in Settings; then try DOWNTIFY_FORCE_IPV4=1; then update the image
DOWNTIFY_COOKIES_FILE seems ignored (especially on Windows / Docker Desktop) The variable needs a container path and a bind mount — a C:\... path never works Skip the variable and upload the file in Settings → YouTube cookies instead
The cookie upload button is greyed out DOWNTIFY_COOKIES_FILE is set, so the deployment owns that file Unset the variable and recreate the container
Uploaded cookies / settings vanish after an update /data isn't a persistent volume Keep - downtify_data:/data in your compose file
Upload rejected as invalid The file isn't a Netscape cookie jar (JSON, spreadsheet or a copied header) Re-export with a cookies.txt browser extension, from a youtube.com tab
A watched playlist re-downloads tracks you already have The files are no longer in the downloads directory Keep /downloads persistent; moving files within it is fine

Cookies expire — if age-restricted downloads start failing again, export a fresh file and upload it as a replacement.

Full details, including how to export the file and what it contains: YouTube Cookies and Troubleshooting in the docs.


🤝 Contributing

Contributions, issues and feature requests are welcome! Check the issues page or open a pull request.

Before sending a pull request, please read CONTRIBUTING.md — it covers local setup, the project's coding and formatting standards (Ruff for Python, Prettier for the frontend), testing requirements, commit conventions, and the PR checklist. All contributions are expected to follow those standards.

If Downtify has been useful to you, consider leaving a ⭐ — it helps the project grow and reach more people!


📄 License

Licensed under the GPL-3.0 License.

Install Downtify on Unraid in a few clicks.

Find Downtify in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for Downtify Review the template variables and paths Click Install

Download Statistics

92,103
Total Downloads

Related apps

Details

Repository
henriquesebastiao/downtify:latest
Last Updated2026-09-11
First Seen2026-09-13

Runtime arguments

Web UI
http://[IP]:[PORT:8000]
Network
bridge
Privileged
false

Template configuration

API PortPorttcp

HTTP port for Downtify API

Target
8000
Default
8000
Downloads DirectoryPathrw

Download directory where files will be stored (must match Hera's music download directory)

Target
/downloads
Default
/mnt/user/data/media/music