using System.Threading.Channels; using Bugsnag; using ErsatzTV.Application; using ErsatzTV.Application.Search; using ErsatzTV.Core; using MediatR; namespace ErsatzTV.Services; public class SearchIndexService : BackgroundService { private readonly ChannelReader _channel; private readonly ILogger _logger; private readonly IServiceScopeFactory _serviceScopeFactory; private readonly SystemStartup _systemStartup; public SearchIndexService( ChannelReader channel, IServiceScopeFactory serviceScopeFactory, SystemStartup systemStartup, ILogger logger) { _channel = channel; _serviceScopeFactory = serviceScopeFactory; _systemStartup = systemStartup; _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await Task.Yield(); await _systemStartup.WaitForDatabase(stoppingToken); try { _logger.LogInformation("Search index worker service started"); await foreach (ISearchIndexBackgroundServiceRequest request in _channel.ReadAllAsync(stoppingToken)) { using IServiceScope scope = _serviceScopeFactory.CreateScope(); IMediator mediator = scope.ServiceProvider.GetRequiredService(); try { switch (request) { case ReindexMediaItems reindexMediaItems: _logger.LogDebug("Reindexing media items: {MediaItemIds}", reindexMediaItems.MediaItemIds); await mediator.Send(reindexMediaItems, stoppingToken); break; case RemoveMediaItems removeMediaItems: _logger.LogDebug("Removing media items: {MediaItemIds}", removeMediaItems.MediaItemIds); await mediator.Send(removeMediaItems, stoppingToken); break; } } catch (Exception ex) { _logger.LogWarning(ex, "Failed to handle search index worker request"); try { IClient client = scope.ServiceProvider.GetRequiredService(); client.Notify(ex); } catch (Exception) { // do nothing } } } } catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException) { _logger.LogInformation("Search index worker service shutting down"); } } }