From 10256677ac66fb1ed74d9cfe26e7de45b7886b2d Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Wed, 10 Jun 2026 10:58:16 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20close=20Phase=202.2/2.3=20=E2=80=94=20m?= =?UTF-8?q?ove=20to=20COMPLETED.md,=20update=20DeepDrftPublic=20proxy=20CL?= =?UTF-8?q?AUDE.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- COMPLETED.md | 11 +++++++++++ DeepDrftPublic/CLAUDE.md | 10 +++++++--- PLAN.md | 19 ------------------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/COMPLETED.md b/COMPLETED.md index b569317..7bea98b 100644 --- a/COMPLETED.md +++ b/COMPLETED.md @@ -6,6 +6,17 @@ Newest entries at the top. Group by phase/wave header (mirroring `PLAN.md` / `CM --- +## Phase 2.2 + 2.3 — Album/genre views and gallery search/filter + +**Status:** Fully landed on 2026-06-10. + +- **What:** Free-text search (`?q=`) across TrackName/Artist/Album via `EF.Functions.ILike` (Postgres, case-insensitive); album/genre exact-match filtering (`?album=`, `?genre=`); new `/albums` browsing page (grid of album cards with cover art and track counts, linking to filtered gallery); new `/genres` browsing page (genre list with counts, linking to filtered gallery); search bar with 400ms debounce and filter-pill dismiss on `TracksView`. Nav updated with Albums and Genres links. +- **Architecture:** Filter is threaded as a separate `TrackFilter` DTO alongside `PagingParameters` (which is external and cannot carry a where-clause). Repository has new `GetPagedFilteredAsync`, `GetDistinctAlbumsAsync`, `GetDistinctGenresAsync` methods. `PersistentComponentState` restore on `TracksView` is skipped when filter params are active. `ClearFilter` preserves `SearchText` (only clears album/genre pill). +- **New types:** `TrackFilter`, `AlbumSummaryDto`, `GenreSummaryDto` in `DeepDrftModels/DTOs/`. +- **Tests:** `TrackFilterQueryTests` in `DeepDrftTests` — 4 in-memory cases plus 1 Postgres-gated `ILike` case (skip when `DEEPDRFT_TEST_PG` env var absent). + +--- + ## Phase 4.1 — HTTP Range + CDN caching **Status:** Fully landed on 2026-06-09 (implementation complete, all acceptance criteria met, merged to dev branch `p4-w1-range-streaming`). diff --git a/DeepDrftPublic/CLAUDE.md b/DeepDrftPublic/CLAUDE.md index fd29266..ed7dea6 100644 --- a/DeepDrftPublic/CLAUDE.md +++ b/DeepDrftPublic/CLAUDE.md @@ -87,11 +87,15 @@ The middleware pipeline in `Program.cs` is ordered as follows: `TrackProxyController` in `Controllers/` is the only HTTP controller. It is a thin proxy only — no domain logic, no data layer. The WASM client points both named HttpClients (`"DeepDrft.API"` and `"DeepDrft.Content"`) at the Blazor host's base address, so all browser requests route through this controller to DeepDrftAPI. Server-side SSR calls DeepDrftAPI directly (server-to-server) via the same named clients — no proxy hop on the server side. -The proxy forwards two public, unauthenticated routes: +The proxy forwards public, unauthenticated routes: - `GET api/track/page` — paged metadata listing -- `GET api/track/{trackId}` — WAV audio streaming (handles `offset` param for seek-beyond-buffer) +- `GET api/track/{trackId}` — WAV audio streaming (handles `Range` header for seek-beyond-buffer) +- `GET api/track/albums` — distinct albums with counts +- `GET api/track/genres` — distinct genres with counts +- `GET api/track/random` — random track selection +- `GET api/track/meta/by-key/{entryKey}` — metadata lookup by vault entry key -Both actions use `HttpCompletionOption.ResponseHeadersRead` for streaming efficiency. Audio streaming registers the upstream response with `HttpContext.Response.RegisterForDispose()` so the stream is properly cleaned up after the response body is sent. +All actions use `HttpCompletionOption.ResponseHeadersRead` for streaming efficiency. Audio streaming registers the upstream response with `HttpContext.Response.RegisterForDispose()` so the stream is properly cleaned up after the response body is sent. ## Development commands diff --git a/PLAN.md b/PLAN.md index 30b2f65..54403de 100644 --- a/PLAN.md +++ b/PLAN.md @@ -79,25 +79,6 @@ These were flagged during the audit but classified as feature work, not defect f These follow from `CONTEXT.md §5`. Direction is strongly implied but no specific UI has been committed. -### 2.2 Album and genre views - -- **What:** `TrackCard` already renders album/genre/release date; the data is there. Missing are gallery groupings (album view, genre view), filters, and the API-side support for filter expressions in `TrackService.GetPaged`. -- **Why it matters:** The track gallery is the only working content surface. Multiple views over the same library is how it earns the "gallery" name. -- **Shape:** Per `CONTEXT.md §6`, the convention is one source of truth, multiple views over it. New views should consume the same `TracksViewModel` / `PagedResult` and differ only at the rendering layer. - - `TrackService.GetPaged` extended to accept a filter expression (or a simple structured filter DTO). - - `PagingParameters` extended with a `Where: Expression>?` or a parallel `FilterParameters` — pick one to avoid drift. - - New routes (`/albums`, `/genres`) consume the same VM with different grouping / filter inputs. -- **Prerequisite:** **2.1** for any view that prominently features cover art (album view especially is impoverished without it). - - -### 2.3 Search and filter on the gallery - -- **What:** `TracksViewModel` exposes sort but no filter. `TrackService.GetPaged` accepts only sort. Simple text search across `TrackName` / `Artist` / `Album` is the obvious first cut. -- **Why it matters:** Once the library has more than ~30 entries, sort-only browsing is friction. -- **Shape:** Same extension to `GetPaged` as 2.2. UI is a debounced text input bound to the VM's filter property. EF Core translates `Contains` to SQLite `LIKE`. -- **Prerequisite:** Fold into 2.2 if both are being done — the same `GetPaged` extension serves both. Doing them separately doubles the API churn. - - --- ## Phase 3 — New content kinds