Files
RadioDJ-Album-art-downloader/README.md
T
minster586 b9a2cc584b Main Upload
This is the start upload I figured this is a way for me to easily tweak with radio DJ and get the album art
2026-09-09 05:03:17 -04:00

6.9 KiB

RDJ Art Manager 🎵

A production-ready, open-source web app to find, review, and apply missing album artwork for a RadioDJ MySQL database.

  • 🔍 Free artwork lookup — iTunes Search API first, MusicBrainz + Cover Art Archive fallback. No API keys, no credit cards.
  • 🗂 Multi-user with roles — embedded SQLite (better-sqlite3) stores accounts and per-user RadioDJ connection profiles. First registered user becomes the admin.
  • 🔐 Secure by default — scrypt password hashing, AES-256-GCM encrypted DB passwords, HttpOnly session cookies, rate-limited auth endpoints.
  • 🎛 Spreadsheet-style review grid — preview artwork candidates per track, pick the right cover, filter, auto-search.
  • 📦 ZIP export — images renamed to the base audio filename (01 Track.mp301 Track.jpg), plus an export-report.json manifest.
  • 🗄 Optional DB update — sets songs.image in the RadioDJ database to the new file name.
  • 🐳 Docker — one docker compose up with ./data persisted.

Quick start

Local (Node.js ≥ 20)

npm install
npm start
# open http://localhost:3000

Docker

docker compose up -d --build
# open http://localhost:3000

SQLite data is persisted in ./data (mapped to /app/data in the container).

Configuration

Env var Default Description
PORT 3000 HTTP port.
DATA_DIR ./data Where the SQLite database lives.
APP_SECRET (auto) Set this in production (min 16 chars). Encrypts stored RadioDJ DB passwords. If unset, a random secret is generated at data/.app-secret. Changing it makes stored DB passwords unreadable.
COOKIE_SECURE 0 Set to 1 when serving over HTTPS so session cookies get the Secure flag.

Usage

  1. Register — the first account becomes the admin.
  2. Open Profiles and add a RadioDJ MySQL connection (host, port, database, user, password). Use Test to verify connectivity — it also reports how many tracks are missing artwork.
  3. Pick the station in the header dropdown and click Load tracks missing artwork. The query matches tracks where song_type = 0 and image is NULL, empty, or a RadioDJ default placeholder (%no_cover%, %no_image%, %no-cover% — case-insensitive). Placeholder rows are flagged in the grid with an amber “placeholder art” badge.
  4. Pick a primary art source in the header (Auto chains iTunes → Deezer → MusicBrainz, or pin a single API), use the grid pagination bar (page size 10/25/30/50/all, prev/next, numbered pages), then click Search artwork per row (or Fetch Art for Current Page for the whole page) to get artwork candidates. Hover a thumbnail to see its source (native tooltip), click it to open the full-size preview overlay (title, artist, source, “View Original Image”, and ✓ Confirm & Select This Art — close with backdrop click or Esc). A row only becomes exportable once you tick its checkbox; fetching art never checks the box for you.
  5. Click Download ZIP & Update Database:
    • Uncheck Update RadioDJ DB to only download the ZIP.
    • When checked, the app runs UPDATE songs SET image = '<new file name>' WHERE id = <song id> for every exported item.
  6. Copy the images from the ZIP into the folder configured in RadioDJ under Options → Options → Album Art, so RadioDJ picks them up.

Note: RadioDJ's songs.image column stores just the artwork file name (relative to its album-art folder). That is exactly what this app writes.

Admin

The admin header button opens the administration panel:

  • List all users and change roles (useradmin). You can't change your own role or demote the last admin.
  • Copy (clone) all station profiles from one user to another — encrypted passwords carry over.

API overview

Method Endpoint Description
POST /api/auth/register Register (first user becomes admin).
POST /api/auth/login / logout Session auth (HttpOnly cookie).
GET /api/auth/me Current session user.
GET/POST /api/profiles List / create RadioDJ connection profiles.
PUT/DELETE /api/profiles/:id Update (blank password keeps stored one) / delete.
POST /api/profiles/:id/test Test MySQL connectivity + count missing artwork.
GET /api/tracks?profileId=&page=&limit= Tracks missing artwork (song_type=0 and image NULL/empty or a no_cover/no_image/no-cover placeholder); limit=all disables paging. Returns {tracks,totalTracks,page,totalPages}.
POST /api/artwork/search {artist,title,album,source} — source auto chains iTunes → Deezer → MusicBrainz/CAA; or itunes/deezer/musicbrainz only.
POST /api/export {profileId,updateDb,items[]} → ZIP download + optional DB update.
GET /api/admin/users (admin) List users.
PUT /api/admin/users/:id/role (admin) Change role.
POST /api/admin/clone-profiles (admin) Copy all profiles between users.
GET /api/health Health check (used by Docker HEALTHCHECK).

Project layout

├── server.js            # Express backend: auth, profiles, MySQL pools, artwork search, ZIP export
├── public/index.html    # Single-page Tailwind dashboard (vanilla JS)
├── data/                # SQLite storage (git-ignored, volume-mapped in Docker)
├── Dockerfile
├── docker-compose.yml
├── smoke-test.ps1       # Optional API smoke test (run while the server is up)
└── package.json

Security notes

  • Passwords: scrypt (N=16384) + constant-time comparison.
  • RadioDJ DB passwords: AES-256-GCM encrypted at rest; never returned by the API.
  • Sessions: random 256-bit tokens in SQLite, HttpOnly + SameSite=Lax cookies, 7-day expiry.
  • Auth endpoints are rate-limited; all API routes require authentication; admin routes require the admin role.
  • SQL access is parameterized; the RadioDJ connection user only needs SELECT/UPDATE on the songs table.

License

MIT — see LICENSE.