Skip to content
Public-Vinyl-RadioPublic

About

A self-hosted vinyl collection manager for DJs.

Topics

Resources

Code of conduct

Stars

7 stars

Watchers

0 watching

Forks

Latest commit

 

History

1,050 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GrooveNet logo

GrooveNet

A self-hosted vinyl collection manager for DJs.

Latest release MIT license Last commit Tests

GrooveNet imports your Discogs collection, enriches it with streaming metadata, analyzes audio for BPM, key, and mood, and helps you build better DJ playlists. Use it in the web or iOS app, from the groovenet CLI, or through the MCP server.

Screenshots

Collection search and track list
Collection
Albums view
Albums
Playlists view
Playlists
Listening history view
Listening history

Run GrooveNet

The simplest self-hosted install needs Docker Compose v2.

git clone https://github.com/Public-Vinyl-Radio/groovenet.git
cd groovenet

cp .env.example .env
# Edit .env: at minimum, set POSTGRES_PASSWORD and your Discogs values.

docker compose -f docker-compose.yml -f docker-compose.local.yml up --build -d
docker compose -f docker-compose.yml -f docker-compose.local.yml run --rm migrate

Open http://localhost:3000, then use Sync from Discogs to import your collection. The stack stores its database, audio, exports, backups, and cookies in Docker volumes.

Required configuration

Set these values in .env before starting:

Variable Purpose
POSTGRES_PASSWORD A strong password for the bundled database
DISCOGS_USER_TOKEN Personal access token from Discogs developer settings
DISCOGS_USERNAME Your Discogs username
DISCOGS_FOLDER_ID 0 for your full collection, or a specific folder ID

Apple Music, YouTube, OpenAI, backup, and analytics settings are optional. See .env.example for the complete list and descriptions.

Everyday commands

# Follow service logs
docker compose -f docker-compose.yml -f docker-compose.local.yml logs -f

# Stop the stack; your data remains in Docker volumes
docker compose -f docker-compose.yml -f docker-compose.local.yml down

# Update source, rebuild, and restart
git pull
docker compose -f docker-compose.yml -f docker-compose.local.yml up --build -d
docker compose -f docker-compose.yml -f docker-compose.local.yml run --rm migrate

The local override binds the app to 127.0.0.1:3000. Set APP_PORT=3001 in .env to use another port. Put GrooveNet behind your own reverse proxy to make it available on a network or public domain.

Features

  • Collection search — fast full-text and fuzzy search with filtering and infinite scroll.
  • Metadata enrichment — link Discogs releases to Apple Music, YouTube, and SoundCloud; bulk-edit and complete metadata with AI.
  • Audio intelligence — analyze downloaded audio with Essentia for BPM, key, and mood.
  • Playlist tools — generate transitions based on BPM, key, mood, and genre.
  • Collection sharing — browse friends’ collections and keep a listening history.
  • Automation — manage the collection through the CLI or MCP server.
  • Data ownership — self-hosted PostgreSQL, backups, restore, and optional remote storage.

Clients and integrations

GrooveNet also includes an iOS SwiftUI app for connecting to a self-hosted server. It currently supports server setup, playlist browsing and generation, and local track downloads. See the iOS app README for Xcode setup and current scope.

Install the CLI from npm:

npm install -g @groovenet/cli
groovenet config set api_base http://localhost:3000/api
groovenet tracks search "acid house" --bpm-min 120 --bpm-max 135

The CLI README covers all commands. The MCP server README explains how to connect GrooveNet to an MCP client.

Development

For contributing or running the hot-reload stack, install mise and just:

mise install
just bootstrap
just compose-dev
just migrate-up

Node 26 and npm 12.0.0 are selected by .mise.toml. The exact engines.npm pins in the root and app manifests must match it: the shared .github/actions/setup-npm action installs the root pin for every JavaScript dependency job, including publishing, and the Docker base installs the app pin for all stages (deps, builder, runner and migrator). New CI install jobs should use that action. Docker publishing calls the same test workflow before building.

engines.npm is advisory under npm's default configuration; it does not install or enforce npm by itself. Bootstrap and CI run node scripts/check-npm-toolchain.mjs before dependency installs to fail on manifest/mise/runtime drift, and Docker asserts its installed version. There is no Corepack/packageManager convention here; mise selects the local executable. When updating npm, change all three pins together and the npm entry in mise.lock via mise, then verify clean root and app installs plus Docker's deps and migrator targets. Run the drift-check tests with mise exec -- node --test --experimental-test-coverage --test-coverage-include=scripts/check-npm-toolchain.mjs scripts/npm-toolchain.test.mjs.

Bootstrap, package tests and dev-container startup use strict npm ci, which must leave tracked lockfiles unchanged. The root lockfile owns packages/* and mcp-server; the app has its own lockfile. The legacy mcp-server/package-lock.json is not used by workspace installs. Dependabot's configuration does not force its npm version: its PRs must pass actual installs under our pin. Pinning cannot repair dependency/lockfile inconsistencies; fix those separately under the pinned npm without unrelated dependency updates.

just bootstrap installs a git pre-commit hook that runs file hygiene checks, ruff over the Python services and Python client, and ESLint over changed web and Node package files. The same hooks run in CI, so the hook only moves the feedback earlier — skip it with git commit --no-verify and CI will still catch it.

Useful checks:

just test
just lint          # web app, client, CLI and MCP server
just typecheck
just lint-all      # every pre-commit hook, over the whole repo

Worktree setup, deployment, releases, and operator-specific overrides are maintainer workflows; see AGENTS.md, RELEASING.md, and the package-level READMEs when you need them.

Storybook

Every app image builds and verifies a fresh static Storybook bundle from its checkout before building Next.js, then packages it in public/storybook. With ENABLE_DEVELOPER_TOOLS=true, the Developer Tools card opens /storybook/index.html on the running app. CI candidates, promoted releases, self-hosted builds and local deployment builds use the same Dockerfile; none needs a separate host build. This adds a Storybook build and about 10 MB of static assets to app images; the migrator target does not build Storybook.

For component development, run just storybook in a separate terminal. It uses the pinned toolchain, serves at http://localhost:6006, and needs no Compose services. To use the Developer Tools link in a hot-reload app, set ENABLE_DEVELOPER_TOOLS=true and STORYBOOK_URL=http://localhost:6006 in .env. When using the 1Password template, export STORYBOOK_URL=http://localhost:6006 before just compose-dev or just worktree-up. Restart the app after changing its environment. Multiple worktrees can run their own server with mise exec -- npm run storybook --prefix my-collection-search -- --port 6007 and a matching URL. For remote browsers or HTTPS apps, use an accessible URL and matching scheme instead of the browser's localhost.

STORYBOOK_URL remains a server-side runtime setting: root-relative paths and HTTP(S) URLs are supported without rebuilding the image. An empty value hides the card (the bundle remains available); ENABLE_DEVELOPER_TOOLS gates the Developer Tools page/navigation, not static assets. Same-origin Storybook goes through the app's existing reverse proxy and its access controls; an external URL uses that server's access controls.

STORYBOOK_STATIC_DIR is retired and ignored by production Compose. Remove it from existing deployment environments. The image's bundle is authoritative; mounting a host directory over /app/public/storybook would hide it. To serve an independently managed catalogue, host it separately and set STORYBOOK_URL to its URL. Recreate the app with the updated Compose files to remove the old mount; pulling an image alone does not change an existing container's mounts.

just build-storybook optionally builds and verifies my-collection-search/public/storybook for local static inspection. This output is ignored by Git and Docker so it cannot contaminate a release build. Run mise exec -- npm run test:storybook --prefix my-collection-search for bundle validator coverage and merged Compose regression checks (Docker Compose is required for those checks).

Python API client

packages/groovenet-python is a small hand-written groovenet_client package used by download-worker and fingerprint-service. App requests share a requests.Session, send X-Groovenet-Client: worker, and raise GroovenetApiError for every non-2xx response. Services own retries and best-effort delivery. Both Docker builds use the repository root as context.

When a Python worker needs an endpoint, add its payload type and thin method, then exact-request and error tests; see the client guide. just generate-spec updates the OpenAPI contract used by the path/verb tests; no Python code is generated.

just test-python-client

Contributing

Contributions are welcome. Please open an issue to discuss larger changes, keep migrations with schema changes, and run the checks above before opening a pull request. See the license for reuse terms.

License

GrooveNet is released under the MIT License.

About

A self-hosted vinyl collection manager for DJs.

Topics

Resources

Code of conduct

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages