- JavaScript 62.5%
- Go 19.3%
- Vue 17.3%
- CSS 0.6%
- Makefile 0.2%
|
All checks were successful
ci / web (push) Successful in 1m36s
ci / osv (push) Successful in 45s
ci / trivy-fs (push) Successful in 1m11s
ci / go (push) Successful in 4m11s
ci / hadolint (push) Successful in 39s
ci / app (push) Successful in 3m25s
ci / deploy (push) Successful in 8s
- The target ran a command only from a device in its controller list, so a command beating the hello (after a reconnect) was dropped. A command now attaches its sender: the hub only relays within one user anyway. - Controllers are no longer dropped when they blink out of the device list; only on bye, or after 30 s of failed sends. - State changes are published from a watcher instead of a timer: a background tab throttles timers (1 s, up to 1 min), which left the phone showing a stale play button. - The phone flips play/pause right away; the next snapshot corrects it. - In remote mode the local engine no longer builds the synth on the first touch or when the track list opens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| assets | ||
| cmd | ||
| internal | ||
| testdata | ||
| web | ||
| .dockerignore | ||
| .gitignore | ||
| .golangci.yml | ||
| .hadolint.yaml | ||
| CLAUDE.md | ||
| Dockerfile | ||
| Dockerfile.base | ||
| go.mod | ||
| go.sum | ||
| Makefile | ||
| osv-scanner.toml | ||
| PUBLIC_SHARE.md | ||
| README.md | ||
| renovate.json | ||
VibeScore
Indexes a collection of Guitar Pro (.gpx / .gp / .gp5) files and renders
each one as a simplified lead sheet: chord diagrams, a bar grid, and chords
aligned above the lyrics — all colored by scale degree.
Features
- Search: title, artist, album, tempo, time signature, key, tags — with facets, sorting, pagination, and a "Starred" tab.
- Pattern library: bass/drum patterns under
_patterns/<type>/are indexed apart from the songs: kept out of the library and its filters. - Public read-only sharing:
/share/<token>links (chart + playback only). SeePUBLIC_SHARE.md. - Multi-user practice state: each user keeps their own starred list, MIDI mix/FX, backing volumes, sync, transpose, markdown note and video memos. Song tags are shared but only the adder can delete theirs. Admins can promote any user's setup as the song's shared default, or publish a draft setup in one click.
- Key detection inferred from the chords actually played, not the file's notated signature.
- Song view: scale strip with fingerings, chords colored by degree, bar grid with diagrams lifted from the tab itself, a tablature mode with per-note interval coloring, a rhythm lane in real engraved notes (Bravura font), and the playing techniques stored in the file (hammer-on / pull-off, slides, bends, vibrato, palm mute, let ring, harmonics, grace notes, tapping, accents and staccato).
- Playback: synth bass, a backing track synced to the bar grid, a meter-aware metronome and count-in (per-bar signatures, dotted pulse in 6/8, 9/8, 12/8), auto-scroll, a floating transport with waveform seek and looping, a 5-band master EQ, karaoke lyric highlighting, transposition.
- App-wide player and playlist: playback survives navigation; a persistent player bar and playlist popover manage the queue.
- Remote control: drive a session playing on one device (laptop) from
another one logged into the same account (phone). The header's device
button lists your open sessions; the controller mirrors the transport,
tempo and snapshots, loop, metronome, master EQ and track mute/solo while
the audio stays on the target. Relayed by the server over SSE
(
/api/remote/*), only between devices of the same user. - In-browser MIDI synth (SpessaSynth): upload
.sf2/.sf3/.dlssoundfonts, optional server-side.sf3compression, a pedalboard FX chain per channel (compressor, Tube Screamer, amp sim with Fender Deluxe Reverb / Blues Junior / Vox AC30 / Marshall Plexi voicings and cabinet, modulation, EQ, delay, reverb), named presets (save, rename, overwrite, delete). The instrument picker applies each pick live and stays open; mixer edits are kept only once saved ("Save mix"), a reload brings back the saved mix. - Multitrack import: a multichannel file (
.ogx, unencrypted Rock Band.mogg, any audio with more than 2 channels) is split server-side with ffmpeg into one stem per instrument: silent channels dropped, stereo pairs kept together, stems named by channel for renaming. - Stem archives: a
.zip/.tar.gzof audio files uploads as one track per supported file (named after it); other members are skipped. - Per-channel MP3 overrides: bind a backing stem to a MIDI channel, synced and loudness-matched.
- Global tempo map: a waveform sync editor (offset/tempo/section markers, START/END points) drives MIDI and MP3 playback together.
- Per-song extras: favourite, bar annotations, fingering overrides, display transposition, markdown note.
- MIDI generation on demand via MuseScore when no
.midexists. - Mobile-friendly, light/dark theme, download original source file.
- Extensible formats: container decoding (BCFZ/BCFS/zip/raw gpif) and GPIF parsing 100% in Go — single static binary, no cgo.
Install
Requires Go ≥ 1.26 (pinned in go.mod) and Node ≥ 18.
Optional, feature by feature: MuseScore for .gp* → .mid generation
(--mscore) and ffmpeg for .sf2 → .sf3 compression. Both are
detected at startup; missing just disables that feature.
# 1. Build the frontend (embedded into the server binary)
make web
# 2. Index the collection (incremental on later runs)
make index ROOT=~/path/to/GuitarPro DB=vibescore.db
# 3. Run the server (API + UI) on http://localhost:8080
make serve ROOT=~/path/to/GuitarPro DB=vibescore.db
The server needs
--root: the database stores only a relative path and reconstructs the absolute path from--root(so the database is portable).
Server flags
Every flag below also reads an environment variable of the same name in caps
with underscores where noted; --help lists them all.
| Flag | Default | What it does |
|---|---|---|
--db |
vibescore.db |
SQLite index database |
--root |
— | collection root (also enables POST /api/reindex) |
--addr |
:8080 |
listen address |
--backing |
<db dir>/backing |
uploaded backing stems |
--soundfont-dir (SOUNDFONT_DIR) |
<db dir>/soundfonts |
uploaded .sf2/.sf3 banks |
--uploads-dir (UPLOADS_DIR) |
<root>/_uploads |
where UI-uploaded scores land when the root is read-only |
--mscore (MSCORE) |
— | MuseScore binary for .mid generation (empty disables it) |
--max-upload (MAX_UPLOAD) |
2G |
max request body size — raise it for large soundfonts |
--dev |
off | permissive CORS for the Vite dev server |
Identity comes from a trusted SSO proxy in front of the app; the header names
and the group→role mapping are configurable:
--auth-user-header (Remote-User), --auth-name-header (Remote-Name),
--auth-groups-header (Remote-Groups), --auth-ro-group / --auth-rw-group
/ --auth-admin-group (comma-separated, each also an env var) and
--auth-logout-url for the link the UI redirects to.
Docker
Dockerfile is multi-stage: it builds the SPA and a static, cgo-free server
binary, then copies it into a prebuilt base image (Dockerfile.base: Debian
slim + headless MuseScore under xvfb + ffmpeg). Build the base once, then
the app:
docker build -f Dockerfile.base -t jcosmao/vibescore-base . # rarely
docker build -t vibescore .
docker run -p 8080:8080 \
-v ~/path/to/GuitarPro:/music:ro \
-v vibescore-data:/data \
vibescore
- Mount the collection at
/music(read-only is fine). - The
/datavolume holds the index database, uploaded backing stems and uploaded soundfonts. make docker/make docker-base/make docker-allwrap the same builds and pushes (Docker Hub,jcosmao/vibescore).- CI builds and pushes the image on every push to master
(
.forgejo/workflows/ci.yml, under kaniko — the runner has no Docker daemon). The base image has its own manual workflow,.forgejo/workflows/docker-base.yml. - First run: the index is empty — click Réindexer in the UI (or
POST /api/reindex) to scan the collection.
Frontend development
make dev # terminal 1: Go API with permissive CORS (:8080)
cd web && npm run dev # terminal 2: Vite (proxies /api → :8080)
Checks
make test # Go test suite
make security # govulncheck, gosec, golangci-lint, npm audit (mirrors CI)
make smoke # drives a headless browser through Play, fails on any JS error
make smoke is the only check that executes the page — it indexes
testdata/ into a throwaway database, opens a song, clicks Play and fails on
any exception. Needs chromium or chrome on PATH (or CHROME=/path/to/binary).
API
REST API under /api. With the server running, the interactive docs (Redoc)
are at http://localhost:8080/api/docs, and the raw spec at
/api/openapi.yaml. Source schema:
internal/api/openapi.yaml.
