using System; using System.Threading; using System.Threading.Tasks; using Jellyfin.Plugin.ShareLinks.Services; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Jellyfin.Plugin.ShareLinks.Lifecycle; /// /// Runs one cleanup pass at startup so stale records do not linger forever. /// public sealed class StartupCleanupHostedService : BackgroundService { private readonly IShareLinkCleanupService _cleanupService; private readonly JellyfinGuestUserService _guestUserService; private readonly ILogger _logger; /// Initializes a new instance of the class. public StartupCleanupHostedService( IShareLinkCleanupService cleanupService, JellyfinGuestUserService guestUserService, ILogger logger) { _cleanupService = cleanupService; _guestUserService = guestUserService; _logger = logger; } /// protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { await _cleanupService.CleanupAsync(stoppingToken).ConfigureAwait(false); } catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { } catch (Exception ex) { _logger.LogWarning(ex, "ShareLinks: startup cleanup failed."); } // Separate try: a device row stranded by an older build breaks the admin // devices page until it goes, so this should still run when the record // cleanup above fails for its own reasons. try { var removed = await _guestUserService.PurgeOrphanedGuestDevicesAsync(stoppingToken).ConfigureAwait(false); if (removed > 0) { _logger.LogInformation("ShareLinks: removed {Count} orphaned guest device(s) at startup.", removed); } } catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { } catch (Exception ex) { _logger.LogWarning(ex, "ShareLinks: orphaned guest device sweep failed."); } } }