using System; using Emby.Server.Implementations.ScheduledTasks; using MediaBrowser.Controller.ScheduledTasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Extensions; /// /// Extensions for registering the scan-leader lease. /// public static class ScanLeaderServiceCollectionExtensions { private const string RedisConnectionStringKey = "Jellyfin:TranscodeStore:RedisConnectionString"; /// /// Registers the scan-leader lease and reports at whether /// timer-driven library tasks are gated to a single instance. /// /// /// Gating is on by default once a Redis connection string is configured: that is only set for a /// multi-instance deployment, which is the only shape where running library scans on every /// instance is wrong. Single-instance installs have no Redis and keep running every task locally. /// /// The service collection. /// The configuration to read Jellyfin:ScanLeader from. /// The logger to report the gating decision on. /// The updated service collection. public static IServiceCollection AddScanLeaderLease( this IServiceCollection serviceCollection, IConfiguration configuration, ILogger logger) { ArgumentNullException.ThrowIfNull(configuration); ArgumentNullException.ThrowIfNull(logger); serviceCollection.Configure(configuration.GetSection(ScanLeaderOptions.ConfigurationSection)); var redisConfigured = !string.IsNullOrEmpty(configuration[RedisConnectionStringKey]); var requested = bool.TryParse(configuration[ScanLeaderOptions.EnabledKey], out var explicitChoice) ? explicitChoice : redisConfigured; var active = requested && redisConfigured; // The task worker reads Enabled from the bound options, so the effective decision has to land // there as well as on the lease registration below. serviceCollection.PostConfigure(options => options.Enabled = active); if (active) { logger.LogInformation( "Scan-leader gating is active: timer-driven library tasks run only on the instance holding the Redis scan-leader lease."); return serviceCollection.AddSingleton(); } if (requested) { logger.LogWarning( "Scan-leader gating is enabled but no Redis connection string is configured ({Key}), so it cannot run: timer-driven library tasks run on every instance.", RedisConnectionStringKey); } else { logger.LogInformation("Scan-leader gating is off: timer-driven library tasks run on every instance."); } return serviceCollection.AddSingleton(); } }