# 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)