diff --git a/ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs index ba3f6bb1..871d29f5 100644 --- a/ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs @@ -163,44 +163,17 @@ public class BuildPlayoutHandler : IRequestHandler 0) { changeCount += 1; - var bulkConfig = new BulkConfig(); bool anyWatermarks = result.AddedItems.Any(i => i.PlayoutItemWatermarks is not null && i.PlayoutItemWatermarks.Count > 0); bool anyGraphicsElements = result.AddedItems.Any(i => i.PlayoutItemGraphicsElements is not null && i.PlayoutItemGraphicsElements.Count > 0); if (anyWatermarks || anyGraphicsElements) { - bulkConfig.SetOutputIdentity = true; + // need to use slow ef core to also insert watermarks and graphics elements properly + await dbContext.AddRangeAsync(result.AddedItems, cancellationToken); } - - await dbContext.BulkInsertAsync(result.AddedItems, bulkConfig, cancellationToken: cancellationToken); - - if (anyWatermarks) - { - // copy playout item ids back to watermarks - var allWatermarks = result.AddedItems.SelectMany(item => - (item.PlayoutItemWatermarks ?? []).Select(watermark => - { - watermark.PlayoutItemId = item.Id; - watermark.PlayoutItem = null; - return watermark; - }) - ).ToList(); - - await dbContext.BulkInsertAsync(allWatermarks, cancellationToken: cancellationToken); - } - - if (anyGraphicsElements) + else { - // copy playout item ids back to graphics elements - var allGraphicsElements = result.AddedItems.SelectMany(item => - (item.PlayoutItemGraphicsElements ?? []).Select(graphicsElement => - { - graphicsElement.PlayoutItemId = item.Id; - graphicsElement.PlayoutItem = null; - return graphicsElement; - }) - ).ToList(); - - await dbContext.BulkInsertAsync(allGraphicsElements, cancellationToken: cancellationToken); + // no watermarks or graphics, bulk insert is ok + await dbContext.BulkInsertAsync(result.AddedItems, cancellationToken: cancellationToken); } } diff --git a/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs b/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs index b909f73b..97fd59ac 100644 --- a/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs +++ b/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs @@ -755,6 +755,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< { if (playoutItem.DisableWatermarks) { + _logger.LogDebug("Watermark is disabled by playout item"); return new DisableWatermark(); } diff --git a/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs b/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs index aa0a9ada..a2164211 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs @@ -199,6 +199,8 @@ public class FFmpegProcessService break; } + _logger.LogDebug("Watermark will come from playout item (custom)"); + string customPath = _imageCache.GetPathForImage( watermark.Image, ArtworkKind.Watermark, @@ -209,6 +211,8 @@ public class FFmpegProcessService None, await IsAnimated(ffprobePath, customPath)); case ChannelWatermarkImageSource.ChannelLogo: + _logger.LogDebug("Watermark will come from playout item (channel logo)"); + Option maybeChannelPath = channel.Artwork.Count == 0 ? //We have to generate the logo on the fly and save it to a local temp path @@ -240,6 +244,8 @@ public class FFmpegProcessService switch (channel.Watermark.ImageSource) { case ChannelWatermarkImageSource.Custom: + _logger.LogDebug("Watermark will come from channel (custom)"); + string customPath = _imageCache.GetPathForImage( channel.Watermark.Image, ArtworkKind.Watermark, @@ -250,6 +256,8 @@ public class FFmpegProcessService None, await IsAnimated(ffprobePath, customPath)); case ChannelWatermarkImageSource.ChannelLogo: + _logger.LogDebug("Watermark will come from channel (channel logo)"); + Option maybeChannelPath = channel.Artwork.Count == 0 ? //We have to generate the logo on the fly and save it to a local temp path @@ -280,6 +288,8 @@ public class FFmpegProcessService switch (watermark.ImageSource) { case ChannelWatermarkImageSource.Custom: + _logger.LogDebug("Watermark will come from global (custom)"); + string customPath = _imageCache.GetPathForImage( watermark.Image, ArtworkKind.Watermark, @@ -290,6 +300,8 @@ public class FFmpegProcessService None, await IsAnimated(ffprobePath, customPath)); case ChannelWatermarkImageSource.ChannelLogo: + _logger.LogDebug("Watermark will come from global (channel logo)"); + Option maybeChannelPath = channel.Artwork.Count == 0 ? //We have to generate the logo on the fly and save it to a local temp path diff --git a/ErsatzTV/Controllers/Api/ChannelController.cs b/ErsatzTV/Controllers/Api/ChannelController.cs index 89f91b6b..7eccf81f 100644 --- a/ErsatzTV/Controllers/Api/ChannelController.cs +++ b/ErsatzTV/Controllers/Api/ChannelController.cs @@ -29,4 +29,17 @@ public class ChannelController(ChannelWriter workerCh return new NotFoundResult(); } + + [HttpPost("/api/channels/{channelNumber}/playout/continue")] + public async Task ContinuePlayout(string channelNumber) + { + Option maybePlayoutId = await mediator.Send(new GetPlayoutIdByChannelNumber(channelNumber)); + foreach (int playoutId in maybePlayoutId) + { + await workerChannel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Continue)); + return new OkResult(); + } + + return new NotFoundResult(); + } } diff --git a/ErsatzTV/Controllers/InternalController.cs b/ErsatzTV/Controllers/InternalController.cs index a252cab0..c633ea6d 100644 --- a/ErsatzTV/Controllers/InternalController.cs +++ b/ErsatzTV/Controllers/InternalController.cs @@ -121,6 +121,10 @@ public class InternalController : ControllerBase string path, CancellationToken cancellationToken) { +#if DEBUG_NO_SYNC + await Task.Delay(100, cancellationToken); + return NotFound(); +#else Either connectionParameters = await _mediator.Send(new GetPlexConnectionParameters(plexMediaSourceId), cancellationToken); @@ -131,6 +135,7 @@ public class InternalController : ControllerBase Url fullPath = new Uri(r.Uri, path).SetQueryParam("X-Plex-Token", r.AuthToken); return new RedirectResult(fullPath.ToString()); }); +#endif } [HttpGet("/media/jellyfin/{*path}")] diff --git a/ErsatzTV/Services/PlexService.cs b/ErsatzTV/Services/PlexService.cs index 7601755b..99d9c05f 100644 --- a/ErsatzTV/Services/PlexService.cs +++ b/ErsatzTV/Services/PlexService.cs @@ -100,6 +100,12 @@ public class PlexService : BackgroundService SynchronizePlexMediaSources request, CancellationToken cancellationToken) { +#if DEBUG_NO_SYNC + _logger.LogDebug("Skipping plex media source sync"); + await Task.Delay(10, cancellationToken); + _ = request; + return []; +#else using IServiceScope scope = _serviceScopeFactory.CreateScope(); IMediator mediator = scope.ServiceProvider.GetRequiredService(); @@ -121,6 +127,7 @@ public class PlexService : BackgroundService error.Value); return new List(); }); +#endif } private async Task CompletePinFlow(