feat(web+api): media detail endpoints+pages, image folder browser (#141, #161 items 3+5) #183

Merged
timothy merged 3 commits from feat/141-media-detail into main 2026-07-07 23:08:44 +02:00
Owner

Closes out #161 items 3+5, the last #141 leftovers (manual close after verification). Phase (b) gate on #91.

Endpoints

  • GET /api/movies/{id}, /api/shows/{id}, /api/seasons/{id}, /api/artists/{id} — detail DTOs with rooted artwork URLs via new shared ApiArtwork.Root() helper (PR #181 convention; Application mappers untouched, Blazor unaffected).
  • GET /api/media-items/{id}/info — full stream/chapter info; UnableToLocateMediaItem → 404, other errors → 422.
  • GET /api/images/folders?parentId= + PUT /api/images/folders/{id}/duration — image folder tree + duration edit (400 non-positive, 404 unknown folder via new ImageFolderExists guard; the shared handler silently no-ops on unknown ids, left intact for Blazor).
  • GET /api/library/browse parentId drill-in extended to Episode (episode order) and MusicVideo (album/track order).

SPA: MediaRouteScreen wrapper owning pathname+popstate (sub-path lesson) dispatches /app/media/{movies|shows|seasons|artists}/{id} detail pages (poster/fanart, metadata chips, actors, media-info dialog, child grids show→seasons→episodes, artist→music videos) and /app/media/images/browser (lazy folder tree, duration edit, search link). Browse cards now navigate for the four detail kinds. Add-to-collection/playlist TODO'd pending #153/#155.

Tests: ErsatzTV.Tests 828 green (+21), Core.Tests 493, Architecture 5, web 330 green (+12), lint clean. 5 new controllers added to the security registry. Post-#182 merge reconciled; OpenAPI artifacts regenerated.

🤖 Generated with Claude Code

Closes out #161 items 3+5, the last #141 leftovers (manual close after verification). Phase (b) gate on #91. **Endpoints** - `GET /api/movies/{id}`, `/api/shows/{id}`, `/api/seasons/{id}`, `/api/artists/{id}` — detail DTOs with **rooted** artwork URLs via new shared `ApiArtwork.Root()` helper (PR #181 convention; Application mappers untouched, Blazor unaffected). - `GET /api/media-items/{id}/info` — full stream/chapter info; UnableToLocateMediaItem → 404, other errors → 422. - `GET /api/images/folders?parentId=` + `PUT /api/images/folders/{id}/duration` — image folder tree + duration edit (400 non-positive, 404 unknown folder via new `ImageFolderExists` guard; the shared handler silently no-ops on unknown ids, left intact for Blazor). - `GET /api/library/browse` `parentId` drill-in extended to `Episode` (episode order) and `MusicVideo` (album/track order). **SPA**: `MediaRouteScreen` wrapper owning pathname+popstate (sub-path lesson) dispatches `/app/media/{movies|shows|seasons|artists}/{id}` detail pages (poster/fanart, metadata chips, actors, media-info dialog, child grids show→seasons→episodes, artist→music videos) and `/app/media/images/browser` (lazy folder tree, duration edit, search link). Browse cards now navigate for the four detail kinds. Add-to-collection/playlist TODO'd pending #153/#155. **Tests**: ErsatzTV.Tests 828 green (+21), Core.Tests 493, Architecture 5, web 330 green (+12), lint clean. 5 new controllers added to the security registry. Post-#182 merge reconciled; OpenAPI artifacts regenerated. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
timothy added 3 commits 2026-07-07 23:02:31 +02:00
Add REST endpoints backing the SPA media detail pages and image browser:

- GET /api/movies/{id}, /api/shows/{id}, /api/seasons/{id}, /api/artists/{id}
  wrapping the existing detail queries; 404 on None.
- GET /api/media-items/{id}/info wrapping GetMediaItemInfo; UnableToLocateMediaItem
  -> 404, other errors -> 422.
- GET /api/images/folders?parentId= and PUT /api/images/folders/{id}/duration
  (validates null-or-positive -> 400; existence guard via new ImageFolderExists
  query -> 404).
- Extend GetLibraryBrowseItems parentId drill-in to Episode (episodes of a season,
  episode-number order) and MusicVideo (an artist's music videos, album/track order),
  alongside the existing TelevisionSeason branch.

Response DTOs live in ErsatzTV.Core/Api/* and never expose Application VMs. Artwork
values are rooted for the SPA via a shared ErsatzTV.Core/Api/ApiArtwork helper
(mirrors the #180/#181 browse-handler logic; handles jellyfin/emby proxy prefixes,
http passthrough, empty). Regenerated OpenAPI v1.json + web v1.d.ts. New controllers
registered in ApiControllerSecurityTests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add the SPA screens over the new detail/info/image endpoints:

- Detail pages at /app/media/{movies|shows|seasons|artists}/{id}. MediaBrowseScreen
  cards for movies/shows/artists (and season/show drill-in cards) navigate to them.
  Shows list seasons -> seasons list episodes -> artists list music videos, each via
  the browse parentId drill-in, paged. Layout: fanart/poster, title/year/plot, chip
  lists, cast row, movie path + FileNotFound/Unavailable warnings. A "Media Info"
  dialog (streams + chapters) backs onto GET /api/media-items/{id}/info. Add-to-
  collection/playlist deferred to #153/#155 (TODO left in code).
- Image folder browser at /app/media/images/browser: lazy expandable tree, per-folder
  image/subfolder counts + duration, edit dialog (PUT set/clear), and a per-folder
  search link (library_folder_id:{id}). Reachable via a "Folder Browser" button on the
  Images browse view.

New /app/media sub-paths are owned by a MediaRouteScreen wrapper that tracks pathname
locally + listens for popstate (App-level routing returns the same 'media' route object
for base and sub-paths). Client modules web/src/api/mediaDetail.ts + imageFolders.ts,
typed via the generated v1.d.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
merge main into feat/141-media-detail (post-#182); regenerate OpenAPI artifacts
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 5m2s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m23s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
685894517d
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
timothy merged commit dbda2d8991 into main 2026-07-07 23:08:44 +02:00
timothy deleted branch feat/141-media-detail 2026-07-07 23:08:44 +02:00
Sign in to join this conversation.