getsetmix

Documentation

GetSetMix is a single-container FastAPI service with a vanilla-JS UI. State lives in SQLite under the data directory; downloads land directly in your music library; finished tracks are appended to a rekordbox.xml you import once in Rekordbox.

Use GetSetMix only for content you have the rights to download.

Docker Compose

# deploy/docker-compose.yml — edit the music path first
services:
  getsetmix:
    image: ghcr.io/Fre0Grella/getsetmix:latest
    ports: ["8765:8765"]
    volumes:
      - getsetmix-data:/data
      - /path/to/your/music:/music
    environment:
      GSM_LANGUAGE: en            # or it
      # GSM_AUTH_TOKEN: change-me # enable if exposed
volumes:
  getsetmix-data:
docker compose -f deploy/docker-compose.yml up -d
# open http://localhost:8765

The image bundles ffmpeg and runs as a single process well under 1 GB of RAM. /data holds the SQLite database, config, covers and the default XML; /music is your library root — files are written straight into it.

Kubernetes

Plain manifests live in deploy/k8s/getsetmix.yaml: a namespace, a ConfigMap, PVCs for data and music, a Deployment with liveness/readiness probes and Prometheus scrape annotations, and a Service. An Ingress example is included commented out.

kubectl apply -f deploy/k8s/getsetmix.yaml
kubectl -n getsetmix get pods

Local app mode

For a laptop next to your controller — starts the server and opens the browser, like a desktop app. Prebuilt executables for Windows, macOS and Linux are attached to every GitHub Release (no Python needed; data lives in ~/.getsetmix, ffmpeg still required on PATH). Or run from source:

pip install -r requirements.txt
# ffmpeg must be on PATH:
#   Linux: apt install ffmpeg · macOS: brew install ffmpeg · Windows: winget install ffmpeg
python run_local.py             # opens http://127.0.0.1:8765
python run_local.py --port 9000 --no-browser

Daily workflow

  1. Paste link — the URL on your clipboard is added. Playlists fan out into one editable row per entry; duplicates of URLs you've already ingested are flagged.
  2. Rows resolve metadata in the background. Fix title and artist (both required), pick a genre from the DJ-oriented list, optionally set album. The camera button on the artwork opens cover search and upload.
  3. Download starts the batch. The ×N selector next to it overrides download parallelism for this batch only (the global default is 2). Edits lock once a track is queued.
  4. Watch per-track states and the batch bar. Cancel download stops in-flight tracks and returns them to staged. A failed track shows its error and a retry button — the rest of the batch continues.
  5. When tracks show Done, refresh the rekordbox xml tree in Rekordbox and find them in the Inbox playlist.

Rekordbox setup

One-time configuration in Rekordbox:

  1. Open Preferences ▸ Advanced ▸ Database ▸ rekordbox xml and point it at the XML path shown in GetSetMix settings (default <data>/rekordbox/getsetmix.xml).
  2. In the sidebar, enable the rekordbox xml source (Preferences ▸ View ▸ Layout if hidden).
  3. After each batch, right-click the xml tree and reload — new tracks appear in the configured playlist (default Inbox). Drag them into your collection from there.

Run the service where Rekordbox can see the same file paths the XML contains — e.g. sync the library folder with Nextcloud/Syncthing to your DJ machine, or mount it over SMB, keeping the same drive/path layout, or set the library root to a path that matches on both machines.

The writer is conservative: it appends with incrementing TrackIDs, writes atomically, and if the XML on disk is ever corrupt it backs it up as *.corrupt.xml and starts a fresh one rather than failing the batch.

Keeping the inbox clean with your collection XML

Export your full collection from Rekordbox (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 — matching by file location, with a title + artist fallback — and removes tracks you've already imported. The Inbox playlist then only ever lists songs still missing from your collection, instead of growing forever.

Filename templates

Set the template in Settings or via GSM_FILENAME_TEMPLATE. Available tokens:

TokenValue
{title}Track title (after your edits)
{artist}Artist
{album}Album, omitted when empty
{genre}Genre, omitted when empty
{source}Source site, e.g. youtube
{id}Source video/track ID

Illegal filesystem characters are stripped, separators left behind by empty tokens are collapsed, and name collisions get a (2)-style suffix with a warning on the track.

Configuration

Everything is editable from the gear icon and persisted to <data>/config.json. Environment variables override on boot:

VariableDefaultPurpose
GSM_DATA_DIR./data (/data in Docker)SQLite DB, config, covers, default XML location
GSM_LIBRARY_ROOT./music (/music)Downloads land directly here
GSM_XML_PATH<data>/rekordbox/getsetmix.xmlInbox XML path (new downloads)
GSM_COLLECTION_XML_PATHunsetFull Rekordbox collection XML — enables the inbox purge
GSM_PLAYLIST_NAMEInboxTarget playlist inside the XML
GSM_OUTPUT_FORMATmp3mp3 (320 kbps) or flac — global only
GSM_CONCURRENCY2Global parallel-download default (1–8)
GSM_FILENAME_TEMPLATE{artist} - {title}See filename templates
GSM_LANGUAGEenen or it
GSM_AUTH_TOKENunsetStatic token auth
GSM_BASIC_USER / GSM_BASIC_PASSunsetHTTP Basic auth alternative

Authentication

GetSetMix is private by default — run it on your LAN or tailnet with no auth. If you expose it, set either a static token or Basic credentials. With auth enabled, the API and /metrics return 401 without credentials; the UI asks for the token once and keeps it in the browser.

# any of these work with GSM_AUTH_TOKEN=sekret
curl -H "X-Auth-Token: sekret"        http://host:8765/api/tracks
curl -H "Authorization: Bearer sekret" http://host:8765/api/tracks
curl "http://host:8765/metrics?token=sekret"   # for Prometheus scrape_configs

API

EndpointPurpose
POST /api/tracks {url}Add a URL; playlists fan out. Returns created ids and a duplicate flag.
GET /api/tracksAll rows plus active download count and busy flag.
PATCH /api/tracks/{id}Edit metadata — only while staged or after an error (409 once queued).
DELETE /api/tracks/{id}Remove a row.
POST /api/tracks/{id}/cover · GET …/coverUpload / fetch the cover override.
GET /api/cover-search?q=Cover candidates from the iTunes search API.
POST /api/tracks/{id}/retryRe-queue a failed track.
POST /api/batch/start {ids?, concurrency?}Start a batch; omit ids to take everything staged.
POST /api/batch/cancelCancel; in-flight tracks return to staged.
GET/PUT /api/settingsRead / update settings (validated).
GET /api/stats · GET /api/history · POST /api/purgeCounters, URL history, manual purge.
GET /healthzProbe endpoint, always unauthenticated.

Metrics

GET /metrics serves Prometheus text format. The Kubernetes manifests already carry scrape annotations.

getsetmix_jobs{status="staged|queued|downloading|tagging|ingested|error|…"}
getsetmix_active_downloads
getsetmix_download_duration_seconds_sum / _count
getsetmix_errors_total{source="youtube|soundcloud|…"}
getsetmix_songs_downloaded{window="30d"|"365d"|"all"}
getsetmix_healthy

Handy Grafana starters: songs per month from getsetmix_songs_downloaded, error rate by source, and average download time from the duration sum/count pair.