#91b: wire deep-scan + external-collections buttons into LibrariesScreen #272

Merged
timothy merged 1 commits from feat/91b-scan-buttons into main 2026-07-11 19:34:28 +02:00
Owner

What

The last SPA pre-work before deleting Blazor Libraries.razor (#91 phase b). Wires the already-shipped scanLibrary(id, deep) + scanCollections(family, id, deep) clients (F9 API, #235) into LibrariesScreen so the SPA reaches parity with Libraries.razor's four scan actions:

Action Blazor SPA (this PR)
Quick scan library ✅ ✅ (already)
Deep scan library ✅ ✅ new — remote library rows only
Quick scan collections ✅ ✅ new — External Collections section
Deep scan collections ✅ ✅ new

How

  • Deep Scan Library: a ScanSearch button on each remote (Plex/Jellyfin/Emby) library row, threading deep through the existing optimistic-pending/scan-status-poll hook. Quick and deep share the per-library lock, so they gate on and reconcile through the same id set. Local libraries get no deep button (parity: Blazor shows it only for remote).
  • External Collections section: one row per remote source with sync-enabled libraries, quick + deep buttons calling scanCollections. Rows derive client-side from getMediaSources() — no new endpoint: GetAllMediaSourcesForApiHandler already filters each source's libraries to sync-enabled entries (ShouldIncludeLibrary = ShouldSyncItems for remote), so a remote source with a non-empty libraries list is exactly GetExternalCollections's Libraries.Any(ShouldSyncItems) filter.
  • useCollectionsScan hook: collections scans have no scan-status poll surface (the endpoint is library-keyed; Blazor observed collections locks via in-process IEntityLocker events). So pending is optimistic + timeout-bounded — 409 "already scanning" is benign (stays disabled), 404/network surfaces the error. Follow-up #271 filed for a proper collections status surface.

Scope

Pure SPA change — no backend / no OpenAPI regen. The removal PR proper (rollback tag → surgical Blazor delete + catch-all fallback → auth cleanup → verify, with the mandatory cold adversarial pass) remains a separate focused follow-up; this PR only lands the capability so Libraries.razor can be deleted at parity.

Verification

  • web typecheck ✅, lint ✅ (0 warnings), full suite 674 tests pass ✅ (8 new LibrariesScreen cases: deep-scan ?deep=true, remote-only deep button, collections row derivation incl. exclusions, quick/deep collections POST, 409-benign, 404-surfaces), production build ✅.
  • Live E2E on a running instance: to follow in review.

Docs

  • docs/blazor-route-parity.md §5 — SPA affordance marked DONE; removal PR's remaining pre-work is now just the cold adversarial pass.
  • docs/decisions.md — derive-vs-endpoint rationale + the optimistic-timeout decision (+ #271 follow-up).

Refs #91

🤖 Generated with Claude Code

## What The last SPA pre-work before deleting Blazor `Libraries.razor` (#91 phase b). Wires the already-shipped `scanLibrary(id, deep)` + `scanCollections(family, id, deep)` clients (F9 API, #235) into `LibrariesScreen` so the SPA reaches parity with `Libraries.razor`'s **four** scan actions: | Action | Blazor | SPA (this PR) | |---|---|---| | Quick scan library | ✅ | ✅ (already) | | **Deep scan library** | ✅ | ✅ **new** — remote library rows only | | **Quick scan collections** | ✅ | ✅ **new** — External Collections section | | **Deep scan collections** | ✅ | ✅ **new** | ## How - **Deep Scan Library**: a `ScanSearch` button on each remote (Plex/Jellyfin/Emby) library row, threading `deep` through the existing optimistic-pending/scan-status-poll hook. Quick and deep share the per-library lock, so they gate on and reconcile through the same id set. Local libraries get no deep button (parity: Blazor shows it only for remote). - **External Collections section**: one row per remote source with sync-enabled libraries, quick + deep buttons calling `scanCollections`. **Rows derive client-side from `getMediaSources()`** — no new endpoint: `GetAllMediaSourcesForApiHandler` already filters each source's `libraries` to sync-enabled entries (`ShouldIncludeLibrary` = `ShouldSyncItems` for remote), so a remote source with a non-empty `libraries` list is *exactly* `GetExternalCollections`'s `Libraries.Any(ShouldSyncItems)` filter. - **`useCollectionsScan` hook**: collections scans have **no** scan-status poll surface (the endpoint is library-keyed; Blazor observed collections locks via in-process `IEntityLocker` events). So pending is optimistic + timeout-bounded — 409 "already scanning" is benign (stays disabled), 404/network surfaces the error. Follow-up **#271** filed for a proper collections status surface. ## Scope Pure SPA change — **no backend / no OpenAPI regen**. The removal PR proper (rollback tag → surgical Blazor delete + catch-all fallback → auth cleanup → verify, with the mandatory cold adversarial pass) remains a separate focused follow-up; this PR only lands the capability so `Libraries.razor` can be deleted at parity. ## Verification - `web` typecheck ✅, lint ✅ (0 warnings), full suite **674 tests pass** ✅ (8 new LibrariesScreen cases: deep-scan `?deep=true`, remote-only deep button, collections row derivation incl. exclusions, quick/deep collections POST, 409-benign, 404-surfaces), production build ✅. - Live E2E on a running instance: to follow in review. ## Docs - `docs/blazor-route-parity.md` §5 — SPA affordance marked DONE; removal PR's remaining pre-work is now just the cold adversarial pass. - `docs/decisions.md` — derive-vs-endpoint rationale + the optimistic-timeout decision (+ #271 follow-up). Refs #91 🤖 Generated with [Claude Code](https://claude.com/claude-code)
timothy added the frontend label 2026-07-11 19:26:28 +02:00
timothy added 1 commit 2026-07-11 19:26:29 +02:00
feat(91b): wire deep-scan + external-collections buttons into LibrariesScreen
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 6s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m35s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 5m22s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m2s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 6m50s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m13s
bb7b189929
Last SPA pre-work before deleting Blazor Libraries.razor (#91 phase b):
wire the shipped scanLibrary(id, deep) + scanCollections(family, id, deep)
clients (F9 API, #235) into LibrariesScreen so the SPA reaches parity with
Libraries.razor's four scan actions.

- Deep Scan Library button on each remote (Plex/Jellyfin/Emby) library row,
  threading `deep` through the existing optimistic-pending/poll hook (quick +
  deep share the per-library lock).
- External Collections section (quick + deep per remote source). Rows derive
  client-side from getMediaSources(): the media-sources API handler already
  filters each source's `libraries` to sync-enabled entries, so a remote
  source with a non-empty libraries list is exactly GetExternalCollections's
  Libraries.Any(ShouldSyncItems) filter — no new endpoint.
- useCollectionsScan hook: collections scans have no scan-status poll surface
  (the endpoint is library-keyed; Blazor observed collections locks via
  in-process IEntityLocker events), so pending is optimistic + timeout-bounded
  (409 benign, 404/network surfaces the error). Follow-up #271 for a proper
  collections status surface.

Pure SPA change (no backend/OpenAPI). Docs: blazor-route-parity.md §5 (SPA
affordance DONE), decisions.md (derive-vs-endpoint + optimistic-timeout).

Refs #91

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
timothy merged commit bb7b189929 into main 2026-07-11 19:34:28 +02:00
timothy deleted branch feat/91b-scan-buttons 2026-07-11 19:34:29 +02:00
Sign in to join this conversation.