From 023557de642f4887245d3f96ef8e30910de613a7 Mon Sep 17 00:00:00 2001 From: Samu Date: Fri, 4 Sep 2026 13:23:49 +0200 Subject: [PATCH] docs: navidrome deep-link spec (replaces in-app player) --- .../specs/2026-09-04-navidrome-link-design.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-04-navidrome-link-design.md diff --git a/docs/superpowers/specs/2026-09-04-navidrome-link-design.md b/docs/superpowers/specs/2026-09-04-navidrome-link-design.md new file mode 100644 index 0000000..b4aacf2 --- /dev/null +++ b/docs/superpowers/specs/2026-09-04-navidrome-link-design.md @@ -0,0 +1,39 @@ +# Replace In-App Player with Navidrome Deep Link — Design Spec + +**Date:** 2026-09-04 +**Status:** Approved design, pending implementation +**Parent:** docs/superpowers/specs/2026-09-03-wave1-hygiene-listening-design.md (supersedes its "Mini player" section) + +## Purpose + +The built-in album player (global audio element + mini-bar) is removed. Listening happens in the user's Navidrome web UI; the item page links directly to the matched album there. Rationale: Navidrome's player is better than ours, and the in-app player added a global-audio architecture for a preview-quality experience. + +## Removals + +- `web/src/player/` (PlayerContext, MiniBar) and their tests; PlayerProvider/MiniBar wiring in `web/src/App.tsx` +- Backend stream/tracks/played routes (`server/src/routes/streamRoutes.ts`) and their tests +- api methods `getAlbumTracks`, `markPlayed`, `streamUrl`; types `Track`, `AlbumTracks`; ItemPage's `usePlayer`/`load` wiring and media mocks +- Media-prototype mocks in `web/test/item.test.tsx` (no audio element remains) + +## Changes + +- **`GET /api/collection/:id` detail** — `matchedAlbum` gains `webUrl: string`: the Navidrome album URL built server-side as `/app/#/album/` (trailing slashes stripped from the configured base; null-safe — `matchedAlbum` stays null when unmatched, and when no Subsonic config exists `webUrl` is still constructed from the stored URL only if the album row exists; unmatched → null either way). +- **Item page** — the ▶ Play album button becomes an anchor **"Listen in Navidrome ↗"** (`target="_blank"` `rel="noreferrer"`), shown whenever `matchedAlbum != null` (regardless of rip status — the album exists in Navidrome if it matched). +- **"Last played X ago"** stays. Stamping now comes solely from library sync reading the server's recently-played list (`getAlbumList2?type=recent`, opportunistic `played`/`playedAt` field). No in-app stamping. + +## Unchanged + +Stats, loans, rip queue, export/backup, sync, matching, all plan-1/2 APIs except the removed routes. + +## Error handling + +No new failure modes. The link is a static anchor; a wrong Subsonic URL produces a broken link the same way a wrong URL breaks sync — surfaced by Settings, not the item page. + +## Testing + +- Backend: detail test asserts `matchedAlbum.webUrl` (`http://n.local/app/#/album/`); stream tests deleted with the routes +- Web: item tests assert the link (href + target) replaces the player tests; api.test drops the removed methods; player/minibar test files deleted + +## Manual device checklist + +- Link opens Navidrome's album page from the phone (PWA → browser tab)