diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/PadUntilExactRequestModel.cs b/ErsatzTV.Core/Api/ScriptedPlayout/PadUntilExactRequestModel.cs new file mode 100644 index 000000000..e037c90b9 --- /dev/null +++ b/ErsatzTV.Core/Api/ScriptedPlayout/PadUntilExactRequestModel.cs @@ -0,0 +1,39 @@ +using System.ComponentModel; + +namespace ErsatzTV.Core.Api.ScriptedPlayout; + +public record PadUntilExactRequestModel +{ + [Description("The 'key' for the content that should be added")] + public string Content { get; set; } + + [Description("The time content should be added until")] + public 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; } + + [Description("Controls whether content will be trimmed to exactly fit until the specified time")] + public bool Trim { get; set; } + + [Description( + "When trim is false, this is the number of times to discard items from the collection to find something that fits until the specified time")] + public int DiscardAttempts { get; set; } + + [Description( + "When false, allows content to run over the specified the specified time before completing this request")] + public bool StopBeforeEnd { get; set; } = true; + + [Description( + "When true, afer scheduling everything that will fit, any remaining time from the specified interval will be unscheduled (offline)")] + public bool OfflineTail { get; set; } + + [Description("Flags this content as filler, which influences EPG grouping")] + public string FillerKind { get; set; } + + [Description("Overrides the title used in the EPG")] + public string CustomTitle { get; set; } + + public bool DisableWatermarks { get; set; } +} diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/PeekItemResponseModel.cs b/ErsatzTV.Core/Api/ScriptedPlayout/PeekItemResponseModel.cs new file mode 100644 index 000000000..e65132061 --- /dev/null +++ b/ErsatzTV.Core/Api/ScriptedPlayout/PeekItemResponseModel.cs @@ -0,0 +1,11 @@ +using System.ComponentModel; + +namespace ErsatzTV.Core.Api.ScriptedPlayout; + +public class PeekItemResponseModel +{ + public string Content { get; set; } + + [Description("Duration in milliseconds")] + public long Milliseconds { get; set; } +} diff --git a/ErsatzTV.Core/Api/ScriptedPlayout/WaitUntilExactRequestModel.cs b/ErsatzTV.Core/Api/ScriptedPlayout/WaitUntilExactRequestModel.cs new file mode 100644 index 000000000..a508a92c0 --- /dev/null +++ b/ErsatzTV.Core/Api/ScriptedPlayout/WaitUntilExactRequestModel.cs @@ -0,0 +1,12 @@ +using System.ComponentModel; + +namespace ErsatzTV.Core.Api.ScriptedPlayout; + +public record WaitUntilExactRequestModel +{ + [Description("The time to wait (insert unscheduled time) until")] + public 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/Scheduling/Engine/ISchedulingEngine.cs b/ErsatzTV.Core/Scheduling/Engine/ISchedulingEngine.cs index 8b361af4c..01a874f4e 100644 --- a/ErsatzTV.Core/Scheduling/Engine/ISchedulingEngine.cs +++ b/ErsatzTV.Core/Scheduling/Engine/ISchedulingEngine.cs @@ -103,6 +103,20 @@ public interface ISchedulingEngine string customTitle, bool disableWatermarks); + bool PadUntilExact( + string content, + DateTimeOffset padUntil, + string fallback, + bool trim, + int discardAttempts, + bool stopBeforeEnd, + bool offlineTail, + Option maybeFillerKind, + string customTitle, + bool disableWatermarks); + + Option PeekNext(string content); + // control instructions void LockGuideGroup(bool advance); void UnlockGuideGroup(); @@ -118,10 +132,13 @@ public interface ISchedulingEngine void SkipItems(string content, int count); void SkipToItem(string content, int season, int episode); ISchedulingEngine WaitUntil(TimeOnly waitUntil, bool tomorrow, bool rewindOnReset); + ISchedulingEngine WaitUntilExact(DateTimeOffset waitUntil, bool rewindOnReset); PlayoutAnchor GetAnchor(); ISchedulingEngineState GetState(); + + TimeSpan DurationForMediaItem(MediaItem mediaItem); } diff --git a/ErsatzTV.Core/Scheduling/Engine/SchedulingEngine.cs b/ErsatzTV.Core/Scheduling/Engine/SchedulingEngine.cs index 5c24ac7e7..b135ae74b 100644 --- a/ErsatzTV.Core/Scheduling/Engine/SchedulingEngine.cs +++ b/ErsatzTV.Core/Scheduling/Engine/SchedulingEngine.cs @@ -586,6 +586,52 @@ public class SchedulingEngine( return true; } + public bool PadUntilExact( + string content, + DateTimeOffset padUntil, + string fallback, + bool trim, + int discardAttempts, + bool stopBeforeEnd, + bool offlineTail, + Option maybeFillerKind, + string customTitle, + bool disableWatermarks) + { + if (!_enumerators.TryGetValue(content, out EnumeratorDetails enumeratorDetails)) + { + logger.LogWarning("Skipping invalid content {Key}", content); + return false; + } + + EnumeratorDetails fallbackEnumeratorDetails = null; + if (!string.IsNullOrEmpty(fallback)) + { + _enumerators.TryGetValue(fallback, out fallbackEnumeratorDetails); + } + + DateTimeOffset targetTime = _state.CurrentTime; + if (targetTime < padUntil) + { + // this is wrong when offset changes? + targetTime = padUntil.ToLocalTime(); + } + + _state.CurrentTime = AddDurationInternal( + targetTime, + stopBeforeEnd, + discardAttempts, + trim, + offlineTail, + GetFillerKind(maybeFillerKind), + customTitle, + disableWatermarks, + enumeratorDetails, + Optional(fallbackEnumeratorDetails)); + + return true; + } + private DateTimeOffset AddDurationInternal( DateTimeOffset targetTime, bool stopBeforeEnd, @@ -844,6 +890,17 @@ public class SchedulingEngine( return result; } + public Option PeekNext(string content) + { + if (!_enumerators.TryGetValue(content, out EnumeratorDetails enumeratorDetails)) + { + logger.LogWarning("Unable to peek next item for invalid content {Key}", content); + return Option.None; + } + + return enumeratorDetails.Enumerator.Current; + } + public void LockGuideGroup(bool advance) { _state.LockGuideGroup(advance); @@ -1006,6 +1063,29 @@ public class SchedulingEngine( return this; } + public ISchedulingEngine WaitUntilExact(DateTimeOffset waitUntil, bool rewindOnReset) + { + var currentTime = _state.CurrentTime; + + if (currentTime > waitUntil) + { + if (rewindOnReset && _state.Mode == PlayoutBuildMode.Reset) + { + // maybe wrong when offset changes? + currentTime = waitUntil.ToLocalTime(); + } + } + else + { + // this is wrong when offset changes? + currentTime = waitUntil.ToLocalTime(); + } + + _state.CurrentTime = currentTime; + + return this; + } + public PlayoutAnchor GetAnchor() { DateTime maxTime = _state.CurrentTime.UtcDateTime; @@ -1199,7 +1279,7 @@ public class SchedulingEngine( } } - private static TimeSpan DurationForMediaItem(MediaItem mediaItem) + public TimeSpan DurationForMediaItem(MediaItem mediaItem) { if (mediaItem is Image image) { diff --git a/ErsatzTV/Controllers/Api/ScriptedScheduleController.cs b/ErsatzTV/Controllers/Api/ScriptedScheduleController.cs index 73b827f93..191bffc3f 100644 --- a/ErsatzTV/Controllers/Api/ScriptedScheduleController.cs +++ b/ErsatzTV/Controllers/Api/ScriptedScheduleController.cs @@ -123,10 +123,10 @@ public class ScriptedScheduleController(IScriptedPlayoutBuilderService scriptedP return Ok(); } - [HttpPost("add_search", Name = "AddSearchQuery")] + [HttpPost("add_search", Name = "AddSearch")] [Tags("Scripted Content")] [EndpointSummary("Add a search query")] - public async Task AddSearchQuery( + public async Task AddSearch( [FromRoute] Guid buildId, [FromBody] @@ -319,7 +319,7 @@ public class ScriptedScheduleController(IScriptedPlayoutBuilderService scriptedP [HttpPost("pad_until", Name = "PadUntil")] [Tags("Scripted Scheduling")] - [EndpointSummary("Add content until a specified time")] + [EndpointSummary("Add content until a specified time of day")] public ActionResult PadUntil( [FromRoute] Guid buildId, [FromBody] PadUntilRequestModel request) @@ -351,6 +351,65 @@ public class ScriptedScheduleController(IScriptedPlayoutBuilderService scriptedP return GetContextInternal(engine); } + [HttpPost("pad_until_exact", Name = "PadUntilExact")] + [Tags("Scripted Scheduling")] + [EndpointSummary("Add content until an exact time")] + public ActionResult PadUntilExact( + [FromRoute] + Guid buildId, + [FromBody] + PadUntilExactRequestModel request) + { + ISchedulingEngine engine = scriptedPlayoutBuilderService.GetEngine(buildId); + if (engine == null) + { + return NotFound($"Active build engine not found for build {buildId}."); + } + + Option maybeFillerKind = Option.None; + if (Enum.TryParse(request.FillerKind, ignoreCase: true, out FillerKind fk)) + { + maybeFillerKind = fk; + } + + engine.PadUntilExact( + request.Content, + request.When, + request.Fallback, + request.Trim, + request.DiscardAttempts, + request.StopBeforeEnd, + request.OfflineTail, + maybeFillerKind, + request.CustomTitle, + request.DisableWatermarks); + return GetContextInternal(engine); + } + + [HttpGet("peek_next/{content}", Name="PeekNext")] + [Tags("Scripted Scheduling")] + [EndpointSummary("Peek the next content item")] + public ActionResult PeekNext(Guid buildId, string content) + { + ISchedulingEngine engine = scriptedPlayoutBuilderService.GetEngine(buildId); + if (engine == null) + { + return NotFound($"Active build engine not found for build {buildId}."); + } + + Option maybeMediaItem = engine.PeekNext(content); + foreach (var mediaItem in maybeMediaItem) + { + return new PeekItemResponseModel + { + Content = content, + Milliseconds = (long)engine.DurationForMediaItem(mediaItem).TotalMilliseconds + }; + } + + return NotFound("Content key does not exist, or collection is empty"); + } + [HttpPost("start_epg_group", Name = "StartEpgGroup")] [Tags("Scripted Control")] [EndpointSummary("Start an EPG group")] @@ -481,10 +540,30 @@ public class ScriptedScheduleController(IScriptedPlayoutBuilderService scriptedP return Ok(); } + [HttpPost("wait_until_exact", Name = "WaitUntilExact")] + [Tags("Scripted Control")] + [EndpointSummary("Wait until an exact time")] + public ActionResult WaitUntilExact( + [FromRoute] + Guid buildId, + [FromBody] + WaitUntilExactRequestModel request) + { + ISchedulingEngine engine = scriptedPlayoutBuilderService.GetEngine(buildId); + if (engine == null) + { + return NotFound($"Active build engine not found for build {buildId}."); + } + + engine.WaitUntilExact(request.When, request.RewindOnReset); + + return GetContextInternal(engine); + } + [HttpPost("wait_until", Name = "WaitUntil")] [Tags("Scripted Control")] - [EndpointSummary("Wait until the specified time")] - public ActionResult WaitUntil( + [EndpointSummary("Wait until the specified time of day")] + public ActionResult WaitUntilTime( [FromRoute] Guid buildId, [FromBody] diff --git a/ErsatzTV/wwwroot/openapi/scripted-schedule-tagged.json b/ErsatzTV/wwwroot/openapi/scripted-schedule-tagged.json index d2c55f677..2a450fcd6 100644 --- a/ErsatzTV/wwwroot/openapi/scripted-schedule-tagged.json +++ b/ErsatzTV/wwwroot/openapi/scripted-schedule-tagged.json @@ -253,7 +253,7 @@ "Scripted Content" ], "summary": "Add a search query", - "operationId": "AddSearchQuery", + "operationId": "AddSearch", "parameters": [ { "name": "buildId", @@ -670,7 +670,7 @@ "tags": [ "Scripted Scheduling" ], - "summary": "Add content until a specified time", + "summary": "Add content until a specified time of day", "operationId": "PadUntil", "parameters": [ { @@ -732,6 +732,123 @@ } } }, + "/api/scripted/playout/build/{buildId}/pad_until_exact": { + "post": { + "tags": [ + "Scripted Scheduling" + ], + "summary": "Add content until an exact time", + "operationId": "PadUntilExact", + "parameters": [ + { + "name": "buildId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/PadUntilExactRequestModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/PadUntilExactRequestModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PadUntilExactRequestModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/PadUntilExactRequestModel" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ContextResponseModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContextResponseModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ContextResponseModel" + } + } + } + } + } + } + }, + "/api/scripted/playout/build/{buildId}/peek_next/{content}": { + "get": { + "tags": [ + "Scripted Scheduling" + ], + "summary": "Peek the next content item", + "operationId": "PeekNext", + "parameters": [ + { + "name": "buildId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "content", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/PeekItemResponseModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/PeekItemResponseModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PeekItemResponseModel" + } + } + } + } + } + } + }, "/api/scripted/playout/build/{buildId}/start_epg_group": { "post": { "tags": [ @@ -1107,12 +1224,79 @@ } } }, + "/api/scripted/playout/build/{buildId}/wait_until_exact": { + "post": { + "tags": [ + "Scripted Control" + ], + "summary": "Wait until an exact time", + "operationId": "WaitUntilExact", + "parameters": [ + { + "name": "buildId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/WaitUntilExactRequestModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/WaitUntilExactRequestModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/WaitUntilExactRequestModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/WaitUntilExactRequestModel" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ContextResponseModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContextResponseModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ContextResponseModel" + } + } + } + } + } + } + }, "/api/scripted/playout/build/{buildId}/wait_until": { "post": { "tags": [ "Scripted Control" ], - "summary": "Wait until the specified time", + "summary": "Wait until the specified time of day", "operationId": "WaitUntil", "parameters": [ { @@ -1551,6 +1735,56 @@ } } }, + "PadUntilExactRequestModel": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "The 'key' for the content that should be added", + "nullable": true + }, + "when": { + "type": "string", + "description": "The time content should be added until", + "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 + }, + "trim": { + "type": "boolean", + "description": "Controls whether content will be trimmed to exactly fit until the specified time" + }, + "discardAttempts": { + "type": "integer", + "description": "When trim is false, this is the number of times to discard items from the collection to find something that fits until the specified time", + "format": "int32" + }, + "stopBeforeEnd": { + "type": "boolean", + "description": "When false, allows content to run over the specified the specified time before completing this request" + }, + "offlineTail": { + "type": "boolean", + "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 + }, + "customTitle": { + "type": "string", + "description": "Overrides the title used in the EPG", + "nullable": true + }, + "disableWatermarks": { + "type": "boolean" + } + } + }, "PadUntilRequestModel": { "type": "object", "properties": { @@ -1605,6 +1839,20 @@ } } }, + "PeekItemResponseModel": { + "type": "object", + "properties": { + "content": { + "type": "string", + "nullable": true + }, + "milliseconds": { + "type": "integer", + "description": "Duration in milliseconds", + "format": "int64" + } + } + }, "SkipItemsRequestModel": { "type": "object", "properties": { @@ -1649,6 +1897,20 @@ } } }, + "WaitUntilExactRequestModel": { + "type": "object", + "properties": { + "when": { + "type": "string", + "description": "The time to wait (insert unscheduled time) until", + "format": "date-time" + }, + "rewindOnReset": { + "type": "boolean", + "description": "When true, the current time of the playout build is allowed to move backward when the playout is reset." + } + } + }, "WaitUntilRequestModel": { "type": "object", "properties": { diff --git a/ErsatzTV/wwwroot/openapi/scripted-schedule.json b/ErsatzTV/wwwroot/openapi/scripted-schedule.json index 2b4e87233..ab5876230 100644 --- a/ErsatzTV/wwwroot/openapi/scripted-schedule.json +++ b/ErsatzTV/wwwroot/openapi/scripted-schedule.json @@ -253,7 +253,7 @@ "ScriptedSchedule" ], "summary": "Add a search query", - "operationId": "AddSearchQuery", + "operationId": "AddSearch", "parameters": [ { "name": "buildId", @@ -670,7 +670,7 @@ "tags": [ "ScriptedSchedule" ], - "summary": "Add content until a specified time", + "summary": "Add content until a specified time of day", "operationId": "PadUntil", "parameters": [ { @@ -732,6 +732,123 @@ } } }, + "/api/scripted/playout/build/{buildId}/pad_until_exact": { + "post": { + "tags": [ + "ScriptedSchedule" + ], + "summary": "Add content until an exact time", + "operationId": "PadUntilExact", + "parameters": [ + { + "name": "buildId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/PadUntilExactRequestModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/PadUntilExactRequestModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PadUntilExactRequestModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/PadUntilExactRequestModel" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ContextResponseModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContextResponseModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ContextResponseModel" + } + } + } + } + } + } + }, + "/api/scripted/playout/build/{buildId}/peek_next/{content}": { + "get": { + "tags": [ + "ScriptedSchedule" + ], + "summary": "Peek the next content item", + "operationId": "PeekNext", + "parameters": [ + { + "name": "buildId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "content", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/PeekItemResponseModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/PeekItemResponseModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PeekItemResponseModel" + } + } + } + } + } + } + }, "/api/scripted/playout/build/{buildId}/start_epg_group": { "post": { "tags": [ @@ -1107,12 +1224,79 @@ } } }, + "/api/scripted/playout/build/{buildId}/wait_until_exact": { + "post": { + "tags": [ + "ScriptedSchedule" + ], + "summary": "Wait until an exact time", + "operationId": "WaitUntilExact", + "parameters": [ + { + "name": "buildId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/WaitUntilExactRequestModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/WaitUntilExactRequestModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/WaitUntilExactRequestModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/WaitUntilExactRequestModel" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ContextResponseModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContextResponseModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ContextResponseModel" + } + } + } + } + } + } + }, "/api/scripted/playout/build/{buildId}/wait_until": { "post": { "tags": [ "ScriptedSchedule" ], - "summary": "Wait until the specified time", + "summary": "Wait until the specified time of day", "operationId": "WaitUntil", "parameters": [ { @@ -1551,6 +1735,56 @@ } } }, + "PadUntilExactRequestModel": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "The 'key' for the content that should be added", + "nullable": true + }, + "when": { + "type": "string", + "description": "The time content should be added until", + "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 + }, + "trim": { + "type": "boolean", + "description": "Controls whether content will be trimmed to exactly fit until the specified time" + }, + "discardAttempts": { + "type": "integer", + "description": "When trim is false, this is the number of times to discard items from the collection to find something that fits until the specified time", + "format": "int32" + }, + "stopBeforeEnd": { + "type": "boolean", + "description": "When false, allows content to run over the specified the specified time before completing this request" + }, + "offlineTail": { + "type": "boolean", + "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 + }, + "customTitle": { + "type": "string", + "description": "Overrides the title used in the EPG", + "nullable": true + }, + "disableWatermarks": { + "type": "boolean" + } + } + }, "PadUntilRequestModel": { "type": "object", "properties": { @@ -1605,6 +1839,20 @@ } } }, + "PeekItemResponseModel": { + "type": "object", + "properties": { + "content": { + "type": "string", + "nullable": true + }, + "milliseconds": { + "type": "integer", + "description": "Duration in milliseconds", + "format": "int64" + } + } + }, "SkipItemsRequestModel": { "type": "object", "properties": { @@ -1649,6 +1897,20 @@ } } }, + "WaitUntilExactRequestModel": { + "type": "object", + "properties": { + "when": { + "type": "string", + "description": "The time to wait (insert unscheduled time) until", + "format": "date-time" + }, + "rewindOnReset": { + "type": "boolean", + "description": "When true, the current time of the playout build is allowed to move backward when the playout is reset." + } + } + }, "WaitUntilRequestModel": { "type": "object", "properties": { @@ -1700,4 +1962,4 @@ "name": "ScriptedSchedule" } ] -} +} \ No newline at end of file diff --git a/scripts/scripted-schedules/entrypoint.py b/scripts/scripted-schedules/entrypoint.py index 9213a5a10..275cf484d 100755 --- a/scripts/scripted-schedules/entrypoint.py +++ b/scripts/scripted-schedules/entrypoint.py @@ -39,7 +39,7 @@ def main(): define_content(api_instance, context, known_args.build_id) if known_args.mode == "reset": - reset_playout(api_instance, context, known_args.build_id) + context = reset_playout(api_instance, context, known_args.build_id) build_playout(api_instance, context, known_args.build_id) except etv_client.ApiException as e: