Browse Source

bug fixes (#1107)

* don't search an empty search index

* fix bug with flood filler prediction check

* extract subtitles on primary worker thread
pull/1108/head
Jason Dove 4 years ago committed by GitHub
parent
commit
585b56a668
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs
  2. 5
      ErsatzTV.Application/ISubtitleWorkerRequest.cs
  3. 8
      ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs
  4. 2
      ErsatzTV.Application/Subtitles/Commands/ExtractEmbeddedSubtitles.cs
  5. 15
      ErsatzTV.Core/Scheduling/PlayoutBuilder.cs
  6. 19
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs
  7. 5
      ErsatzTV.Infrastructure/Search/SearchIndex.cs
  8. 66
      ErsatzTV/Services/SubtitleWorkerService.cs
  9. 4
      ErsatzTV/Services/WorkerService.cs
  10. 2
      ErsatzTV/Startup.cs

8
ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs

@ -15,13 +15,13 @@ namespace ErsatzTV.Application.Channels;
public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseError, ChannelViewModel>> public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseError, ChannelViewModel>>
{ {
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly ChannelWriter<ISubtitleWorkerRequest> _ffmpegWorkerChannel; private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
public UpdateChannelHandler( public UpdateChannelHandler(
ChannelWriter<ISubtitleWorkerRequest> ffmpegWorkerChannel, ChannelWriter<IBackgroundServiceRequest> workerChannel,
IDbContextFactory<TvContext> dbContextFactory) IDbContextFactory<TvContext> dbContextFactory)
{ {
_ffmpegWorkerChannel = ffmpegWorkerChannel; _workerChannel = workerChannel;
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
} }
@ -85,7 +85,7 @@ public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseEr
foreach (Playout playout in maybePlayout) foreach (Playout playout in maybePlayout)
{ {
await _ffmpegWorkerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id)); await _workerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id));
} }
} }

5
ErsatzTV.Application/ISubtitleWorkerRequest.cs

@ -1,5 +0,0 @@
namespace ErsatzTV.Application;
public interface ISubtitleWorkerRequest
{
}

8
ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs

@ -17,7 +17,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
private readonly IClient _client; private readonly IClient _client;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IFFmpegSegmenterService _ffmpegSegmenterService; private readonly IFFmpegSegmenterService _ffmpegSegmenterService;
private readonly ChannelWriter<ISubtitleWorkerRequest> _ffmpegWorkerChannel; private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
private readonly IPlayoutBuilder _playoutBuilder; private readonly IPlayoutBuilder _playoutBuilder;
public BuildPlayoutHandler( public BuildPlayoutHandler(
@ -25,13 +25,13 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
IPlayoutBuilder playoutBuilder, IPlayoutBuilder playoutBuilder,
IFFmpegSegmenterService ffmpegSegmenterService, IFFmpegSegmenterService ffmpegSegmenterService,
ChannelWriter<ISubtitleWorkerRequest> ffmpegWorkerChannel) ChannelWriter<IBackgroundServiceRequest> workerChannel)
{ {
_client = client; _client = client;
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_playoutBuilder = playoutBuilder; _playoutBuilder = playoutBuilder;
_ffmpegSegmenterService = ffmpegSegmenterService; _ffmpegSegmenterService = ffmpegSegmenterService;
_ffmpegWorkerChannel = ffmpegWorkerChannel; _workerChannel = workerChannel;
} }
public async Task<Either<BaseError, Unit>> Handle(BuildPlayout request, CancellationToken cancellationToken) public async Task<Either<BaseError, Unit>> Handle(BuildPlayout request, CancellationToken cancellationToken)
@ -56,7 +56,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
_ffmpegSegmenterService.PlayoutUpdated(playout.Channel.Number); _ffmpegSegmenterService.PlayoutUpdated(playout.Channel.Number);
} }
await _ffmpegWorkerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id)); await _workerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id));
} }
catch (Exception ex) catch (Exception ex)
{ {

2
ErsatzTV.Application/Subtitles/Commands/ExtractEmbeddedSubtitles.cs

@ -3,4 +3,4 @@
namespace ErsatzTV.Application.Subtitles; namespace ErsatzTV.Application.Subtitles;
public record ExtractEmbeddedSubtitles(Option<int> PlayoutId) : IRequest<Either<BaseError, Unit>>, public record ExtractEmbeddedSubtitles(Option<int> PlayoutId) : IRequest<Either<BaseError, Unit>>,
ISubtitleWorkerRequest; IBackgroundServiceRequest;

15
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

@ -737,17 +737,18 @@ public class PlayoutBuilder : IPlayoutBuilder
(anchor.EnumeratorState = new CollectionEnumeratorState { Seed = Random.Next(), Index = 0 }), (anchor.EnumeratorState = new CollectionEnumeratorState { Seed = Random.Next(), Index = 0 }),
() => new CollectionEnumeratorState { Seed = Random.Next(), Index = 0 }); () => new CollectionEnumeratorState { Seed = Random.Next(), Index = 0 });
if (await _mediaCollectionRepository.IsCustomPlaybackOrder(collectionKey.CollectionId ?? 0)) int collectionId = collectionKey.CollectionId ?? 0;
if (collectionKey.CollectionType == ProgramScheduleItemCollectionType.Collection &&
await _mediaCollectionRepository.IsCustomPlaybackOrder(collectionId))
{ {
Option<Collection> collectionWithItems = Option<Collection> maybeCollectionWithItems =
await _mediaCollectionRepository.GetCollectionWithCollectionItemsUntracked( await _mediaCollectionRepository.GetCollectionWithCollectionItemsUntracked(collectionId);
collectionKey.CollectionId ?? 0);
if (collectionKey.CollectionType == ProgramScheduleItemCollectionType.Collection && foreach (Collection collectionWithItems in maybeCollectionWithItems)
collectionWithItems.IsSome)
{ {
return new CustomOrderCollectionEnumerator( return new CustomOrderCollectionEnumerator(
collectionWithItems.ValueUnsafe(), collectionWithItems,
mediaItems, mediaItems,
state); state);
} }

19
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs

@ -91,15 +91,18 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul
AddFiller(nextState, collectionEnumerators, scheduleItem, playoutItem, itemChapters)); AddFiller(nextState, collectionEnumerators, scheduleItem, playoutItem, itemChapters));
// LogScheduledItem(scheduleItem, mediaItem, itemStartTime); // LogScheduledItem(scheduleItem, mediaItem, itemStartTime);
DateTimeOffset actualEndTime = playoutItems.Max(p => p.FinishOffset); if (playoutItems.Count > 0)
if (Math.Abs((itemEndTimeWithFiller - actualEndTime).TotalSeconds) > 1)
{ {
_logger.LogWarning( DateTimeOffset actualEndTime = playoutItems.Max(p => p.FinishOffset);
"Filler prediction failure: predicted {PredictedDuration} doesn't match actual {ActualDuration}", if (Math.Abs((itemEndTimeWithFiller - actualEndTime).TotalSeconds) > 1)
itemEndTimeWithFiller, {
actualEndTime); _logger.LogWarning(
"Filler prediction failure: predicted {PredictedDuration} doesn't match actual {ActualDuration}",
// _logger.LogWarning("Playout items: {@PlayoutItems}", playoutItems); itemEndTimeWithFiller,
actualEndTime);
// _logger.LogWarning("Playout items: {@PlayoutItems}", playoutItems);
}
} }
nextState = nextState with nextState = nextState with

5
ErsatzTV.Infrastructure/Search/SearchIndex.cs

@ -185,8 +185,9 @@ public sealed class SearchIndex : ISearchIndex
}; };
client.Breadcrumbs.Leave("SearchIndex.Search", BreadcrumbType.State, metadata); client.Breadcrumbs.Leave("SearchIndex.Search", BreadcrumbType.State, metadata);
if (string.IsNullOrWhiteSpace(searchQuery.Replace("*", string.Empty).Replace("?", string.Empty))) if (string.IsNullOrWhiteSpace(searchQuery.Replace("*", string.Empty).Replace("?", string.Empty)) ||
_writer.MaxDoc == 0)
{ {
return new SearchResult(new List<SearchItem>(), 0); return new SearchResult(new List<SearchItem>(), 0);
} }

66
ErsatzTV/Services/SubtitleWorkerService.cs

@ -1,66 +0,0 @@
using System.Threading.Channels;
using Bugsnag;
using ErsatzTV.Application;
using ErsatzTV.Application.Subtitles;
using MediatR;
namespace ErsatzTV.Services;
public class SubtitleWorkerService : BackgroundService
{
private readonly ChannelReader<ISubtitleWorkerRequest> _channel;
private readonly ILogger<SubtitleWorkerService> _logger;
private readonly IServiceScopeFactory _serviceScopeFactory;
public SubtitleWorkerService(
ChannelReader<ISubtitleWorkerRequest> channel,
IServiceScopeFactory serviceScopeFactory,
ILogger<SubtitleWorkerService> logger)
{
_channel = channel;
_serviceScopeFactory = serviceScopeFactory;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("Subtitle worker service started");
await foreach (ISubtitleWorkerRequest request in _channel.ReadAllAsync(cancellationToken))
{
using IServiceScope scope = _serviceScopeFactory.CreateScope();
try
{
switch (request)
{
case ExtractEmbeddedSubtitles extractEmbeddedSubtitles:
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
await mediator.Send(extractEmbeddedSubtitles, cancellationToken);
break;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to handle subtitle worker request");
try
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
catch (Exception)
{
// do nothing
}
}
}
}
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
{
_logger.LogInformation("Subtitle worker service shutting down");
}
}
}

4
ErsatzTV/Services/WorkerService.cs

@ -6,6 +6,7 @@ using ErsatzTV.Application.MediaCollections;
using ErsatzTV.Application.MediaSources; using ErsatzTV.Application.MediaSources;
using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Playouts;
using ErsatzTV.Application.Search; using ErsatzTV.Application.Search;
using ErsatzTV.Application.Subtitles;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Interfaces.Locking; using ErsatzTV.Core.Interfaces.Locking;
using MediatR; using MediatR;
@ -96,6 +97,9 @@ public class WorkerService : BackgroundService
case MatchTraktListItems matchTraktListItems: case MatchTraktListItems matchTraktListItems:
await mediator.Send(matchTraktListItems, cancellationToken); await mediator.Send(matchTraktListItems, cancellationToken);
break; break;
case ExtractEmbeddedSubtitles extractEmbeddedSubtitles:
await mediator.Send(extractEmbeddedSubtitles, cancellationToken);
break;
} }
} }
catch (ObjectDisposedException) when (cancellationToken.IsCancellationRequested) catch (ObjectDisposedException) when (cancellationToken.IsCancellationRequested)

2
ErsatzTV/Startup.cs

@ -375,7 +375,6 @@ public class Startup
AddChannel<IJellyfinBackgroundServiceRequest>(services); AddChannel<IJellyfinBackgroundServiceRequest>(services);
AddChannel<IEmbyBackgroundServiceRequest>(services); AddChannel<IEmbyBackgroundServiceRequest>(services);
AddChannel<IFFmpegWorkerRequest>(services); AddChannel<IFFmpegWorkerRequest>(services);
AddChannel<ISubtitleWorkerRequest>(services);
AddChannel<ISearchIndexBackgroundServiceRequest>(services); AddChannel<ISearchIndexBackgroundServiceRequest>(services);
services.AddScoped<IFFmpegVersionHealthCheck, FFmpegVersionHealthCheck>(); services.AddScoped<IFFmpegVersionHealthCheck, FFmpegVersionHealthCheck>();
@ -470,7 +469,6 @@ public class Startup
services.AddHostedService<EmbyService>(); services.AddHostedService<EmbyService>();
services.AddHostedService<JellyfinService>(); services.AddHostedService<JellyfinService>();
services.AddHostedService<PlexService>(); services.AddHostedService<PlexService>();
services.AddHostedService<SubtitleWorkerService>();
#endif #endif
services.AddHostedService<FFmpegLocatorService>(); services.AddHostedService<FFmpegLocatorService>();
services.AddHostedService<WorkerService>(); services.AddHostedService<WorkerService>();

Loading…
Cancel
Save