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/browseparentId 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.
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>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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 sharedApiArtwork.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 newImageFolderExistsguard; the shared handler silently no-ops on unknown ids, left intact for Blazor).GET /api/library/browseparentIddrill-in extended toEpisode(episode order) andMusicVideo(album/track order).SPA:
MediaRouteScreenwrapper 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
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>