@ -27,6 +27,7 @@ public class SchedulingEngine(
@@ -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(
@@ -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 )
{
logger . LogWarning ( "Skipping invalid or empty collection {Name}" , collectionName ) ;
return ;
}
return ;
}
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 ) ;
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 ;
}
if ( _ enumerators . TryAdd ( key , details ) )
{
logger . LogDebug (
"Added collection {Name} with key {Key} and order {Order}" ,
collectionName ,
key ,
playbackOrder ) ;
_ 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 ) ;
ApplyHistory ( historyKey , items , enumerator , 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 ) ;
}
}
}
@ -152,11 +156,16 @@ public class SchedulingEngine(
@@ -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 < Playlist ContentResult> maybeResult = await helper . GetEnumerator (
guids ,
searches ,
groupBy ,
@ -166,7 +175,7 @@ public class SchedulingEngine(
@@ -166,7 +175,7 @@ public class SchedulingEngine(
state ,
CancellationToken . None ) ;
foreach ( Marathon ContentResult result in maybeResult )
foreach ( Playlist ContentResult result in maybeResult )
{
foreach ( PlaylistEnumerator enumerator in Optional ( result . PlaylistEnumerator ) )
{
@ -183,8 +192,6 @@ public class SchedulingEngine(
@@ -183,8 +192,6 @@ public class SchedulingEngine(
}
}
}
await Task . Delay ( 1 0 ) ;
}
public async Task AddMultiCollection (
@ -193,33 +200,36 @@ public class SchedulingEngine(
@@ -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 )
{
logger . LogWarning ( "Skipping invalid or empty multi collection {Name}" , multiCollectionName ) ;
return ;
}
return ;
}
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 ) ;
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 ;
}
if ( _ enumerators . TryAdd ( key , details ) )
{
logger . LogDebug (
"Added multi collection {Name} with key {Key} and order {Order}" ,
multiCollectionName ,
key ,
playbackOrder ) ;
_ 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 ) ;
ApplyHistory ( historyKey , items , enumerator , 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 ) ;
}
}
}
@ -230,36 +240,78 @@ public class SchedulingEngine(
@@ -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 ;
}
int index = _ enumerators . Count ;
Dictionary < PlaylistItem , List < MediaItem > > itemMap =
await mediaCollectionRepository . GetPlaylistItemMap ( playlistGroup , playlist , cancellationToken ) ;
var state = new CollectionEnumeratorState { Seed = _ state . Seed + index , Index = 0 } ;
var state = new CollectionEnumeratorState { Seed = _ state . Seed + index , Index = 0 } ;
var enumerator = await PlaylistEnumerator . Create (
mediaCollectionRepository ,
itemMap ,
state ,
false ,
CancellationToken . None ) ;
var enumerator = await PlaylistEnumerator . Create (
mediaCollectionRepository ,
itemMap ,
state ,
false ,
CancellationToken . None ) ;
string historyKey = HistoryDetails . KeyForSchedulingContent ( key , PlaybackOrder . None ) ;
var details = new EnumeratorDetails ( enumerator , historyKey , PlaybackOrder . None ) ;
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 ) ;
if ( _ enumerators . TryAdd ( key , details ) )
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 ) ;
ApplyPlaylistHistory (
historyKey ,
itemMap . ToImmutableDictionary ( m = > CollectionKey . ForPlaylistItem ( m . Key ) , m = > m . Value ) ,
enumerator ) ;
string historyKey = HistoryDetails . KeyForSchedulingPlaylistContent ( key ) ;
var details = new EnumeratorDetails ( enumerator , historyKey , PlaybackOrder . None ) ;
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(
@@ -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 )
{
logger . LogWarning ( "Skipping invalid or empty smart collection {Name}" , smartCollectionName ) ;
return ;
}
return ;
}
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 ) ;
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 ;
}
if ( _ enumerators . TryAdd ( key , details ) )
{
logger . LogDebug (
"Added smart collection {Name} with key {Key} and order {Order}" ,
smartCollectionName ,
key ,
playbackOrder ) ;
_ 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 ) ;
ApplyHistory ( historyKey , items , enumerator , 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 ) ;
}
}
}
@ -307,33 +362,36 @@ public class SchedulingEngine(
@@ -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 )
{
logger . LogWarning ( "Skipping invalid or empty search query {Query}" , query ) ;
return ;
}
return ;
}
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 ) ;
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 ;
}
if ( _ enumerators . TryAdd ( key , details ) )
{
logger . LogDebug (
"Added search query {Query} with key {Key} and order {Order}" ,
query ,
key ,
playbackOrder ) ;
_ 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 ) ;
ApplyHistory ( historyKey , items , enumerator , 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 ) ;
}
}
}
@ -343,33 +401,36 @@ public class SchedulingEngine(
@@ -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 )
{
logger . LogWarning ( "Skipping invalid or empty show with key {Key}" , key ) ;
return ;
}
return ;
}
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 ) ;
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 ;
}
if ( _ enumerators . TryAdd ( key , details ) )
{
logger . LogDebug (
"Added show with key {Key} and order {Order}" ,
key ,
playbackOrder ) ;
_ 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 ) ;
ApplyHistory ( historyKey , items , enumerator , playbackOrder ) ;
}
if ( _ enumerators . TryAdd ( key , details ) )
{
logger . LogDebug (
"Added show with key {Key} and order {Order}" ,
key ,
playbackOrder ) ;
ApplyHistory ( historyKey , items , enumerator , playbackOrder ) ;
}
}
}
@ -648,18 +709,15 @@ public class SchedulingEngine(
@@ -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(
@@ -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(
@@ -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(
@@ -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(
@@ -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(
@@ -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 > _f illerKind = new ( ) ;
private Option < string > _ preRollPlaylist = Option < string > . None ;
// track is_done calls when current_time has not advanced
private DateTimeOffset _l astCheckedTime ;
@ -1497,6 +1580,10 @@ public class SchedulingEngine(
@@ -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(
@@ -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(
@@ -1555,9 +1643,4 @@ public class SchedulingEngine(
}
}
}
private record EnumeratorDetails (
IMediaCollectionEnumerator Enumerator ,
string HistoryKey ,
PlaybackOrder PlaybackOrder ) ;
}