Self-hosted service
henriquesebastiao/downtify avatar
henriquesebastiao/downtify

Downtify: a self-hosted Spotify to tagged audio pipeline with a built-in player

Download and listen to your music, playlists, and podcasts, along with album art and metadata

749 stars79 forksPythonAGPL-3.0

At a glance

What is it?
Downtify is a self-hosted Python web app that resolves Spotify and YouTube Music links, matches them to audio on YouTube Music, and writes tagged files to your own library. It needs no Spotify API credentials, and the downloader sits inside a larger player, podcast and multi-user application.
Who is it for?
Downtify fits a household or small team that wants one self-hosted library, a player and a scheduled download watcher without wiring up a Spotify developer application. It is the wrong tool if you need commercial catalogue rights, if your audio matching has to be exact on long or obscure tracks, or if you cannot run Docker.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The pipeline is three hops: embed page, YouTube Music match, yt-dlp download

Downtify is not a client for an official API. The README describes it as a self-hosted web app that turns Spotify and YouTube Music links into a tagged audio library, and the pipeline reads like a relay. Metadata comes first, resolved from the public open.spotify.com/embed pages for tracks, albums and playlists. The audio source is separate: ytmusicapi searches YouTube Music and picks the best result by comparing durations, and when no good match exists the app falls back to searching standard YouTube. The file itself is fetched by yt-dlp, converted by ffmpeg into the format and bitrate you choose, and tagged by mutagen with title, artist, album, year and cover art. The README states that none of this touches the Spotify API, so no credentials of any kind are involved. Metadata and audio are two independent sources stitched together by duration, which is the whole design and also its weak point.

Duration matching is what decides whether a download is right

Because metadata comes from Spotify and audio comes from YouTube Music, something has to line the two up, and the README names the method: durations are compared and the best result wins. That is a reasonable heuristic for a mainstream single, where the same recording is uploaded by several channels with identical run times. It gets weaker in the cases where duration tells you nothing, such as a live cut with chatter before the song, a long radio edit, an album track where the YouTube upload carries an intro or an outro, or a rare release with only one upload. When the match is poor, the failure is silent in the sense that the file still downloads, still gets tagged with the Spotify title and artist, and still plays. Nothing in the documentation describes a manual re-match or an edit of the chosen source after the fact. There is also no Premium requirement and no account, which removes the obvious friction but also removes the option of asking a Spotify-side source for an authoritative file.

One container, two mounted folders and a compose file you can read in full

Everything runs inside a single Docker container, and the repository ships a Dockerfile and a docker-compose.yml. The Dockerfile is a multi-stage build: it copies a static ffmpeg binary from one image, a Deno binary from another, builds the Vue frontend with npm ci in a Node stage so the runtime image never ships a stale dist, and settles on a minimal python:3.15-alpine runtime. The compose file below is the whole of the service definition as the repository ships it. The two bind mounts are the parts that matter: one holds downloaded audio, the other holds application data. Port 8000 is published on the host.

yaml
services:
  downtify:
    container_name: downtify
    image: 'downtify:${TAG-latest}'
    restart: unless-stopped
    build: .
    ports:
      - '8000:8000'
    volumes:
      - ./docker/downloads:/downloads
      - ./docker/data:/data
yaml
    dns:
      - 1.1.1.1
      - 1.0.0.1

If you would rather run the code directly, pyproject.toml declares the runtime and the libraries the server depends on, and the resolver for the audio step is one of them.

The rest of the compose file is commented configuration you can uncomment as needed: an existing music folder mounted into the collection path, the download folder of slskd when using the slskd audio source, a cookies.txt file for requests that need one, and environment variables such as DOWNTIFY_PORT, DOWNTIFY_COOKIES_FILE, TZ and DOWNTIFY_MONITOR_SYNC_TIME. The DNS block is set explicitly rather than inherited.

The README links an installation page in the project documentation rather than listing every step inline, so the compose file and the Dockerfile are the practical entry points.

toml
requires-python = ">=3.11,<3.15"
dependencies = [
    "fastapi>=0.142.2",
    "feedparser>=6.0.14",
    "httpx>=0.28.1",
    "load-dotenv>=0.1.0",
    "loguru>=0.7.3",
    "mutagen>=1.48.1",
    "uvicorn>=0.54.0",
    "websockets>=17.1",
    "yt-dlp>=2026.8.19",
    "ytmusicapi>=1.12.3",
    "zeroconf>=0.151.5",
]

Around the downloader sits a library app: player, monitor, podcasts and users

The downloader is the part that is easy to describe and the part you could build yourself with yt-dlp. The surrounding application is what makes Downtify worth a container. There is a built-in player with a full-screen now playing view, synced lyrics, an editable queue, a sleep timer, keyboard shortcuts, media key support and a ten-band equalizer that the README marks as experimental. A Playlist Monitor watches Spotify or YouTube Music playlists and artists and downloads new tracks and releases on a schedule you set, and artist watches can be narrowed to albums, singles or EPs and to releases from a given point onward. Podcasts can be subscribed to by RSS feed, Spotify link or name, with resume positions kept per episode. Lyrics come from LRCLIB and NetEase, plain or time-synced, in an order you choose. Deezer charts, a Finder-style search view and a Discover feature widen the sources beyond what you already own.

Accounts, sharing and what AGPL-3.0 asks of a hosted deployment

Downtify supports sign in with a username and password, with admins able to add accounts for the rest of the household. Each user sees only the tracks, likes and playlists they added, admins see everything, and the README notes that files are never stored twice, which is what makes per-user visibility affordable rather than merely tidy. An activity log records who signed in and who is playing what. The licence is AGPL-3.0, which is the part to think about before you deploy this anywhere. The strong copyleft covers modified versions offered over a network: if you change Downtify and let users reach it over HTTP, you are required to offer those users the corresponding source. Running the unmodified container for a household or a small group is a different question from shipping a forked, network-facing service, and the distinction matters for anyone who plans to build on top of it. Nothing here is legal advice, so treat the licence text and the licence file in the repository root as the source to read.

Two feature areas are labelled experimental: mobile pairing and the equalizer

The README marks the mobile apps as experimental and states plainly that the server side is ready while the Android app itself is still in development. The pairing model described is a QR code that links a phone app to the server, streaming the library at original quality or transcoded for mobile data, with offline copies and casting. Until the Android app ships, that path is a promise rather than something you can try. The ten-band equalizer in the player is flagged as experimental in the same table, which is a milder warning but still means the audio path can change. Elsewhere the feature list is presented without qualification: MP3, FLAC, M4A, OGG and OPUS output at a bitrate you pick, artist and album downloads, free-text search, CSV imports from Soundiiz, TuneMyMusic and Exportify, a liked songs playlist that also shows up in your media server, and a Spotify-style discovery feed built from Deezer first with Spotify picks added as they arrive. If your requirement list includes any of the experimental parts, verify the current state before you plan around them.

Downtify against running yt-dlp by hand

The honest alternative is not another downloader but the command line tool itself. yt-dlp, which Downtify depends on, gives you a file when you point it at a URL, and it can be wrapped in a script that reads a list of links and writes to a folder. The difference in approach is the surrounding workflow. Hand-running yt-dlp means you supply the metadata yourself, tag the file yourself with a separate tool, and keep track of what you have already downloaded. Downtify resolves the metadata from the Spotify side, matches the audio by duration, tags title, artist, album, year and cover art automatically, and then keeps the result in a library you can browse, search, play and monitor for new releases on a schedule. That is worth a container when your library is large and shared, and it is overhead when you need one file once. The trade is control: the wrapper gives you fewer knobs than the underlying tool and adds its own assumptions about what a good match looks like.

Editorial conclusion

Downtify fits a household or small team that wants one self-hosted library, a player and a scheduled download watcher without wiring up a Spotify developer application. It is the wrong tool if you need commercial catalogue rights, if your audio matching has to be exact on long or obscure tracks, or if you cannot run Docker. Before adopting it, check the AGPL-3.0 obligations for any network-facing modification, and confirm that Python 3.11 to 3.14 matches the pyproject.toml range if you plan to run from source rather than the published image.

Frequently asked questions

Does Downtify need a Spotify account or API key?

No. The README states it never touches the Spotify API, and metadata for tracks, albums and playlists is resolved from the public open.spotify.com/embed pages, while the audio is matched on YouTube Music through ytmusicapi. No account and no Premium subscription are required.

Which audio formats can Downtify download?

MP3, FLAC, M4A, OGG and OPUS, at the bitrate you choose. ffmpeg performs the conversion and mutagen embeds title, artist, album, year and cover art into the finished file.

How do I run Downtify on my own machine?

As a Docker container. The repository ships a Dockerfile and a docker-compose.yml that publish port 8000 on the host and mount ./docker/downloads and ./docker/data, and the project documentation has an installation page linked from the README. Running from source needs Python 3.11 or newer, with the range in pyproject.toml capped below 3.15.

Official sources

  1. henriquesebastiao/downtify on GitHub
  2. License: AGPL-3.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/henriquesebastiao-downtify.svg)](https://hysenlabs.com/projects/henriquesebastiao-downtify)

Was this useful?