bug fixes (#1107)
* don't search an empty search index * fix bug with flood filler prediction check * extract subtitles on primary worker thread
This commit is contained in:
@@ -15,13 +15,13 @@ namespace ErsatzTV.Application.Channels;
|
||||
public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseError, ChannelViewModel>>
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly ChannelWriter<ISubtitleWorkerRequest> _ffmpegWorkerChannel;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
|
||||
public UpdateChannelHandler(
|
||||
ChannelWriter<ISubtitleWorkerRequest> ffmpegWorkerChannel,
|
||||
ChannelWriter<IBackgroundServiceRequest> workerChannel,
|
||||
IDbContextFactory<TvContext> dbContextFactory)
|
||||
{
|
||||
_ffmpegWorkerChannel = ffmpegWorkerChannel;
|
||||
_workerChannel = workerChannel;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseEr
|
||||
|
||||
foreach (Playout playout in maybePlayout)
|
||||
{
|
||||
await _ffmpegWorkerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id));
|
||||
await _workerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
namespace ErsatzTV.Application;
|
||||
|
||||
public interface ISubtitleWorkerRequest
|
||||
{
|
||||
}
|
||||
@@ -17,7 +17,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
private readonly IClient _client;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly IFFmpegSegmenterService _ffmpegSegmenterService;
|
||||
private readonly ChannelWriter<ISubtitleWorkerRequest> _ffmpegWorkerChannel;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
private readonly IPlayoutBuilder _playoutBuilder;
|
||||
|
||||
public BuildPlayoutHandler(
|
||||
@@ -25,13 +25,13 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
IDbContextFactory<TvContext> dbContextFactory,
|
||||
IPlayoutBuilder playoutBuilder,
|
||||
IFFmpegSegmenterService ffmpegSegmenterService,
|
||||
ChannelWriter<ISubtitleWorkerRequest> ffmpegWorkerChannel)
|
||||
ChannelWriter<IBackgroundServiceRequest> workerChannel)
|
||||
{
|
||||
_client = client;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_playoutBuilder = playoutBuilder;
|
||||
_ffmpegSegmenterService = ffmpegSegmenterService;
|
||||
_ffmpegWorkerChannel = ffmpegWorkerChannel;
|
||||
_workerChannel = workerChannel;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
await _ffmpegWorkerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id));
|
||||
await _workerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
namespace ErsatzTV.Application.Subtitles;
|
||||
|
||||
public record ExtractEmbeddedSubtitles(Option<int> PlayoutId) : IRequest<Either<BaseError, Unit>>,
|
||||
ISubtitleWorkerRequest;
|
||||
IBackgroundServiceRequest;
|
||||
|
||||
@@ -737,17 +737,18 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
(anchor.EnumeratorState = new CollectionEnumeratorState { Seed = Random.Next(), Index = 0 }),
|
||||
() => new CollectionEnumeratorState { Seed = Random.Next(), Index = 0 });
|
||||
|
||||
if (await _mediaCollectionRepository.IsCustomPlaybackOrder(collectionKey.CollectionId ?? 0))
|
||||
{
|
||||
Option<Collection> collectionWithItems =
|
||||
await _mediaCollectionRepository.GetCollectionWithCollectionItemsUntracked(
|
||||
collectionKey.CollectionId ?? 0);
|
||||
int collectionId = collectionKey.CollectionId ?? 0;
|
||||
|
||||
if (collectionKey.CollectionType == ProgramScheduleItemCollectionType.Collection &&
|
||||
collectionWithItems.IsSome)
|
||||
if (collectionKey.CollectionType == ProgramScheduleItemCollectionType.Collection &&
|
||||
await _mediaCollectionRepository.IsCustomPlaybackOrder(collectionId))
|
||||
{
|
||||
Option<Collection> maybeCollectionWithItems =
|
||||
await _mediaCollectionRepository.GetCollectionWithCollectionItemsUntracked(collectionId);
|
||||
|
||||
foreach (Collection collectionWithItems in maybeCollectionWithItems)
|
||||
{
|
||||
return new CustomOrderCollectionEnumerator(
|
||||
collectionWithItems.ValueUnsafe(),
|
||||
collectionWithItems,
|
||||
mediaItems,
|
||||
state);
|
||||
}
|
||||
|
||||
@@ -91,15 +91,18 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul
|
||||
AddFiller(nextState, collectionEnumerators, scheduleItem, playoutItem, itemChapters));
|
||||
// LogScheduledItem(scheduleItem, mediaItem, itemStartTime);
|
||||
|
||||
DateTimeOffset actualEndTime = playoutItems.Max(p => p.FinishOffset);
|
||||
if (Math.Abs((itemEndTimeWithFiller - actualEndTime).TotalSeconds) > 1)
|
||||
if (playoutItems.Count > 0)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Filler prediction failure: predicted {PredictedDuration} doesn't match actual {ActualDuration}",
|
||||
itemEndTimeWithFiller,
|
||||
actualEndTime);
|
||||
DateTimeOffset actualEndTime = playoutItems.Max(p => p.FinishOffset);
|
||||
if (Math.Abs((itemEndTimeWithFiller - actualEndTime).TotalSeconds) > 1)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Filler prediction failure: predicted {PredictedDuration} doesn't match actual {ActualDuration}",
|
||||
itemEndTimeWithFiller,
|
||||
actualEndTime);
|
||||
|
||||
// _logger.LogWarning("Playout items: {@PlayoutItems}", playoutItems);
|
||||
// _logger.LogWarning("Playout items: {@PlayoutItems}", playoutItems);
|
||||
}
|
||||
}
|
||||
|
||||
nextState = nextState with
|
||||
|
||||
@@ -186,7 +186,8 @@ public sealed class SearchIndex : ISearchIndex
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using ErsatzTV.Application.MediaCollections;
|
||||
using ErsatzTV.Application.MediaSources;
|
||||
using ErsatzTV.Application.Playouts;
|
||||
using ErsatzTV.Application.Search;
|
||||
using ErsatzTV.Application.Subtitles;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Interfaces.Locking;
|
||||
using MediatR;
|
||||
@@ -96,6 +97,9 @@ public class WorkerService : BackgroundService
|
||||
case MatchTraktListItems matchTraktListItems:
|
||||
await mediator.Send(matchTraktListItems, cancellationToken);
|
||||
break;
|
||||
case ExtractEmbeddedSubtitles extractEmbeddedSubtitles:
|
||||
await mediator.Send(extractEmbeddedSubtitles, cancellationToken);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException) when (cancellationToken.IsCancellationRequested)
|
||||
|
||||
@@ -375,7 +375,6 @@ public class Startup
|
||||
AddChannel<IJellyfinBackgroundServiceRequest>(services);
|
||||
AddChannel<IEmbyBackgroundServiceRequest>(services);
|
||||
AddChannel<IFFmpegWorkerRequest>(services);
|
||||
AddChannel<ISubtitleWorkerRequest>(services);
|
||||
AddChannel<ISearchIndexBackgroundServiceRequest>(services);
|
||||
|
||||
services.AddScoped<IFFmpegVersionHealthCheck, FFmpegVersionHealthCheck>();
|
||||
@@ -470,7 +469,6 @@ public class Startup
|
||||
services.AddHostedService<EmbyService>();
|
||||
services.AddHostedService<JellyfinService>();
|
||||
services.AddHostedService<PlexService>();
|
||||
services.AddHostedService<SubtitleWorkerService>();
|
||||
#endif
|
||||
services.AddHostedService<FFmpegLocatorService>();
|
||||
services.AddHostedService<WorkerService>();
|
||||
|
||||
Reference in New Issue
Block a user