Files
ersatztv/docs/blazor-route-parity.md
T
timothyandClaude Opus 4.8 f869dfe87a feat(api): GET /api/collections/{id}/items (paged) + confirm POST-items 422 guard (#155)
Adds a paged collection-items endpoint reusing LibraryBrowseItemResponseModel
so the SPA lists a manual collection's full contents (all media kinds), replacing
the lossy Lucene name-based preview. Confirms POST /items already returns 422 for
bogus ids (guarded by ValidateMediaItems, fb3f2856); adds endpoint-level coverage.

fixes #155

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 18:26:52 +02:00

11 KiB
Raw Blame History

Blazor → SPA route parity tracker

Purpose: the living route-by-route tracker for ersatztv#91 phase (b) — retiring the legacy Blazor Server UI once every route it serves has a ChicoryTV SPA equivalent and a redirect. Update this doc in the same PR that migrates, redirects, or removes any route below — this table is the single source of truth for "what's left."

Sources of truth checked when compiling this table: ErsatzTV/LegacyUiRedirects.cs (redirect map), web/src/App.tsx (SPA route table), ErsatzTV/Pages/**/*.razor (Blazor pages still present).

How to read this

  • REDIRECTED: in LegacyUiRedirects.cs's Map — the Blazor route 302s to the SPA route.
  • SPA-READY (not yet redirected): the SPA screen exists and covers the functionality, but the Blazor route is still reachable directly (no redirect entry yet) — adding the redirect is a small, low-risk PR once the SPA screen has been spot-checked against the Blazor page it replaces.
  • BLAZOR-ONLY: no SPA equivalent yet; removing/redirecting this route is blocked on the listed issue(s).

Section 1 — REDIRECTED

(ErsatzTV/LegacyUiRedirects.cs's Map, verified current as of this doc — 13 entries, unchanged since ersatztv#91 phase (a)/PR #148):

Blazor route SPA route
/ /app
/channels /app/channels
/channels/add /app/new-channel
/schedules /app/schedules
/playouts /app/playouts
/media/libraries /app/libraries
/settings/ffmpeg /app/settings/streaming
/settings/hdhr /app/settings/system
/settings/logging /app/settings/logging
/settings/playout /app/settings/playout
/settings/scanner /app/settings/scanner
/settings/ui /app/settings/general
/settings/xmltv /app/settings/xmltv

Note: the settings sub-routes above are already redirected, not Blazor-only as an earlier draft of this table implied — LegacyUiRedirects.cs covers all seven /settings/* pages that have SPA equivalents. There is no Blazor-only settings sub-route left except the ones with no SPA screen at all (there are none currently — every Blazor Settings/*.razor page has both an SPA screen and a redirect).

Section 2 — SPA-READY, not yet redirected

Confirmed as of this doc: the SPA screen exists (verified against web/src/App.tsx's route table and web/src/screens/) but LegacyUiRedirects.cs has no entry for the Blazor route yet. Scheduling-parity work (#144/#162, DONE 2026-07-07, PRs #170#175/#179) built the SPA screens for blocks/templates/decos/deco-templates/playout editors, #145 (PR #182, merged to main) built the troubleshooting/YAML-validator screens, and #141 (PR #183, merged to main) built the media detail pages + image folder browser (MediaDetailScreen.tsx's MovieDetailScreen/ShowDetailScreen/ SeasonDetailScreen/ArtistDetailScreen, ImageBrowserScreen.tsx, both dispatched via App.tsx's MediaRouteScreen sub-route wrapper, same pattern as PlayoutsRouteScreen) — none of these have been added to the redirect map yet.

Blazor route Blazor file SPA route Notes
/channels/{Id:int?} ChannelEditor.razor /app/edit-channel/{id} allowSubPaths
/channels/numbers ChannelNumbers.razor /app/channels merged into channels table
/search Search.razor /app/search
/system/logs Logs.razor /app/logs
/system/troubleshooting Troubleshooting/Troubleshooting.razor /app/troubleshooting
/system/troubleshooting/block-playout Troubleshooting/BlockPlayoutTroubleshooting.razor (+BlockPlayoutHistory.razor) /app/troubleshooting/blocks covered by PR #182 / #145
/system/troubleshooting/sequential-schedule Troubleshooting/YamlValidator.razor /app/troubleshooting/yaml covered by PR #182 / #145
/blocks, /blocks/{Id:int} Blocks.razor, BlockEditor.razor /app/blocks(/{id}) allowSubPaths; #144 S1
/templates, /templates/{Id:int} Templates.razor, TemplateEditor.razor /app/templates(/{id}) allowSubPaths; #144 S2
/decos, /decos/{Id:int} Decos.razor, DecoEditor.razor /app/decos(/{id}) allowSubPaths; #144 S3
/deco-templates, /deco-templates/{Id:int} DecoTemplates.razor, DecoTemplateEditor.razor /app/deco-templates(/{id}) allowSubPaths; #144 S4
/playouts/add(/{kind}) PlayoutEditor.razor variants /app/playouts merged into playouts screen creation flow; #144 S5
/playouts/classic/{Id}, /playouts/block/{Id}, /playouts/scripted/{Id}, /playouts/sequential/{Id} ClassicPlayoutEditor.razor, BlockPlayoutEditor.razor, ScriptedPlayoutEditor.razor, SequentialPlayoutEditor.razor /app/playouts merged; #144 S5/S6
/playouts/{Id:int}/alternate-schedules PlayoutAlternateSchedulesEditor.razor /app/playouts/{id}/alternate-schedules allowSubPaths (PlayoutsRouteScreen); #144 S6/#162
/playouts/{Id:int}/templates PlayoutTemplatesEditor.razor /app/playouts/{id}/templates allowSubPaths (PlayoutsRouteScreen); #144 S6/#162
/schedules/{Id:int}, /schedules/add, /schedules/{Id:int}/items ScheduleEditor.razor, ScheduleItemsEditor.razor /app/schedules merged into schedules screen
/media/filler/presets(/add, /{Id}/edit) FillerPresets.razor, FillerPresetEditor.razor /app/filler-presets allowSubPaths
/media/collections(/add, /{Id}/edit, /{Id}) ManualCollections.razor, CollectionEditor.razor, CollectionItems.razor + SmartCollections.razor/SmartCollectionEditor.razor /app/collections allowSubPaths
/media/trash Trash.razor /app/trash
/media/playlists(/{Id}) Playlists.razor, PlaylistEditor.razor /app/collections merged into collections screen
/media/trakt/lists(/{Id}) TraktLists.razor, TraktListEditor.razor /app/trakt-lists(/{id}) allowSubPaths
/ffmpeg(/add, /{Id}) FFmpeg.razor, FFmpegEditor.razor /app/ffmpeg-profiles allowSubPaths
/watermarks(/add, /{Id}) Watermarks.razor, WatermarkEditor.razor /app/watermarks allowSubPaths
/media/sources/{local,plex,jellyfin,emby}/... LocalLibraries.razor, PlexMediaSources.razor, JellyfinMediaSources.razor, EmbyMediaSources.razor + editors /app/libraries merged into libraries screen
/media/movies(/page/{n}) MovieList.razor /app/media?kind=movies generic browse (MediaBrowseScreen); PR #183 / #141
/media/movies/{MovieId:int} Movie.razor /app/media/movies/{id} detail page (MovieDetailScreen); PR #183 / #141
/media/tv/shows(/page/{n}) TelevisionShowList.razor /app/media?kind=shows generic browse; PR #183 / #141
/media/tv/shows/{ShowId:int} TelevisionSeasonList.razor /app/media/shows/{id} show + season list (ShowDetailScreen); PR #183 / #141
/media/tv/seasons(/page/{n}) TelevisionSeasonSearchResults.razor /app/media?kind=shows no dedicated season-list SPA screen; covered via show drill-in; PR #183 / #141
/media/tv/seasons/{SeasonId:int} TelevisionEpisodeList.razor /app/media/seasons/{id} season + episode list (SeasonDetailScreen); PR #183 / #141
/media/tv/episodes(/page/{n}) EpisodeList.razor /app/media/seasons/{id} no standalone SPA episode browse; covered via season detail drill-in; PR #183 / #141
/media/music/artists(/page/{n}) ArtistList.razor /app/media?kind=artists generic browse; PR #183 / #141
/media/music/artists/{ArtistId:int} Artist.razor /app/media/artists/{id} detail page (ArtistDetailScreen); PR #183 / #141
/media/music/videos(/page/{n}) MusicVideoList.razor /app/media?kind=music-videos generic browse; PR #183 / #141
/media/music/songs(/page/{n}) SongList.razor /app/media?kind=songs no dedicated SPA song browse beyond generic grid; PR #183 / #141
/media/other/videos(/page/{n}) OtherVideoList.razor /app/media?kind=other-videos generic browse; PR #183 / #141
/media/remote/streams(/page/{n}) RemoteStreamList.razor /app/media?kind=remote-streams generic browse; PR #183 / #141
/media/images(/page/{n}) ImageList.razor /app/media?kind=images generic browse; PR #183 / #141
/media/browser/images ImageBrowser.razor /app/media/images/browser interactive image grid picker used by channel editors etc. (ImageBrowserScreen); PR #183 / #141

Section 3 — BLAZOR-ONLY (blocking issues)

Multi/rerun collections & playlist variants — API gaps #151/#152/#153

Blazor route File Blocking issue
/media/multi-collections(/add, /{Id}/edit) MultiCollections.razor, MultiCollectionEditor.razor #151 (multi-collection management API)
/media/rerun-collections(/add, /{Id}/edit) RerunCollections.razor, RerunCollectionEditor.razor #152 (rerun-collection management API)
/media/playlists(/{Id}) editing depth beyond what /app/collections covers Playlists.razor, PlaylistEditor.razor #153 (playlist variant management API)

#155 RESOLVED (collection-items enumeration): GET /api/collections/{id}/items (paged) now returns a manual collection's full contents across all media kinds (reusing LibraryBrowseItemResponseModel), so the SPA /app/collections items view lists real members instead of the old lossy Lucene collection:"name" search preview. The POST /api/collections/{id}/items bogus-id case already returns 422 (guarded by AddItemsToCollectionHandler.ValidateMediaItems), not 500.

Playback troubleshooting — #145

Blazor route File Blocking issue
/system/troubleshooting/playback Troubleshooting/PlaybackTroubleshooting.razor #145 (playback troubleshooting diagnostics API) — distinct from block-playout troubleshooting, which is already covered (Section 2)

Section 4 — Blazor home / escape hatch

Not gated on an issue — kept separate from Section 3 because it isn't blocked on anything, just the intentional exit ramp until phase (b) removes Blazor entirely.

Blazor route File Notes
/system/health Index.razor Blazor's own home page; reachable via Settings → System "Classic UI" link.

Cross-reference: docs/handoffs/chicorytv-issue-queue.md

Keep this table and that handoff doc in sync at a high level — this table is the detailed route inventory; the handoff doc tracks issue sequencing/session planning. If they disagree on an issue's status, the more recently updated one wins; fix the stale one in the same PR you notice it.