Blazor parity conveniences: BlockPlayoutTroubleshootingScreen now persists the block-history
page-size selector to localStorage (ctv-block-history-page-size, same ctv- namespace as
ctv-theme) and restores it on mount, and gates the per-block History action on block.id >= 0
(mirrors BlockPlayoutTroubleshooting.razor, which hides it for synthesized/virtual blocks).
BlocksScreen and TemplatesScreen list screens gain a client-side name/group search filter box,
matching the filter already present on the troubleshooting blocks list.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- POST /api/playlists/{id:int}/items wraps the existing AddItemsToPlaylist
command (mirrors CollectionController.AddItems); controller pre-checks
playlist existence for a real 404, and the handler now rejects adds to
system (generated) playlists, matching the guard already applied to
rename/delete/replace-items so the Blazor path gets the same protection.
- GET /api/search/all-items wraps the existing QuerySearchIndexAllItems
query, returning a new SearchResultAllItemsResponseModel (never expose
the VM directly) so the SPA's shared "add all to collection/playlist"
component can materialize ids before calling the add endpoints, same
two-step flow Blazor's Search.razor already uses.
- Show-detail DTO check: ShowDetailResponseModel already exposes
libraryId, title, and mediaSourceKind (serialized as a string enum via
the global StringEnumConverter) - no changes needed.
Adds controller tests (route table + per-action) for both endpoints and
regenerates the OpenAPI document, endpoint index, and SPA client types.
- BlocksScreen: add copy-to-group dialog (mirrors TemplatesScreen), wiring the
already-existing copyBlock() API client
- WatermarksScreen: add copy via /add?from={id} prefill (mirrors
FFmpegProfilesScreen), no new endpoint needed
- TrashScreen: add Select all / Clear selection to the action bar; bump
PAGE_SIZE to the search API's max (100) — true paging needs a page param
the API doesn't have yet
- TraktListsScreen: fix stale callout text — "View matched items" now opens
the SPA's own search screen, not the Classic UI
docs/decisions.md: record the YAML-validator paste-textarea deviation, the
channel-number prompt-renumber deviation, extend the "table not calendar"
convention to the deco-templates editor, and document the trash 100-item cap.
Backend slice for the ChicoryTV playouts and collections screens.
PlayoutController:
- POST /api/playouts/{id}/erase-items (204; 404 pre-check; 422 unless
Block/Sequential/Scripted) -> ErasePlayoutItems
- POST /api/playouts/{id}/erase-items-and-history (204; 404; 422 unless
Classic/Block/Sequential/Scripted) -> ErasePlayoutHistory
- GET /api/playouts/items/{id}/scheduling-context (200/404) decodes a
playout item's stored context by row id via a new
GetPlayoutItemSchedulingContext query that reuses ProcessSchedulingContext
- PlayoutItemResponseModel gains HasSchedulingContext (no raw JSON in list)
- PlayoutListItemResponseModel gains PlayoutMode (ChannelNumber already present)
CollectionController:
- PUT /api/collections/{id}/custom-order (204; 404 pre-check; 422) with
UpdateCollectionCustomOrderRequest deriving CustomIndex from array order
- GetCollectionItemsHandler orders by CustomIndex (nulls last) then title/id
when the collection's UseCustomPlaybackOrder is set
Tests: controller route + behavior tests, OpenAPI ProblemDetails TestCases,
GetCollectionItems custom-order handler test. Regenerated v1.json, v1.d.ts,
endpoint-index.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- api-conventions.md §5a: runtime Newtonsoft casing vs generated spec, the
schema transformer that mirrors it, and the contract test guarding it.
- decisions.md: append the "wire format is source of truth; spec follows via the
real contract resolver" decision.
- spa-conventions.md §4: trust the generated key casing; note the removed
troubleshooting escape hatch and runtime-cased test mocks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- decisions.md: Download Media Sample is gated while a session is active in
the SPA (Blazor only gated Download Results) — intentional deviation.
- blazor-route-parity.md: playback row notes entry points are the channels
table + movie detail page only for now (other media kinds need a hand-built
?mediaItem= URL).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- blazor-route-parity.md: move /system/troubleshooting/playback from Section 3
(BLAZOR-ONLY) to Section 2 (SPA-ready); Section 3 now has no blocking rows.
- decisions.md: poll-based completion feedback (no SPA push channel) and the
datetime-local vs Chronic deviation.
- spa-conventions.md: new HlsPlayer reusable-component + test-mocking note.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add three GET endpoints to TroubleshootController for the SPA port of the
playback troubleshooting page:
- /api/troubleshoot/playback/stream-selectors (List<string>)
- /api/troubleshoot/playback/subtitles/{mediaItemId} (404 pre-check via
GetMediaItemInfo; maps SubtitleViewModel -> TroubleshootingSubtitleResponseModel)
- /api/troubleshoot/playback/status (TroubleshootingPlaybackStatusResponseModel:
idle/running/completed/failed + exitCode/speed + logs.txt tail)
Regenerate v1.json, endpoint-index.md, and the web API types.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cache-save hang resolved: run #371 test log shows actions/cache restore (732MB NuGet) +
save (~1s) + hit-skip working; warm Build&test ~9min. Record the Microsoft.OpenApi 2.0.0
(NU1903/GHSA-v5pm-xwqc-g5wc) assessment: DoS-only, unreachable (we generate not parse specs),
correctly non-blocking; low-priority bump to 2.7.5 available.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CLAUDE.md Conventions: turn "read docs before exploring" into a hard rule with red-flag
framing (do NOT reverse-engineer conventions from source; applies to subagents too) and a
"which doc to update in the same PR" table. Add a lightweight, PR-only, NON-BLOCKING
`docs-reminder` CI job that warns when a screen/route change skips
docs/blazor-route-parity.md. No dotnet/node in that job (no actions/cache), so it can't hit
the VM-127 cache-save issues (server-management#570). Documented in docs/ci-cd.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a ChicoryTV SPA Playlist editor at /app/playlists over the new
playlist-CRUD REST API: a group tree (add/rename/delete groups,
add/delete playlists) plus a playlist item editor (per-item Collection
Type over the 12 playlist item types, type-conditional playback order,
count, Play All, Show In EPG, reorder/copy/remove) and a draft playout
preview. System groups and playlists are read-only, mirroring the Blazor
Playlists.razor / PlaylistEditor.razor pages.
- web/src/api/playlists.ts: group/playlist/item CRUD + preview client fns
- web/src/screens/PlaylistsScreen.tsx: group tree + editor + preview
- App.tsx: route, ScreenId, nav, dispatch wiring
- docs: blazor-route-parity + domain-model updated to SPA DONE
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add write endpoints to PlaylistController for the playlist editor: group
create/rename/delete, playlist create/read/update/delete, item list read,
and draft playout preview. Introduces a RenamePlaylistGroup command/handler
(the one missing Application-layer operation) plus request/response DTOs.
Endpoints (verb / route / route-Name):
- POST /api/playlists/groups CreatePlaylistGroup
- PUT /api/playlists/groups/{id} UpdatePlaylistGroup (rename)
- DELETE /api/playlists/groups/{id} DeletePlaylistGroup
- GET /api/playlists/{id} GetPlaylistById
- GET /api/playlists/{id}/items GetPlaylistItems
- POST /api/playlists CreatePlaylistInGroup
- PUT /api/playlists/{id} UpdatePlaylist (rename + replace items)
- DELETE /api/playlists/{id} DeletePlaylist
- POST /api/playlists/preview PreviewPlaylist
404-vs-422: unknown-id on GET items / PUT / DELETE returns 404 via a
controller-side existence pre-check (mirrors TemplateController.DeleteGroup),
leaving existing shared handlers untouched; validation failures return 422.
RenamePlaylistGroup returns NotFoundError -> 404 for a missing group.
Regenerated wwwroot/openapi/v1.json, docs/endpoint-index.md, and the SPA
client types (web/src/api/generated/v1.d.ts). No SPA screen in this change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add two React SPA editor screens over the existing REST APIs, retiring the
last two "Classic UI only" media-collection editors.
MultiCollectionsScreen (/app/multi-collections, #151): list + in-screen
editor. Name + a list of manual/smart collection items, each with a
Schedule-As-Group toggle; two add-pickers dedupe already-present entries.
Items save with playbackOrder "Chronological" (matches the Blazor editor).
RerunCollectionsScreen (/app/rerun-collections, #152): list + in-screen
editor. Name, a Collection Type select restricted to the REST-supported
selection set (IsSupportedSelectionType) — Collection/MultiCollection/
SmartCollection plus the media-item types TelevisionShow/TelevisionSeason/
Artist/Movie/Episode/MusicVideo/OtherVideo/Song/Image/RemoteStream (every
one maps to a working LibraryBrowseMediaType picker); Playlist is excluded.
A conditional single-selection picker per type, and First/Rerun playback
order selects with the Blazor type-conditional option sets (reset on type
change).
Both screens toggle list<->editor via local state (mirroring
CollectionsScreen), no sub-path routing. Wired into App.tsx (imports,
ScreenId, routes, mediaNavIds, ScreenContent). Added messageFrom*Error
helpers + type re-export to the api clients.
Tests: multiCollections/rerunCollections api-client param+body tests,
both screen tests (list render, create body, rerun type-switch swaps
picker + order options, delete), and an App.tsx nav-registration test.
Docs: blazor-route-parity.md (rows moved to SPA DONE), domain-model.md.
web verify gate: 353 tests pass, eslint clean, tsc -b + vite build clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three CI wins from ersatztv#190, all config-only:
- Cache ~/.nuget/packages via actions/cache@v4 in both the `test` and
`migrations` jobs, keyed on Directory.Packages.props + global.json, so
dotnet restore no longer starts from scratch every run.
- Shallow-checkout the `test` job (fetch-depth: 1) since it never runs
git describe/log; only `build` needs full history for versioning. The
`migrations` job's checkout was already effectively shallow (default
fetch-depth 1), documented explicitly.
- Reorder the Dockerfile so the openapi-generator jar wget happens before
the openapi spec COPY, decoupling the ~30MB download layer from spec
changes (e.g. v1.json) that previously busted it on every API PR.
Documented all three in docs/ci-cd.md alongside the existing job/Dockerfile
notes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Adds MultiCollectionController + RerunCollectionController (list/get/create/update/
delete) over the existing MediatR handlers, with response/request DTOs, controller +
contract tests, regenerated OpenAPI + TS types, and thin SPA api-client modules. SPA
editor screens are a follow-up. Rerun request DTO resolves the chosen entity id into
the minimal VM the handler reads (documented inline).
Refs #151, #152
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- #184: ApiControllerSecurityTests scans ErsatzTV.Controllers.Api assembly
instead of a hand-maintained array (9 controllers were unlisted; 2 mutating)
- Trakt matched-items link now navigates to SPA /app/search (was Classic UI)
- GET /api/search runs its 10 per-kind queries in parallel (context-safe)
- Remove dead "New Group" header buttons from blocks/templates/decos/deco-templates
- docs/api-conventions.md §6 updated for the assembly-scan change
fixes#184
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds docs/testing.md as the authoritative testing map (consolidated
from docs/contributing.md §8, now shrunk to a pointer), and a
generated docs/endpoint-index.md via scripts/generate-endpoint-index.py
(hooked into scripts/update-openapi.sh). Updates docs/README.md's
reading order and removes the "still to come" placeholder.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>