diff --git a/CHANGELOG.md b/CHANGELOG.md index 4398a3df8..aff287faa 100644 --- a/CHANGELOG.md +++ b/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 - Add page to reorder channels (edit channel numbers) using drag and drop - 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 - Fix green output when libplacebo tonemapping is used with NVIDIA acceleration and 10-bit output in FFmpeg Profile diff --git a/ErsatzTV.Application/Configuration/Commands/UpdatePlayoutSettingsHandler.cs b/ErsatzTV.Application/Configuration/Commands/UpdatePlayoutSettingsHandler.cs index 80841f0d4..65cd6a3fe 100644 --- a/ErsatzTV.Application/Configuration/Commands/UpdatePlayoutSettingsHandler.cs +++ b/ErsatzTV.Application/Configuration/Commands/UpdatePlayoutSettingsHandler.cs @@ -32,15 +32,31 @@ public class UpdatePlayoutSettingsHandler : IRequestHandler validation = await Validate(request); - return await validation.Apply(_ => ApplyUpdate(dbContext, request.PlayoutSettings, cancellationToken)); + return await validation.Apply(_ => ApplyUpdate( + dbContext, + request.PlayoutSettings, + cancellationToken)); } - private async Task ApplyUpdate(TvContext dbContext, PlayoutSettingsViewModel playoutSettings, CancellationToken cancellationToken) + private async Task 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( ConfigElementKey.PlayoutSkipMissingItems, - playoutSettings.SkipMissingItems, cancellationToken); + playoutSettings.SkipMissingItems, + cancellationToken); + + await _configElementRepository.Upsert( + ConfigElementKey.PlayoutScriptedScheduleTimeoutSeconds, + playoutSettings.ScriptedScheduleTimeoutSeconds, + cancellationToken); // continue all playouts to proper number of days List playouts = await dbContext.Playouts diff --git a/ErsatzTV.Application/Configuration/PlayoutSettingsViewModel.cs b/ErsatzTV.Application/Configuration/PlayoutSettingsViewModel.cs index beb21c018..ae3c562b7 100644 --- a/ErsatzTV.Application/Configuration/PlayoutSettingsViewModel.cs +++ b/ErsatzTV.Application/Configuration/PlayoutSettingsViewModel.cs @@ -4,4 +4,5 @@ public class PlayoutSettingsViewModel { public int DaysToBuild { get; set; } public bool SkipMissingItems { get; set; } + public int ScriptedScheduleTimeoutSeconds { get; set; } } diff --git a/ErsatzTV.Application/Configuration/Queries/GetPlayoutSettingsHandler.cs b/ErsatzTV.Application/Configuration/Queries/GetPlayoutSettingsHandler.cs index d7bca37e9..a4af36a5b 100644 --- a/ErsatzTV.Application/Configuration/Queries/GetPlayoutSettingsHandler.cs +++ b/ErsatzTV.Application/Configuration/Queries/GetPlayoutSettingsHandler.cs @@ -19,10 +19,16 @@ public class GetPlayoutSettingsHandler : IRequestHandler skipMissingItems = await _configElementRepository.GetValue(ConfigElementKey.PlayoutSkipMissingItems, cancellationToken); + Option scriptedScheduleTimeoutSeconds = + await _configElementRepository.GetValue( + ConfigElementKey.PlayoutScriptedScheduleTimeoutSeconds, + cancellationToken); + return new PlayoutSettingsViewModel { DaysToBuild = await daysToBuild.IfNoneAsync(2), - SkipMissingItems = await skipMissingItems.IfNoneAsync(false) + SkipMissingItems = await skipMissingItems.IfNoneAsync(false), + ScriptedScheduleTimeoutSeconds = await scriptedScheduleTimeoutSeconds.IfNoneAsync(30) }; } } diff --git a/ErsatzTV.Application/ErsatzTV.Application.csproj b/ErsatzTV.Application/ErsatzTV.Application.csproj index f1666f267..bd7a18fc9 100644 --- a/ErsatzTV.Application/ErsatzTV.Application.csproj +++ b/ErsatzTV.Application/ErsatzTV.Application.csproj @@ -20,7 +20,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/ErsatzTV.Core/Domain/ConfigElementKey.cs b/ErsatzTV.Core/Domain/ConfigElementKey.cs index 9a4a81e27..2a068c045 100644 --- a/ErsatzTV.Core/Domain/ConfigElementKey.cs +++ b/ErsatzTV.Core/Domain/ConfigElementKey.cs @@ -46,6 +46,10 @@ public class ConfigElementKey public static ConfigElementKey LibraryRefreshInterval => new("scanner.library_refresh_interval"); public static ConfigElementKey PlayoutDaysToBuild => new("playout.days_to_build"); 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 XmltvDaysToBuild => new("xmltv.days_to_build"); public static ConfigElementKey XmltvBlockBehavior => new("xmltv.block_behavior"); diff --git a/ErsatzTV.Core/ErsatzTV.Core.csproj b/ErsatzTV.Core/ErsatzTV.Core.csproj index 279c1183d..ada279d14 100644 --- a/ErsatzTV.Core/ErsatzTV.Core.csproj +++ b/ErsatzTV.Core/ErsatzTV.Core.csproj @@ -25,12 +25,12 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - - + + diff --git a/ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs b/ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs index 3a5d53d9a..834a2abcb 100644 --- a/ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs +++ b/ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs @@ -29,6 +29,8 @@ public class ScriptedPlayoutBuilder( Guid buildId = scriptedPlayoutBuilderService.StartSession(schedulingEngine); + var timeoutSeconds = 30; + try { var args = CommandLineParser.SplitCommandLine(playout.ScheduleFile).ToList(); @@ -72,7 +74,16 @@ public class ScriptedPlayoutBuilder( schedulingEngine.RestoreOrReset(Optional(playout.Anchor)); - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + Option maybeTimeoutSeconds = await configElementRepository.GetValue( + 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); Command command = Cli.Wrap(scriptFile) @@ -99,8 +110,8 @@ public class ScriptedPlayoutBuilder( } catch (OperationCanceledException) { - logger.LogWarning("Scripted playout build timed out after 30 seconds"); - throw new TimeoutException("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 {timeoutSeconds} seconds"); } catch (Exception ex) { diff --git a/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj b/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj index 1fc7d1327..20c8e2837 100644 --- a/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj +++ b/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj @@ -11,7 +11,7 @@ - + diff --git a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj index acfc7c171..5ec74d881 100644 --- a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj +++ b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj @@ -34,9 +34,9 @@ - + - + diff --git a/ErsatzTV/ErsatzTV.csproj b/ErsatzTV/ErsatzTV.csproj index 3844b309a..4b298f4b3 100644 --- a/ErsatzTV/ErsatzTV.csproj +++ b/ErsatzTV/ErsatzTV.csproj @@ -55,9 +55,9 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + - + diff --git a/ErsatzTV/Pages/Settings/PlayoutSettings.razor b/ErsatzTV/Pages/Settings/PlayoutSettings.razor index fab1f6f5c..e93a395ae 100644 --- a/ErsatzTV/Pages/Settings/PlayoutSettings.razor +++ b/ErsatzTV/Pages/Settings/PlayoutSettings.razor @@ -27,6 +27,12 @@ Controls whether file-not-found or unavailable items should be included in playouts + +
+ Scripted Schedule Timeout +
+ +
@@ -54,7 +60,7 @@ try { _playoutSettings = await Mediator.Send(new GetPlayoutSettings(), token); - _playoutSuccess = _playoutSettings.DaysToBuild > 0; + _playoutSuccess = _playoutSettings.DaysToBuild > 0 && _playoutSettings.ScriptedScheduleTimeoutSeconds >= 10; } 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 ValidateScriptedScheduleTimeoutSeconds(int seconds) => seconds < 10 ? "Scripted schedule timeout seconds must be at least 10" : null; + private async Task SavePlayoutSettings() { await _form.Validate();