From 7bd8cefe2e0ee34efd10a563ca38f6224b6813d2 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Sat, 29 Nov 2025 15:47:08 -0600 Subject: [PATCH] more dotnet 10 updates (#2676) * update more libraries to dotnet 10 * fix dockerfiles * fix numeric types --- .../Api/ScriptedPlayout/ContentAll.cs | 6 +- .../Api/ScriptedPlayout/ContentCollection.cs | 5 +- .../ScriptedPlayout/ContentCreatePlaylist.cs | 4 +- .../Api/ScriptedPlayout/ContentMarathon.cs | 4 +- .../ScriptedPlayout/ContentMultiCollection.cs | 4 +- .../Api/ScriptedPlayout/ContentPlaylist.cs | 6 +- .../Api/ScriptedPlayout/ContentSearch.cs | 15 + .../Api/ScriptedPlayout/ContentShow.cs | 4 +- .../ScriptedPlayout/ContentSmartCollection.cs | 4 +- .../Api/ScriptedPlayout/ControlGraphicsOff.cs | 0 .../Api/ScriptedPlayout/ControlGraphicsOn.cs | 2 +- .../Api/ScriptedPlayout/ControlPreRollOn.cs | 2 +- .../Api/ScriptedPlayout/ControlSkipItems.cs | 4 +- .../Api/ScriptedPlayout/ControlSkipToItem.cs | 6 +- .../ScriptedPlayout/ControlStartEpgGroup.cs | 2 +- .../Api/ScriptedPlayout/ControlWaitUntil.cs | 2 +- .../ScriptedPlayout/ControlWaitUntilExact.cs | 2 +- .../ScriptedPlayout/ControlWatermarkOff.cs | 0 .../Api/ScriptedPlayout/ControlWatermarkOn.cs | 2 +- .../Api/ScriptedPlayout/PeekItemDuration.cs | 4 +- .../Api/ScriptedPlayout/PlaylistItem.cs | 4 +- .../Api/ScriptedPlayout/PlayoutContext.cs | 8 +- .../Api/ScriptedPlayout/PlayoutCount.cs | 8 +- .../Api/ScriptedPlayout/PlayoutDuration.cs | 10 +- .../Api/ScriptedPlayout/PlayoutPadToNext.cs | 10 +- .../Api/ScriptedPlayout/PlayoutPadUntil.cs | 10 +- .../ScriptedPlayout/PlayoutPadUntilExact.cs | 10 +- .../ErsatzTV.Core.Nullable.csproj | 14 + .../Api/ScriptedPlayout/ContentSearch.cs | 8 - ErsatzTV.Core/ErsatzTV.Core.csproj | 5 + ErsatzTV.Scanner/ErsatzTV.Scanner.csproj | 4 +- ErsatzTV.sln | 8 + ErsatzTV/ErsatzTV.csproj | 17 +- ErsatzTV/Startup.cs | 8 +- .../openapi/scripted-schedule-tagged.json | 361 +++++++++++------- .../wwwroot/openapi/scripted-schedule.json | 361 +++++++++++------- ErsatzTV/wwwroot/openapi/v1.json | 224 +++++++---- docker/Dockerfile | 2 + docker/arm32v7/Dockerfile | 2 + docker/arm64/Dockerfile | 2 + docker/ffmpeg-tests/Dockerfile | 2 + 41 files changed, 725 insertions(+), 431 deletions(-) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ContentAll.cs (72%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ContentCollection.cs (79%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ContentCreatePlaylist.cs (71%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ContentMarathon.cs (92%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ContentMultiCollection.cs (84%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ContentPlaylist.cs (70%) create mode 100644 ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentSearch.cs rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ContentShow.cs (77%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ContentSmartCollection.cs (84%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ControlGraphicsOff.cs (100%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ControlGraphicsOn.cs (81%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ControlPreRollOn.cs (77%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ControlSkipItems.cs (69%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ControlSkipToItem.cs (63%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ControlStartEpgGroup.cs (88%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ControlWaitUntil.cs (91%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ControlWaitUntilExact.cs (86%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ControlWatermarkOff.cs (100%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/ControlWatermarkOn.cs (75%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/PeekItemDuration.cs (60%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/PlaylistItem.cs (62%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/PlayoutContext.cs (61%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/PlayoutCount.cs (66%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/PlayoutDuration.cs (85%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/PlayoutPadToNext.cs (85%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/PlayoutPadUntil.cs (88%) rename {ErsatzTV.Core => ErsatzTV.Core.Nullable}/Api/ScriptedPlayout/PlayoutPadUntilExact.cs (85%) create mode 100644 ErsatzTV.Core.Nullable/ErsatzTV.Core.Nullable.csproj delete mode 100644 ErsatzTV.Core/Api/ScriptedPlayout/ContentSearch.cs diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ContentAll.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentAll.cs similarity index 72% rename from ErsatzTV.Core/Api/ScriptedPlayout/ContentAll.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentAll.cs index 83a5edf34..396d4d887 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ContentAll.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentAll.cs @@ -5,13 +5,13 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ContentAll { [Description("The 'key' for the content that should be added")] - public string Content { get; set; } + public required string Content { get; set; } [Description("Flags this content as filler, which influences EPG grouping")] - public string FillerKind { get; set; } + public string? FillerKind { get; set; } [Description("Overrides the title used in the EPG")] - public string CustomTitle { get; set; } + public string? CustomTitle { get; set; } public bool DisableWatermarks { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ContentCollection.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentCollection.cs similarity index 79% rename from ErsatzTV.Core/Api/ScriptedPlayout/ContentCollection.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentCollection.cs index 10228a84f..2a9230f78 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ContentCollection.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentCollection.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; namespace ErsatzTV.Core.Api.ScriptedPlayout; @@ -7,10 +8,10 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ContentCollection { [Description("Unique name used to reference this content throughout the scripted schedule")] - public string Key { get; init; } + public required string Key { get; init; } [Description("The name of the existing manual collection")] - public string Collection { get; init; } + public required string Collection { get; init; } [Description("The playback order; only chronological and shuffle are currently supported")] public string Order { get; init; } = "shuffle"; diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ContentCreatePlaylist.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentCreatePlaylist.cs similarity index 71% rename from ErsatzTV.Core/Api/ScriptedPlayout/ContentCreatePlaylist.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentCreatePlaylist.cs index 9d988245b..3701895d7 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ContentCreatePlaylist.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentCreatePlaylist.cs @@ -5,8 +5,8 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ContentCreatePlaylist { [Description("Unique name used to reference this content throughout the scripted schedule")] - public string Key { get; set; } + public required string Key { get; set; } [Description("List of playlist items")] - public List Items { get; set; } + public required List Items { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ContentMarathon.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentMarathon.cs similarity index 92% rename from ErsatzTV.Core/Api/ScriptedPlayout/ContentMarathon.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentMarathon.cs index abc4d83f9..898a28644 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ContentMarathon.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentMarathon.cs @@ -5,11 +5,11 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ContentMarathon { [Description("Unique name used to reference this content throughout the scripted schedule")] - public string Key { get; set; } + public required string Key { get; set; } [Description( "Tells the scheduler how to group the combined content (returned from all guids and searches). Valid values are show, season, artist and album.")] - public string GroupBy { get; set; } + public required string GroupBy { get; set; } [Description("Playback order within each group; only chronological and shuffle are currently supported")] public string ItemOrder { get; set; } = "shuffle"; diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ContentMultiCollection.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentMultiCollection.cs similarity index 84% rename from ErsatzTV.Core/Api/ScriptedPlayout/ContentMultiCollection.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentMultiCollection.cs index 5674b38b3..dc2d269e2 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ContentMultiCollection.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentMultiCollection.cs @@ -7,10 +7,10 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ContentMultiCollection { [Description("Unique name used to reference this content throughout the scripted schedule")] - public string Key { get; set; } + public required string Key { get; set; } [Description("The name of the existing multi-collection")] - public string MultiCollection { get; set; } + public required string MultiCollection { get; set; } [Description("The playback order; only chronological and shuffle are currently supported")] public string Order { get; set; } = "shuffle"; diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ContentPlaylist.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentPlaylist.cs similarity index 70% rename from ErsatzTV.Core/Api/ScriptedPlayout/ContentPlaylist.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentPlaylist.cs index 6bf668691..da8d56542 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ContentPlaylist.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentPlaylist.cs @@ -5,11 +5,11 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ContentPlaylist { [Description("Unique name used to reference this content throughout the scripted schedule")] - public string Key { get; set; } + public required string Key { get; set; } [Description("The name of the existing playlist")] - public string Playlist { get; set; } + public required string Playlist { get; set; } [Description("The name of the existing playlist group that contains the named playlist")] - public string PlaylistGroup { get; set; } + public required string PlaylistGroup { get; set; } } diff --git a/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentSearch.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentSearch.cs new file mode 100644 index 000000000..ac05576d2 --- /dev/null +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentSearch.cs @@ -0,0 +1,15 @@ +using System.ComponentModel; + +namespace ErsatzTV.Core.Api.ScriptedPlayout; + +public record ContentSearch +{ + [Description("Unique name used to reference this content throughout the scripted schedule")] + public required string Key { get; set; } + + [Description("The search query")] + public required string Query { get; set; } + + [Description("The playback order; only chronological and shuffle are currently supported")] + public string Order { get; set; } = "shuffle"; +} diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ContentShow.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentShow.cs similarity index 77% rename from ErsatzTV.Core/Api/ScriptedPlayout/ContentShow.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentShow.cs index 887b2e1bd..fadd25140 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ContentShow.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentShow.cs @@ -5,10 +5,10 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ContentShow { [Description("Unique name used to reference this content throughout the scripted schedule")] - public string Key { get; set; } + public required string Key { get; set; } [Description("List of show identifiers")] - public Dictionary Guids { get; set; } = []; + public required Dictionary Guids { get; set; } = []; [Description("The playback order; only chronological and shuffle are currently supported")] public string Order { get; set; } = "shuffle"; diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ContentSmartCollection.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentSmartCollection.cs similarity index 84% rename from ErsatzTV.Core/Api/ScriptedPlayout/ContentSmartCollection.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentSmartCollection.cs index 1e95aeb3f..652f5ac92 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ContentSmartCollection.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ContentSmartCollection.cs @@ -7,10 +7,10 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ContentSmartCollection { [Description("Unique name used to reference this content throughout the scripted schedule")] - public string Key { get; set; } + public required string Key { get; set; } [Description("The name of the existing smart collection")] - public string SmartCollection { get; set; } + public required string SmartCollection { get; set; } [Description("The playback order; only chronological and shuffle are currently supported")] public string Order { get; set; } = "shuffle"; diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ControlGraphicsOff.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlGraphicsOff.cs similarity index 100% rename from ErsatzTV.Core/Api/ScriptedPlayout/ControlGraphicsOff.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlGraphicsOff.cs diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ControlGraphicsOn.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlGraphicsOn.cs similarity index 81% rename from ErsatzTV.Core/Api/ScriptedPlayout/ControlGraphicsOn.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlGraphicsOn.cs index 023a0b772..a18a072cf 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ControlGraphicsOn.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlGraphicsOn.cs @@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ControlGraphicsOn { [Description("A list of graphics elements to turn on.")] - public List Graphics { get; set; } + public required List Graphics { get; set; } public Dictionary Variables { get; set; } = []; } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ControlPreRollOn.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlPreRollOn.cs similarity index 77% rename from ErsatzTV.Core/Api/ScriptedPlayout/ControlPreRollOn.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlPreRollOn.cs index 10739507c..24d6e399a 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ControlPreRollOn.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlPreRollOn.cs @@ -5,5 +5,5 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ControlPreRollOn { [Description("The 'key' for the scripted playlist")] - public string Playlist { get; set; } + public required string Playlist { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ControlSkipItems.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlSkipItems.cs similarity index 69% rename from ErsatzTV.Core/Api/ScriptedPlayout/ControlSkipItems.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlSkipItems.cs index 8486d6297..4dd57ece5 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ControlSkipItems.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlSkipItems.cs @@ -5,8 +5,8 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ControlSkipItems { [Description("The 'key' for the content")] - public string Content { get; set; } + public required string Content { get; set; } [Description("The number of items to skip")] - public int Count { get; set; } + public required int Count { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ControlSkipToItem.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlSkipToItem.cs similarity index 63% rename from ErsatzTV.Core/Api/ScriptedPlayout/ControlSkipToItem.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlSkipToItem.cs index c4e167af3..613d17178 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ControlSkipToItem.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlSkipToItem.cs @@ -5,11 +5,11 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ControlSkipToItem { [Description("The 'key' for the content")] - public string Content { get; set; } + public required string Content { get; set; } [Description("The season number")] - public int Season { get; set; } + public required int Season { get; set; } [Description("The episode number")] - public int Episode { get; set; } + public required int Episode { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ControlStartEpgGroup.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlStartEpgGroup.cs similarity index 88% rename from ErsatzTV.Core/Api/ScriptedPlayout/ControlStartEpgGroup.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlStartEpgGroup.cs index 0367cd5a9..fe140aed2 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ControlStartEpgGroup.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlStartEpgGroup.cs @@ -8,5 +8,5 @@ public record ControlStartEpgGroup public bool Advance { get; set; } = true; [Description("Custom title to apply to all items in the EPG group.")] - public string CustomTitle { get; set; } + public string? CustomTitle { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ControlWaitUntil.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlWaitUntil.cs similarity index 91% rename from ErsatzTV.Core/Api/ScriptedPlayout/ControlWaitUntil.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlWaitUntil.cs index 8330903bc..d446db7a6 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ControlWaitUntil.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlWaitUntil.cs @@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ControlWaitUntil { [Description("The time of day to wait (insert unscheduled time) until")] - public string When { get; set; } + public required string When { get; set; } [Description("When true, will wait until the specified time tomorrow if it has already passed today.")] public bool Tomorrow { get; set; } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ControlWaitUntilExact.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlWaitUntilExact.cs similarity index 86% rename from ErsatzTV.Core/Api/ScriptedPlayout/ControlWaitUntilExact.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlWaitUntilExact.cs index be2f69ffc..a6dbf1b90 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ControlWaitUntilExact.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlWaitUntilExact.cs @@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ControlWaitUntilExact { [Description("The time to wait (insert unscheduled time) until")] - public DateTimeOffset When { get; set; } + public required DateTimeOffset When { get; set; } [Description("When true, the current time of the playout build is allowed to move backward when the playout is reset.")] public bool RewindOnReset { get; set; } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ControlWatermarkOff.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlWatermarkOff.cs similarity index 100% rename from ErsatzTV.Core/Api/ScriptedPlayout/ControlWatermarkOff.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlWatermarkOff.cs diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ControlWatermarkOn.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlWatermarkOn.cs similarity index 75% rename from ErsatzTV.Core/Api/ScriptedPlayout/ControlWatermarkOn.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlWatermarkOn.cs index d978e111c..41ce5e85b 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ControlWatermarkOn.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/ControlWatermarkOn.cs @@ -5,5 +5,5 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record ControlWatermarkOn { [Description("A list of existing watermark names to turn on")] - public List Watermark { get; set; } + public required List Watermark { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/PeekItemDuration.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PeekItemDuration.cs similarity index 60% rename from ErsatzTV.Core/Api/ScriptedPlayout/PeekItemDuration.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PeekItemDuration.cs index 923695b96..fb1b1ae2b 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/PeekItemDuration.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PeekItemDuration.cs @@ -4,8 +4,8 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public class PeekItemDuration { - public string Content { get; set; } + public required string Content { get; set; } [Description("Duration in milliseconds")] - public long Milliseconds { get; set; } + public required long Milliseconds { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/PlaylistItem.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlaylistItem.cs similarity index 62% rename from ErsatzTV.Core/Api/ScriptedPlayout/PlaylistItem.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlaylistItem.cs index caa12689c..5501ad612 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/PlaylistItem.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlaylistItem.cs @@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record PlaylistItem { [Description("The 'key' for the content")] - public string Content { get; set; } + public required string Content { get; set; } - public int Count { get; set; } + public required int Count { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/PlayoutContext.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutContext.cs similarity index 61% rename from ErsatzTV.Core/Api/ScriptedPlayout/PlayoutContext.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutContext.cs index 9e06383d0..bd0f85942 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/PlayoutContext.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutContext.cs @@ -5,14 +5,14 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record PlayoutContext { [Description("The current time of the playout build")] - public DateTimeOffset CurrentTime { get; set; } + public required DateTimeOffset CurrentTime { get; set; } [Description("The start time of the playout build")] - public DateTimeOffset StartTime { get; set; } + public required DateTimeOffset StartTime { get; set; } [Description("The finish time of the playout build")] - public DateTimeOffset FinishTime { get; set; } + public required DateTimeOffset FinishTime { get; set; } [Description("Indicates whether the current playout build is complete")] - public bool IsDone { get; set; } + public required bool IsDone { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/PlayoutCount.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutCount.cs similarity index 66% rename from ErsatzTV.Core/Api/ScriptedPlayout/PlayoutCount.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutCount.cs index a058afef3..aea0d424f 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/PlayoutCount.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutCount.cs @@ -5,15 +5,15 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record PlayoutCount { [Description("The 'key' for the content that should be added")] - public string Content { get; set; } + public required string Content { get; set; } - public int Count { get; set; } + public required int Count { get; set; } [Description("Flags this content as filler, which influences EPG grouping")] - public string FillerKind { get; set; } + public string? FillerKind { get; set; } [Description("Overrides the title used in the EPG")] - public string CustomTitle { get; set; } + public string? CustomTitle { get; set; } public bool DisableWatermarks { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/PlayoutDuration.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutDuration.cs similarity index 85% rename from ErsatzTV.Core/Api/ScriptedPlayout/PlayoutDuration.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutDuration.cs index 73e8b9d93..31bc05a19 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/PlayoutDuration.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutDuration.cs @@ -5,14 +5,14 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record PlayoutDuration { [Description("The 'key' for the content that should be added")] - public string Content { get; set; } + public required string Content { get; set; } [Description("The amount of time to add using the referenced content")] - public string Duration { get; set; } + public required string Duration { get; set; } [Description( "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit.")] - public string Fallback { get; set; } + public string? Fallback { get; set; } [Description("Controls whether content will be trimmed to exactly fit the specified duration")] public bool Trim { get; set; } @@ -29,10 +29,10 @@ public record PlayoutDuration public bool OfflineTail { get; set; } [Description("Flags this content as filler, which influences EPG grouping")] - public string FillerKind { get; set; } + public string? FillerKind { get; set; } [Description("Overrides the title used in the EPG")] - public string CustomTitle { get; set; } + public string? CustomTitle { get; set; } public bool DisableWatermarks { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/PlayoutPadToNext.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutPadToNext.cs similarity index 85% rename from ErsatzTV.Core/Api/ScriptedPlayout/PlayoutPadToNext.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutPadToNext.cs index e83385a0c..4d24ba81c 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/PlayoutPadToNext.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutPadToNext.cs @@ -5,14 +5,14 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record PlayoutPadToNext { [Description("The 'key' for the content that should be added")] - public string Content { get; set; } + public required string Content { get; set; } [Description("The minutes interval")] - public int Minutes { get; set; } + public required int Minutes { get; set; } [Description( "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit.")] - public string Fallback { get; set; } + public string? Fallback { get; set; } [Description("Controls whether content will be trimmed to exactly fit the specified interval")] public bool Trim { get; set; } @@ -29,10 +29,10 @@ public record PlayoutPadToNext public bool OfflineTail { get; set; } = true; [Description("Flags this content as filler, which influences EPG grouping")] - public string FillerKind { get; set; } + public string? FillerKind { get; set; } [Description("Overrides the title used in the EPG")] - public string CustomTitle { get; set; } + public string? CustomTitle { get; set; } public bool DisableWatermarks { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/PlayoutPadUntil.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutPadUntil.cs similarity index 88% rename from ErsatzTV.Core/Api/ScriptedPlayout/PlayoutPadUntil.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutPadUntil.cs index 169f9e613..61734e2be 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/PlayoutPadUntil.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutPadUntil.cs @@ -5,10 +5,10 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record PlayoutPadUntil { [Description("The 'key' for the content that should be added")] - public string Content { get; set; } + public required string Content { get; set; } [Description("The time of day that content should be added until")] - public string When { get; set; } + public required string When { get; set; } [Description( "Only used when the current playout time is already after the specified pad until time. When true, content will be scheduled until the specified time of day (the next day). When false, no content will be scheduled by this request.")] @@ -16,7 +16,7 @@ public record PlayoutPadUntil [Description( "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit.")] - public string Fallback { get; set; } + public string? Fallback { get; set; } [Description("Controls whether content will be trimmed to exactly fit until the specified time")] public bool Trim { get; set; } @@ -34,10 +34,10 @@ public record PlayoutPadUntil public bool OfflineTail { get; set; } [Description("Flags this content as filler, which influences EPG grouping")] - public string FillerKind { get; set; } + public string? FillerKind { get; set; } [Description("Overrides the title used in the EPG")] - public string CustomTitle { get; set; } + public string? CustomTitle { get; set; } public bool DisableWatermarks { get; set; } } diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/PlayoutPadUntilExact.cs b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutPadUntilExact.cs similarity index 85% rename from ErsatzTV.Core/Api/ScriptedPlayout/PlayoutPadUntilExact.cs rename to ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutPadUntilExact.cs index 1f57a14f6..a69a9603f 100644 --- a/ErsatzTV.Core/Api/ScriptedPlayout/PlayoutPadUntilExact.cs +++ b/ErsatzTV.Core.Nullable/Api/ScriptedPlayout/PlayoutPadUntilExact.cs @@ -5,14 +5,14 @@ namespace ErsatzTV.Core.Api.ScriptedPlayout; public record PlayoutPadUntilExact { [Description("The 'key' for the content that should be added")] - public string Content { get; set; } + public required string Content { get; set; } [Description("The time content should be added until")] - public DateTimeOffset When { get; set; } + public required DateTimeOffset When { get; set; } [Description( "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit.")] - public string Fallback { get; set; } + public string? Fallback { get; set; } [Description("Controls whether content will be trimmed to exactly fit until the specified time")] public bool Trim { get; set; } @@ -30,10 +30,10 @@ public record PlayoutPadUntilExact public bool OfflineTail { get; set; } [Description("Flags this content as filler, which influences EPG grouping")] - public string FillerKind { get; set; } + public string? FillerKind { get; set; } [Description("Overrides the title used in the EPG")] - public string CustomTitle { get; set; } + public string? CustomTitle { get; set; } public bool DisableWatermarks { get; set; } } diff --git a/ErsatzTV.Core.Nullable/ErsatzTV.Core.Nullable.csproj b/ErsatzTV.Core.Nullable/ErsatzTV.Core.Nullable.csproj new file mode 100644 index 000000000..2ef3508b5 --- /dev/null +++ b/ErsatzTV.Core.Nullable/ErsatzTV.Core.Nullable.csproj @@ -0,0 +1,14 @@ + + + + net10.0 + enable + enable + ErsatzTV.Core + + + + + + + diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/ContentSearch.cs b/ErsatzTV.Core/Api/ScriptedPlayout/ContentSearch.cs deleted file mode 100644 index 82f0b890e..000000000 --- a/ErsatzTV.Core/Api/ScriptedPlayout/ContentSearch.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ErsatzTV.Core.Api.ScriptedPlayout; - -public record ContentSearch -{ - public string Key { get; set; } - public string Query { get; set; } - public string Order { get; set; } = "shuffle"; -} diff --git a/ErsatzTV.Core/ErsatzTV.Core.csproj b/ErsatzTV.Core/ErsatzTV.Core.csproj index dba80a000..8b66bbf6b 100644 --- a/ErsatzTV.Core/ErsatzTV.Core.csproj +++ b/ErsatzTV.Core/ErsatzTV.Core.csproj @@ -6,6 +6,7 @@ enable latest-Recommended true + disable @@ -47,4 +48,8 @@ + + + + diff --git a/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj b/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj index 0b0bbcd54..ed5904484 100644 --- a/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj +++ b/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj @@ -28,8 +28,8 @@ - - + + diff --git a/ErsatzTV.sln b/ErsatzTV.sln index 09896fc59..f81b2667b 100644 --- a/ErsatzTV.sln +++ b/ErsatzTV.sln @@ -26,6 +26,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ErsatzTV.Infrastructure.MyS EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ErsatzTV.Infrastructure.Sqlite", "ErsatzTV.Infrastructure.Sqlite\ErsatzTV.Infrastructure.Sqlite.csproj", "{C105FD01-9D6F-4574-A987-BD39E7B349E8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ErsatzTV.Core.Nullable", "ErsatzTV.Core.Nullable\ErsatzTV.Core.Nullable.csproj", "{557D88A6-C982-4FFD-8FD2-6446CB07D093}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -105,6 +107,12 @@ Global {C105FD01-9D6F-4574-A987-BD39E7B349E8}.Release|Any CPU.Build.0 = Release|Any CPU {C105FD01-9D6F-4574-A987-BD39E7B349E8}.Debug No Sync|Any CPU.ActiveCfg = Debug|Any CPU {C105FD01-9D6F-4574-A987-BD39E7B349E8}.Debug No Sync|Any CPU.Build.0 = Debug|Any CPU + {557D88A6-C982-4FFD-8FD2-6446CB07D093}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557D88A6-C982-4FFD-8FD2-6446CB07D093}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557D88A6-C982-4FFD-8FD2-6446CB07D093}.Release|Any CPU.ActiveCfg = Release|Any CPU + {557D88A6-C982-4FFD-8FD2-6446CB07D093}.Release|Any CPU.Build.0 = Release|Any CPU + {557D88A6-C982-4FFD-8FD2-6446CB07D093}.Debug No Sync|Any CPU.ActiveCfg = Debug|Any CPU + {557D88A6-C982-4FFD-8FD2-6446CB07D093}.Debug No Sync|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {CE7F1ACD-F286-4761-A7BC-A541A1E25C86} = {325E6DA0-52B3-4431-98A2-72C36F403704} diff --git a/ErsatzTV/ErsatzTV.csproj b/ErsatzTV/ErsatzTV.csproj index d6e9fcd0b..bb6c32110 100644 --- a/ErsatzTV/ErsatzTV.csproj +++ b/ErsatzTV/ErsatzTV.csproj @@ -39,16 +39,16 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -61,8 +61,8 @@ - - + + @@ -72,6 +72,7 @@ + diff --git a/ErsatzTV/Startup.cs b/ErsatzTV/Startup.cs index 384da8a0c..39ced9500 100644 --- a/ErsatzTV/Startup.cs +++ b/ErsatzTV/Startup.cs @@ -4,6 +4,7 @@ using System.IO.Abstractions; using System.Reflection; using System.Runtime.InteropServices; using System.Text; +using System.Text.Json.Serialization; using System.Threading.Channels; using BlazorSortable; using Bugsnag.AspNet.Core; @@ -92,7 +93,7 @@ using Microsoft.Extensions.Primitives; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Tokens; using Microsoft.IO; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using MudBlazor.Services; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -162,10 +163,11 @@ public class Startup { options.ShouldInclude += a => a.GroupName == "scripted-schedule"; var tag = new OpenApiTag { Name = "ScriptedSchedule" }; + var tagReference = new OpenApiTagReference("ScriptedSchedule"); options.AddOperationTransformer((operation, _, _) => { operation.Tags.Clear(); - operation.Tags.Add(tag); + operation.Tags.Add(tagReference); return Task.CompletedTask; }); options.AddDocumentTransformer((document, _, _) => @@ -176,6 +178,8 @@ public class Startup }); }); + services.ConfigureHttpJsonOptions(o => o.SerializerOptions.NumberHandling = JsonNumberHandling.Strict); + OidcHelper.Init(Configuration); JwtHelper.Init(Configuration); SearchHelper.Init(Configuration); diff --git a/ErsatzTV/wwwroot/openapi/scripted-schedule-tagged.json b/ErsatzTV/wwwroot/openapi/scripted-schedule-tagged.json index aba89b254..c9c8e262e 100644 --- a/ErsatzTV/wwwroot/openapi/scripted-schedule-tagged.json +++ b/ErsatzTV/wwwroot/openapi/scripted-schedule-tagged.json @@ -1,5 +1,5 @@ { - "openapi": "3.0.1", + "openapi": "3.1.1", "info": { "title": "ErsatzTV | scripted-schedule-tagged", "version": "1.0.0" @@ -1487,22 +1487,28 @@ "components": { "schemas": { "ContentAll": { + "required": [ + "content" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content that should be added", - "nullable": true + "description": "The 'key' for the content that should be added" }, "fillerKind": { - "type": "string", - "description": "Flags this content as filler, which influences EPG grouping", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Flags this content as filler, which influences EPG grouping" }, "customTitle": { - "type": "string", - "description": "Overrides the title used in the EPG", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Overrides the title used in the EPG" }, "disableWatermarks": { "type": "boolean" @@ -1510,60 +1516,64 @@ } }, "ContentCollection": { + "required": [ + "key", + "collection" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "collection": { "type": "string", - "description": "The name of the existing manual collection", - "nullable": true + "description": "The name of the existing manual collection" }, "order": { "type": "string", - "description": "The playback order; only chronological and shuffle are currently supported", - "nullable": true + "description": "The playback order; only chronological and shuffle are currently supported" } } }, "ContentCreatePlaylist": { + "required": [ + "key", + "items" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "items": { "type": "array", "items": { "$ref": "#/components/schemas/PlaylistItem" }, - "description": "List of playlist items", - "nullable": true + "description": "List of playlist items" } } }, "ContentMarathon": { + "required": [ + "key", + "groupBy" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "groupBy": { "type": "string", - "description": "Tells the scheduler how to group the combined content (returned from all guids and searches). Valid values are show, season, artist and album.", - "nullable": true + "description": "Tells the scheduler how to group the combined content (returned from all guids and searches). Valid values are show, season, artist and album." }, "itemOrder": { "type": "string", - "description": "Playback order within each group; only chronological and shuffle are currently supported", - "nullable": true + "description": "Playback order within each group; only chronological and shuffle are currently supported" }, "guids": { "type": "object", @@ -1573,16 +1583,14 @@ "type": "string" } }, - "description": "List of external content identifiers", - "nullable": true + "description": "List of external content identifiers" }, "searches": { "type": "array", "items": { "type": "string" }, - "description": "List of search queries", - "nullable": true + "description": "List of search queries" }, "playAllItems": { "type": "boolean", @@ -1595,102 +1603,111 @@ } }, "ContentMultiCollection": { + "required": [ + "key", + "multiCollection" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "multiCollection": { "type": "string", - "description": "The name of the existing multi-collection", - "nullable": true + "description": "The name of the existing multi-collection" }, "order": { "type": "string", - "description": "The playback order; only chronological and shuffle are currently supported", - "nullable": true + "description": "The playback order; only chronological and shuffle are currently supported" } } }, "ContentPlaylist": { + "required": [ + "key", + "playlist", + "playlistGroup" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "playlist": { "type": "string", - "description": "The name of the existing playlist", - "nullable": true + "description": "The name of the existing playlist" }, "playlistGroup": { "type": "string", - "description": "The name of the existing playlist group that contains the named playlist", - "nullable": true + "description": "The name of the existing playlist group that contains the named playlist" } } }, "ContentSearch": { + "required": [ + "key", + "query" + ], "type": "object", "properties": { "key": { "type": "string", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "query": { "type": "string", - "nullable": true + "description": "The search query" }, "order": { "type": "string", - "nullable": true + "description": "The playback order; only chronological and shuffle are currently supported" } } }, "ContentShow": { + "required": [ + "key", + "guids" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "guids": { "type": "object", "additionalProperties": { "type": "string" }, - "description": "List of show identifiers", - "nullable": true + "description": "List of show identifiers" }, "order": { "type": "string", - "description": "The playback order; only chronological and shuffle are currently supported", - "nullable": true + "description": "The playback order; only chronological and shuffle are currently supported" } } }, "ContentSmartCollection": { + "required": [ + "key", + "smartCollection" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "smartCollection": { "type": "string", - "description": "The name of the existing smart collection", - "nullable": true + "description": "The name of the existing smart collection" }, "order": { "type": "string", - "description": "The playback order; only chronological and shuffle are currently supported", - "nullable": true + "description": "The playback order; only chronological and shuffle are currently supported" } } }, @@ -1702,12 +1719,14 @@ "items": { "type": "string" }, - "description": "A list of graphics elements to turn off. All graphics elements will be turned off if this list is null or empty", - "nullable": true + "description": "A list of graphics elements to turn off. All graphics elements will be turned off if this list is null or empty" } } }, "ControlGraphicsOn": { + "required": [ + "graphics" + ], "type": "object", "properties": { "graphics": { @@ -1715,35 +1734,38 @@ "items": { "type": "string" }, - "description": "A list of graphics elements to turn on.", - "nullable": true + "description": "A list of graphics elements to turn on." }, "variables": { "type": "object", "additionalProperties": { "type": "string" - }, - "nullable": true + } } } }, "ControlPreRollOn": { + "required": [ + "playlist" + ], "type": "object", "properties": { "playlist": { "type": "string", - "description": "The 'key' for the scripted playlist", - "nullable": true + "description": "The 'key' for the scripted playlist" } } }, "ControlSkipItems": { + "required": [ + "content", + "count" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content", - "nullable": true + "description": "The 'key' for the content" }, "count": { "type": "integer", @@ -1753,12 +1775,16 @@ } }, "ControlSkipToItem": { + "required": [ + "content", + "season", + "episode" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content", - "nullable": true + "description": "The 'key' for the content" }, "season": { "type": "integer", @@ -1780,19 +1806,23 @@ "description": "When true, will make a new EPG group. When false, will continue the existing EPG group." }, "customTitle": { - "type": "string", - "description": "Custom title to apply to all items in the EPG group.", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Custom title to apply to all items in the EPG group." } } }, "ControlWaitUntil": { + "required": [ + "when" + ], "type": "object", "properties": { "when": { "type": "string", - "description": "The time of day to wait (insert unscheduled time) until", - "nullable": true + "description": "The time of day to wait (insert unscheduled time) until" }, "tomorrow": { "type": "boolean", @@ -1805,6 +1835,9 @@ } }, "ControlWaitUntilExact": { + "required": [ + "when" + ], "type": "object", "properties": { "when": { @@ -1826,12 +1859,14 @@ "items": { "type": "string" }, - "description": "A list of existing watermark names to turn off. All (scripted) watermarks will be turned off if this list is null or empty.", - "nullable": true + "description": "A list of existing watermark names to turn off. All (scripted) watermarks will be turned off if this list is null or empty." } } }, "ControlWatermarkOn": { + "required": [ + "watermark" + ], "type": "object", "properties": { "watermark": { @@ -1839,17 +1874,19 @@ "items": { "type": "string" }, - "description": "A list of existing watermark names to turn on", - "nullable": true + "description": "A list of existing watermark names to turn on" } } }, "PeekItemDuration": { + "required": [ + "content", + "milliseconds" + ], "type": "object", "properties": { "content": { - "type": "string", - "nullable": true + "type": "string" }, "milliseconds": { "type": "integer", @@ -1859,12 +1896,15 @@ } }, "PlaylistItem": { + "required": [ + "content", + "count" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content", - "nullable": true + "description": "The 'key' for the content" }, "count": { "type": "integer", @@ -1873,6 +1913,12 @@ } }, "PlayoutContext": { + "required": [ + "currentTime", + "startTime", + "finishTime", + "isDone" + ], "type": "object", "properties": { "currentTime": { @@ -1897,26 +1943,33 @@ } }, "PlayoutCount": { + "required": [ + "content", + "count" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content that should be added", - "nullable": true + "description": "The 'key' for the content that should be added" }, "count": { "type": "integer", "format": "int32" }, "fillerKind": { - "type": "string", - "description": "Flags this content as filler, which influences EPG grouping", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Flags this content as filler, which influences EPG grouping" }, "customTitle": { - "type": "string", - "description": "Overrides the title used in the EPG", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Overrides the title used in the EPG" }, "disableWatermarks": { "type": "boolean" @@ -1924,22 +1977,26 @@ } }, "PlayoutDuration": { + "required": [ + "content", + "duration" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content that should be added", - "nullable": true + "description": "The 'key' for the content that should be added" }, "duration": { "type": "string", - "description": "The amount of time to add using the referenced content", - "nullable": true + "description": "The amount of time to add using the referenced content" }, "fallback": { - "type": "string", - "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit.", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit." }, "trim": { "type": "boolean", @@ -1959,14 +2016,18 @@ "description": "When true, afer scheduling everything that will fit, any remaining time from the specified duration will be unscheduled (offline)" }, "fillerKind": { - "type": "string", - "description": "Flags this content as filler, which influences EPG grouping", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Flags this content as filler, which influences EPG grouping" }, "customTitle": { - "type": "string", - "description": "Overrides the title used in the EPG", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Overrides the title used in the EPG" }, "disableWatermarks": { "type": "boolean" @@ -1974,12 +2035,15 @@ } }, "PlayoutPadToNext": { + "required": [ + "content", + "minutes" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content that should be added", - "nullable": true + "description": "The 'key' for the content that should be added" }, "minutes": { "type": "integer", @@ -1987,9 +2051,11 @@ "format": "int32" }, "fallback": { - "type": "string", - "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit.", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit." }, "trim": { "type": "boolean", @@ -2009,14 +2075,18 @@ "description": "When true, afer scheduling everything that will fit, any remaining time from the specified interval will be unscheduled (offline)" }, "fillerKind": { - "type": "string", - "description": "Flags this content as filler, which influences EPG grouping", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Flags this content as filler, which influences EPG grouping" }, "customTitle": { - "type": "string", - "description": "Overrides the title used in the EPG", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Overrides the title used in the EPG" }, "disableWatermarks": { "type": "boolean" @@ -2024,26 +2094,30 @@ } }, "PlayoutPadUntil": { + "required": [ + "content", + "when" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content that should be added", - "nullable": true + "description": "The 'key' for the content that should be added" }, "when": { "type": "string", - "description": "The time of day that content should be added until", - "nullable": true + "description": "The time of day that content should be added until" }, "tomorrow": { "type": "boolean", "description": "Only used when the current playout time is already after the specified pad until time. When true, content will be scheduled until the specified time of day (the next day). When false, no content will be scheduled by this request." }, "fallback": { - "type": "string", - "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit.", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit." }, "trim": { "type": "boolean", @@ -2063,14 +2137,18 @@ "description": "When true, afer scheduling everything that will fit, any remaining time from the specified interval will be unscheduled (offline)" }, "fillerKind": { - "type": "string", - "description": "Flags this content as filler, which influences EPG grouping", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Flags this content as filler, which influences EPG grouping" }, "customTitle": { - "type": "string", - "description": "Overrides the title used in the EPG", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Overrides the title used in the EPG" }, "disableWatermarks": { "type": "boolean" @@ -2078,12 +2156,15 @@ } }, "PlayoutPadUntilExact": { + "required": [ + "content", + "when" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content that should be added", - "nullable": true + "description": "The 'key' for the content that should be added" }, "when": { "type": "string", @@ -2091,9 +2172,11 @@ "format": "date-time" }, "fallback": { - "type": "string", - "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit.", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit." }, "trim": { "type": "boolean", @@ -2113,14 +2196,18 @@ "description": "When true, afer scheduling everything that will fit, any remaining time from the specified interval will be unscheduled (offline)" }, "fillerKind": { - "type": "string", - "description": "Flags this content as filler, which influences EPG grouping", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Flags this content as filler, which influences EPG grouping" }, "customTitle": { - "type": "string", - "description": "Overrides the title used in the EPG", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Overrides the title used in the EPG" }, "disableWatermarks": { "type": "boolean" diff --git a/ErsatzTV/wwwroot/openapi/scripted-schedule.json b/ErsatzTV/wwwroot/openapi/scripted-schedule.json index c03289d7f..d70777bc0 100644 --- a/ErsatzTV/wwwroot/openapi/scripted-schedule.json +++ b/ErsatzTV/wwwroot/openapi/scripted-schedule.json @@ -1,5 +1,5 @@ { - "openapi": "3.0.1", + "openapi": "3.1.1", "info": { "title": "ErsatzTV | scripted-schedule", "version": "1.0.0" @@ -1487,22 +1487,28 @@ "components": { "schemas": { "ContentAll": { + "required": [ + "content" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content that should be added", - "nullable": true + "description": "The 'key' for the content that should be added" }, "fillerKind": { - "type": "string", - "description": "Flags this content as filler, which influences EPG grouping", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Flags this content as filler, which influences EPG grouping" }, "customTitle": { - "type": "string", - "description": "Overrides the title used in the EPG", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Overrides the title used in the EPG" }, "disableWatermarks": { "type": "boolean" @@ -1510,60 +1516,64 @@ } }, "ContentCollection": { + "required": [ + "key", + "collection" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "collection": { "type": "string", - "description": "The name of the existing manual collection", - "nullable": true + "description": "The name of the existing manual collection" }, "order": { "type": "string", - "description": "The playback order; only chronological and shuffle are currently supported", - "nullable": true + "description": "The playback order; only chronological and shuffle are currently supported" } } }, "ContentCreatePlaylist": { + "required": [ + "key", + "items" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "items": { "type": "array", "items": { "$ref": "#/components/schemas/PlaylistItem" }, - "description": "List of playlist items", - "nullable": true + "description": "List of playlist items" } } }, "ContentMarathon": { + "required": [ + "key", + "groupBy" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "groupBy": { "type": "string", - "description": "Tells the scheduler how to group the combined content (returned from all guids and searches). Valid values are show, season, artist and album.", - "nullable": true + "description": "Tells the scheduler how to group the combined content (returned from all guids and searches). Valid values are show, season, artist and album." }, "itemOrder": { "type": "string", - "description": "Playback order within each group; only chronological and shuffle are currently supported", - "nullable": true + "description": "Playback order within each group; only chronological and shuffle are currently supported" }, "guids": { "type": "object", @@ -1573,16 +1583,14 @@ "type": "string" } }, - "description": "List of external content identifiers", - "nullable": true + "description": "List of external content identifiers" }, "searches": { "type": "array", "items": { "type": "string" }, - "description": "List of search queries", - "nullable": true + "description": "List of search queries" }, "playAllItems": { "type": "boolean", @@ -1595,102 +1603,111 @@ } }, "ContentMultiCollection": { + "required": [ + "key", + "multiCollection" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "multiCollection": { "type": "string", - "description": "The name of the existing multi-collection", - "nullable": true + "description": "The name of the existing multi-collection" }, "order": { "type": "string", - "description": "The playback order; only chronological and shuffle are currently supported", - "nullable": true + "description": "The playback order; only chronological and shuffle are currently supported" } } }, "ContentPlaylist": { + "required": [ + "key", + "playlist", + "playlistGroup" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "playlist": { "type": "string", - "description": "The name of the existing playlist", - "nullable": true + "description": "The name of the existing playlist" }, "playlistGroup": { "type": "string", - "description": "The name of the existing playlist group that contains the named playlist", - "nullable": true + "description": "The name of the existing playlist group that contains the named playlist" } } }, "ContentSearch": { + "required": [ + "key", + "query" + ], "type": "object", "properties": { "key": { "type": "string", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "query": { "type": "string", - "nullable": true + "description": "The search query" }, "order": { "type": "string", - "nullable": true + "description": "The playback order; only chronological and shuffle are currently supported" } } }, "ContentShow": { + "required": [ + "key", + "guids" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "guids": { "type": "object", "additionalProperties": { "type": "string" }, - "description": "List of show identifiers", - "nullable": true + "description": "List of show identifiers" }, "order": { "type": "string", - "description": "The playback order; only chronological and shuffle are currently supported", - "nullable": true + "description": "The playback order; only chronological and shuffle are currently supported" } } }, "ContentSmartCollection": { + "required": [ + "key", + "smartCollection" + ], "type": "object", "properties": { "key": { "type": "string", - "description": "Unique name used to reference this content throughout the scripted schedule", - "nullable": true + "description": "Unique name used to reference this content throughout the scripted schedule" }, "smartCollection": { "type": "string", - "description": "The name of the existing smart collection", - "nullable": true + "description": "The name of the existing smart collection" }, "order": { "type": "string", - "description": "The playback order; only chronological and shuffle are currently supported", - "nullable": true + "description": "The playback order; only chronological and shuffle are currently supported" } } }, @@ -1702,12 +1719,14 @@ "items": { "type": "string" }, - "description": "A list of graphics elements to turn off. All graphics elements will be turned off if this list is null or empty", - "nullable": true + "description": "A list of graphics elements to turn off. All graphics elements will be turned off if this list is null or empty" } } }, "ControlGraphicsOn": { + "required": [ + "graphics" + ], "type": "object", "properties": { "graphics": { @@ -1715,35 +1734,38 @@ "items": { "type": "string" }, - "description": "A list of graphics elements to turn on.", - "nullable": true + "description": "A list of graphics elements to turn on." }, "variables": { "type": "object", "additionalProperties": { "type": "string" - }, - "nullable": true + } } } }, "ControlPreRollOn": { + "required": [ + "playlist" + ], "type": "object", "properties": { "playlist": { "type": "string", - "description": "The 'key' for the scripted playlist", - "nullable": true + "description": "The 'key' for the scripted playlist" } } }, "ControlSkipItems": { + "required": [ + "content", + "count" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content", - "nullable": true + "description": "The 'key' for the content" }, "count": { "type": "integer", @@ -1753,12 +1775,16 @@ } }, "ControlSkipToItem": { + "required": [ + "content", + "season", + "episode" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content", - "nullable": true + "description": "The 'key' for the content" }, "season": { "type": "integer", @@ -1780,19 +1806,23 @@ "description": "When true, will make a new EPG group. When false, will continue the existing EPG group." }, "customTitle": { - "type": "string", - "description": "Custom title to apply to all items in the EPG group.", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Custom title to apply to all items in the EPG group." } } }, "ControlWaitUntil": { + "required": [ + "when" + ], "type": "object", "properties": { "when": { "type": "string", - "description": "The time of day to wait (insert unscheduled time) until", - "nullable": true + "description": "The time of day to wait (insert unscheduled time) until" }, "tomorrow": { "type": "boolean", @@ -1805,6 +1835,9 @@ } }, "ControlWaitUntilExact": { + "required": [ + "when" + ], "type": "object", "properties": { "when": { @@ -1826,12 +1859,14 @@ "items": { "type": "string" }, - "description": "A list of existing watermark names to turn off. All (scripted) watermarks will be turned off if this list is null or empty.", - "nullable": true + "description": "A list of existing watermark names to turn off. All (scripted) watermarks will be turned off if this list is null or empty." } } }, "ControlWatermarkOn": { + "required": [ + "watermark" + ], "type": "object", "properties": { "watermark": { @@ -1839,17 +1874,19 @@ "items": { "type": "string" }, - "description": "A list of existing watermark names to turn on", - "nullable": true + "description": "A list of existing watermark names to turn on" } } }, "PeekItemDuration": { + "required": [ + "content", + "milliseconds" + ], "type": "object", "properties": { "content": { - "type": "string", - "nullable": true + "type": "string" }, "milliseconds": { "type": "integer", @@ -1859,12 +1896,15 @@ } }, "PlaylistItem": { + "required": [ + "content", + "count" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content", - "nullable": true + "description": "The 'key' for the content" }, "count": { "type": "integer", @@ -1873,6 +1913,12 @@ } }, "PlayoutContext": { + "required": [ + "currentTime", + "startTime", + "finishTime", + "isDone" + ], "type": "object", "properties": { "currentTime": { @@ -1897,26 +1943,33 @@ } }, "PlayoutCount": { + "required": [ + "content", + "count" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content that should be added", - "nullable": true + "description": "The 'key' for the content that should be added" }, "count": { "type": "integer", "format": "int32" }, "fillerKind": { - "type": "string", - "description": "Flags this content as filler, which influences EPG grouping", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Flags this content as filler, which influences EPG grouping" }, "customTitle": { - "type": "string", - "description": "Overrides the title used in the EPG", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Overrides the title used in the EPG" }, "disableWatermarks": { "type": "boolean" @@ -1924,22 +1977,26 @@ } }, "PlayoutDuration": { + "required": [ + "content", + "duration" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content that should be added", - "nullable": true + "description": "The 'key' for the content that should be added" }, "duration": { "type": "string", - "description": "The amount of time to add using the referenced content", - "nullable": true + "description": "The amount of time to add using the referenced content" }, "fallback": { - "type": "string", - "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit.", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit." }, "trim": { "type": "boolean", @@ -1959,14 +2016,18 @@ "description": "When true, afer scheduling everything that will fit, any remaining time from the specified duration will be unscheduled (offline)" }, "fillerKind": { - "type": "string", - "description": "Flags this content as filler, which influences EPG grouping", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Flags this content as filler, which influences EPG grouping" }, "customTitle": { - "type": "string", - "description": "Overrides the title used in the EPG", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Overrides the title used in the EPG" }, "disableWatermarks": { "type": "boolean" @@ -1974,12 +2035,15 @@ } }, "PlayoutPadToNext": { + "required": [ + "content", + "minutes" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content that should be added", - "nullable": true + "description": "The 'key' for the content that should be added" }, "minutes": { "type": "integer", @@ -1987,9 +2051,11 @@ "format": "int32" }, "fallback": { - "type": "string", - "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit.", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit." }, "trim": { "type": "boolean", @@ -2009,14 +2075,18 @@ "description": "When true, afer scheduling everything that will fit, any remaining time from the specified interval will be unscheduled (offline)" }, "fillerKind": { - "type": "string", - "description": "Flags this content as filler, which influences EPG grouping", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Flags this content as filler, which influences EPG grouping" }, "customTitle": { - "type": "string", - "description": "Overrides the title used in the EPG", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Overrides the title used in the EPG" }, "disableWatermarks": { "type": "boolean" @@ -2024,26 +2094,30 @@ } }, "PlayoutPadUntil": { + "required": [ + "content", + "when" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content that should be added", - "nullable": true + "description": "The 'key' for the content that should be added" }, "when": { "type": "string", - "description": "The time of day that content should be added until", - "nullable": true + "description": "The time of day that content should be added until" }, "tomorrow": { "type": "boolean", "description": "Only used when the current playout time is already after the specified pad until time. When true, content will be scheduled until the specified time of day (the next day). When false, no content will be scheduled by this request." }, "fallback": { - "type": "string", - "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit.", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit." }, "trim": { "type": "boolean", @@ -2063,14 +2137,18 @@ "description": "When true, afer scheduling everything that will fit, any remaining time from the specified interval will be unscheduled (offline)" }, "fillerKind": { - "type": "string", - "description": "Flags this content as filler, which influences EPG grouping", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Flags this content as filler, which influences EPG grouping" }, "customTitle": { - "type": "string", - "description": "Overrides the title used in the EPG", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Overrides the title used in the EPG" }, "disableWatermarks": { "type": "boolean" @@ -2078,12 +2156,15 @@ } }, "PlayoutPadUntilExact": { + "required": [ + "content", + "when" + ], "type": "object", "properties": { "content": { "type": "string", - "description": "The 'key' for the content that should be added", - "nullable": true + "description": "The 'key' for the content that should be added" }, "when": { "type": "string", @@ -2091,9 +2172,11 @@ "format": "date-time" }, "fallback": { - "type": "string", - "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit.", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "The 'key' for the content that should be used to fill any remaining unscheduled time. One item will be selected to be looped and trimmed to exactly fit." }, "trim": { "type": "boolean", @@ -2113,14 +2196,18 @@ "description": "When true, afer scheduling everything that will fit, any remaining time from the specified interval will be unscheduled (offline)" }, "fillerKind": { - "type": "string", - "description": "Flags this content as filler, which influences EPG grouping", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Flags this content as filler, which influences EPG grouping" }, "customTitle": { - "type": "string", - "description": "Overrides the title used in the EPG", - "nullable": true + "type": [ + "null", + "string" + ], + "description": "Overrides the title used in the EPG" }, "disableWatermarks": { "type": "boolean" diff --git a/ErsatzTV/wwwroot/openapi/v1.json b/ErsatzTV/wwwroot/openapi/v1.json index d3512df18..5c0bd5eb4 100644 --- a/ErsatzTV/wwwroot/openapi/v1.json +++ b/ErsatzTV/wwwroot/openapi/v1.json @@ -1,5 +1,5 @@ { - "openapi": "3.0.1", + "openapi": "3.1.1", "info": { "title": "ErsatzTV | v1", "version": "1.0.0" @@ -605,24 +605,34 @@ "format": "int32" }, "number": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "name": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "fFmpegProfile": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "language": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "streamingMode": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] } } }, @@ -638,8 +648,10 @@ "format": "int32" }, "appVersion": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] } } }, @@ -674,8 +686,10 @@ "type": "object", "properties": { "name": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "threadCount": { "type": "integer", @@ -685,20 +699,26 @@ "$ref": "#/components/schemas/HardwareAccelerationKind" }, "vaapiDisplay": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "vaapiDriver": { "$ref": "#/components/schemas/VaapiDriver" }, "vaapiDevice": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "qsvExtraHardwareFrames": { - "type": "integer", - "format": "int32", - "nullable": true + "type": [ + "null", + "integer" + ], + "format": "int32" }, "resolutionId": { "type": "integer", @@ -711,12 +731,16 @@ "$ref": "#/components/schemas/FFmpegProfileVideoFormat" }, "videoProfile": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "videoPreset": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "allowBFrames": { "type": "boolean" @@ -773,12 +797,16 @@ "type": "object", "properties": { "query": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "name": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] } } }, @@ -818,8 +846,10 @@ "format": "int32" }, "name": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "threadCount": { "type": "integer", @@ -829,24 +859,32 @@ "$ref": "#/components/schemas/HardwareAccelerationKind" }, "vaapiDisplay": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "vaapiDriver": { "$ref": "#/components/schemas/VaapiDriver" }, "vaapiDevice": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "qsvExtraHardwareFrames": { - "type": "integer", - "format": "int32", - "nullable": true + "type": [ + "null", + "integer" + ], + "format": "int32" }, "resolution": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "scalingBehavior": { "$ref": "#/components/schemas/ScalingBehavior" @@ -855,12 +893,16 @@ "$ref": "#/components/schemas/FFmpegProfileVideoFormat" }, "videoProfile": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "videoPreset": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "allowBFrames": { "type": "boolean" @@ -905,8 +947,10 @@ "type": "boolean" }, "deinterlaceVideo": { - "type": "boolean", - "nullable": true + "type": [ + "null", + "boolean" + ] } } }, @@ -967,12 +1011,16 @@ "type": "object", "properties": { "channelNumber": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "state": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "transcodedUntil": { "type": "string", @@ -1005,8 +1053,10 @@ "format": "int32" }, "name": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "width": { "type": "integer", @@ -1035,8 +1085,10 @@ "type": "object", "properties": { "showTitle": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "deepScan": { "type": "boolean", @@ -1057,12 +1109,16 @@ "format": "int32" }, "name": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "query": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] } } }, @@ -1102,8 +1158,10 @@ "format": "int32" }, "name": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "threadCount": { "type": "integer", @@ -1113,20 +1171,26 @@ "$ref": "#/components/schemas/HardwareAccelerationKind" }, "vaapiDisplay": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "vaapiDriver": { "$ref": "#/components/schemas/VaapiDriver" }, "vaapiDevice": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "qsvExtraHardwareFrames": { - "type": "integer", - "format": "int32", - "nullable": true + "type": [ + "null", + "integer" + ], + "format": "int32" }, "resolutionId": { "type": "integer", @@ -1139,12 +1203,16 @@ "$ref": "#/components/schemas/FFmpegProfileVideoFormat" }, "videoProfile": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "videoPreset": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "allowBFrames": { "type": "boolean" @@ -1206,12 +1274,16 @@ "format": "int32" }, "name": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] }, "query": { - "type": "string", - "nullable": true + "type": [ + "null", + "string" + ] } } }, diff --git a/docker/Dockerfile b/docker/Dockerfile index c2773430f..25412bd86 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -28,6 +28,7 @@ COPY artwork/* ./artwork/ COPY ErsatzTV/*.csproj ./ErsatzTV/ COPY ErsatzTV.Application/*.csproj ./ErsatzTV.Application/ COPY ErsatzTV.Core/*.csproj ./ErsatzTV.Core/ +COPY ErsatzTV.Core.Nullable/*.csproj ./ErsatzTV.Core.Nullable/ COPY ErsatzTV.FFmpeg/*.csproj ./ErsatzTV.FFmpeg/ COPY ErsatzTV.Infrastructure/*.csproj ./ErsatzTV.Infrastructure/ COPY ErsatzTV.Infrastructure.Sqlite/*.csproj ./ErsatzTV.Infrastructure.Sqlite/ @@ -39,6 +40,7 @@ RUN dotnet restore -r linux-x64 ErsatzTV/ COPY ErsatzTV/. ./ErsatzTV/ COPY ErsatzTV.Application/. ./ErsatzTV.Application/ COPY ErsatzTV.Core/. ./ErsatzTV.Core/ +COPY ErsatzTV.Core.Nullable/. ./ErsatzTV.Core.Nullable/ COPY ErsatzTV.FFmpeg/. ./ErsatzTV.FFmpeg/ COPY ErsatzTV.Infrastructure/. ./ErsatzTV.Infrastructure/ COPY ErsatzTV.Infrastructure.Sqlite/. ./ErsatzTV.Infrastructure.Sqlite/ diff --git a/docker/arm32v7/Dockerfile b/docker/arm32v7/Dockerfile index 24280b7db..546cd8717 100644 --- a/docker/arm32v7/Dockerfile +++ b/docker/arm32v7/Dockerfile @@ -14,6 +14,7 @@ COPY artwork/* ./artwork/ COPY ErsatzTV/*.csproj ./ErsatzTV/ COPY ErsatzTV.Application/*.csproj ./ErsatzTV.Application/ COPY ErsatzTV.Core/*.csproj ./ErsatzTV.Core/ +COPY ErsatzTV.Core.Nullable/*.csproj ./ErsatzTV.Core.Nullable/ COPY ErsatzTV.FFmpeg/*.csproj ./ErsatzTV.FFmpeg/ COPY ErsatzTV.Infrastructure/*.csproj ./ErsatzTV.Infrastructure/ COPY ErsatzTV.Infrastructure.Sqlite/*.csproj ./ErsatzTV.Infrastructure.Sqlite/ @@ -25,6 +26,7 @@ RUN dotnet restore -r linux-arm ErsatzTV/ COPY ErsatzTV/. ./ErsatzTV/ COPY ErsatzTV.Application/. ./ErsatzTV.Application/ COPY ErsatzTV.Core/. ./ErsatzTV.Core/ +COPY ErsatzTV.Core.Nullable/. ./ErsatzTV.Core.Nullable/ COPY ErsatzTV.FFmpeg/. ./ErsatzTV.FFmpeg/ COPY ErsatzTV.Infrastructure/. ./ErsatzTV.Infrastructure/ COPY ErsatzTV.Infrastructure.Sqlite/. ./ErsatzTV.Infrastructure.Sqlite/ diff --git a/docker/arm64/Dockerfile b/docker/arm64/Dockerfile index ab8dcfb07..3e122386a 100644 --- a/docker/arm64/Dockerfile +++ b/docker/arm64/Dockerfile @@ -14,6 +14,7 @@ COPY artwork/* ./artwork/ COPY ErsatzTV/*.csproj ./ErsatzTV/ COPY ErsatzTV.Application/*.csproj ./ErsatzTV.Application/ COPY ErsatzTV.Core/*.csproj ./ErsatzTV.Core/ +COPY ErsatzTV.Core.Nullable/*.csproj ./ErsatzTV.Core.Nullable/ COPY ErsatzTV.FFmpeg/*.csproj ./ErsatzTV.FFmpeg/ COPY ErsatzTV.Infrastructure/*.csproj ./ErsatzTV.Infrastructure/ COPY ErsatzTV.Infrastructure.Sqlite/*.csproj ./ErsatzTV.Infrastructure.Sqlite/ @@ -25,6 +26,7 @@ RUN dotnet restore -r linux-arm64 ErsatzTV/ COPY ErsatzTV/. ./ErsatzTV/ COPY ErsatzTV.Application/. ./ErsatzTV.Application/ COPY ErsatzTV.Core/. ./ErsatzTV.Core/ +COPY ErsatzTV.Core.Nullable/. ./ErsatzTV.Core.Nullable/ COPY ErsatzTV.FFmpeg/. ./ErsatzTV.FFmpeg/ COPY ErsatzTV.Infrastructure/. ./ErsatzTV.Infrastructure/ COPY ErsatzTV.Infrastructure.Sqlite/. ./ErsatzTV.Infrastructure.Sqlite/ diff --git a/docker/ffmpeg-tests/Dockerfile b/docker/ffmpeg-tests/Dockerfile index c4fc33817..5f3cedef4 100644 --- a/docker/ffmpeg-tests/Dockerfile +++ b/docker/ffmpeg-tests/Dockerfile @@ -13,6 +13,7 @@ COPY artwork/* ./artwork/ COPY ErsatzTV/*.csproj ./ErsatzTV/ COPY ErsatzTV.Application/*.csproj ./ErsatzTV.Application/ COPY ErsatzTV.Core/*.csproj ./ErsatzTV.Core/ +COPY ErsatzTV.Core.Nullable/*.csproj ./ErsatzTV.Core.Nullable/ COPY ErsatzTV.FFmpeg/*.csproj ./ErsatzTV.FFmpeg/ COPY ErsatzTV.Infrastructure/*.csproj ./ErsatzTV.Infrastructure/ COPY ErsatzTV.Infrastructure.Sqlite/*.csproj ./ErsatzTV.Infrastructure.Sqlite/ @@ -25,6 +26,7 @@ RUN dotnet restore -r linux-x64 ErsatzTV/ COPY ErsatzTV/. ./ErsatzTV/ COPY ErsatzTV.Application/. ./ErsatzTV.Application/ COPY ErsatzTV.Core/. ./ErsatzTV.Core/ +COPY ErsatzTV.Core.Nullable/. ./ErsatzTV.Core.Nullable/ COPY ErsatzTV.FFmpeg/. ./ErsatzTV.FFmpeg/ COPY ErsatzTV.Infrastructure/. ./ErsatzTV.Infrastructure/ COPY ErsatzTV.Infrastructure.Sqlite/. ./ErsatzTV.Infrastructure.Sqlite/