From daf7114ce22599aafc29ea32d7c86774e1e4b6a1 Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Thu, 5 May 2022 10:04:24 -0500 Subject: [PATCH] bug fixes and logging (#786) --- CHANGELOG.md | 2 ++ .../Streaming/HlsSessionWorker.cs | 2 +- ...layoutItemProcessByChannelNumberHandler.cs | 12 +++++++++- .../ExtractEmbeddedSubtitlesHandler.cs | 4 ++-- ErsatzTV.Infrastructure/Images/ImageCache.cs | 24 +++++++++---------- .../RunOnce/RebuildSearchIndexService.cs | 21 ++++++++++++++++ ErsatzTV/Services/SchedulerService.cs | 5 ---- ErsatzTV/Startup.cs | 4 ++++ 8 files changed, 52 insertions(+), 22 deletions(-) create mode 100644 ErsatzTV/Services/RunOnce/RebuildSearchIndexService.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4385745a..f933b9f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix processing local movie fallback metadata - Fix search edge case where very recently added items (hours) would not be returned by relative date queries - Fix search index validation on startup; improper validation was causing a rebuild with every startup +- Block library scanning until search index has been recreated/upgraded +- Fix occasional erroneous log messages when HLS channel playback times out because all clients have left ### Added - Add `show_genre` and `show_tag` to search index for seasons and episodes diff --git a/ErsatzTV.Application/Streaming/HlsSessionWorker.cs b/ErsatzTV.Application/Streaming/HlsSessionWorker.cs index 6b581281..8513dc02 100644 --- a/ErsatzTV.Application/Streaming/HlsSessionWorker.cs +++ b/ErsatzTV.Application/Streaming/HlsSessionWorker.cs @@ -276,7 +276,7 @@ public class HlsSessionWorker : IHlsSessionWorker return false; } } - catch (TaskCanceledException) + catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException) { _logger.LogInformation("Terminating HLS process for channel {Channel}", _channelNumber); return false; diff --git a/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs b/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs index 18f5552c..5a109de5 100644 --- a/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs +++ b/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs @@ -14,6 +14,7 @@ using ErsatzTV.Core.Scheduling; using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Extensions; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; namespace ErsatzTV.Application.Streaming; @@ -24,6 +25,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< private readonly IFFmpegProcessService _ffmpegProcessService; private readonly IJellyfinPathReplacementService _jellyfinPathReplacementService; private readonly ILocalFileSystem _localFileSystem; + private readonly ILogger _logger; private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IPlexPathReplacementService _plexPathReplacementService; private readonly ISongVideoGenerator _songVideoGenerator; @@ -39,7 +41,8 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< IMediaCollectionRepository mediaCollectionRepository, ITelevisionRepository televisionRepository, IArtistRepository artistRepository, - ISongVideoGenerator songVideoGenerator) + ISongVideoGenerator songVideoGenerator, + ILogger logger) : base(dbContextFactory) { _ffmpegProcessService = ffmpegProcessService; @@ -51,6 +54,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< _televisionRepository = televisionRepository; _artistRepository = artistRepository; _songVideoGenerator = songVideoGenerator; + _logger = logger; } protected override async Task> GetProcess( @@ -201,6 +205,12 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< DateTimeOffset finish = maybeDuration.Match(d => now.Add(d), () => now); + _logger.LogWarning( + "Error locating playout item {@Error}. Will display error from {Start} to {Finish}", + error, + now, + finish); + switch (error) { case UnableToLocatePlayoutItem: diff --git a/ErsatzTV.Application/Subtitles/Commands/ExtractEmbeddedSubtitlesHandler.cs b/ErsatzTV.Application/Subtitles/Commands/ExtractEmbeddedSubtitlesHandler.cs index ef0db617..8d4d808a 100644 --- a/ErsatzTV.Application/Subtitles/Commands/ExtractEmbeddedSubtitlesHandler.cs +++ b/ErsatzTV.Application/Subtitles/Commands/ExtractEmbeddedSubtitlesHandler.cs @@ -155,7 +155,7 @@ public class ExtractEmbeddedSubtitlesHandler : IRequestHandler.None); // ReSharper disable once ConvertToUsingDeclaration - using (FileStream fs = File.OpenRead(targetFile)) + using (var image = await Image.LoadAsync(targetFile)) { - using (var image = await Image.LoadAsync(fs)) + // resize before calculating blur hash; it doesn't need giant images + if (image.Height > 200) { - // resize before calculating blur hash; it doesn't need giant images - if (image.Height > 200) - { - image.Mutate(i => i.Resize(0, 200)); - } - else if (image.Width > 200) - { - image.Mutate(i => i.Resize(200, 0)); - } - - return Blurhasher.Encode(image, x, y); + image.Mutate(i => i.Resize(0, 200)); } + else if (image.Width > 200) + { + image.Mutate(i => i.Resize(200, 0)); + } + + return Blurhasher.Encode(image, x, y); } } @@ -151,6 +148,7 @@ public class ImageCache : IImageCache _localFileSystem.EnsureFolderExists(folder); // ReSharper disable once ConvertToUsingDeclaration + // ReSharper disable once UseAwaitUsing using (FileStream fs = File.OpenWrite(targetFile)) { using (Image image = Blurhasher.Decode(blurHash, targetSize.Width, targetSize.Height)) diff --git a/ErsatzTV/Services/RunOnce/RebuildSearchIndexService.cs b/ErsatzTV/Services/RunOnce/RebuildSearchIndexService.cs new file mode 100644 index 00000000..3fa762ac --- /dev/null +++ b/ErsatzTV/Services/RunOnce/RebuildSearchIndexService.cs @@ -0,0 +1,21 @@ +using ErsatzTV.Application.Search; +using MediatR; + +namespace ErsatzTV.Services.RunOnce; + +public class RebuildSearchIndexService : IHostedService +{ + private readonly IServiceScopeFactory _serviceScopeFactory; + + public RebuildSearchIndexService(IServiceScopeFactory serviceScopeFactory) => + _serviceScopeFactory = serviceScopeFactory; + + public async Task StartAsync(CancellationToken cancellationToken) + { + using IServiceScope scope = _serviceScopeFactory.CreateScope(); + IMediator mediator = scope.ServiceProvider.GetRequiredService(); + await mediator.Send(new RebuildSearchIndex(), cancellationToken); + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; +} diff --git a/ErsatzTV/Services/SchedulerService.cs b/ErsatzTV/Services/SchedulerService.cs index 6a3d131c..1624feba 100644 --- a/ErsatzTV/Services/SchedulerService.cs +++ b/ErsatzTV/Services/SchedulerService.cs @@ -8,7 +8,6 @@ using ErsatzTV.Application.MediaCollections; using ErsatzTV.Application.MediaSources; using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Plex; -using ErsatzTV.Application.Search; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Locking; using ErsatzTV.Core.Scheduling; @@ -93,7 +92,6 @@ public class SchedulerService : BackgroundService try { await DeleteOrphanedArtwork(cancellationToken); - await RebuildSearchIndex(cancellationToken); await BuildPlayouts(cancellationToken); await ScanLocalMediaSources(cancellationToken); await ScanPlexMediaSources(cancellationToken); @@ -266,9 +264,6 @@ public class SchedulerService : BackgroundService } } - private ValueTask RebuildSearchIndex(CancellationToken cancellationToken) => - _workerChannel.WriteAsync(new RebuildSearchIndex(), cancellationToken); - private ValueTask DeleteOrphanedArtwork(CancellationToken cancellationToken) => _workerChannel.WriteAsync(new DeleteOrphanedArtwork(), cancellationToken); } diff --git a/ErsatzTV/Startup.cs b/ErsatzTV/Startup.cs index e9e8f2ad..8b1cd090 100644 --- a/ErsatzTV/Startup.cs +++ b/ErsatzTV/Startup.cs @@ -418,11 +418,15 @@ public class Startup // services.AddTransient(typeof(IRequestHandler<,>), typeof(GetRecentLogEntriesHandler<>)); + // run-once/blocking startup services services.AddHostedService(); services.AddHostedService(); services.AddHostedService(); services.AddHostedService(); services.AddHostedService(); + services.AddHostedService(); + + // background services services.AddHostedService(); services.AddHostedService(); services.AddHostedService();