add all content sources to scripted schedules (#2340)
* add show content * add multi collection content * add smart collection content * add playlist content * fix infinite loop * add marathon content
This commit is contained in:
@@ -1,34 +1,117 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Scheduling.Engine;
|
||||
using IronPython.Runtime;
|
||||
|
||||
namespace ErsatzTV.Core.Scheduling.ScriptedScheduling.Modules;
|
||||
|
||||
[SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")]
|
||||
[SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits")]
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public class ContentModule(ISchedulingEngine schedulingEngine)
|
||||
{
|
||||
public bool add_search(string key, string query, string order)
|
||||
public void add_search(string key, string query, string order)
|
||||
{
|
||||
if (!Enum.TryParse(order, ignoreCase: true, out PlaybackOrder playbackOrder))
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
schedulingEngine.AddSearch(key, query, playbackOrder).GetAwaiter().GetResult();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool add_collection(string key, string collection, string order)
|
||||
public void add_collection(string key, string collection, string order)
|
||||
{
|
||||
if (!Enum.TryParse(order, ignoreCase: true, out PlaybackOrder playbackOrder))
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
schedulingEngine.AddCollection(key, collection, playbackOrder).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
return true;
|
||||
public void add_marathon(
|
||||
string key,
|
||||
string group_by,
|
||||
string item_order = "shuffle",
|
||||
PythonDictionary guids = null,
|
||||
PythonList searches = null,
|
||||
bool play_all_items = false,
|
||||
bool shuffle_groups = false)
|
||||
{
|
||||
|
||||
if (!Enum.TryParse(item_order, ignoreCase: true, out PlaybackOrder itemPlaybackOrder))
|
||||
{
|
||||
itemPlaybackOrder = PlaybackOrder.Shuffle;
|
||||
}
|
||||
|
||||
var mappedGuids = new Dictionary<string, List<string>>();
|
||||
if (guids != null)
|
||||
{
|
||||
foreach (KeyValuePair<object, object> guid in guids)
|
||||
{
|
||||
var guidKey = guid.Key.ToString();
|
||||
if (guidKey is not null && guid.Value is PythonList guidValues)
|
||||
{
|
||||
mappedGuids.Add(guidKey, guidValues.Select(x => x.ToString()).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var mappedSearches = new List<string>();
|
||||
if (searches != null)
|
||||
{
|
||||
mappedSearches.AddRange(searches.Select(x => x.ToString()));
|
||||
}
|
||||
|
||||
// guids OR searches are required
|
||||
if (mappedGuids.Count == 0 && mappedSearches.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
schedulingEngine
|
||||
.AddMarathon(key, mappedGuids, mappedSearches, group_by, shuffle_groups, itemPlaybackOrder, play_all_items)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
public void add_multi_collection(string key, string multi_collection, string order)
|
||||
{
|
||||
if (!Enum.TryParse(order, ignoreCase: true, out PlaybackOrder playbackOrder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
schedulingEngine.AddMultiCollection(key, multi_collection, playbackOrder).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public void add_playlist(string key, string playlist, string playlist_group)
|
||||
{
|
||||
schedulingEngine.AddPlaylist(key, playlist, playlist_group).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public void add_smart_collection(string key, string smart_collection, string order)
|
||||
{
|
||||
if (!Enum.TryParse(order, ignoreCase: true, out PlaybackOrder playbackOrder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
schedulingEngine.AddSmartCollection(key, smart_collection, playbackOrder).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public void add_show(string key, PythonDictionary guids, string order)
|
||||
{
|
||||
if (!Enum.TryParse(order, ignoreCase: true, out PlaybackOrder playbackOrder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
schedulingEngine
|
||||
.AddShow(key, guids.ToDictionary(k => k.Key.ToString(), k => k.Value.ToString()), playbackOrder)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,19 @@ namespace ErsatzTV.Core.Scheduling.ScriptedScheduling.Modules;
|
||||
[SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")]
|
||||
[SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits")]
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public class PlayoutModule(ISchedulingEngine schedulingEngine)
|
||||
{
|
||||
public int FailureCount { get; private set; }
|
||||
|
||||
// content instructions
|
||||
|
||||
public void add_count(string content, int count, string filler_kind = null, string custom_title = null, bool disable_watermarks = false)
|
||||
public void add_count(
|
||||
string content,
|
||||
int count,
|
||||
string filler_kind = null,
|
||||
string custom_title = null,
|
||||
bool disable_watermarks = false)
|
||||
{
|
||||
Option<FillerKind> maybeFillerKind = Option<FillerKind>.None;
|
||||
if (Enum.TryParse(filler_kind, ignoreCase: true, out FillerKind fillerKind))
|
||||
@@ -19,7 +27,15 @@ public class PlayoutModule(ISchedulingEngine schedulingEngine)
|
||||
maybeFillerKind = fillerKind;
|
||||
}
|
||||
|
||||
schedulingEngine.AddCount(content, count, maybeFillerKind, custom_title, disable_watermarks);
|
||||
bool success = schedulingEngine.AddCount(content, count, maybeFillerKind, custom_title, disable_watermarks);
|
||||
if (success)
|
||||
{
|
||||
FailureCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
FailureCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ public class ScriptedPlayoutBuilder(
|
||||
// define content first
|
||||
engine.Operations.Invoke(defineContentFunc);
|
||||
|
||||
var context = new PythonPlayoutContext(schedulingEngine.GetState(), engine);
|
||||
var context = new PythonPlayoutContext(schedulingEngine.GetState(), playoutModule, engine);
|
||||
|
||||
// reset if applicable
|
||||
if (mode is PlayoutBuildMode.Reset && resetPlayoutFunc != null)
|
||||
@@ -134,12 +134,18 @@ public class ScriptedPlayoutBuilder(
|
||||
public class PythonPlayoutContext
|
||||
{
|
||||
private readonly ISchedulingEngineState _state;
|
||||
private readonly PlayoutModule _playoutModule;
|
||||
private readonly ScriptEngine _scriptEngine;
|
||||
private readonly dynamic _datetimeModule;
|
||||
private const int MaxFailures = 10;
|
||||
|
||||
public PythonPlayoutContext(ISchedulingEngineState state, ScriptEngine scriptEngine)
|
||||
public PythonPlayoutContext(
|
||||
ISchedulingEngineState state,
|
||||
PlayoutModule playoutModule,
|
||||
ScriptEngine scriptEngine)
|
||||
{
|
||||
_state = state;
|
||||
_playoutModule = playoutModule;
|
||||
_scriptEngine = scriptEngine;
|
||||
_datetimeModule = _scriptEngine.ImportModule("datetime");
|
||||
}
|
||||
@@ -147,7 +153,17 @@ public class ScriptedPlayoutBuilder(
|
||||
public object current_time => ToPythonDateTime(_state.CurrentTime);
|
||||
//public object finish => ToPythonDateTime(_state.Finish);
|
||||
|
||||
public bool is_done() => _state.CurrentTime >= _state.Finish;
|
||||
public bool is_done()
|
||||
{
|
||||
if (_playoutModule.FailureCount >= MaxFailures)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Script execution halted after {MaxFailures} consecutive failures to add content."
|
||||
);
|
||||
}
|
||||
|
||||
return _state.CurrentTime >= _state.Finish;
|
||||
}
|
||||
|
||||
private object ToPythonDateTime(DateTimeOffset dto)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user