using System; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using ErsatzTV.Application; using ErsatzTV.Application.Maintenance.Commands; using ErsatzTV.Application.MediaCollections.Commands; using ErsatzTV.Application.MediaSources.Commands; using ErsatzTV.Application.Playouts.Commands; using ErsatzTV.Application.Search.Commands; using ErsatzTV.Core; using LanguageExt; using MediatR; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Unit = LanguageExt.Unit; namespace ErsatzTV.Services { public class WorkerService : BackgroundService { private readonly ChannelReader _channel; private readonly ILogger _logger; private readonly IServiceScopeFactory _serviceScopeFactory; public WorkerService( ChannelReader channel, IServiceScopeFactory serviceScopeFactory, ILogger logger) { _channel = channel; _serviceScopeFactory = serviceScopeFactory; _logger = logger; } protected override async Task ExecuteAsync(CancellationToken cancellationToken) { _logger.LogInformation("Worker service started"); await foreach (IBackgroundServiceRequest request in _channel.ReadAllAsync(cancellationToken)) { try { using IServiceScope scope = _serviceScopeFactory.CreateScope(); IMediator mediator = scope.ServiceProvider.GetRequiredService(); switch (request) { case BuildPlayout buildPlayout: Either buildPlayoutResult = await mediator.Send( buildPlayout, cancellationToken); buildPlayoutResult.BiIter( _ => _logger.LogDebug("Built playout {PlayoutId}", buildPlayout.PlayoutId), error => _logger.LogWarning( "Unable to build playout {PlayoutId}: {Error}", buildPlayout.PlayoutId, error.Value)); break; case IScanLocalLibrary scanLocalLibrary: Either scanResult = await mediator.Send( scanLocalLibrary, cancellationToken); scanResult.BiIter( name => _logger.LogDebug( "Done scanning local library {Library}", name), error => _logger.LogWarning( "Unable to scan local library {LibraryId}: {Error}", scanLocalLibrary.LibraryId, error.Value)); break; case RebuildSearchIndex rebuildSearchIndex: await mediator.Send(rebuildSearchIndex, cancellationToken); break; case DeleteOrphanedArtwork deleteOrphanedArtwork: _logger.LogInformation("Deleting orphaned artwork from the database"); await mediator.Send(deleteOrphanedArtwork, cancellationToken); break; case AddTraktList addTraktList: await mediator.Send(addTraktList, cancellationToken); break; case DeleteTraktList deleteTraktList: await mediator.Send(deleteTraktList, cancellationToken); break; case MatchTraktListItems matchTraktListItems: await mediator.Send(matchTraktListItems, cancellationToken); break; } } catch (Exception ex) { _logger.LogWarning(ex, "Failed to process background service request"); } } } } }