Browse Source

add scripted playout timeout setting (#2456)

* add setting for scripted playout build timeout

* update dependencies
pull/2457/head
Jason Dove 10 months ago committed by GitHub
parent
commit
18523dce64
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 24
      ErsatzTV.Application/Configuration/Commands/UpdatePlayoutSettingsHandler.cs
  3. 1
      ErsatzTV.Application/Configuration/PlayoutSettingsViewModel.cs
  4. 8
      ErsatzTV.Application/Configuration/Queries/GetPlayoutSettingsHandler.cs
  5. 2
      ErsatzTV.Application/ErsatzTV.Application.csproj
  6. 4
      ErsatzTV.Core/Domain/ConfigElementKey.cs
  7. 8
      ErsatzTV.Core/ErsatzTV.Core.csproj
  8. 17
      ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs
  9. 2
      ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj
  10. 4
      ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj
  11. 4
      ErsatzTV/ErsatzTV.csproj
  12. 10
      ErsatzTV/Pages/Settings/PlayoutSettings.razor

2
CHANGELOG.md

@ -52,6 +52,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Future work will add other collection options which will pad to the full block duration - Future work will add other collection options which will pad to the full block duration
- Add page to reorder channels (edit channel numbers) using drag and drop - Add page to reorder channels (edit channel numbers) using drag and drop
- New page is at **Channels** > **Edit Channel Numbers** - New page is at **Channels** > **Edit Channel Numbers**
- Scripted schedules: add setting to configure timeout of scripted playout build
- New setting is at **Settings** > **Playout** > **Scripted Schedule Timeout**
### Fixed ### Fixed
- Fix green output when libplacebo tonemapping is used with NVIDIA acceleration and 10-bit output in FFmpeg Profile - Fix green output when libplacebo tonemapping is used with NVIDIA acceleration and 10-bit output in FFmpeg Profile

24
ErsatzTV.Application/Configuration/Commands/UpdatePlayoutSettingsHandler.cs

@ -32,15 +32,31 @@ public class UpdatePlayoutSettingsHandler : IRequestHandler<UpdatePlayoutSetting
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Unit> validation = await Validate(request); Validation<BaseError, Unit> validation = await Validate(request);
return await validation.Apply<Unit, Unit>(_ => ApplyUpdate(dbContext, request.PlayoutSettings, cancellationToken)); return await validation.Apply<Unit, Unit>(_ => ApplyUpdate(
dbContext,
request.PlayoutSettings,
cancellationToken));
} }
private async Task<Unit> ApplyUpdate(TvContext dbContext, PlayoutSettingsViewModel playoutSettings, CancellationToken cancellationToken) private async Task<Unit> ApplyUpdate(
TvContext dbContext,
PlayoutSettingsViewModel playoutSettings,
CancellationToken cancellationToken)
{ {
await _configElementRepository.Upsert(ConfigElementKey.PlayoutDaysToBuild, playoutSettings.DaysToBuild, cancellationToken); await _configElementRepository.Upsert(
ConfigElementKey.PlayoutDaysToBuild,
playoutSettings.DaysToBuild,
cancellationToken);
await _configElementRepository.Upsert( await _configElementRepository.Upsert(
ConfigElementKey.PlayoutSkipMissingItems, ConfigElementKey.PlayoutSkipMissingItems,
playoutSettings.SkipMissingItems, cancellationToken); playoutSettings.SkipMissingItems,
cancellationToken);
await _configElementRepository.Upsert(
ConfigElementKey.PlayoutScriptedScheduleTimeoutSeconds,
playoutSettings.ScriptedScheduleTimeoutSeconds,
cancellationToken);
// continue all playouts to proper number of days // continue all playouts to proper number of days
List<Playout> playouts = await dbContext.Playouts List<Playout> playouts = await dbContext.Playouts

1
ErsatzTV.Application/Configuration/PlayoutSettingsViewModel.cs

@ -4,4 +4,5 @@ public class PlayoutSettingsViewModel
{ {
public int DaysToBuild { get; set; } public int DaysToBuild { get; set; }
public bool SkipMissingItems { get; set; } public bool SkipMissingItems { get; set; }
public int ScriptedScheduleTimeoutSeconds { get; set; }
} }

8
ErsatzTV.Application/Configuration/Queries/GetPlayoutSettingsHandler.cs

@ -19,10 +19,16 @@ public class GetPlayoutSettingsHandler : IRequestHandler<GetPlayoutSettings, Pla
Option<bool> skipMissingItems = Option<bool> skipMissingItems =
await _configElementRepository.GetValue<bool>(ConfigElementKey.PlayoutSkipMissingItems, cancellationToken); await _configElementRepository.GetValue<bool>(ConfigElementKey.PlayoutSkipMissingItems, cancellationToken);
Option<int> scriptedScheduleTimeoutSeconds =
await _configElementRepository.GetValue<int>(
ConfigElementKey.PlayoutScriptedScheduleTimeoutSeconds,
cancellationToken);
return new PlayoutSettingsViewModel return new PlayoutSettingsViewModel
{ {
DaysToBuild = await daysToBuild.IfNoneAsync(2), DaysToBuild = await daysToBuild.IfNoneAsync(2),
SkipMissingItems = await skipMissingItems.IfNoneAsync(false) SkipMissingItems = await skipMissingItems.IfNoneAsync(false),
ScriptedScheduleTimeoutSeconds = await scriptedScheduleTimeoutSeconds.IfNoneAsync(30)
}; };
} }
} }

2
ErsatzTV.Application/ErsatzTV.Application.csproj

@ -20,7 +20,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Serilog.Formatting.Compact.Reader" Version="4.0.0" /> <PackageReference Include="Serilog.Formatting.Compact.Reader" Version="4.0.0" />
<PackageReference Include="WebMarkupMin.Core" Version="2.19.0" /> <PackageReference Include="WebMarkupMin.Core" Version="2.19.0" />
<PackageReference Include="Winista.MimeDetect" Version="1.1.0" /> <PackageReference Include="Winista.MimeDetect" Version="1.1.0" />

4
ErsatzTV.Core/Domain/ConfigElementKey.cs

@ -46,6 +46,10 @@ public class ConfigElementKey
public static ConfigElementKey LibraryRefreshInterval => new("scanner.library_refresh_interval"); public static ConfigElementKey LibraryRefreshInterval => new("scanner.library_refresh_interval");
public static ConfigElementKey PlayoutDaysToBuild => new("playout.days_to_build"); public static ConfigElementKey PlayoutDaysToBuild => new("playout.days_to_build");
public static ConfigElementKey PlayoutSkipMissingItems => new("playout.skip_missing_items"); public static ConfigElementKey PlayoutSkipMissingItems => new("playout.skip_missing_items");
public static ConfigElementKey PlayoutScriptedScheduleTimeoutSeconds =>
new("playout.scripted_schedule_timeout_seconds");
public static ConfigElementKey XmltvTimeZone => new("xmltv.time_zone"); public static ConfigElementKey XmltvTimeZone => new("xmltv.time_zone");
public static ConfigElementKey XmltvDaysToBuild => new("xmltv.days_to_build"); public static ConfigElementKey XmltvDaysToBuild => new("xmltv.days_to_build");
public static ConfigElementKey XmltvBlockBehavior => new("xmltv.block_behavior"); public static ConfigElementKey XmltvBlockBehavior => new("xmltv.block_behavior");

8
ErsatzTV.Core/ErsatzTV.Core.csproj

@ -25,12 +25,12 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="NCalcSync" Version="5.6.0" /> <PackageReference Include="NCalcSync" Version="5.7.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Serilog" Version="4.3.0" /> <PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="SkiaSharp" Version="3.119.0" /> <PackageReference Include="SkiaSharp" Version="3.119.1" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.119.0" /> <PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.119.1" />
<PackageReference Include="System.CommandLine" Version="2.0.0-rc.1.25451.107" /> <PackageReference Include="System.CommandLine" Version="2.0.0-rc.1.25451.107" />
<PackageReference Include="TimeSpanParserUtil" Version="1.2.0" /> <PackageReference Include="TimeSpanParserUtil" Version="1.2.0" />
<PackageReference Include="YamlDotNet" Version="16.3.0" /> <PackageReference Include="YamlDotNet" Version="16.3.0" />

17
ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs

@ -29,6 +29,8 @@ public class ScriptedPlayoutBuilder(
Guid buildId = scriptedPlayoutBuilderService.StartSession(schedulingEngine); Guid buildId = scriptedPlayoutBuilderService.StartSession(schedulingEngine);
var timeoutSeconds = 30;
try try
{ {
var args = CommandLineParser.SplitCommandLine(playout.ScheduleFile).ToList(); var args = CommandLineParser.SplitCommandLine(playout.ScheduleFile).ToList();
@ -72,7 +74,16 @@ public class ScriptedPlayoutBuilder(
schedulingEngine.RestoreOrReset(Optional(playout.Anchor)); schedulingEngine.RestoreOrReset(Optional(playout.Anchor));
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); Option<int> maybeTimeoutSeconds = await configElementRepository.GetValue<int>(
ConfigElementKey.PlayoutScriptedScheduleTimeoutSeconds,
cancellationToken);
foreach (int seconds in maybeTimeoutSeconds)
{
timeoutSeconds = seconds;
}
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cts.Token); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cts.Token);
Command command = Cli.Wrap(scriptFile) Command command = Cli.Wrap(scriptFile)
@ -99,8 +110,8 @@ public class ScriptedPlayoutBuilder(
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
logger.LogWarning("Scripted playout build timed out after 30 seconds"); logger.LogWarning("Scripted playout build timed out after {TimeoutSeconds} seconds", timeoutSeconds);
throw new TimeoutException("Scripted playout build timed out after 30 seconds"); throw new TimeoutException($"Scripted playout build timed out after {timeoutSeconds} seconds");
} }
catch (Exception ex) catch (Exception ex)
{ {

2
ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj

@ -11,7 +11,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CliWrap" Version="3.9.0" /> <PackageReference Include="CliWrap" Version="3.9.0" />
<PackageReference Include="Hardware.Info" Version="101.0.1.1" /> <PackageReference Include="Hardware.Info" Version="101.1.0" />
<PackageReference Include="LanguageExt.Core" Version="4.4.9" /> <PackageReference Include="LanguageExt.Core" Version="4.4.9" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.9" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.9" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.9" />

4
ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj

@ -34,9 +34,9 @@
<PackageReference Include="Refit.Newtonsoft.Json" Version="8.0.0" /> <PackageReference Include="Refit.Newtonsoft.Json" Version="8.0.0" />
<PackageReference Include="Refit.Xml" Version="8.0.0" /> <PackageReference Include="Refit.Xml" Version="8.0.0" />
<PackageReference Include="RichTextKit.Stbear" Version="0.4.167.3" /> <PackageReference Include="RichTextKit.Stbear" Version="0.4.167.3" />
<PackageReference Include="Scriban.Signed" Version="6.3.0" /> <PackageReference Include="Scriban.Signed" Version="6.4.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" /> <PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
<PackageReference Include="SkiaSharp" Version="3.119.0" /> <PackageReference Include="SkiaSharp" Version="3.119.1" />
<PackageReference Include="TagLibSharp" Version="2.3.0" /> <PackageReference Include="TagLibSharp" Version="2.3.0" />
<PackageReference Include="TimeZoneConverter" Version="7.0.0" /> <PackageReference Include="TimeZoneConverter" Version="7.0.0" />
</ItemGroup> </ItemGroup>

4
ErsatzTV/ErsatzTV.csproj

@ -55,9 +55,9 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="MudBlazor" Version="8.12.0" /> <PackageReference Include="MudBlazor" Version="8.12.0" />
<PackageReference Include="NaturalSort.Extension" Version="4.3.0" /> <PackageReference Include="NaturalSort.Extension" Version="4.4.0" />
<PackageReference Include="Refit.HttpClientFactory" Version="8.0.0" /> <PackageReference Include="Refit.HttpClientFactory" Version="8.0.0" />
<PackageReference Include="Scalar.AspNetCore" Version="2.8.1" /> <PackageReference Include="Scalar.AspNetCore" Version="2.8.6" />
<PackageReference Include="Serilog" Version="4.3.0" /> <PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" /> <PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" /> <PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />

10
ErsatzTV/Pages/Settings/PlayoutSettings.razor

@ -27,6 +27,12 @@
<MudText Typo="Typo.caption" Style="font-weight: normal">Controls whether file-not-found or unavailable items should be included in playouts</MudText> <MudText Typo="Typo.caption" Style="font-weight: normal">Controls whether file-not-found or unavailable items should be included in playouts</MudText>
</MudCheckBox> </MudCheckBox>
</MudStack> </MudStack>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Scripted Schedule Timeout</MudText>
</div>
<MudTextField @bind-Value="_playoutSettings.ScriptedScheduleTimeoutSeconds" Validation="@(new Func<int, string>(ValidateScriptedScheduleTimeoutSeconds))" Required="true" RequiredError="Scripted schedule timeout is required!" Adornment="Adornment.End" AdornmentText="seconds"/>
</MudStack>
</MudContainer> </MudContainer>
</div> </div>
</MudForm> </MudForm>
@ -54,7 +60,7 @@
try try
{ {
_playoutSettings = await Mediator.Send(new GetPlayoutSettings(), token); _playoutSettings = await Mediator.Send(new GetPlayoutSettings(), token);
_playoutSuccess = _playoutSettings.DaysToBuild > 0; _playoutSuccess = _playoutSettings.DaysToBuild > 0 && _playoutSettings.ScriptedScheduleTimeoutSeconds >= 10;
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -64,6 +70,8 @@
private static string ValidatePlayoutDaysToBuild(int daysToBuild) => daysToBuild <= 0 ? "Playout days to build must be greater than zero" : null; private static string ValidatePlayoutDaysToBuild(int daysToBuild) => daysToBuild <= 0 ? "Playout days to build must be greater than zero" : null;
private static string ValidateScriptedScheduleTimeoutSeconds(int seconds) => seconds < 10 ? "Scripted schedule timeout seconds must be at least 10" : null;
private async Task SavePlayoutSettings() private async Task SavePlayoutSettings()
{ {
await _form.Validate(); await _form.Validate();

Loading…
Cancel
Save