using System.Threading.Channels; using ErsatzTV.Application.Emby; using ErsatzTV.Application.Jellyfin; using ErsatzTV.Application.Plex; using ErsatzTV.Application.Subtitles; using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Locking; using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Extensions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace ErsatzTV.Application.Libraries; public class QueueShowScanByLibraryIdHandler( IDbContextFactory dbContextFactory, IEntityLocker locker, IMediator mediator, ChannelWriter workerChannel, ILogger logger) : IRequestHandler { public async Task Handle(QueueShowScanByLibraryId request, CancellationToken cancellationToken) { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Option maybeLibrary = await dbContext.Libraries .AsNoTracking() .SelectOneAsync(l => l.Id, l => l.Id == request.LibraryId, cancellationToken); foreach (Library library in maybeLibrary) { bool shouldSyncItems = library switch { PlexLibrary plexLibrary => plexLibrary.ShouldSyncItems, JellyfinLibrary jellyfinLibrary => jellyfinLibrary.ShouldSyncItems, EmbyLibrary embyLibrary => embyLibrary.ShouldSyncItems, _ => true }; if (!shouldSyncItems) { logger.LogWarning("Library sync is disabled for library id {Id}", library.Id); return QueueShowScanResult.SyncDisabled; } // A false from LockLibrary means a scan is already in progress; we own no release. if (!locker.LockLibrary(library.Id)) { logger.LogWarning("Library {Id} is already being scanned, cannot scan individual show", library.Id); return QueueShowScanResult.AlreadyScanning; } logger.LogDebug( "Queued show scan for library id {Id}, show: {ShowTitle}, deepScan: {DeepScan}", library.Id, request.ShowTitle, request.DeepScan); try { QueueShowScanResult outcome; switch (library) { case PlexLibrary: Either plexResult = await mediator.Send( new SynchronizePlexShowById(library.Id, request.ShowId, request.DeepScan), cancellationToken); outcome = plexResult.IsRight ? QueueShowScanResult.Queued : QueueShowScanResult.ScanFailed; break; case JellyfinLibrary: Either jellyfinResult = await mediator.Send( new SynchronizeJellyfinShowById(library.Id, request.ShowId, request.DeepScan), cancellationToken); outcome = jellyfinResult.IsRight ? QueueShowScanResult.Queued : QueueShowScanResult.ScanFailed; break; case EmbyLibrary: Either embyResult = await mediator.Send( new SynchronizeEmbyShowById(library.Id, request.ShowId, request.DeepScan), cancellationToken); outcome = embyResult.IsRight ? QueueShowScanResult.Queued : QueueShowScanResult.ScanFailed; break; case LocalLibrary: logger.LogWarning("Single show scanning is not supported for local libraries"); outcome = QueueShowScanResult.Unsupported; break; default: logger.LogWarning("Unknown library type for library {Id}", library.Id); outcome = QueueShowScanResult.Unsupported; break; } if (outcome == QueueShowScanResult.Queued && request.DeepScan) { await workerChannel.WriteAsync(new ExtractEmbeddedShowSubtitles(request.ShowId), cancellationToken); } return outcome; } finally { // Always unlock the library when we're done locker.UnlockLibrary(library.Id); } } return QueueShowScanResult.NotFound; } }