From f7c699248ca2e8a96b4f310d41f8b29f9f383041 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Wed, 22 Oct 2025 08:59:06 -0500 Subject: [PATCH] fix collection and network scanners (#2557) --- .../CallEmbyCollectionScannerHandler.cs | 35 +++++++++++++++---- .../CallJellyfinCollectionScannerHandler.cs | 35 +++++++++++++++---- .../CallPlexCollectionScannerHandler.cs | 35 +++++++++++++++---- .../Commands/CallPlexNetworkScannerHandler.cs | 35 +++++++++++++++---- ErsatzTV.Core/Metadata/FakeLibraryId.cs | 10 ++++++ .../Commands/SynchronizeEmbyCollections.cs | 3 +- .../SynchronizeEmbyCollectionsHandler.cs | 14 ++++++-- .../SynchronizeJellyfinCollections.cs | 2 +- .../SynchronizeJellyfinCollectionsHandler.cs | 14 ++++++-- .../Commands/SynchronizePlexCollections.cs | 3 +- .../SynchronizePlexCollectionsHandler.cs | 14 ++++++-- .../Plex/Commands/SynchronizePlexNetworks.cs | 3 +- .../SynchronizePlexNetworksHandler.cs | 14 ++++++-- .../Core/Emby/EmbyCollectionScanner.cs | 16 +++++---- .../Jellyfin/JellyfinCollectionScanner.cs | 16 +++++---- .../Core/Plex/PlexCollectionScanner.cs | 16 +++++---- .../Core/Plex/PlexNetworkScanner.cs | 1 - ErsatzTV.Scanner/Worker.cs | 32 ++++++++++++++--- 18 files changed, 231 insertions(+), 67 deletions(-) create mode 100644 ErsatzTV.Core/Metadata/FakeLibraryId.cs diff --git a/ErsatzTV.Application/Emby/Commands/CallEmbyCollectionScannerHandler.cs b/ErsatzTV.Application/Emby/Commands/CallEmbyCollectionScannerHandler.cs index a5a544086..1671c0cfd 100644 --- a/ErsatzTV.Application/Emby/Commands/CallEmbyCollectionScannerHandler.cs +++ b/ErsatzTV.Application/Emby/Commands/CallEmbyCollectionScannerHandler.cs @@ -2,7 +2,9 @@ using System.Globalization; using ErsatzTV.Application.Libraries; using ErsatzTV.Core; using ErsatzTV.Core.Errors; +using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Repositories; +using ErsatzTV.Core.Metadata; using ErsatzTV.FFmpeg.Runtime; using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Extensions; @@ -13,11 +15,15 @@ namespace ErsatzTV.Application.Emby; public class CallEmbyCollectionScannerHandler : CallLibraryScannerHandler, IRequestHandler> { + private readonly IScannerProxyService _scannerProxyService; + public CallEmbyCollectionScannerHandler( IDbContextFactory dbContextFactory, IConfigElementRepository configElementRepository, + IScannerProxyService scannerProxyService, IRuntimeInfo runtimeInfo) : base(dbContextFactory, configElementRepository, runtimeInfo) { + _scannerProxyService = scannerProxyService; } public async Task> @@ -68,16 +74,31 @@ public class CallEmbyCollectionScannerHandler : CallLibraryScannerHandler + Option maybeScanId = _scannerProxyService.StartScan(FakeLibraryId.EmbyCollections); + foreach (var scanId in maybeScanId) { - "scan-emby-collections", request.EmbyMediaSourceId.ToString(CultureInfo.InvariantCulture) - }; + try + { + var arguments = new List + { + "scan-emby-collections", + request.EmbyMediaSourceId.ToString(CultureInfo.InvariantCulture), + GetBaseUrl(scanId) + }; - if (request.ForceScan) - { - arguments.Add("--force"); + if (request.ForceScan) + { + arguments.Add("--force"); + } + + return await base.PerformScan(parameters, arguments, cancellationToken).MapT(_ => Unit.Default); + } + finally + { + _scannerProxyService.EndScan(scanId); + } } - return await base.PerformScan(parameters, arguments, cancellationToken).MapT(_ => Unit.Default); + return BaseError.New("Emby collections are already scanning"); } } diff --git a/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinCollectionScannerHandler.cs b/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinCollectionScannerHandler.cs index 2434ee59d..50da2cfc4 100644 --- a/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinCollectionScannerHandler.cs +++ b/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinCollectionScannerHandler.cs @@ -2,7 +2,9 @@ using System.Globalization; using ErsatzTV.Application.Libraries; using ErsatzTV.Core; using ErsatzTV.Core.Errors; +using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Repositories; +using ErsatzTV.Core.Metadata; using ErsatzTV.FFmpeg.Runtime; using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Extensions; @@ -13,11 +15,15 @@ namespace ErsatzTV.Application.Jellyfin; public class CallJellyfinCollectionScannerHandler : CallLibraryScannerHandler, IRequestHandler> { + private readonly IScannerProxyService _scannerProxyService; + public CallJellyfinCollectionScannerHandler( IDbContextFactory dbContextFactory, IConfigElementRepository configElementRepository, + IScannerProxyService scannerProxyService, IRuntimeInfo runtimeInfo) : base(dbContextFactory, configElementRepository, runtimeInfo) { + _scannerProxyService = scannerProxyService; } public async Task> @@ -68,16 +74,31 @@ public class CallJellyfinCollectionScannerHandler : CallLibraryScannerHandler + Option maybeScanId = _scannerProxyService.StartScan(FakeLibraryId.JellyfinCollections); + foreach (var scanId in maybeScanId) { - "scan-jellyfin-collections", request.JellyfinMediaSourceId.ToString(CultureInfo.InvariantCulture) - }; + try + { + var arguments = new List + { + "scan-jellyfin-collections", + request.JellyfinMediaSourceId.ToString(CultureInfo.InvariantCulture), + GetBaseUrl(scanId), + }; - if (request.ForceScan) - { - arguments.Add("--force"); + if (request.ForceScan) + { + arguments.Add("--force"); + } + + return await base.PerformScan(parameters, arguments, cancellationToken).MapT(_ => Unit.Default); + } + finally + { + _scannerProxyService.EndScan(scanId); + } } - return await base.PerformScan(parameters, arguments, cancellationToken).MapT(_ => Unit.Default); + return BaseError.New("Jellyfin collections are already scanning"); } } diff --git a/ErsatzTV.Application/Plex/Commands/CallPlexCollectionScannerHandler.cs b/ErsatzTV.Application/Plex/Commands/CallPlexCollectionScannerHandler.cs index 3de7c2910..ab0075c83 100644 --- a/ErsatzTV.Application/Plex/Commands/CallPlexCollectionScannerHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/CallPlexCollectionScannerHandler.cs @@ -2,7 +2,9 @@ using System.Globalization; using ErsatzTV.Application.Libraries; using ErsatzTV.Core; using ErsatzTV.Core.Errors; +using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Repositories; +using ErsatzTV.Core.Metadata; using ErsatzTV.FFmpeg.Runtime; using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Extensions; @@ -13,11 +15,15 @@ namespace ErsatzTV.Application.Plex; public class CallPlexCollectionScannerHandler : CallLibraryScannerHandler, IRequestHandler> { + private readonly IScannerProxyService _scannerProxyService; + public CallPlexCollectionScannerHandler( IDbContextFactory dbContextFactory, IConfigElementRepository configElementRepository, + IScannerProxyService scannerProxyService, IRuntimeInfo runtimeInfo) : base(dbContextFactory, configElementRepository, runtimeInfo) { + _scannerProxyService = scannerProxyService; } public async Task> @@ -68,16 +74,31 @@ public class CallPlexCollectionScannerHandler : CallLibraryScannerHandler + Option maybeScanId = _scannerProxyService.StartScan(FakeLibraryId.PlexCollections); + foreach (var scanId in maybeScanId) { - "scan-plex-collections", request.PlexMediaSourceId.ToString(CultureInfo.InvariantCulture) - }; + try + { + var arguments = new List + { + "scan-plex-collections", + request.PlexMediaSourceId.ToString(CultureInfo.InvariantCulture), + GetBaseUrl(scanId) + }; - if (request.ForceScan) - { - arguments.Add("--force"); + if (request.ForceScan) + { + arguments.Add("--force"); + } + + return await base.PerformScan(parameters, arguments, cancellationToken).MapT(_ => Unit.Default); + } + finally + { + _scannerProxyService.EndScan(scanId); + } } - return await base.PerformScan(parameters, arguments, cancellationToken).MapT(_ => Unit.Default); + return BaseError.New("Plex collections are already scanning"); } } diff --git a/ErsatzTV.Application/Plex/Commands/CallPlexNetworkScannerHandler.cs b/ErsatzTV.Application/Plex/Commands/CallPlexNetworkScannerHandler.cs index 05cbc7cd5..7850b36c0 100644 --- a/ErsatzTV.Application/Plex/Commands/CallPlexNetworkScannerHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/CallPlexNetworkScannerHandler.cs @@ -3,7 +3,9 @@ using ErsatzTV.Application.Libraries; using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Errors; +using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Repositories; +using ErsatzTV.Core.Metadata; using ErsatzTV.FFmpeg.Runtime; using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Extensions; @@ -14,11 +16,15 @@ namespace ErsatzTV.Application.Plex; public class CallPlexNetworkScannerHandler : CallLibraryScannerHandler, IRequestHandler> { + private readonly IScannerProxyService _scannerProxyService; + public CallPlexNetworkScannerHandler( IDbContextFactory dbContextFactory, IConfigElementRepository configElementRepository, + IScannerProxyService scannerProxyService, IRuntimeInfo runtimeInfo) : base(dbContextFactory, configElementRepository, runtimeInfo) { + _scannerProxyService = scannerProxyService; } public async Task> @@ -70,16 +76,31 @@ public class CallPlexNetworkScannerHandler : CallLibraryScannerHandler + Option maybeScanId = _scannerProxyService.StartScan(FakeLibraryId.PlexNetworks); + foreach (var scanId in maybeScanId) { - "scan-plex-networks", request.PlexLibraryId.ToString(CultureInfo.InvariantCulture) - }; + try + { + var arguments = new List + { + "scan-plex-networks", + request.PlexLibraryId.ToString(CultureInfo.InvariantCulture), + GetBaseUrl(scanId) + }; - if (request.ForceScan) - { - arguments.Add("--force"); + if (request.ForceScan) + { + arguments.Add("--force"); + } + + return await base.PerformScan(parameters, arguments, cancellationToken).MapT(_ => Unit.Default); + } + finally + { + _scannerProxyService.EndScan(scanId); + } } - return await base.PerformScan(parameters, arguments, cancellationToken).MapT(_ => Unit.Default); + return BaseError.New("Plex networks are already scanning"); } } diff --git a/ErsatzTV.Core/Metadata/FakeLibraryId.cs b/ErsatzTV.Core/Metadata/FakeLibraryId.cs new file mode 100644 index 000000000..67bca35ac --- /dev/null +++ b/ErsatzTV.Core/Metadata/FakeLibraryId.cs @@ -0,0 +1,10 @@ +namespace ErsatzTV.Core.Metadata; + +public static class FakeLibraryId +{ + public const int PlexCollections = -1; + public const int JellyfinCollections = -2; + public const int EmbyCollections = -3; + + public const int PlexNetworks = -10; +} diff --git a/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyCollections.cs b/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyCollections.cs index 8ac7d971c..dd2767690 100644 --- a/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyCollections.cs +++ b/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyCollections.cs @@ -2,4 +2,5 @@ namespace ErsatzTV.Scanner.Application.Emby; -public record SynchronizeEmbyCollections(int EmbyMediaSourceId, bool ForceScan) : IRequest>; +public record SynchronizeEmbyCollections(string BaseUrl, int EmbyMediaSourceId, bool ForceScan) + : IRequest>; diff --git a/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyCollectionsHandler.cs b/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyCollectionsHandler.cs index 2f8c5703d..07827b36d 100644 --- a/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyCollectionsHandler.cs +++ b/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyCollectionsHandler.cs @@ -3,12 +3,14 @@ using ErsatzTV.Core.Domain; using ErsatzTV.Core.Emby; using ErsatzTV.Core.Interfaces.Emby; using ErsatzTV.Core.Interfaces.Repositories; +using ErsatzTV.Scanner.Core.Interfaces; namespace ErsatzTV.Scanner.Application.Emby; public class SynchronizeEmbyCollectionsHandler : IRequestHandler> { private readonly IConfigElementRepository _configElementRepository; + private readonly IScannerProxy _scannerProxy; private readonly IEmbySecretStore _embySecretStore; private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IEmbyCollectionScanner _scanner; @@ -17,12 +19,14 @@ public class SynchronizeEmbyCollectionsHandler : IRequestHandler> Handle( @@ -48,7 +52,8 @@ public class SynchronizeEmbyCollectionsHandler : IRequestHandler> ValidateLibraryRefreshInterval(CancellationToken cancellationToken) => @@ -82,6 +87,8 @@ public class SynchronizeEmbyCollectionsHandler : IRequestHandler> SynchronizeCollections(RequestParameters parameters) { + _scannerProxy.SetBaseUrl(parameters.BaseUrl); + var lastScan = new DateTimeOffset( parameters.MediaSource.LastCollectionsScan ?? SystemTime.MinValueUtc, TimeSpan.Zero); @@ -108,7 +115,8 @@ public class SynchronizeEmbyCollectionsHandler : IRequestHandler>; diff --git a/ErsatzTV.Scanner/Application/Jellyfin/Commands/SynchronizeJellyfinCollectionsHandler.cs b/ErsatzTV.Scanner/Application/Jellyfin/Commands/SynchronizeJellyfinCollectionsHandler.cs index fe9559d9d..90d233664 100644 --- a/ErsatzTV.Scanner/Application/Jellyfin/Commands/SynchronizeJellyfinCollectionsHandler.cs +++ b/ErsatzTV.Scanner/Application/Jellyfin/Commands/SynchronizeJellyfinCollectionsHandler.cs @@ -3,6 +3,7 @@ using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Jellyfin; using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Jellyfin; +using ErsatzTV.Scanner.Core.Interfaces; namespace ErsatzTV.Scanner.Application.Jellyfin; @@ -10,6 +11,7 @@ public class SynchronizeJellyfinCollectionsHandler : IRequestHandler> { private readonly IConfigElementRepository _configElementRepository; + private readonly IScannerProxy _scannerProxy; private readonly IJellyfinSecretStore _jellyfinSecretStore; private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IJellyfinCollectionScanner _scanner; @@ -18,12 +20,14 @@ public class IMediaSourceRepository mediaSourceRepository, IJellyfinSecretStore jellyfinSecretStore, IJellyfinCollectionScanner scanner, - IConfigElementRepository configElementRepository) + IConfigElementRepository configElementRepository, + IScannerProxy scannerProxy) { _mediaSourceRepository = mediaSourceRepository; _jellyfinSecretStore = jellyfinSecretStore; _scanner = scanner; _configElementRepository = configElementRepository; + _scannerProxy = scannerProxy; } @@ -50,7 +54,8 @@ public class connectionParameters, connectionParameters.MediaSource, request.ForceScan, - libraryRefreshInterval)); + libraryRefreshInterval, + request.BaseUrl)); } private Task> ValidateLibraryRefreshInterval(CancellationToken cancellationToken) => @@ -83,6 +88,8 @@ public class private async Task> SynchronizeCollections(RequestParameters parameters) { + _scannerProxy.SetBaseUrl(parameters.BaseUrl); + var lastScan = new DateTimeOffset( parameters.MediaSource.LastCollectionsScan ?? SystemTime.MinValueUtc, TimeSpan.Zero); @@ -110,7 +117,8 @@ public class ConnectionParameters ConnectionParameters, JellyfinMediaSource MediaSource, bool ForceScan, - int LibraryRefreshInterval); + int LibraryRefreshInterval, + string BaseUrl); private record ConnectionParameters(JellyfinMediaSource MediaSource, JellyfinConnection ActiveConnection) { diff --git a/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexCollections.cs b/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexCollections.cs index bbb500113..9f3686e68 100644 --- a/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexCollections.cs +++ b/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexCollections.cs @@ -2,4 +2,5 @@ namespace ErsatzTV.Scanner.Application.Plex; -public record SynchronizePlexCollections(int PlexMediaSourceId, bool ForceScan) : IRequest>; +public record SynchronizePlexCollections(string BaseUrl, int PlexMediaSourceId, bool ForceScan) + : IRequest>; diff --git a/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexCollectionsHandler.cs b/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexCollectionsHandler.cs index 58963340b..1ed276f11 100644 --- a/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexCollectionsHandler.cs +++ b/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexCollectionsHandler.cs @@ -3,12 +3,14 @@ using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Plex; using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Plex; +using ErsatzTV.Scanner.Core.Interfaces; namespace ErsatzTV.Scanner.Application.Plex; public class SynchronizePlexCollectionsHandler : IRequestHandler> { private readonly IConfigElementRepository _configElementRepository; + private readonly IScannerProxy _scannerProxy; private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IPlexSecretStore _plexSecretStore; private readonly IPlexCollectionScanner _scanner; @@ -17,12 +19,14 @@ public class SynchronizePlexCollectionsHandler : IRequestHandler> Handle( @@ -48,7 +52,8 @@ public class SynchronizePlexCollectionsHandler : IRequestHandler> ValidateLibraryRefreshInterval(CancellationToken cancellationToken) => @@ -83,6 +88,8 @@ public class SynchronizePlexCollectionsHandler : IRequestHandler>; +public record SynchronizePlexNetworks(string BaseUrl, int PlexLibraryId, bool ForceScan) + : IRequest>; diff --git a/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexNetworksHandler.cs b/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexNetworksHandler.cs index 3b35b605a..56942ca1c 100644 --- a/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexNetworksHandler.cs +++ b/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexNetworksHandler.cs @@ -3,6 +3,7 @@ using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Plex; using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Plex; +using ErsatzTV.Scanner.Core.Interfaces; namespace ErsatzTV.Scanner.Application.Plex; @@ -12,6 +13,7 @@ public class SynchronizePlexNetworksHandler : IRequestHandler> Handle( @@ -52,7 +56,8 @@ public class SynchronizePlexNetworksHandler : IRequestHandler> PlexLibraryMustExist( @@ -92,6 +97,8 @@ public class SynchronizePlexNetworksHandler : IRequestHandler SyncCollectionItems( string address, string apiKey, EmbyCollection collection) @@ -111,11 +111,15 @@ public class EmbyCollectionScanner : IEmbyCollectionScanner if (!await _scannerProxy.ReindexMediaItems(changedIds, CancellationToken.None)) { _logger.LogWarning("Failed to reindex media items from scanner process"); + return false; } + + return true; } catch (Exception ex) { _logger.LogWarning(ex, "Failed to synchronize Emby collection {Name}", collection.Name); + return false; } } } diff --git a/ErsatzTV.Scanner/Core/Jellyfin/JellyfinCollectionScanner.cs b/ErsatzTV.Scanner/Core/Jellyfin/JellyfinCollectionScanner.cs index b1e0fefb7..39076f429 100644 --- a/ErsatzTV.Scanner/Core/Jellyfin/JellyfinCollectionScanner.cs +++ b/ErsatzTV.Scanner/Core/Jellyfin/JellyfinCollectionScanner.cs @@ -2,7 +2,6 @@ using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Jellyfin; using ErsatzTV.Core.Interfaces.Repositories; -using ErsatzTV.Core.MediaSources; using ErsatzTV.Scanner.Core.Interfaces; using Microsoft.Extensions.Logging; @@ -63,10 +62,11 @@ public class JellyfinCollectionScanner : IJellyfinCollectionScanner await _jellyfinCollectionRepository.AddCollection(collection); } - await SyncCollectionItems(address, apiKey, mediaSourceId, collection); - - // save collection etag - await _jellyfinCollectionRepository.SetEtag(collection); + if (await SyncCollectionItems(address, apiKey, mediaSourceId, collection)) + { + // save collection etag + await _jellyfinCollectionRepository.SetEtag(collection); + } } // remove missing collections (and remove any lingering tags from those collections) @@ -85,7 +85,7 @@ public class JellyfinCollectionScanner : IJellyfinCollectionScanner return Unit.Default; } - private async Task SyncCollectionItems( + private async Task SyncCollectionItems( string address, string apiKey, int mediaSourceId, @@ -115,11 +115,15 @@ public class JellyfinCollectionScanner : IJellyfinCollectionScanner if (!await _scannerProxy.ReindexMediaItems(changedIds, CancellationToken.None)) { _logger.LogWarning("Failed to reindex media items from scanner process"); + return false; } + + return true; } catch (Exception ex) { _logger.LogWarning(ex, "Failed to synchronize Jellyfin collection {Name}", collection.Name); + return false; } } } diff --git a/ErsatzTV.Scanner/Core/Plex/PlexCollectionScanner.cs b/ErsatzTV.Scanner/Core/Plex/PlexCollectionScanner.cs index 788bf7829..79d36785b 100644 --- a/ErsatzTV.Scanner/Core/Plex/PlexCollectionScanner.cs +++ b/ErsatzTV.Scanner/Core/Plex/PlexCollectionScanner.cs @@ -2,7 +2,6 @@ using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Plex; using ErsatzTV.Core.Interfaces.Repositories; -using ErsatzTV.Core.MediaSources; using ErsatzTV.Core.Plex; using ErsatzTV.Scanner.Core.Interfaces; using Microsoft.Extensions.Logging; @@ -64,10 +63,11 @@ public class PlexCollectionScanner : IPlexCollectionScanner await _plexCollectionRepository.AddCollection(collection); } - await SyncCollectionItems(connection, token, collection, cancellationToken); - - // save collection etag - await _plexCollectionRepository.SetEtag(collection); + if (await SyncCollectionItems(connection, token, collection, cancellationToken)) + { + // save collection etag + await _plexCollectionRepository.SetEtag(collection); + } } // remove missing collections (and remove any lingering tags from those collections) @@ -85,7 +85,7 @@ public class PlexCollectionScanner : IPlexCollectionScanner return Unit.Default; } - private async Task SyncCollectionItems( + private async Task SyncCollectionItems( PlexConnection connection, PlexServerAuthToken token, PlexCollection collection, @@ -116,11 +116,15 @@ public class PlexCollectionScanner : IPlexCollectionScanner if (!await _scannerProxy.ReindexMediaItems(changedIds, CancellationToken.None)) { _logger.LogWarning("Failed to reindex media items from scanner process"); + return false; } + + return true; } catch (Exception ex) { _logger.LogWarning(ex, "Failed to synchronize Plex collection {Name}", collection.Name); + return false; } } } diff --git a/ErsatzTV.Scanner/Core/Plex/PlexNetworkScanner.cs b/ErsatzTV.Scanner/Core/Plex/PlexNetworkScanner.cs index 45a68a0f3..68f669717 100644 --- a/ErsatzTV.Scanner/Core/Plex/PlexNetworkScanner.cs +++ b/ErsatzTV.Scanner/Core/Plex/PlexNetworkScanner.cs @@ -2,7 +2,6 @@ using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Plex; using ErsatzTV.Core.Interfaces.Repositories; -using ErsatzTV.Core.MediaSources; using ErsatzTV.Core.Plex; using ErsatzTV.Scanner.Core.Interfaces; using Microsoft.Extensions.Logging; diff --git a/ErsatzTV.Scanner/Worker.cs b/ErsatzTV.Scanner/Worker.cs index b89647190..328d3f4f5 100644 --- a/ErsatzTV.Scanner/Worker.cs +++ b/ErsatzTV.Scanner/Worker.cs @@ -83,10 +83,12 @@ public class Worker : BackgroundService var scanPlexCollectionsCommand = new Command("scan-plex-collections", "Scan Plex collections"); scanPlexCollectionsCommand.Arguments.Add(mediaSourceIdArgument); + scanPlexCollectionsCommand.Arguments.Add(baseUrlArgument); scanPlexCollectionsCommand.Options.Add(forceOption); var scanPlexNetworksCommand = new Command("scan-plex-networks", "Scan Plex networks"); scanPlexNetworksCommand.Arguments.Add(libraryIdArgument); + scanPlexNetworksCommand.Arguments.Add(baseUrlArgument); scanPlexNetworksCommand.Options.Add(forceOption); var scanEmbyCommand = new Command("scan-emby", "Scan an Emby library"); @@ -97,6 +99,7 @@ public class Worker : BackgroundService var scanEmbyCollectionsCommand = new Command("scan-emby-collections", "Scan Emby collections"); scanEmbyCollectionsCommand.Arguments.Add(mediaSourceIdArgument); + scanEmbyCollectionsCommand.Arguments.Add(baseUrlArgument); scanEmbyCollectionsCommand.Options.Add(forceOption); var scanJellyfinCommand = new Command("scan-jellyfin", "Scan a Jellyfin library"); @@ -107,6 +110,7 @@ public class Worker : BackgroundService var scanJellyfinCollectionsCommand = new Command("scan-jellyfin-collections", "Scan Jellyfin collections"); scanJellyfinCollectionsCommand.Arguments.Add(mediaSourceIdArgument); + scanJellyfinCollectionsCommand.Arguments.Add(baseUrlArgument); scanJellyfinCollectionsCommand.Options.Add(forceOption); // Show-specific scanning commands @@ -188,11 +192,16 @@ public class Worker : BackgroundService SetProcessPriority(force); int mediaSourceId = parseResult.GetValue(mediaSourceIdArgument); + string? baseUrl = parseResult.GetValue(baseUrlArgument); + if (baseUrl is null) + { + return; + } using IServiceScope scope = _serviceScopeFactory.CreateScope(); IMediator mediator = scope.ServiceProvider.GetRequiredService(); - var scan = new SynchronizePlexCollections(mediaSourceId, force); + var scan = new SynchronizePlexCollections(baseUrl, mediaSourceId, force); await mediator.Send(scan, token); } }); @@ -205,11 +214,16 @@ public class Worker : BackgroundService SetProcessPriority(force); int libraryId = parseResult.GetValue(libraryIdArgument); + string? baseUrl = parseResult.GetValue(baseUrlArgument); + if (baseUrl is null) + { + return; + } using IServiceScope scope = _serviceScopeFactory.CreateScope(); IMediator mediator = scope.ServiceProvider.GetRequiredService(); - var scan = new SynchronizePlexNetworks(libraryId, force); + var scan = new SynchronizePlexNetworks(baseUrl, libraryId, force); await mediator.Send(scan, token); } }); @@ -245,11 +259,16 @@ public class Worker : BackgroundService SetProcessPriority(force); int mediaSourceId = parseResult.GetValue(mediaSourceIdArgument); + string? baseUrl = parseResult.GetValue(baseUrlArgument); + if (baseUrl is null) + { + return; + } using IServiceScope scope = _serviceScopeFactory.CreateScope(); IMediator mediator = scope.ServiceProvider.GetRequiredService(); - var scan = new SynchronizeEmbyCollections(mediaSourceId, force); + var scan = new SynchronizeEmbyCollections(baseUrl, mediaSourceId, force); await mediator.Send(scan, token); } }); @@ -285,11 +304,16 @@ public class Worker : BackgroundService SetProcessPriority(force); int mediaSourceId = parseResult.GetValue(mediaSourceIdArgument); + string? baseUrl = parseResult.GetValue(baseUrlArgument); + if (baseUrl is null) + { + return; + } using IServiceScope scope = _serviceScopeFactory.CreateScope(); IMediator mediator = scope.ServiceProvider.GetRequiredService(); - var scan = new SynchronizeJellyfinCollections(mediaSourceId, force); + var scan = new SynchronizeJellyfinCollections(baseUrl, mediaSourceId, force); await mediator.Send(scan, token); } });