Skip to content
Fre0GrellaPublic

About

Self-hosted DJ ingestion service that discovers, downloads, tags, and imports tracks into Rekordbox. Supports YouTube, SoundCloud, and more.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

GetSetMix

Get it when you hear it. Set it with one thumb. Mix it when you play.

Download GetSetMix Setup · Docs · How Setup works

CI Release Latest release

Get: share a video to GetSetMix from the phone's share menu. Set: GetSetMix opens with the track, pick a genre. Mix: the track is in Rekordbox's GetSetMix playlist, downloaded and tagged.

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.

How it works

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.

Features

GetSetMix on a computer: a batch downloading, tracks ready to review, one already in the library   GetSetMix on a phone: a track opened for editing, with one-tap genre picks

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

Install

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.

Quickstart — Docker Compose

# 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:8765

Kubernetes (first-class)

Plain 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 pods

Notes:

  • replicas: 1 with strategy: Recreate is intentional — state is SQLite on a PVC.
  • Mount your real music library into the music PVC (or replace it with hostPath/NFS as your cluster dictates).
  • A Helm chart is not included; the manifests are small enough to kustomize directly.

Local app mode (Linux / Windows / macOS)

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-browser

YouTube 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 the yt-dlp-ejs scripts that yt-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/status reports both.

Using it

  1. 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.
  2. 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.
  3. Press Download to start the batch (the ×N selector overrides parallelism for this batch only). Edits lock once a track is queued.
  4. Watch per-track progress and the batch bar; cancel anytime (in-flight tracks are stopped and re-staged).
  5. 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.

Skip the XML: gsm-companion

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 install

To 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.

Share to GetSetMix (Android)

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.

  1. 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-manager tls: example). Any HTTPS reverse proxy works too.
  2. Install it. Open the HTTPS URL in Chrome/Edge on your Android phone → menu → Install app / Add to Home screen.
  3. 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.

Configuration

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.

Metrics

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.

Development, CI/CD & releases

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 rails

Conventional Commits are mandatory

Commit 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.

CI/CD workflows (.github/workflows/)

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:

  1. Replace Fre0Grella with your GitHub username in deploy/k8s/getsetmix.yaml, site/*.html, and this README (grep -rl Fre0Grella .).

  2. Settings ▸ Pages ▸ Source: GitHub Actions to enable the site.

  3. After the first publish, Packages ▸ getsetmix ▸ Package settings ▸ Change visibility if you want the image public (no imagePullSecrets needed in K8s).

  4. (optional) Settings ▸ Branches → protect main and require the Commit lint check so only Conventional Commits can land.

  5. If main requires signed commits, give the release bot a signing key so its version-bump commit is accepted (run once, locally, with gh authenticated):

    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_email in release.yml to 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.

Project layout

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

API sketch

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

About

Self-hosted DJ ingestion service that discovers, downloads, tags, and imports tracks into Rekordbox. Supports YouTube, SoundCloud, and more.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages