- JavaScript 65.4%
- Vue 17.3%
- Go 16.4%
- CSS 0.6%
- Makefile 0.2%
A secret copied with the newline still arrives non-empty, so the preflight called it present and the API answered 401 — indistinguishable from an expired token. The check now names that case, which is the likeliest reason a token that was just created is already rejected. Co-Authored-By: Claude Opus 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 by title, artist, album, tempo, time signature, key, and user-assigned tags (e.g. acoustic, chant — filtered with AND semantics) — with facets, sorting, pagination, and a "Starred" (favourites) tab — the default tab when the app opens.
- Multi-user practice state: identity comes from the SSO proxy headers.
Each user (read-only included) keeps their OWN starred list and per-song
practice overlays — MIDI mix + FX, backing-track volumes/mutes/offsets,
backing sync, transpose — layered over the song's shared defaults. Admins
get a "defaults…" picker on the song page listing every user's saved
version per kind, and can promote one as the shared default (what everyone
without an override gets). Library content (files, tags, notes, stems,
bindings) stays writer-gated. One-shot migration of the legacy global
starred flags:
-migrate-starred-to=<user>(orMIGRATE_STARRED_TO). Each user also keeps a private markdown note per song next to the shared one, and video memos are per-user property: anyone (read-only included) uploads their own clips — visible to all, labelled with the uploader — but only the uploader or an admin can rename/delete them. Non-admins can request their setup as the song default ("→ proposer comme défaut" in the song mixer panel); an admin sees pending requests (Config page and 🎧 chips on the song panels), listens to the song with the requester's settings (/song/:id?as=<user>, admin-only preview with an accept/reject banner) and one-click promotes mix/FX, sync, stem volumes and transpose as the shared default. - Key detection inferred from the chords actually played (diatonic coverage: IV–V vamps, minor keys), not the file's notated signature.
- Song view:
- the key's I→VII scale with fingerings;
- song chords annotated with their degree (roman numerals), sorted by degree and colored per degree (adapts to the theme);
- bar grid:
%for a repeated chord,𝄽for a chordless bar, hover for the diagram — the fingering is lifted per occurrence from the tab itself (the actual voicing played at that spot), falling back to the file/library shape when the tab holds no clear chord there; hovering a chord in tablature mode shows the same diagram; - chords aligned to the changes above the lyrics.
- Playback: tunable synth bass (presets), a real backing track synced to the bar grid, metronome, auto-scroll, a floating mini-transport that stays reachable while the sheet scrolls (SoundCloud-style waveform seek bar with color-coded section labels for one-click jumps, shift-drag range looping and a large bar/timecode readout), and karaoke lyric highlighting that follows the bars. The transport is tinted by the active source (MIDI/track) and view (chords/tab); in tab mode a section strip sits right above it.
- In-browser MIDI synth (SpessaSynth): upload .sf2 / .sf3 / .dls sound banks (SFZ isn't supported by the synth — convert to sf2 with Polyphone). Fonts are typed GM (a full General-MIDI set — eligible as the one global default that renders every song) or specific (a subset: one voice/ instrument — override-only, never the default); the type auto-detects from the preset coverage and can be forced in Config. Pick any preset of any uploaded bank per mixer channel via a fuzzy-search modal. Each mixer row keeps only volume + pan; all tone/FX shaping lives in the pedalboard insert (FX button): comp/sustain → drive → tube → modulation (chorus/tremolo/phaser) → 3-band EQ → volume → delay → reverb (hall/plate/spring), patched into that channel's audio path only, each pedal toggleable with its own knobs. A row's whole sound (instrument + pedalboard) can be saved as a named preset, stored server-side and reusable on every song.
- Per-channel MP3 overrides: bind an existing backing stem to any MIDI mixer channel (e.g. the voice) — the synth channel mutes and the stem plays mixed into MIDI playback, kept in sync on one clock (pitch-preserving under tempo changes, with a per-track offset nudge). Unbind to return it to the backing panel. Overrides ride the MIDI master volume and get an automatic loudness makeup (quiet stem exports are boosted to compete with the synth's mastered level; the row's volume slider stacks on top).
- Global tempo map: the backing-sync grid (offset / tempo / section markers) is the song's one timeline — MIDI playback is rate-warped bar by bar to follow it, so synth channels and MP3 overrides stay locked together. The transport shows the map's average tempo (total beats over the real timeline, so it matches the song exactly rather than the base bpm), and that overrides the GP's embedded tempo on MIDI too. A waveform sync editor (🌊 in the mixer panels) shows the audio Audacity-style with the bar/beat grid, sections and chords overlaid: click a transient to pin the nearest bar there (per-bar tempo-map resolution), right-click to remove a marker, drag a line to fine-adjust, shift-drag to slide the whole grid, wheel to zoom, click the labels/ruler to seek. Edits stay a draft until Save applies them to the song.
- Per-song, without touching the source file: favourite, per-bar annotations (text + custom diagram), fingering overrides, display transposition (±11) that MIDI playback and the backing audio follow (SoundTouch pitch-shift, tempo preserved), markdown note.
- Download the original source file.
- Light / dark theme, persisted.
- Extensible formats: container decoding (BCFZ / BCFS / zip / raw gpif) and GPIF parsing 100 % in Go, no cgo → single static binary.
Install
Requires Go ≥ 1.21 and Node ≥ 18.
# 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).
Docker
A multi-stage Dockerfile builds the SPA and a static server binary (no cgo)
into a small Alpine image.
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 and uploaded backing tracks. - 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. npm run build
accepts an undeclared identifier and go test never opens a browser, so a
missing import can kill playback on every platform while both stay green — which
is exactly what happened once. It indexes testdata/ into a throwaway database,
serves it, opens a song, clicks Play and fails on any exception. It needs
chromium or chrome on PATH (or CHROME=/path/to/binary) and skips loudly
without one. It does not verify that sound comes out: the repo carries no
soundfont, so it stops at "no soundfont installed".
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.
