add create_playlist, pre_roll_on, pre_roll_off to scripted schedules (#2387)
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
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; }
|
||||
|
||||
[Description("List of playlist items")]
|
||||
public List<PlaylistItem> Items { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ErsatzTV.Core.Api.ScriptedPlayout;
|
||||
|
||||
public record ControlPreRollOn
|
||||
{
|
||||
[Description("The 'key' for the scripted playlist")]
|
||||
public string Playlist { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ErsatzTV.Core.Api.ScriptedPlayout;
|
||||
|
||||
public record PlaylistItem
|
||||
{
|
||||
[Description("The 'key' for the content")]
|
||||
public string Content { get; set; }
|
||||
|
||||
public int Count { get; set; }
|
||||
}
|
||||
@@ -18,5 +18,6 @@ public enum ProgramScheduleItemCollectionType
|
||||
Image = 60,
|
||||
RemoteStream = 70,
|
||||
|
||||
FakeCollection = 100
|
||||
FakeCollection = 100,
|
||||
FakePlaylistItem = 101
|
||||
}
|
||||
|
||||
@@ -51,6 +51,11 @@ public class CollectionKey : Record<CollectionKey>
|
||||
{
|
||||
CollectionType = item.CollectionType
|
||||
},
|
||||
ProgramScheduleItemCollectionType.FakePlaylistItem => new CollectionKey
|
||||
{
|
||||
CollectionType = item.CollectionType,
|
||||
CollectionId = item.CollectionId
|
||||
},
|
||||
ProgramScheduleItemCollectionType.Movie => new CollectionKey
|
||||
{
|
||||
CollectionType = item.CollectionType,
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Scheduling;
|
||||
|
||||
namespace ErsatzTV.Core.Scheduling.Engine;
|
||||
|
||||
public record EnumeratorDetails(
|
||||
IMediaCollectionEnumerator Enumerator,
|
||||
string HistoryKey,
|
||||
PlaybackOrder PlaybackOrder);
|
||||
@@ -42,6 +42,7 @@ public interface ISchedulingEngine
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task AddPlaylist(string key, string playlist, string playlistGroup, CancellationToken cancellationToken);
|
||||
Task CreatePlaylist(string key, Dictionary<string, int> playlistItems, CancellationToken cancellationToken);
|
||||
|
||||
Task AddSmartCollection(
|
||||
string key,
|
||||
@@ -129,6 +130,9 @@ public interface ISchedulingEngine
|
||||
Task GraphicsOff(List<string> graphicsElements, CancellationToken cancellationToken);
|
||||
Task WatermarkOn(List<string> watermarks);
|
||||
Task WatermarkOff(List<string> watermarks);
|
||||
void PreRollOn(string content);
|
||||
void PreRollOff();
|
||||
|
||||
void SkipItems(string content, int count);
|
||||
void SkipToItem(string content, int season, int episode);
|
||||
ISchedulingEngine WaitUntil(TimeOnly waitUntil, bool tomorrow, bool rewindOnReset);
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace ErsatzTV.Core.Scheduling.Engine;
|
||||
|
||||
public class MarathonHelper(IMediaCollectionRepository mediaCollectionRepository)
|
||||
{
|
||||
public async Task<Option<MarathonContentResult>> GetEnumerator(
|
||||
public async Task<Option<PlaylistContentResult>> GetEnumerator(
|
||||
Dictionary<string, List<string>> guids,
|
||||
List<string> searches,
|
||||
string groupBy,
|
||||
@@ -70,7 +70,7 @@ public class MarathonHelper(IMediaCollectionRepository mediaCollectionRepository
|
||||
shuffleGroups,
|
||||
cancellationToken);
|
||||
|
||||
return new MarathonContentResult(
|
||||
return new PlaylistContentResult(
|
||||
enumerator,
|
||||
itemMap.ToImmutableDictionary(x => CollectionKey.ForPlaylistItem(x.Key), x => x.Value));
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,6 +3,6 @@ using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Core.Scheduling.Engine;
|
||||
|
||||
public record MarathonContentResult(
|
||||
public record PlaylistContentResult(
|
||||
PlaylistEnumerator PlaylistEnumerator,
|
||||
ImmutableDictionary<CollectionKey, List<MediaItem>> Content);
|
||||
@@ -0,0 +1,66 @@
|
||||
using System.Collections.Immutable;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
|
||||
namespace ErsatzTV.Core.Scheduling.Engine;
|
||||
|
||||
public class PlaylistHelper(IMediaCollectionRepository mediaCollectionRepository)
|
||||
{
|
||||
public async Task<Option<PlaylistContentResult>> GetEnumerator(
|
||||
Dictionary<string, EnumeratorDetails> enumerators,
|
||||
Dictionary<string, ImmutableList<MediaItem>> enumeratorMediaItems,
|
||||
Dictionary<string, int> playlistItems,
|
||||
CollectionEnumeratorState state,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Dictionary<PlaylistItem, List<MediaItem>> itemMap = [];
|
||||
|
||||
int playlistIndex = 0;
|
||||
var allKeys = playlistItems.Keys.ToList();
|
||||
for (var index = 0; index < allKeys.Count; index++)
|
||||
{
|
||||
string key = allKeys[index];
|
||||
ImmutableList<MediaItem> mediaItems = null;
|
||||
if (!enumerators.TryGetValue(key, out EnumeratorDetails enumeratorDetails) ||
|
||||
!enumeratorMediaItems.TryGetValue(key, out mediaItems))
|
||||
{
|
||||
Console.WriteLine($"Something is wrong with the playlist with key {key}");
|
||||
Console.WriteLine($"details: {(enumeratorDetails is null ? "null" : "not null")}");
|
||||
Console.WriteLine($"items: {(mediaItems?.Count ?? -1)}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int count = playlistItems[key];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
PlaylistItem playlistItem = new()
|
||||
{
|
||||
Index = playlistIndex,
|
||||
|
||||
CollectionType = ProgramScheduleItemCollectionType.FakePlaylistItem,
|
||||
CollectionId = playlistIndex,
|
||||
|
||||
PlayAll = false,
|
||||
PlaybackOrder = enumeratorDetails.PlaybackOrder,
|
||||
|
||||
IncludeInProgramGuide = true
|
||||
};
|
||||
|
||||
itemMap.Add(playlistItem, mediaItems.ToList());
|
||||
playlistIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
PlaylistEnumerator enumerator = await PlaylistEnumerator.Create(
|
||||
mediaCollectionRepository,
|
||||
itemMap,
|
||||
state,
|
||||
shufflePlaylistItems: false,
|
||||
cancellationToken);
|
||||
|
||||
return new PlaylistContentResult(
|
||||
enumerator,
|
||||
itemMap.ToImmutableDictionary(x => CollectionKey.ForPlaylistItem(x.Key), x => x.Value));
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ public class SchedulingEngine(
|
||||
private readonly Dictionary<string, Option<GraphicsElement>> _graphicsElementCache = new();
|
||||
private readonly Dictionary<string, Option<ChannelWatermark>> _watermarkCache = new();
|
||||
private readonly Dictionary<string, EnumeratorDetails> _enumerators = new();
|
||||
private readonly Dictionary<string, ImmutableList<MediaItem>> _enumeratorMediaItems = new();
|
||||
private readonly SchedulingEngineState _state = new(0);
|
||||
private PlayoutReferenceData _referenceData;
|
||||
|
||||
@@ -112,33 +113,36 @@ public class SchedulingEngine(
|
||||
PlaybackOrder playbackOrder,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_enumerators.ContainsKey(key))
|
||||
if (_enumerators.ContainsKey(key))
|
||||
{
|
||||
int index = _enumerators.Count;
|
||||
List<MediaItem> items =
|
||||
await mediaCollectionRepository.GetCollectionItemsByName(collectionName, cancellationToken);
|
||||
if (items.Count == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
int index = _enumerators.Count;
|
||||
List<MediaItem> items =
|
||||
await mediaCollectionRepository.GetCollectionItemsByName(collectionName, cancellationToken);
|
||||
if (items.Count == 0)
|
||||
{
|
||||
logger.LogWarning("Skipping invalid or empty collection {Name}", collectionName);
|
||||
return;
|
||||
}
|
||||
|
||||
_enumeratorMediaItems[key] = items.ToImmutableList();
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
foreach (var enumerator in EnumeratorForContent(items, state, playbackOrder))
|
||||
{
|
||||
string historyKey = HistoryDetails.KeyForSchedulingContent(key, playbackOrder);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, playbackOrder);
|
||||
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
{
|
||||
logger.LogWarning("Skipping invalid or empty collection {Name}", collectionName);
|
||||
return;
|
||||
}
|
||||
logger.LogDebug(
|
||||
"Added collection {Name} with key {Key} and order {Order}",
|
||||
collectionName,
|
||||
key,
|
||||
playbackOrder);
|
||||
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
foreach (var enumerator in EnumeratorForContent(items, state, playbackOrder))
|
||||
{
|
||||
string historyKey = HistoryDetails.KeyForSchedulingContent(key, playbackOrder);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, playbackOrder);
|
||||
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Added collection {Name} with key {Key} and order {Order}",
|
||||
collectionName,
|
||||
key,
|
||||
playbackOrder);
|
||||
|
||||
ApplyHistory(historyKey, items, enumerator, playbackOrder);
|
||||
}
|
||||
ApplyHistory(historyKey, items, enumerator, playbackOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,11 +156,16 @@ public class SchedulingEngine(
|
||||
PlaybackOrder itemPlaybackOrder,
|
||||
bool playAllItems)
|
||||
{
|
||||
if (_enumerators.ContainsKey(key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var helper = new MarathonHelper(mediaCollectionRepository);
|
||||
|
||||
int index = _enumerators.Count;
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
Option<MarathonContentResult> maybeResult = await helper.GetEnumerator(
|
||||
Option<PlaylistContentResult> maybeResult = await helper.GetEnumerator(
|
||||
guids,
|
||||
searches,
|
||||
groupBy,
|
||||
@@ -166,7 +175,7 @@ public class SchedulingEngine(
|
||||
state,
|
||||
CancellationToken.None);
|
||||
|
||||
foreach (MarathonContentResult result in maybeResult)
|
||||
foreach (PlaylistContentResult result in maybeResult)
|
||||
{
|
||||
foreach (PlaylistEnumerator enumerator in Optional(result.PlaylistEnumerator))
|
||||
{
|
||||
@@ -183,8 +192,6 @@ public class SchedulingEngine(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(10);
|
||||
}
|
||||
|
||||
public async Task AddMultiCollection(
|
||||
@@ -193,33 +200,36 @@ public class SchedulingEngine(
|
||||
PlaybackOrder playbackOrder,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_enumerators.ContainsKey(key))
|
||||
if (_enumerators.ContainsKey(key))
|
||||
{
|
||||
int index = _enumerators.Count;
|
||||
List<MediaItem> items =
|
||||
await mediaCollectionRepository.GetMultiCollectionItemsByName(multiCollectionName, cancellationToken);
|
||||
if (items.Count == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
int index = _enumerators.Count;
|
||||
List<MediaItem> items =
|
||||
await mediaCollectionRepository.GetMultiCollectionItemsByName(multiCollectionName, cancellationToken);
|
||||
if (items.Count == 0)
|
||||
{
|
||||
logger.LogWarning("Skipping invalid or empty multi collection {Name}", multiCollectionName);
|
||||
return;
|
||||
}
|
||||
|
||||
_enumeratorMediaItems[key] = items.ToImmutableList();
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
foreach (var enumerator in EnumeratorForContent(items, state, playbackOrder))
|
||||
{
|
||||
string historyKey = HistoryDetails.KeyForSchedulingContent(key, playbackOrder);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, playbackOrder);
|
||||
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
{
|
||||
logger.LogWarning("Skipping invalid or empty multi collection {Name}", multiCollectionName);
|
||||
return;
|
||||
}
|
||||
logger.LogDebug(
|
||||
"Added multi collection {Name} with key {Key} and order {Order}",
|
||||
multiCollectionName,
|
||||
key,
|
||||
playbackOrder);
|
||||
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
foreach (var enumerator in EnumeratorForContent(items, state, playbackOrder))
|
||||
{
|
||||
string historyKey = HistoryDetails.KeyForSchedulingContent(key, playbackOrder);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, playbackOrder);
|
||||
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Added multi collection {Name} with key {Key} and order {Order}",
|
||||
multiCollectionName,
|
||||
key,
|
||||
playbackOrder);
|
||||
|
||||
ApplyHistory(historyKey, items, enumerator, playbackOrder);
|
||||
}
|
||||
ApplyHistory(historyKey, items, enumerator, playbackOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,36 +240,78 @@ public class SchedulingEngine(
|
||||
string playlistGroup,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_enumerators.ContainsKey(key))
|
||||
if (_enumerators.ContainsKey(key))
|
||||
{
|
||||
int index = _enumerators.Count;
|
||||
Dictionary<PlaylistItem, List<MediaItem>> itemMap =
|
||||
await mediaCollectionRepository.GetPlaylistItemMap(playlistGroup, playlist, cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
int index = _enumerators.Count;
|
||||
Dictionary<PlaylistItem, List<MediaItem>> itemMap =
|
||||
await mediaCollectionRepository.GetPlaylistItemMap(playlistGroup, playlist, cancellationToken);
|
||||
|
||||
var enumerator = await PlaylistEnumerator.Create(
|
||||
mediaCollectionRepository,
|
||||
itemMap,
|
||||
state,
|
||||
false,
|
||||
CancellationToken.None);
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
|
||||
string historyKey = HistoryDetails.KeyForSchedulingContent(key, PlaybackOrder.None);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, PlaybackOrder.None);
|
||||
var enumerator = await PlaylistEnumerator.Create(
|
||||
mediaCollectionRepository,
|
||||
itemMap,
|
||||
state,
|
||||
false,
|
||||
CancellationToken.None);
|
||||
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
string historyKey = HistoryDetails.KeyForSchedulingContent(key, PlaybackOrder.None);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, PlaybackOrder.None);
|
||||
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Added playlist {Group} / {Name} with key {Key}",
|
||||
playlistGroup,
|
||||
playlist,
|
||||
key);
|
||||
|
||||
ApplyPlaylistHistory(
|
||||
historyKey,
|
||||
itemMap.ToImmutableDictionary(m => CollectionKey.ForPlaylistItem(m.Key), m => m.Value),
|
||||
enumerator);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CreatePlaylist(
|
||||
string key,
|
||||
Dictionary<string, int> playlistItems,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (_enumerators.ContainsKey(key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var helper = new PlaylistHelper(mediaCollectionRepository);
|
||||
|
||||
int index = _enumerators.Count;
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
Option<PlaylistContentResult> maybeResult = await helper.GetEnumerator(
|
||||
_enumerators,
|
||||
_enumeratorMediaItems,
|
||||
playlistItems,
|
||||
state,
|
||||
CancellationToken.None);
|
||||
|
||||
foreach (PlaylistContentResult result in maybeResult)
|
||||
{
|
||||
foreach (PlaylistEnumerator enumerator in Optional(result.PlaylistEnumerator))
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Added playlist {Group} / {Name} with key {Key}",
|
||||
playlistGroup,
|
||||
playlist,
|
||||
key);
|
||||
string historyKey = HistoryDetails.KeyForSchedulingPlaylistContent(key);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, PlaybackOrder.None);
|
||||
|
||||
ApplyPlaylistHistory(
|
||||
historyKey,
|
||||
itemMap.ToImmutableDictionary(m => CollectionKey.ForPlaylistItem(m.Key), m => m.Value),
|
||||
enumerator);
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
{
|
||||
logger.LogDebug("Created playlist with key {Key}", key);
|
||||
ApplyPlaylistHistory(
|
||||
historyKey,
|
||||
result.Content,
|
||||
enumerator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,33 +322,36 @@ public class SchedulingEngine(
|
||||
PlaybackOrder playbackOrder,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_enumerators.ContainsKey(key))
|
||||
if (_enumerators.ContainsKey(key))
|
||||
{
|
||||
int index = _enumerators.Count;
|
||||
List<MediaItem> items =
|
||||
await mediaCollectionRepository.GetSmartCollectionItemsByName(smartCollectionName, cancellationToken);
|
||||
if (items.Count == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
int index = _enumerators.Count;
|
||||
List<MediaItem> items =
|
||||
await mediaCollectionRepository.GetSmartCollectionItemsByName(smartCollectionName, cancellationToken);
|
||||
if (items.Count == 0)
|
||||
{
|
||||
logger.LogWarning("Skipping invalid or empty smart collection {Name}", smartCollectionName);
|
||||
return;
|
||||
}
|
||||
|
||||
_enumeratorMediaItems[key] = items.ToImmutableList();
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
foreach (var enumerator in EnumeratorForContent(items, state, playbackOrder))
|
||||
{
|
||||
string historyKey = HistoryDetails.KeyForSchedulingContent(key, playbackOrder);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, playbackOrder);
|
||||
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
{
|
||||
logger.LogWarning("Skipping invalid or empty smart collection {Name}", smartCollectionName);
|
||||
return;
|
||||
}
|
||||
logger.LogDebug(
|
||||
"Added smart collection {Name} with key {Key} and order {Order}",
|
||||
smartCollectionName,
|
||||
key,
|
||||
playbackOrder);
|
||||
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
foreach (var enumerator in EnumeratorForContent(items, state, playbackOrder))
|
||||
{
|
||||
string historyKey = HistoryDetails.KeyForSchedulingContent(key, playbackOrder);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, playbackOrder);
|
||||
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Added smart collection {Name} with key {Key} and order {Order}",
|
||||
smartCollectionName,
|
||||
key,
|
||||
playbackOrder);
|
||||
|
||||
ApplyHistory(historyKey, items, enumerator, playbackOrder);
|
||||
}
|
||||
ApplyHistory(historyKey, items, enumerator, playbackOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,33 +362,36 @@ public class SchedulingEngine(
|
||||
PlaybackOrder playbackOrder,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_enumerators.ContainsKey(key))
|
||||
if (_enumerators.ContainsKey(key))
|
||||
{
|
||||
int index = _enumerators.Count;
|
||||
List<MediaItem> items =
|
||||
await mediaCollectionRepository.GetSmartCollectionItems(query, string.Empty, cancellationToken);
|
||||
if (items.Count == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
int index = _enumerators.Count;
|
||||
List<MediaItem> items =
|
||||
await mediaCollectionRepository.GetSmartCollectionItems(query, string.Empty, cancellationToken);
|
||||
if (items.Count == 0)
|
||||
{
|
||||
logger.LogWarning("Skipping invalid or empty search query {Query}", query);
|
||||
return;
|
||||
}
|
||||
|
||||
_enumeratorMediaItems[key] = items.ToImmutableList();
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
foreach (var enumerator in EnumeratorForContent(items, state, playbackOrder))
|
||||
{
|
||||
string historyKey = HistoryDetails.KeyForSchedulingContent(key, playbackOrder);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, playbackOrder);
|
||||
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
{
|
||||
logger.LogWarning("Skipping invalid or empty search query {Query}", query);
|
||||
return;
|
||||
}
|
||||
logger.LogDebug(
|
||||
"Added search query {Query} with key {Key} and order {Order}",
|
||||
query,
|
||||
key,
|
||||
playbackOrder);
|
||||
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
foreach (var enumerator in EnumeratorForContent(items, state, playbackOrder))
|
||||
{
|
||||
string historyKey = HistoryDetails.KeyForSchedulingContent(key, playbackOrder);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, playbackOrder);
|
||||
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Added search query {Query} with key {Key} and order {Order}",
|
||||
query,
|
||||
key,
|
||||
playbackOrder);
|
||||
|
||||
ApplyHistory(historyKey, items, enumerator, playbackOrder);
|
||||
}
|
||||
ApplyHistory(historyKey, items, enumerator, playbackOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -343,33 +401,36 @@ public class SchedulingEngine(
|
||||
Dictionary<string, string> guids,
|
||||
PlaybackOrder playbackOrder)
|
||||
{
|
||||
if (!_enumerators.ContainsKey(key))
|
||||
if (_enumerators.ContainsKey(key))
|
||||
{
|
||||
int index = _enumerators.Count;
|
||||
List<MediaItem> items =
|
||||
await mediaCollectionRepository.GetShowItemsByShowGuids(
|
||||
guids.Map(g => $"{g.Key}://{g.Value}").ToList());
|
||||
if (items.Count == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
int index = _enumerators.Count;
|
||||
List<MediaItem> items =
|
||||
await mediaCollectionRepository.GetShowItemsByShowGuids(
|
||||
guids.Map(g => $"{g.Key}://{g.Value}").ToList());
|
||||
if (items.Count == 0)
|
||||
{
|
||||
logger.LogWarning("Skipping invalid or empty show with key {Key}", key);
|
||||
return;
|
||||
}
|
||||
|
||||
_enumeratorMediaItems[key] = items.ToImmutableList();
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
foreach (var enumerator in EnumeratorForContent(items, state, playbackOrder))
|
||||
{
|
||||
string historyKey = HistoryDetails.KeyForSchedulingContent(key, playbackOrder);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, playbackOrder);
|
||||
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
{
|
||||
logger.LogWarning("Skipping invalid or empty show with key {Key}", key);
|
||||
return;
|
||||
}
|
||||
logger.LogDebug(
|
||||
"Added show with key {Key} and order {Order}",
|
||||
key,
|
||||
playbackOrder);
|
||||
|
||||
var state = new CollectionEnumeratorState { Seed = _state.Seed + index, Index = 0 };
|
||||
foreach (var enumerator in EnumeratorForContent(items, state, playbackOrder))
|
||||
{
|
||||
string historyKey = HistoryDetails.KeyForSchedulingContent(key, playbackOrder);
|
||||
var details = new EnumeratorDetails(enumerator, historyKey, playbackOrder);
|
||||
|
||||
if (_enumerators.TryAdd(key, details))
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Added show with key {Key} and order {Order}",
|
||||
key,
|
||||
playbackOrder);
|
||||
|
||||
ApplyHistory(historyKey, items, enumerator, playbackOrder);
|
||||
}
|
||||
ApplyHistory(historyKey, items, enumerator, playbackOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -648,18 +709,15 @@ public class SchedulingEngine(
|
||||
TimeSpan remainingToFill = targetTime - _state.CurrentTime;
|
||||
while (!done && enumeratorDetails.Enumerator.Current.IsSome && remainingToFill > TimeSpan.Zero)
|
||||
{
|
||||
// foreach (string preRollSequence in context.GetPreRollSequence())
|
||||
// {
|
||||
// context.PushFillerKind(FillerKind.PreRoll);
|
||||
// await executeSequence(preRollSequence);
|
||||
// context.PopFillerKind();
|
||||
//
|
||||
// remainingToFill = targetTime - context.CurrentTime;
|
||||
// if (remainingToFill <= TimeSpan.Zero)
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
foreach (string preRollPlaylist in _state.GetPreRollPlaylist())
|
||||
{
|
||||
AddFillerPlaylist(preRollPlaylist, FillerKind.PreRoll);
|
||||
remainingToFill = targetTime - _state.CurrentTime;
|
||||
if (remainingToFill <= TimeSpan.Zero)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (MediaItem mediaItem in enumeratorDetails.Enumerator.Current)
|
||||
{
|
||||
@@ -801,23 +859,46 @@ public class SchedulingEngine(
|
||||
return offlineTail ? targetTime : _state.CurrentTime;
|
||||
}
|
||||
|
||||
private void AddFillerPlaylist(string playlist, FillerKind fillerKind)
|
||||
{
|
||||
if (!_enumerators.TryGetValue(playlist, out EnumeratorDetails enumeratorDetails))
|
||||
{
|
||||
logger.LogWarning("Skipping invalid filler playlist {Key}", playlist);
|
||||
return;
|
||||
}
|
||||
|
||||
if (enumeratorDetails.Enumerator is PlaylistEnumerator playlistEnumerator)
|
||||
{
|
||||
int count = playlistEnumerator.CountForFiller;
|
||||
AddCountInternal(
|
||||
enumeratorDetails,
|
||||
count,
|
||||
fillerKind,
|
||||
customTitle: null,
|
||||
disableWatermarks: true,
|
||||
disableFiller: true);
|
||||
}
|
||||
}
|
||||
|
||||
private bool AddCountInternal(
|
||||
EnumeratorDetails enumeratorDetails,
|
||||
int count,
|
||||
Option<FillerKind> fillerKind,
|
||||
string customTitle,
|
||||
bool disableWatermarks)
|
||||
bool disableWatermarks,
|
||||
bool disableFiller = false)
|
||||
{
|
||||
var result = false;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
// foreach (string preRollSequence in context.GetPreRollSequence())
|
||||
// {
|
||||
// context.PushFillerKind(FillerKind.PreRoll);
|
||||
// await executeSequence(preRollSequence);
|
||||
// context.PopFillerKind();
|
||||
// }
|
||||
if (!disableFiller)
|
||||
{
|
||||
foreach (string preRollPlaylist in _state.GetPreRollPlaylist())
|
||||
{
|
||||
AddFillerPlaylist(preRollPlaylist, FillerKind.PreRoll);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (MediaItem mediaItem in enumeratorDetails.Enumerator.Current)
|
||||
{
|
||||
@@ -978,6 +1059,10 @@ public class SchedulingEngine(
|
||||
}
|
||||
}
|
||||
|
||||
public void PreRollOn(string content) => _state.PreRollOn(content);
|
||||
|
||||
public void PreRollOff() => _state.PreRollOff();
|
||||
|
||||
public void SkipItems(string content, int count)
|
||||
{
|
||||
if (!_enumerators.TryGetValue(content, out EnumeratorDetails enumeratorDetails))
|
||||
@@ -1365,11 +1450,6 @@ public class SchedulingEngine(
|
||||
return fillerKind;
|
||||
}
|
||||
|
||||
// foreach (FillerKind fillerKind in _state.GetFillerKind())
|
||||
// {
|
||||
// return fillerKind;
|
||||
// }
|
||||
|
||||
return FillerKind.None;
|
||||
}
|
||||
|
||||
@@ -1422,7 +1502,8 @@ public class SchedulingEngine(
|
||||
|
||||
public record SerializedState(
|
||||
int? GuideGroup,
|
||||
bool? GuideGroupLocked);
|
||||
bool? GuideGroupLocked,
|
||||
string PreRollPlaylist);
|
||||
|
||||
private class SchedulingEngineState(int guideGroup) : ISchedulingEngineState
|
||||
{
|
||||
@@ -1430,6 +1511,8 @@ public class SchedulingEngine(
|
||||
private bool _guideGroupLocked;
|
||||
private readonly Dictionary<int, string> _graphicsElements = [];
|
||||
private readonly System.Collections.Generic.HashSet<int> _channelWatermarkIds = [];
|
||||
private readonly Stack<FillerKind> _fillerKind = new();
|
||||
private Option<string> _preRollPlaylist = Option<string>.None;
|
||||
|
||||
// track is_done calls when current_time has not advanced
|
||||
private DateTimeOffset _lastCheckedTime;
|
||||
@@ -1497,6 +1580,10 @@ public class SchedulingEngine(
|
||||
public void ClearChannelWatermarkIds() => _channelWatermarkIds.Clear();
|
||||
public List<int> GetChannelWatermarkIds() => _channelWatermarkIds.ToList();
|
||||
|
||||
public void PreRollOn(string playlist) => _preRollPlaylist = playlist;
|
||||
public void PreRollOff() => _preRollPlaylist = Option<string>.None;
|
||||
public Option<string> GetPreRollPlaylist() => _preRollPlaylist;
|
||||
|
||||
// result
|
||||
public Option<DateTimeOffset> RemoveBefore { get; set; }
|
||||
public bool ClearItems { get; set; }
|
||||
@@ -1529,15 +1616,16 @@ public class SchedulingEngine(
|
||||
|
||||
public string SerializeContext()
|
||||
{
|
||||
// string preRollSequence = null;
|
||||
// foreach (string sequence in _preRollSequence)
|
||||
// {
|
||||
// preRollSequence = sequence;
|
||||
// }
|
||||
string preRollPlaylist = null;
|
||||
foreach (string playlist in _preRollPlaylist)
|
||||
{
|
||||
preRollPlaylist = playlist;
|
||||
}
|
||||
|
||||
var state = new SerializedState(
|
||||
_guideGroup,
|
||||
_guideGroupLocked);
|
||||
_guideGroupLocked,
|
||||
preRollPlaylist);
|
||||
|
||||
return JsonConvert.SerializeObject(state, Formatting.None, JsonSettings);
|
||||
}
|
||||
@@ -1555,9 +1643,4 @@ public class SchedulingEngine(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record EnumeratorDetails(
|
||||
IMediaCollectionEnumerator Enumerator,
|
||||
string HistoryKey,
|
||||
PlaybackOrder PlaybackOrder);
|
||||
}
|
||||
|
||||
@@ -75,6 +75,17 @@ internal static class HistoryDetails
|
||||
return JsonConvert.SerializeObject(historyKey, Formatting.None, JsonSettings);
|
||||
}
|
||||
|
||||
public static string KeyForSchedulingPlaylistContent(string key)
|
||||
{
|
||||
var historyKey = new Dictionary<string, object>
|
||||
{
|
||||
{ "Key", key },
|
||||
{ "Order", nameof(PlaybackOrder.None) }
|
||||
};
|
||||
|
||||
return JsonConvert.SerializeObject(historyKey, Formatting.None, JsonSettings);
|
||||
}
|
||||
|
||||
public static string KeyForCollectionKey(CollectionKey collectionKey)
|
||||
{
|
||||
dynamic key = new
|
||||
|
||||
@@ -121,7 +121,7 @@ public class EnumeratorCache(IMediaCollectionRepository mediaCollectionRepositor
|
||||
itemPlaybackOrder = PlaybackOrder.Shuffle;
|
||||
}
|
||||
|
||||
Option<MarathonContentResult> maybeResult = await helper.GetEnumerator(
|
||||
Option<PlaylistContentResult> maybeResult = await helper.GetEnumerator(
|
||||
guids,
|
||||
marathon.Searches,
|
||||
marathon.GroupBy,
|
||||
@@ -131,7 +131,7 @@ public class EnumeratorCache(IMediaCollectionRepository mediaCollectionRepositor
|
||||
state,
|
||||
cancellationToken);
|
||||
|
||||
foreach (MarathonContentResult result in maybeResult)
|
||||
foreach (PlaylistContentResult result in maybeResult)
|
||||
{
|
||||
foreach ((CollectionKey collectionKey, List<MediaItem> mediaItems) in result.Content)
|
||||
{
|
||||
|
||||
@@ -123,6 +123,29 @@ public class ScriptedScheduleController(IScriptedPlayoutBuilderService scriptedP
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("create_playlist", Name="CreatePlaylist")]
|
||||
[Tags("Scripted Content")]
|
||||
[EndpointSummary("Create a playlist")]
|
||||
public async Task<IActionResult> CreatePlaylist(
|
||||
[FromRoute]
|
||||
Guid buildId,
|
||||
[FromBody]
|
||||
ContentCreatePlaylist request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ISchedulingEngine engine = scriptedPlayoutBuilderService.GetEngine(buildId);
|
||||
if (engine == null)
|
||||
{
|
||||
return NotFound($"Active build engine not found for build {buildId}.");
|
||||
}
|
||||
|
||||
await engine.CreatePlaylist(
|
||||
request.Key,
|
||||
request.Items.ToDictionary(i => i.Content, i => i.Count),
|
||||
cancellationToken);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("add_search", Name = "AddSearch")]
|
||||
[Tags("Scripted Content")]
|
||||
[EndpointSummary("Add a search query")]
|
||||
@@ -510,6 +533,36 @@ public class ScriptedScheduleController(IScriptedPlayoutBuilderService scriptedP
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("pre_roll_on", Name = "PreRollOn")]
|
||||
[Tags("Scripted Control")]
|
||||
[EndpointSummary("Turn on pre-roll playlist")]
|
||||
public IActionResult PreRollOn([FromRoute] Guid buildId, [FromBody] ControlPreRollOn request)
|
||||
{
|
||||
ISchedulingEngine engine = scriptedPlayoutBuilderService.GetEngine(buildId);
|
||||
if (engine == null)
|
||||
{
|
||||
return NotFound($"Active build engine not found for build {buildId}.");
|
||||
}
|
||||
|
||||
engine.PreRollOn(request.Playlist);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("pre_roll_off", Name = "PreRollOff")]
|
||||
[Tags("Scripted Control")]
|
||||
[EndpointSummary("Turn off pre-roll playlist")]
|
||||
public IActionResult PreRollOff([FromRoute] Guid buildId)
|
||||
{
|
||||
ISchedulingEngine engine = scriptedPlayoutBuilderService.GetEngine(buildId);
|
||||
if (engine == null)
|
||||
{
|
||||
return NotFound($"Active build engine not found for build {buildId}.");
|
||||
}
|
||||
|
||||
engine.PreRollOff();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("skip_items", Name = "SkipItems")]
|
||||
[Tags("Scripted Control")]
|
||||
[EndpointSummary("Skip a specific number of items")]
|
||||
|
||||
@@ -247,6 +247,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/scripted/playout/build/{buildId}/create_playlist": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Scripted Content"
|
||||
],
|
||||
"summary": "Create a playlist",
|
||||
"operationId": "CreatePlaylist",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "buildId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json-patch+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ContentCreatePlaylist"
|
||||
}
|
||||
},
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ContentCreatePlaylist"
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ContentCreatePlaylist"
|
||||
}
|
||||
},
|
||||
"application/*+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ContentCreatePlaylist"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/scripted/playout/build/{buildId}/add_search": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -1124,6 +1174,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/scripted/playout/build/{buildId}/pre_roll_on": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Scripted Control"
|
||||
],
|
||||
"summary": "Turn on pre-roll playlist",
|
||||
"operationId": "PreRollOn",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "buildId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json-patch+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ControlPreRollOn"
|
||||
}
|
||||
},
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ControlPreRollOn"
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ControlPreRollOn"
|
||||
}
|
||||
},
|
||||
"application/*+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ControlPreRollOn"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/scripted/playout/build/{buildId}/pre_roll_off": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Scripted Control"
|
||||
],
|
||||
"summary": "Turn off pre-roll playlist",
|
||||
"operationId": "PreRollOff",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "buildId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/scripted/playout/build/{buildId}/skip_items": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -1404,6 +1529,24 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ContentCreatePlaylist": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "Unique name used to reference this content throughout the scripted schedule",
|
||||
"nullable": true
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PlaylistItem"
|
||||
},
|
||||
"description": "List of playlist items",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ContentMarathon": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1584,6 +1727,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ControlPreRollOn": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"playlist": {
|
||||
"type": "string",
|
||||
"description": "The 'key' for the scripted playlist",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ControlSkipItems": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1700,6 +1853,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"PlaylistItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "The 'key' for the content",
|
||||
"nullable": true
|
||||
},
|
||||
"count": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PlayoutContext": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -247,6 +247,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/scripted/playout/build/{buildId}/create_playlist": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"ScriptedSchedule"
|
||||
],
|
||||
"summary": "Create a playlist",
|
||||
"operationId": "CreatePlaylist",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "buildId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json-patch+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ContentCreatePlaylist"
|
||||
}
|
||||
},
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ContentCreatePlaylist"
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ContentCreatePlaylist"
|
||||
}
|
||||
},
|
||||
"application/*+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ContentCreatePlaylist"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/scripted/playout/build/{buildId}/add_search": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -1124,6 +1174,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/scripted/playout/build/{buildId}/pre_roll_on": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"ScriptedSchedule"
|
||||
],
|
||||
"summary": "Turn on pre-roll playlist",
|
||||
"operationId": "PreRollOn",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "buildId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json-patch+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ControlPreRollOn"
|
||||
}
|
||||
},
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ControlPreRollOn"
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ControlPreRollOn"
|
||||
}
|
||||
},
|
||||
"application/*+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ControlPreRollOn"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/scripted/playout/build/{buildId}/pre_roll_off": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"ScriptedSchedule"
|
||||
],
|
||||
"summary": "Turn off pre-roll playlist",
|
||||
"operationId": "PreRollOff",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "buildId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/scripted/playout/build/{buildId}/skip_items": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -1404,6 +1529,24 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ContentCreatePlaylist": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "Unique name used to reference this content throughout the scripted schedule",
|
||||
"nullable": true
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PlaylistItem"
|
||||
},
|
||||
"description": "List of playlist items",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ContentMarathon": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1584,6 +1727,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ControlPreRollOn": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"playlist": {
|
||||
"type": "string",
|
||||
"description": "The 'key' for the scripted playlist",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ControlSkipItems": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1700,6 +1853,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"PlaylistItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "The 'key' for the content",
|
||||
"nullable": true
|
||||
},
|
||||
"count": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PlayoutContext": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
Reference in New Issue
Block a user