fix(api): playlist add-items RemoteStream + validation; per-show scan by exact id (#217, #219) #224

Closed
timothy wants to merge 0 commits from fix/217-219-api-fixes into main
Owner

Closes #217, closes #219 (adversarial-reviewer#18 gates).

#217 — AddItemsToPlaylistHandler: RemoteStream added to the apply dictionary (was silently dropped); aggregate existence validation across all ten kinds mirroring AddItemsToCollectionHandler. Collections needed no changes (already fully validated — see issue scope correction). Tests: bogus id per kind → 422, valid RemoteStream persisted.

#219 — POST /api/libraries/{id}/scan-show contract: ScanShowRequest(int ShowId, ...) replaces title; controller pre-checks show ∈ library → genuine 404 ProblemDetails; queues with DB-resolved title. GetShowIdByTitle (substring LIKE, no OrderBy) removed with its only caller; replaced by GetShowTitle(libraryId, showId). Breaking change acceptable: endpoint shipped days ago (PR #216), no external consumers. SPA sends show.id. OpenAPI + TS regenerated; error-contract tests extended.

Full matrix green: ErsatzTV.Tests 990/990, Core 493, Scanner 1467, Architecture 5; web tsc + 477/477.

🤖 Generated with Claude Code

Closes #217, closes #219 (adversarial-reviewer#18 gates). **#217** — `AddItemsToPlaylistHandler`: RemoteStream added to the apply dictionary (was silently dropped); aggregate existence validation across all ten kinds mirroring `AddItemsToCollectionHandler`. Collections needed no changes (already fully validated — see issue scope correction). Tests: bogus id per kind → 422, valid RemoteStream persisted. **#219** — `POST /api/libraries/{id}/scan-show` contract: `ScanShowRequest(int ShowId, ...)` replaces title; controller pre-checks show ∈ library → genuine 404 ProblemDetails; queues with DB-resolved title. `GetShowIdByTitle` (substring LIKE, no OrderBy) removed with its only caller; replaced by `GetShowTitle(libraryId, showId)`. Breaking change acceptable: endpoint shipped days ago (PR #216), no external consumers. SPA sends `show.id`. OpenAPI + TS regenerated; error-contract tests extended. Full matrix green: ErsatzTV.Tests 990/990, Core 493, Scanner 1467, Architecture 5; web tsc + 477/477. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
timothy added 2 commits 2026-07-10 23:29:53 +02:00
AddItemsToPlaylistHandler only validated existence for movies/shows/
seasons/episodes, leaving artist/music-video/other-video/song/image/
remote-stream ids unchecked (silently accepted, or in RemoteStream's
case silently dropped entirely - the apply dictionary never included
CollectionType.RemoteStream). Mirror AddItemsToCollectionHandler's
established pattern: add RemoteStream to the apply dictionary, and add
an aggregate existence check (ValidateMediaItems/GetRequestedMediaItemIds)
across all ten kinds against dbContext.MediaItems.

Add ErsatzTV.Tests/Application/MediaCollections/PlaylistHandlerTests.cs
covering: a bogus id of each of the ten kinds fails validation; a valid
RemoteStream id is actually persisted to the playlist (regression test
for the drop bug).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(api): resolve per-show scan by exact show id, not substring title (fixes #219)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m11s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 12s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 9m49s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
8ddf0ae169
POST /api/libraries/{id}/scan-show resolved the target show via
GetShowIdByTitle, an EF.Functions.Like "%title%" substring match with
no OrderBy - non-deterministic under duplicate/overlapping titles and
capable of scanning the wrong show. The Blazor UI never had this bug
(it always passed the exact show id); this endpoint shipped days ago
in PR #216 with no external consumers, so the contract break is safe.

BREAKING CHANGE: ScanShowRequest now takes `showId: int` instead of
`showTitle: string`. Replaced ITelevisionRepository.GetShowIdByTitle
with GetShowTitle(libraryId, showId), which also enforces the show
belongs to the given library. LibrariesController.ScanShow now returns
a genuine 404 ProblemDetails (via ApiResults.NotFoundProblem, the
established pre-check pattern from TemplateController.DeleteGroup)
when the show id doesn't exist in that library, then queues
QueueShowScanByLibraryId with the DB-resolved title.

SPA: libraries.ts ScanShowParams.showId replaces showTitle;
MediaDetailScreen.tsx passes show.id. Extended
ApiErrorResponseMetadataTests and OpenApiErrorResponseContractTests
with the new 404 contract for ScanShow. Regenerated v1.json / v1.d.ts
via scripts/update-openapi.sh + npm run generate:api.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
timothy added 1 commit 2026-07-11 00:31:11 +02:00
Merge main: CI MySQL host-port unpin (collision fix)
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 9s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Failing after 5m46s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m17s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
9da582924a
Author
Owner

Merged to main via integration PR #239 (all six gate-batch branches integrated in one CI round after pairwise doc conflicts; this PR's commits are reachable from main — see the merge commits there). Closing manually since Gitea did not auto-detect the branch merge.

Merged to main via integration PR #239 (all six gate-batch branches integrated in one CI round after pairwise doc conflicts; this PR's commits are reachable from main — see the merge commits there). Closing manually since Gitea did not auto-detect the branch merge.
timothy closed this pull request 2026-07-11 11:37:51 +02:00

Pull request closed

This pull request cannot be reopened because the branch was deleted.
Sign in to join this conversation.