Files
ersatztv/ErsatzTV.Application/MediaSources/Queries/GetCollectionsScanStatusHandler.cs
T
timothyandClaude Opus 4.8 6d31758cca
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 10s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m13s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 9m51s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
feat(api): #271 collections scan-status REST surface + authoritative SPA reconcile
Add GET /api/media-sources/collections-scan-status (MediaSourcesController →
GetCollectionsScanStatus handler) reporting which media-source families
(plex/jellyfin/emby) currently hold their external-collections scan lock,
reading IEntityLocker.Are{X}CollectionsLocked(). The lock is family-global
(no source id) and boolean (no percent), so the DTO carries just {family} and
returns only active families — the counterpart to GET /api/libraries/scan-status.

SPA: useCollectionsScan now polls this endpoint and reconciles optimistic
pending against the active-family set (seeding on mount so an in-progress scan
disables buttons immediately), using the same grace-tick helper as library
scans (now generic over the pending key type). Drops COLLECTIONS_PENDING_TIMEOUT_MS
— a long deep scan no longer re-enables the button early, and a fast scan no
longer wedges it disabled for the full timeout. A row shows Scanning when its
family is active or it has an in-grace optimistic pending key.

Tests: handler (3), controller route+delegation (2), SPA api fn + hook reconcile
(mount-seed / 202-promote / 409-keeps-disabled / 404-error). OpenAPI + TS types
regenerated. Docs: api-conventions §3b, blazor-route-parity §5, decisions.md.

Unblocks #91b (arc item 4): Libraries.razor's collections-scan affordance now
has full authoritative parity.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 14:23:58 +02:00

39 lines
1.5 KiB
C#

#nullable enable
using ErsatzTV.Core.Api.MediaSources;
using ErsatzTV.Core.Interfaces.Locking;
namespace ErsatzTV.Application.MediaSources;
// Reports which media-source families currently hold their external-collections scan lock. The lock
// is the running scan (the scan-collections controllers acquire it before enqueueing and the scanner
// releases it on completion), so IEntityLocker is the authoritative source — analogous to how
// GetLibraryScanStatus reads IScannerProxyService.GetActiveScans(). Collections locks are family-global,
// so this returns at most one entry per family, and only for families actively scanning.
public class GetCollectionsScanStatusHandler(IEntityLocker entityLocker)
: IRequestHandler<GetCollectionsScanStatus, List<CollectionsScanStatusResponseModel>>
{
public Task<List<CollectionsScanStatusResponseModel>> Handle(
GetCollectionsScanStatus request,
CancellationToken cancellationToken)
{
var result = new List<CollectionsScanStatusResponseModel>();
if (entityLocker.ArePlexCollectionsLocked())
{
result.Add(new CollectionsScanStatusResponseModel("plex"));
}
if (entityLocker.AreJellyfinCollectionsLocked())
{
result.Add(new CollectionsScanStatusResponseModel("jellyfin"));
}
if (entityLocker.AreEmbyCollectionsLocked())
{
result.Add(new CollectionsScanStatusResponseModel("emby"));
}
return Task.FromResult(result);
}
}