Browse Source

fix saving watermarks and graphics on playout items (#2313)

pull/2314/head
Jason Dove 3 days ago committed by GitHub
parent
commit
0841bc400b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 37
      ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs
  2. 1
      ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs
  3. 12
      ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs
  4. 13
      ErsatzTV/Controllers/Api/ChannelController.cs
  5. 5
      ErsatzTV/Controllers/InternalController.cs
  6. 7
      ErsatzTV/Services/PlexService.cs

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

@ -163,44 +163,17 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro @@ -163,44 +163,17 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
if (result.AddedItems.Count > 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);
}
}

1
ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs

@ -755,6 +755,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< @@ -755,6 +755,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
{
if (playoutItem.DisableWatermarks)
{
_logger.LogDebug("Watermark is disabled by playout item");
return new DisableWatermark();
}

12
ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs

@ -199,6 +199,8 @@ public class FFmpegProcessService @@ -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 @@ -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<string> 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 @@ -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 @@ -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<string> 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 @@ -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 @@ -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<string> maybeChannelPath = channel.Artwork.Count == 0
?
//We have to generate the logo on the fly and save it to a local temp path

13
ErsatzTV/Controllers/Api/ChannelController.cs

@ -29,4 +29,17 @@ public class ChannelController(ChannelWriter<IBackgroundServiceRequest> workerCh @@ -29,4 +29,17 @@ public class ChannelController(ChannelWriter<IBackgroundServiceRequest> workerCh
return new NotFoundResult();
}
[HttpPost("/api/channels/{channelNumber}/playout/continue")]
public async Task<IActionResult> ContinuePlayout(string channelNumber)
{
Option<int> 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();
}
}

5
ErsatzTV/Controllers/InternalController.cs

@ -121,6 +121,10 @@ public class InternalController : ControllerBase @@ -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<BaseError, PlexConnectionParametersViewModel> connectionParameters =
await _mediator.Send(new GetPlexConnectionParameters(plexMediaSourceId), cancellationToken);
@ -131,6 +135,7 @@ public class InternalController : ControllerBase @@ -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}")]

7
ErsatzTV/Services/PlexService.cs

@ -100,6 +100,12 @@ public class PlexService : BackgroundService @@ -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<IMediator>();
@ -121,6 +127,7 @@ public class PlexService : BackgroundService @@ -121,6 +127,7 @@ public class PlexService : BackgroundService
error.Value);
return new List<PlexMediaSource>();
});
#endif
}
private async Task CompletePinFlow(

Loading…
Cancel
Save