Files
muzick/05-music-frontend.md

5.0 KiB

music — frontend spec

React (vite). Dark theme by default, blue accent. Web UI is v1; backend is API-first so a mobile app can reuse the same endpoints later.

Theme

  • Dark by default. Blue accent (--accent: #2D7FF9 or similar; pick one and define tints like kdrive's accent system).
  • Reuse the design-token approach from kdrive (CSS vars, Geist font). This is a distinct app with its own palette but shared visual language.
  • Animations: tasteful — now-playing bar slide-up, queue/lyrics panel slide-in, album art crossfade on track change, subtle hover/press states, progress bar smoothing, shuffle/loop button state transitions.

Layout

  • Left sidebar: nav (Artists / Albums / Songs / Favorites / Recently played / Vibe), profile/avatar (opens settings modal).
  • Main: list/grid views per section.
  • Bottom now-playing bar (persistent): cover thumb, title, artist + feats, prev / play-pause / next, volume slider, shuffle toggle, loop toggle (off → all → one), progress/seek bar, lyrics button, queue button.

Views

Artists

  • List/grid of artists (image if enriched, else monogram). Click → artist page: header (image, name, genres), albums, all tracks.

Albums

  • Grid of album covers (sort: name / year / artist). Click → album page: cover, title, artist, year, ordered tracklist, play / shuffle album.

Songs

  • Virtualized list (library can be large): title, artist, album, duration, play count, favorite toggle. Sort by title/artist/added/plays. Fuzzy search box.

Favorites / Recently played

  • Favorites: from /api/favorites. Recently played: from /api/history.

Vibe

  • "Start a vibe" from current track / an artist / a genre. Calls /api/vibe. Shows the mixed queue; recommendation candidates are visually tagged subtly (small dot/"suggested") but interleaved, not grouped.

Player controls (now-playing bar)

  • Global shuffle button (prominent, maybe in topbar or sidebar too): one tap → GET /api/shuffle/all → loads entire library shuffled as queue, starts playing. The headline feature.
  • Prev / Play-Pause / Next.
  • Loop toggle: cycles off → loop-all (current queue/album) → loop-one (single track). Distinct icons per state.
  • Volume slider.
  • Seek bar: draggable, shows elapsed/total.
  • Cover + title + artist + feats: feats rendered subtly after artist (e.g. "Artist · feat. X, Y").
  • Lyrics button: opens lyrics panel.
  • Queue button: opens queue panel.

Playback engine (client)

  • Web Audio / <audio> with /api/stream/{id} (range-enabled).
  • Playback modes from prefs:
    • basic: sequential.
    • gapless: preload next track, start without silence (dual audio elements or Web Audio buffering).
    • crossfade: fade out current / fade in next over crossfade_ms.
    • interstitial: play the configured interstitial track between every song (the gag mode).
  • Log plays to POST /api/history with completed once threshold reached (≥50% or final 10s).

Lyrics panel

  • Slide-in panel. Calls /api/lyrics/{track_id}.
  • Synced (LRC): auto-scroll, highlight active line, tap a line to seek.
  • Falls back to plain text or "no lyrics found".
  • Provider chain handled server-side (Musixmatch → LRCLIB), frontend just renders.

Queue panel

  • Slide-in. Shows prev tracks (history within session) and next tracks (upcoming).
  • Reorder (drag), remove, jump-to.
  • Queue resets on session end (no persistence — per the decision).

Favorites / dislike

  • Heart toggle on tracks/now-playing → POST /api/favorite/{id}.
  • Dislike control (e.g. thumbs-down in now-playing context menu) → POST /api/dislike/{id}. Track hides immediately. A toast confirms.
  • When a dislike reminder fires (server sweep), the app surfaces a toast: "You disliked '' — deleting in 24h. Undo?" with an Undo action calling DELETE /api/dislike/{id}. (Also delivered via ntfy out-of-app.)

Settings modal (avatar click)

  • Appearance: theme (dark default, allow light), blue accent + maybe a couple alt accents.
  • Playback: mode (basic/gapless/crossfade/interstitial), crossfade ms (when crossfade), interstitial track picker (when interstitial).
  • Recommendations: enable/disable vibe acquisition, max candidates (read-only display of the cap), Last.fm key field, MusicBrainz UA.
  • Library: reindex button (POST /api/library/reindex), enrich button (POST /api/enrich).
  • Persist via /api/prefs.
  • Global fuzzy search (topbar) → /api/search, grouped results: Artists / Albums / Songs. Handles Cyrillic/mixed scripts (display original, match normalized).

API-first note

  • All state lives behind the documented endpoints so a future mobile client reuses them. No frontend-only business logic that the API can't reproduce. Keep auth simple for v1 (WG-gated, like the rest); leave room for token auth later for mobile over the tunnel.

Deploy

  • Same pattern as kdrive: vite build → served by the FastAPI app (or its own static mount). nginx vhost via the panel: music.kvmx.ru (replaces the swingmusic/navidrome entry) → http://localhost:<port>.