Repository navigation
Draft
Conversation
Vynull is a headless daemon and the API is the real contract every client builds against (the web UI, the overlay, and MPRIS are all just clients). Document it so a separate frontend, native or otherwise, can be built against a contract instead of reading the Go handlers. docs/api/openapi.yaml covers the full surface: ~54 paths / 73 operations across status/players, the library (tracks, edits, playlists, tags), playback, analysis (waveforms, cues, beatgrids, artwork), import/export, settings/menu, overlay, device link, diagnostics, and the file browser. Schemas are transcribed from the Go response structs. It also states the posture for frontend authors: no auth (trusted LAN; bind 0.0.0.0 for cross-device), poll-based live updates (beat_age_ms for playhead interpolation), partial CORS, and the raw-bytes endpoints (artwork, waveform-png, preview). Validated: YAML parses, every schema ref resolves.
Add GET|HEAD /api/audio/{trackID} to the OpenAPI spec: serves a library
track's audio by ID with Range/seek support, native formats direct and
others transcoded to WAV, and the X-Audio-Source header. It's the audio
primitive a separate frontend (native app, phone remote) can build on.
Make the HTTP API self-describing. New endpoints, always on (not gated on --web) so even a headless daemon documents itself: GET /api/openapi.yaml the spec GET /api/docs a browsable Swagger UI /api/swagger/* its assets Swagger UI is vendored (swagger-ui-dist 5.17.14, Apache-2.0, in docs/api/swagger/) and embedded, so the docs page pulls nothing from a CDN and works offline on the link-local network, matching the rest of the UI. The spec lives in docs/api/ as its canonical home; a small apidocs package there embeds it and the assets. Adds ~1.6MB to the binary. Verified live: all four endpoints serve, and the docs page references only local /api/swagger/ assets (no external calls).
Wrap the API mux in a CORS middleware so every endpoint sends permissive CORS (Access-Control-Allow-Origin: *, methods, Content-Type) and answers preflight OPTIONS uniformly — instead of the former per-handler patchwork where some endpoints set it and some (e.g. /api/status) didn't, and most POST endpoints had no preflight at all. This lets a browser frontend on another origin call the whole API. Vynull is a trusted-LAN tool with no auth, so this only makes the already open posture consistent. Existing per-handler CORS lines remain correct (the middleware sets the same header via Set, so no duplication — verified) and are now redundant; they can be removed in a later cleanup. Spec's CORS note updated to match.
Add GET /api/events, a Server-Sent Events stream pushing live deck +
now-playing state, so a frontend gets real-time updates instead of polling
/api/players at ~1 Hz. Each message is an 'event: state' carrying
{players, nowplaying} (the same shapes those endpoints return), sent on
change — effectively ~4 Hz while a deck is active (beat_age_ms keeps
moving) and nothing while idle, with a heartbeat to hold the connection.
A client interpolates the playhead between events with beat_age_ms, so a
few per second render smoothly. CORS applies via the mux middleware.
Documented in the spec; the 'live updates' note now points at the stream.
Verified live: text/event-stream, initial snapshot primed on connect.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Vynull is a headless daemon, and the HTTP API is the real contract every client builds against. The bundled web UI, the now-playing overlay, and MPRIS are all just clients of it. This PR makes that explicit and gives anyone the pieces needed to build a separate frontend (a native app, a phone remote, an alternative UI) without reading the Go handlers.
Four parts, each a self-contained commit:
An OpenAPI 3 spec (
docs/api/openapi.yaml). Full coverage of the API, ~56 paths across status/players, the library (tracks, per-track edits, playlists, tags), playback, analysis (waveforms, cues, beatgrids, artwork, audio), import/export, settings/menu, overlay, device link, diagnostics, and the file browser. Schemas are transcribed from the actual Go response structs. It also states the posture a frontend author needs up front: no auth (trusted LAN; bind0.0.0.0for cross-device), the live-update options, and the raw-bytes endpoints.A self-hosted Swagger UI, served alongside the spec. New endpoints, always on so even a headless daemon with
--weboff documents itself:GET /api/openapi.yamlthe specGET /api/docsa browsable Swagger UI (including "Try it out" against the live API)/api/swagger/*its assetsSwagger UI is vendored (swagger-ui-dist 5.17.14, Apache-2.0, under
docs/api/swagger/) and embedded, so the docs page pulls nothing from a CDN and works offline on the link-local network, matching how the rest of the UI bundles its fonts and assets. This adds about 1.6 MB to the binary.Uniform CORS. A middleware wraps the API mux so every endpoint sends permissive CORS (
Access-Control-Allow-Origin: *, methods,Content-Type) and answers preflightOPTIONSuniformly, instead of the former per-handler patchwork where some endpoints set it and most POST routes had no preflight at all. Since Vynull is a trusted-LAN tool with no auth, this only makes the already-open posture consistent so a browser frontend on another origin works. The old per-handler CORS lines remain correct (the middleware sets the same header, so no duplication, verified) and are now redundant; a later cleanup can drop them.An SSE live-state stream (
GET /api/events). A Server-Sent Events stream pushing live deck + now-playing state, so a frontend gets real-time updates instead of polling/api/players. Each message is anevent: statecarrying{players, nowplaying}(the same shapes those endpoints return), sent on change, effectively a few Hz while a deck is active and a heartbeat when idle. A client interpolates the playhead between events withbeat_age_ms.Net effect: a frontend author gets a browsable contract, open CORS, audio streaming (from #56), and a real-time state stream. That is the foundation the "separate frontends" idea needs.
Hardware testing
OPTIONSpreflight are present on every endpoint with no duplicate headers; the SSE stream istext/event-streamand primes the client on connect.Checklist
go build ./...,go vet ./..., andgo test ./...passgofmt -l .is cleanGPL-3.0-or-later); vendored Swagger UI is Apache-2.0 (docs/api/swagger/LICENSE)