Get it when you hear it. Set it with one thumb. Mix it when you play.
Download GetSetMix Setup · Docs · How Setup works
Getting a track you just heard into Rekordbox usually means a session at the computer, later: find the song again, download it, fix its tags, import it. By then you've often forgotten what it was.
GetSetMix takes that session away. When you hear something you like, in a video, a set, or one you just found with Shazam, you share its YouTube or SoundCloud link from your phone, right then. By the time you sit down to play, it's downloaded, tagged, and already in your Rekordbox collection.
GET. You hear a track you want. In the YouTube or SoundCloud app on your phone, tap Share ▸ GetSetMix. That's the whole step: GetSetMix is in the share menu like any other app, and it opens straight on the track. A playlist or a whole set becomes one row per track.
SET. Title and artist are already filled in. Pick the genre with one tap, fix anything that's off, and send it. Tracks you already own are flagged before you download them twice.
MIX. Your home server downloads it (MP3 320 or FLAC), tags it with the cover and keeps it in your library, which is also your backup. Your computer fetches it on its own, and when you open Rekordbox it's already in the GetSetMix playlist. Nothing to search, download or import.
No home server? GetSetMix also runs on just your computer: you paste the link there instead, and Set and Mix work the same. (Share to GetSetMix is Android only: iPhones don't let web apps into the share menu.)
Use GetSetMix only for content you have the rights to download.
Get: from your phone, the moment you hear it
- Share to GetSetMix from YouTube, SoundCloud or any app that shares a link: install GetSetMix on Android from the browser and it appears in the share menu
- Single tracks, playlists and whole sets, from anything yt-dlp supports
- Or paste a link on the computer: Ctrl+V anywhere on the page
Set: the metadata, right the first time
- Every field editable before anything downloads, with a DJ-genre picker and cover search or upload
- Duplicate detection against your collection, by source link or a fuzzy title and artist match
- ID3v2.4 and FLAC tags with embedded artwork; filenames from a template (
{artist} - {title}, and more) - Batch downloads with progress per track, adjustable parallelism, cancel and retry; one failure never stops the batch
Mix: in Rekordbox when you play
- The companion writes new tracks straight into the Rekordbox collection, safely (backup, one transaction, integrity check), whenever you open Rekordbox through its shortcut
- With a home server, tracks download to each paired computer on their own, one way, so the server stays the backup
- Or, without the companion, a Rekordbox XML with an Inbox playlist that drops what you've already imported
Running it
- GetSetMix Setup for Windows and macOS: on this computer, or on a home server with Docker or Kubernetes and automatic updates
- Phone-first web UI, light or dark, in English and Italian
- Private by default, with optional token or Basic Auth; Prometheus
/metrics; runs in well under 1 GB of RAM
Download GetSetMix Setup from the latest release
(GetSetMix-Setup-windows-x64.exe, or GetSetMix-Setup-macos.zip) and open it. It asks one
question, where GetSetMix should run, and does the rest:
| Just this computer | With my home server | |
|---|---|---|
| GetSetMix runs | in its own window, while it's open | on the server, always on |
| Send tracks from your phone | no | yes (Share from YouTube / SoundCloud) |
| Setup installs | the app, ffmpeg and Deno; a GetSetMix shortcut | GetSetMix on Docker or Kubernetes, with automatic updates (Watchtower / Keel) |
| Tracks reach Rekordbox | straight away, through the companion | downloaded to this computer every 15 minutes while it's on, then imported |
For the home server, Setup shows one command to paste on the server (over SSH, or at its own keyboard). The server reports what it has back to Setup, you choose in the window, and the server installs GetSetMix, turns on updates and pairs your computer. If GetSetMix already runs there, Setup keeps it, with its tracks and settings, and only adds updates and the pairing. Re-run Setup on a second computer to pair that one too.
Tracks reach the computer one way: the server keeps every track (your backup), and deleting one on the computer never touches the server's copy. Already sync the server's music folder with Nextcloud or Syncthing? Pick My server's music folder is already synced here and Setup only watches it.
The phone app needs HTTPS. Setup turns it on with Tailscale when the
server has it (tailscale serve); without it, the phone can still open
GetSetMix on your home Wi-Fi but can't install it or share to it.
Windows may say it doesn't know the publisher (More info > Run anyway); on macOS, right-click GetSetMix Setup > Open the first time. Neither is signed yet.
The sections below are the same installs by hand.
# deploy/docker-compose.yml (edit the music volume path)
services:
getsetmix:
build: ..
ports: ["8765:8765"]
volumes:
- getsetmix-data:/data
- /path/to/your/music/library:/music
environment:
GSM_LANGUAGE: en # or it
# GSM_AUTH_TOKEN: change-me # enable token auth if exposed
volumes:
getsetmix-data:docker compose -f deploy/docker-compose.yml up -d --build
# open http://localhost:8765Plain manifests live in deploy/k8s/getsetmix.yaml (Namespace, ConfigMap, PVCs for data + music, Deployment with health probes and Prometheus scrape annotations, Service, optional Ingress commented out).
# 1. build & push the image, then point the Deployment at it
docker build -t ghcr.io/Fre0Grella/getsetmix:latest .
docker push ghcr.io/Fre0Grella/getsetmix:latest
# 2. apply
kubectl apply -f deploy/k8s/getsetmix.yaml
kubectl -n getsetmix get podsNotes:
replicas: 1withstrategy: Recreateis intentional — state is SQLite on a PVC.- Mount your real music library into the
musicPVC (or replace it withhostPath/NFS as your cluster dictates). - A Helm chart is not included; the manifests are small enough to kustomize directly.
Same UI, not always-on — auto-opens your browser. Grab a prebuilt executable from the Releases page (no Python needed, data lives in ~/.getsetmix), or run from source:
pip install -r requirements.txt
# ffmpeg must be on PATH (apt install ffmpeg / brew install ffmpeg / winget install ffmpeg)
# and so must a JavaScript runtime for YouTube — Deno is the default:
# winget install DenoLand.Deno / brew install deno / curl -fsSL https://deno.land/install.sh | sh
python run_local.py # opens http://127.0.0.1:8765
python run_local.py --port 9000 --no-browserYouTube downloads fail with
HTTP Error 403? YouTube only serves media to clients that solve its JavaScript challenges. yt-dlp does that with an external runtime (Deno, or Node ≥ 22) plus theyt-dlp-ejsscripts thatyt-dlp[default]installs. The Docker image ships both and is rebuilt every week with the newest yt-dlp; in local mode GetSetMix shows a banner when the runtime is missing or yt-dlp is out of date.GET /api/statusreports both.
- Paste a link into the field in the bottom bar, or press Ctrl+V anywhere on the page (playlists fan out into one row per entry). With the field empty, Add reads the clipboard. On Android, install the app and share links to it from YouTube or SoundCloud.
- Rows resolve their metadata. Fix title/artist, pick a genre, optionally set the album and the cover. On a computer every field is editable in place (hover the artwork to search or upload a cover); on a phone, tap a row to open it, with one-tap genre picks under the genre field.
- Press Download to start the batch (the ×N selector overrides parallelism for this batch only). Edits lock once a track is queued.
- Watch per-track progress and the batch bar; cancel anytime (in-flight tracks are stopped and re-staged).
- When tracks show Done, open Rekordbox → File ▸ Import collection / reload the rekordbox.xml source and find them in the Inbox playlist.
In Rekordbox, point Preferences ▸ Advanced ▸ Database ▸ rekordbox xml at the XML path shown in Settings (default <data>/rekordbox/getsetmix.xml), then refresh the rekordbox xml tree in the sidebar after each batch.
companion/ is the program on the computer that runs
Rekordbox, and GetSetMix Setup installs it for you. It adds new tracks
straight into the Rekordbox collection (into a GetSetMix playlist), so
there is no XML to refresh. With a home server it also downloads them first
(gsm-companion fetch, every 15 minutes while the computer is on). Nothing
has to stay running: Rekordbox must be closed while its database is written,
so you open Rekordbox through a Rekordbox (GetSetMix) shortcut that imports
first (≈1 s) and then starts it. By hand:
gsm-companion setup && gsm-companion installTo keep the inbox tidy, export your full collection (File ▸ Export Collection in xml format) and set its path as Rekordbox collection XML in Settings (or GSM_COLLECTION_XML_PATH). Before each batch GetSetMix compares the inbox XML against the collection (by file location, falling back to title + artist) and removes the tracks you've already imported — the Inbox playlist only ever shows what's still missing from your collection.
GetSetMix ships as an installable PWA with a Web Share Target, so you can push a link into it straight from the YouTube or SoundCloud share sheet instead of copy-pasting.
- Expose the instance over HTTPS. Browsers only let a site install (and register a share target) from a secure origin. The simplest route is the Kubernetes deployment behind a TLS Ingress — uncomment and adapt the Ingress block in
deploy/k8s/getsetmix.yaml(it includes a cert-managertls:example). Any HTTPS reverse proxy works too. - Install it. Open the HTTPS URL in Chrome/Edge on your Android phone → menu → Install app / Add to Home screen.
- Share. In the YouTube or SoundCloud app, tap Share → GetSetMix. The app opens, the link is fetched, and the track appears staged in the list — edit metadata and hit Download as usual.
Notes:
- Android only. iOS Safari doesn't implement the Web Share Target API, so GetSetMix can't appear in the iPhone share sheet (paste-link still works).
- If you've enabled
GSM_AUTH_TOKEN/ Basic Auth, open the installed app and authenticate once first — the token is stored in the browser and reused when you share.
Everything is editable in the UI (settings icon) and persisted to <data>/config.json. The theme (Auto, Light or Dark) is the exception: it's kept per device, in the browser. Environment variables override on boot:
| Variable | Default | Purpose |
|---|---|---|
GSM_DATA_DIR |
./data (/data in Docker) |
SQLite DB, config, covers, default XML location |
GSM_LIBRARY_ROOT |
./music (/music in Docker) |
Downloads land directly here (no inbox folder) |
GSM_XML_PATH |
<data>/rekordbox/getsetmix.xml |
Inbox XML path (new downloads) |
GSM_COLLECTION_XML_PATH |
(unset) | Full Rekordbox collection XML; when set, already-imported tracks are purged from the inbox XML before each batch |
GSM_PLAYLIST_NAME |
Inbox |
Target playlist inside the XML |
GSM_OUTPUT_FORMAT |
mp3 |
mp3 (320 kbps) or flac — global only |
GSM_CONCURRENCY |
2 |
Global parallel-download default |
GSM_FILENAME_TEMPLATE |
{artist} - {title} |
Tokens: {title} {artist} {album} {source} {id} {genre} |
GSM_LANGUAGE |
en |
en or it |
GSM_AUTH_TOKEN |
(unset) | Static token auth (X-Auth-Token, Authorization: Bearer, or ?token=) |
GSM_BASIC_USER / GSM_BASIC_PASS |
(unset) | HTTP Basic Auth alternative |
With auth enabled the API and /metrics return 401 without credentials; the UI prompts for the token once and stores it in the browser.
GET /metrics (Prometheus text format):
getsetmix_jobs{status="..."} # job counts by status
getsetmix_active_downloads
getsetmix_download_duration_seconds_sum / _count
getsetmix_errors_total{source="..."}
getsetmix_songs_downloaded{window="30d"|"365d"|"all"}
getsetmix_healthy
GET /healthz returns ok for probes.
pip install -r requirements.txt pytest httpx ruff pre-commit
python -m pytest tests/ -v # unit + API tests (no network needed)
ruff check app tests run_local.py
pre-commit install --hook-type commit-msg --hook-type pre-commit # local guard railsCommit messages must follow Conventional Commits — versioning is fully automated from them, so a non-conforming message fails CI:
| Commit | Example | Version bump |
|---|---|---|
fix: |
fix: stop crash on empty playlist |
patch — 1.0.0 → 1.0.1 |
feat: |
feat: share target for YouTube |
minor — 1.0.0 → 1.1.0 |
feat!: / BREAKING CHANGE: footer |
feat!: drop Python 3.10 |
major — 1.0.0 → 2.0.0 |
Other types (chore, docs, ci, refactor, test, …) are valid and release nothing on their own. The local commit-msg hook (from pre-commit install above) and the commitlint.yml workflow both enforce the format; to hard-block bad commits, enable branch protection on main and require the Commit lint check.
| Workflow | Trigger | What it does |
|---|---|---|
ci.yml |
push to main, every PR |
ruff lint, pytest suite (with ffmpeg), and a no-push Docker build as a PR safety net |
commitlint.yml |
push to main, every PR |
validates commit messages against Conventional Commits |
docker-publish.yml |
push to main |
builds the image for linux/amd64 + linux/arm64 and pushes to GHCR as latest, main, and the commit SHA |
release.yml |
push to main |
automated semantic versioning: bumps the version from the commits, tags vX.Y.Z, creates the GitHub Release, builds GetSetMix Setup (Windows / macOS), the standalone executables (Windows / macOS / Linux, PyInstaller) and the immutable versioned images (:1.2.3, :1.2, :1) |
pages.yml |
push to main touching site/** |
deploys the landing + docs site in site/ to GitHub Pages |
companion.yml |
changes under companion/, companion-v* tags |
tests the companion and Setup on Windows/macOS/Linux and builds GetSetMix Setup as a downloadable artifact (before a release, try it with wizard --demo: a real install needs the released app and image); when companion/VERSION names an unreleased version on main, builds Windows + universal macOS binaries and publishes the companion-vX.Y.Z release |
One-time setup after pushing to GitHub:
-
Replace
Fre0Grellawith your GitHub username indeploy/k8s/getsetmix.yaml,site/*.html, and this README (grep -rl Fre0Grella .). -
Settings ▸ Pages ▸ Source: GitHub Actions to enable the site.
-
After the first publish, Packages ▸ getsetmix ▸ Package settings ▸ Change visibility if you want the image public (no
imagePullSecretsneeded in K8s). -
(optional) Settings ▸ Branches → protect
mainand require the Commit lint check so only Conventional Commits can land. -
If
mainrequires signed commits, give the release bot a signing key so its version-bump commit is accepted (run once, locally, withghauthenticated):ssh-keygen -t ed25519 -C "getsetmix-release-bot" -f gsm_release_key -N "" gh ssh-key add gsm_release_key.pub --type signing --title "getsetmix release bot" gh secret set SSH_SIGNING_PRIVATE_KEY < gsm_release_key gh secret set SSH_SIGNING_PUBLIC_KEY < gsm_release_key.pub rm gsm_release_key gsm_release_key.pub # the keys now live in GitHub
Set
git_committer_emailinrelease.ymlto a verified email on the account that owns the signing key so the bot's commits show as Verified.
Releasing is automatic — just merge Conventional Commits to main:
feat: … → semantic-release tags v1.1.0, publishes the GitHub Release with
getsetmix-windows-x64.exe / -macos-arm64 / -linux-x64, and pushes
ghcr.io/Fre0Grella/getsetmix:1.1.0 (+ :1.1, :1)
The website lives in site/ — a static landing page (index.html) and documentation (docs.html), no build step. Edit and push; the workflow handles the rest.
app/
main.py # FastAPI app, API routes, auth middleware
worker.py # asyncio download queue (yt-dlp + ffmpeg)
metadata.py # server-side metadata fetch, playlist fan-out
tagger.py # ID3v2.4 / FLAC tagging + cover embedding
rekordbox.py # DJ_PLAYLISTS XML writer (atomic, corruption-safe)
naming.py # filename templates + sanitization
db.py # SQLite persistence (tracks, history, counters)
metrics.py # Prometheus exposition
config.py # settings + env overrides
link.py # /api/link: paired computers download finished tracks
static/ # the UI (vanilla JS, no build step)
companion/ # Go: the Rekordbox companion, which is also GetSetMix Setup
internal/setup/ # Setup's window (local web UI) and install steps
internal/serverscript/ # the scripts Setup sends to a home server
internal/fetch/ # one-way download from the server
scripts/build-setup.sh # builds Setup for Windows / macOS
deploy/
docker-compose.yml
k8s/getsetmix.yaml
site/ # landing + docs, deployed to GitHub Pages
tests/ # pytest suite run by CI
.github/workflows/ # ci.yml · docker-publish.yml · pages.yml
run_local.py # local app mode
companion/ # gsm-companion (Go): imports deliveries into Rekordbox's master.db
| Method & path | Purpose |
|---|---|
POST /api/tracks {url} |
Add URL (playlist fans out); returns ids + duplicate flag |
GET /api/tracks |
All rows + active download count |
PATCH /api/tracks/{id} |
Edit metadata (only before download / after error) |
DELETE /api/tracks/{id} |
Remove row |
POST /api/tracks/{id}/cover / GET …/cover |
Upload / fetch cover |
GET /api/cover-search?q= |
Cover candidates (iTunes) |
POST /api/tracks/{id}/retry |
Retry a failed track |
POST /api/batch/start {ids?, concurrency?} |
Start batch |
POST /api/batch/cancel |
Cancel batch, re-stage in-flight |
GET/PUT /api/settings |
Read / update settings |
GET /api/stats · GET /api/history · POST /api/purge |
Stats, URL history, manual purge |


