private static Validation <BaseError, ProgramSchedule> PlaybackOrdersMustBeValid( ReplaceProgramScheduleItems request, ProgramSchedule programSchedule) { var keyOrders = new Dictionary <CollectionKey, System.Collections.Generic.HashSet <PlaybackOrder> >(); foreach (ReplaceProgramScheduleItem item in request.Items) { var key = new CollectionKey( item.CollectionType, item.CollectionId, item.MediaItemId, item.MultiCollectionId, item.SmartCollectionId); if (keyOrders.TryGetValue(key, out System.Collections.Generic.HashSet <PlaybackOrder> playbackOrders)) { playbackOrders.Add(item.PlaybackOrder); keyOrders[key] = playbackOrders; } else { keyOrders.Add(key, new System.Collections.Generic.HashSet <PlaybackOrder> { item.PlaybackOrder }); } } return(Optional(keyOrders.Values.Count(set => set.Count != 1)) .Filter(count => count == 0) .Map(_ => programSchedule) .ToValidation <BaseError>("A collection must not use multiple playback orders")); }
public async Task <Option <string> > GetNameFromKey(CollectionKey emptyCollection) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); return(emptyCollection.CollectionType switch { ProgramScheduleItemCollectionType.Artist => await dbContext.Artists.Include(a => a.ArtistMetadata) .SelectOneAsync(a => a.Id, a => a.Id == emptyCollection.MediaItemId.Value) .MapT(a => a.ArtistMetadata.Head().Title), ProgramScheduleItemCollectionType.Collection => await dbContext.Collections .SelectOneAsync(c => c.Id, c => c.Id == emptyCollection.CollectionId.Value) .MapT(c => c.Name), ProgramScheduleItemCollectionType.MultiCollection => await dbContext.MultiCollections .SelectOneAsync(c => c.Id, c => c.Id == emptyCollection.MultiCollectionId.Value) .MapT(c => c.Name), ProgramScheduleItemCollectionType.SmartCollection => await dbContext.SmartCollections .SelectOneAsync(c => c.Id, c => c.Id == emptyCollection.SmartCollectionId.Value) .MapT(c => c.Name), ProgramScheduleItemCollectionType.TelevisionSeason => await dbContext.Seasons .Include(s => s.SeasonMetadata) .Include(s => s.Show) .ThenInclude(s => s.ShowMetadata) .SelectOneAsync(a => a.Id, a => a.Id == emptyCollection.MediaItemId.Value) .MapT(s => $"{s.Show.ShowMetadata.Head().Title} Season {s.SeasonNumber}"), ProgramScheduleItemCollectionType.TelevisionShow => await dbContext.Shows.Include(s => s.ShowMetadata) .SelectOneAsync(a => a.Id, a => a.Id == emptyCollection.MediaItemId.Value) .MapT(s => s.ShowMetadata.Head().Title), _ => None });
/// <summary> /// The inverse of {@link #registerLoadingCollectionXRef}. Here, we are done /// processing the said collection entry, so we remove it from the /// load context. /// </summary> /// <param name="key">The key of the collection we are done processing. </param> /// <remarks> /// The idea here is that other loading collections can now reference said /// collection directly from the {@link PersistenceContext} because it /// has completed its load cycle. /// Implementation note: package protected, as this is meant solely for use /// by {@link CollectionLoadContext} to be able to locate collections /// being loaded by other {@link CollectionLoadContext}s/{@link ResultSet}s. /// </remarks> internal void UnregisterLoadingCollectionXRef(CollectionKey key) { if (!HasRegisteredLoadingCollectionEntries) { return; } xrefLoadingCollectionEntries.Remove(key); }
/// <summary> /// Register a loading collection xref. /// </summary> /// <param name="entryKey">The xref collection key </param> /// <param name="entry">The corresponding loading collection entry </param> /// <remarks> /// This xref map is used because sometimes a collection is in process of /// being loaded from one result set, but needs to be accessed from the /// context of another "nested" result set processing. /// Implementation note: package protected, as this is meant solely for use /// by {@link CollectionLoadContext} to be able to locate collections /// being loaded by other {@link CollectionLoadContext}s/{@link ResultSet}s. /// </remarks> internal void RegisterLoadingCollectionXRef(CollectionKey entryKey, LoadingCollectionEntry entry) { if (xrefLoadingCollectionEntries == null) { xrefLoadingCollectionEntries = new Dictionary <CollectionKey, LoadingCollectionEntry>(); } xrefLoadingCollectionEntries[entryKey] = entry; }
public static async Task <List <MediaItem> > Collect( IMediaCollectionRepository mediaCollectionRepository, ITelevisionRepository televisionRepository, IArtistRepository artistRepository, CollectionKey collectionKey) { switch (collectionKey.CollectionType) { case ProgramScheduleItemCollectionType.Collection: List <MediaItem> collectionItems = await mediaCollectionRepository.GetItems(collectionKey.CollectionId ?? 0); return(collectionItems); case ProgramScheduleItemCollectionType.TelevisionShow: List <Episode> showItems = await televisionRepository.GetShowItems(collectionKey.MediaItemId ?? 0); return(showItems.Cast <MediaItem>().ToList()); case ProgramScheduleItemCollectionType.TelevisionSeason: List <Episode> seasonItems = await televisionRepository.GetSeasonItems(collectionKey.MediaItemId ?? 0); return(seasonItems.Cast <MediaItem>().ToList()); case ProgramScheduleItemCollectionType.Artist: List <MusicVideo> artistItems = await artistRepository.GetArtistItems(collectionKey.MediaItemId ?? 0); return(artistItems.Cast <MediaItem>().ToList()); case ProgramScheduleItemCollectionType.MultiCollection: List <MediaItem> multiCollectionItems = await mediaCollectionRepository.GetMultiCollectionItems( collectionKey.MultiCollectionId ?? 0); return(multiCollectionItems); case ProgramScheduleItemCollectionType.SmartCollection: List <MediaItem> smartCollectionItems = await mediaCollectionRepository.GetSmartCollectionItems( collectionKey.SmartCollectionId ?? 0); return(smartCollectionItems); default: return(new List <MediaItem>()); } }
�������� /// <summary> �������� /// 1. Recreate the collection key -> collection map �������� /// 2. rebuild the collection entries �������� /// 3. call Interceptor.postFlush() �������� /// </summary> ��������protected virtual void PostFlush(ISessionImplementor session) �������� { ������������if(log.IsDebugEnabled) ������������ { ����������������log.Debug("post flush"); ������������ } � ������������IPersistenceContext persistenceContext = session.PersistenceContext; ������������persistenceContext.CollectionsByKey.Clear(); ������������persistenceContext.BatchFetchQueue.ClearSubselects(); ������������ //the database has changed now, so the subselect results need to be invalidated � ������������ // NH Different implementation: In NET an iterator is immutable; ������������ // we need something to hold the persistent collection to remove, and it must be less intrusive as possible ������������IDictionary cEntries = persistenceContext.CollectionEntries; ������������List <IPersistentCollection> keysToRemove = new List <IPersistentCollection>(cEntries.Count); ������������foreach(DictionaryEntry me in cEntries) ������������ { ����������������CollectionEntry collectionEntry = (CollectionEntry)me.Value; ����������������IPersistentCollection persistentCollection = (IPersistentCollection)me.Key; ����������������collectionEntry.PostFlush(persistentCollection); ����������������if(collectionEntry.LoadedPersister == null) ���������������� { ��������������������keysToRemove.Add(persistentCollection); ���������������� } ����������������else ���������������� { ��������������������//otherwise recreate the mapping between the collection and its key ��������������������CollectionKey collectionKey = ������������������������new CollectionKey(collectionEntry.LoadedPersister, collectionEntry.LoadedKey, session.EntityMode); ��������������������persistenceContext.CollectionsByKey[collectionKey] = persistentCollection; ���������������� } ������������ } ������������foreach(IPersistentCollection key in keysToRemove) ������������ { ����������������persistenceContext.CollectionEntries.Remove(key); ������������ } ������������session.Interceptor.PostFlush((ICollection)persistenceContext.EntitiesByKey.Values); �������� }
public void Should_Not_Crash_Mid_Roll_One_Chapter() { Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromHours(1)); var scheduleItem = new ProgramScheduleItemOne { Id = 1, Index = 1, Collection = collectionOne, CollectionId = collectionOne.Id, StartTime = null, PlaybackOrder = PlaybackOrder.Chronological, TailFiller = null, FallbackFiller = null, MidRollFiller = new FillerPreset { FillerKind = FillerKind.MidRoll, FillerMode = FillerMode.Pad, PadToNearestMinute = 15 } }; var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator( new List <ProgramScheduleItem> { scheduleItem }, new CollectionEnumeratorState()); var enumerator = new ChronologicalMediaCollectionEnumerator( collectionOne.MediaItems, new CollectionEnumeratorState()); PlayoutBuilderState startState = StartState(scheduleItemsEnumerator); Dictionary <CollectionKey, IMediaCollectionEnumerator> enumerators = CollectionEnumerators( scheduleItem, enumerator); // too lazy to make another enumerator for the filler that we don't want enumerators.Add(CollectionKey.ForFillerPreset(scheduleItem.MidRollFiller), enumerator); List <PlayoutItem> playoutItems = Scheduler() .AddFiller( startState, enumerators, scheduleItem, new PlayoutItem(), new List <MediaChapter> { new() });
public void Should_Not_Touch_Enumerator() { var collection = new Collection { Id = 1, Name = "Filler Items", MediaItems = new List <MediaItem>() }; for (var i = 0; i < 5; i++) { collection.MediaItems.Add(TestMovie(i + 1, TimeSpan.FromHours(i + 1), new DateTime(2020, 2, i + 1))); } var fillerPreset = new FillerPreset { FillerKind = FillerKind.PreRoll, FillerMode = FillerMode.Count, Count = 3, Collection = collection, CollectionId = collection.Id }; var enumerator = new ChronologicalMediaCollectionEnumerator( collection.MediaItems, new CollectionEnumeratorState { Index = 0, Seed = 1 }); DateTimeOffset result = PlayoutModeSchedulerBase <ProgramScheduleItem> .CalculateEndTimeWithFiller( new Dictionary <CollectionKey, IMediaCollectionEnumerator> { { CollectionKey.ForFillerPreset(fillerPreset), enumerator } }, new ProgramScheduleItemOne { PreRollFiller = fillerPreset }, new DateTimeOffset(2020, 2, 1, 12, 0, 0, TimeSpan.FromHours(-5)), new TimeSpan(0, 12, 30), new List <MediaChapter>()); result.Should().Be(new DateTimeOffset(2020, 2, 1, 18, 12, 30, TimeSpan.FromHours(-5))); enumerator.State.Index.Should().Be(0); enumerator.State.Seed.Should().Be(1); }
/// <summary> /// Locate the LoadingCollectionEntry within *any* of the tracked /// <see cref="CollectionLoadContext"/>s. /// </summary> /// <param name="key">The collection key. </param> /// <returns> The located entry; or null. </returns> /// <remarks> /// Implementation note: package protected, as this is meant solely for use /// by <see cref="CollectionLoadContext"/> to be able to locate collections /// being loaded by other <see cref="CollectionLoadContext"/>s/ResultSets. /// </remarks> internal LoadingCollectionEntry LocateLoadingCollectionEntry(CollectionKey key) { if (xrefLoadingCollectionEntries == null) { return(null); } if (log.IsDebugEnabled()) { log.Debug("attempting to locate loading collection entry [{0}] in any result-set context", key); } LoadingCollectionEntry rtn; xrefLoadingCollectionEntries.TryGetValue(key, out rtn); if (log.IsDebugEnabled()) { log.Debug("collection [{0}] {1} in load context", key, (rtn == null ? "located" : "not located")); } return(rtn); }
public PersistentCollection GetLoadingCollection( ICollectionPersister persister, object id, object resultSetId ) { CollectionKey ckey = new CollectionKey( persister, id ); LoadingCollectionEntry lce = GetLoadingCollectionEntry( ckey ); if( lce == null ) { // look for existing collection PersistentCollection pc = GetCollection( ckey ); if( pc != null ) { CollectionEntry ce = GetCollectionEntry( pc ); if( ce.initialized ) { log.Debug( "collection already initialized: ignoring" ); return null; //ignore this row of results! Note the early exit } else { log.Debug( "uninitialized collection: initializing" ); } } else { object entity = GetCollectionOwner( id, persister ); if( entity != null && GetEntry( entity ).Status != Status.Loading ) { // important, to account for newly saved entities in query log.Debug( "owning entity already loaded: ignoring" ); return null; } else { //create one log.Debug( "new collection: instantiating" ); pc = persister.CollectionType.Instantiate( this, persister ); } } pc.BeforeInitialize( persister ); pc.BeginRead(); AddLoadingCollectionEntry( ckey, pc, id, resultSetId ); return pc; } else { if( lce.ResultSetId == resultSetId ) { log.Debug( "reading row" ); return lce.Collection; } else { //ignore this row, the collection is in process of being loaded somewhere further "up" the stack log.Debug( "collection is already being initialized: ignoring row" ); return null; } } }
public override Tuple <PlayoutBuilderState, List <PlayoutItem> > Schedule( PlayoutBuilderState playoutBuilderState, Dictionary <CollectionKey, IMediaCollectionEnumerator> collectionEnumerators, ProgramScheduleItemOne scheduleItem, ProgramScheduleItem nextScheduleItem, DateTimeOffset hardStop) { IMediaCollectionEnumerator contentEnumerator = collectionEnumerators[CollectionKey.ForScheduleItem(scheduleItem)]; foreach (MediaItem mediaItem in contentEnumerator.Current) { // find when we should start this item, based on the current time DateTimeOffset itemStartTime = GetStartTimeAfter( playoutBuilderState, scheduleItem); TimeSpan itemDuration = DurationForMediaItem(mediaItem); List <MediaChapter> itemChapters = ChaptersForMediaItem(mediaItem); var playoutItem = new PlayoutItem { MediaItemId = mediaItem.Id, Start = itemStartTime.UtcDateTime, Finish = itemStartTime.UtcDateTime + itemDuration, InPoint = TimeSpan.Zero, OutPoint = itemDuration, GuideGroup = playoutBuilderState.NextGuideGroup, FillerKind = scheduleItem.GuideMode == GuideMode.Filler ? FillerKind.Tail : FillerKind.None, WatermarkId = scheduleItem.WatermarkId, PreferredAudioLanguageCode = scheduleItem.PreferredAudioLanguageCode, PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode, SubtitleMode = scheduleItem.SubtitleMode }; DateTimeOffset itemEndTimeWithFiller = CalculateEndTimeWithFiller( collectionEnumerators, scheduleItem, itemStartTime, itemDuration, itemChapters); List <PlayoutItem> playoutItems = AddFiller( playoutBuilderState, collectionEnumerators, scheduleItem, playoutItem, itemChapters); PlayoutBuilderState nextState = playoutBuilderState with { CurrentTime = itemEndTimeWithFiller }; nextState.ScheduleItemsEnumerator.MoveNext(); contentEnumerator.MoveNext(); // LogScheduledItem(scheduleItem, mediaItem, itemStartTime); // only play one item from collection, so always advance to the next item // _logger.LogDebug( // "Advancing to next schedule item after playout mode {PlayoutMode}", // "One"); DateTimeOffset nextItemStart = GetStartTimeAfter(nextState, nextScheduleItem); if (scheduleItem.TailFiller != null) { (nextState, playoutItems) = AddTailFiller( nextState, collectionEnumerators, scheduleItem, playoutItems, nextItemStart); } if (scheduleItem.FallbackFiller != null) { (nextState, playoutItems) = AddFallbackFiller( nextState, collectionEnumerators, scheduleItem, playoutItems, nextItemStart); } nextState = nextState with { NextGuideGroup = nextState.IncrementGuideGroup }; return(Tuple(nextState, playoutItems)); } return(Tuple(playoutBuilderState, new List <PlayoutItem>())); } }
private void AddCollection( PersistentCollection collection, CollectionEntry entry, object key ) { collectionEntries[ collection ] = entry; CollectionKey ck = new CollectionKey( entry.loadedPersister, key ); PersistentCollection old = ( PersistentCollection ) collectionsByKey[ ck ]; collectionsByKey[ ck ] = collection; if( old != null ) { if( old == collection ) { throw new AssertionFailure( "collection added twice" ); } // or should it actually throw an exception? old.UnsetSession( this ); collectionEntries.Remove( old ); // watch out for a case where old is still referenced // somewhere in the object graph! (which is a user error) } }
public void Should_Not_Have_Gap_With_Unused_Tail_And_Unused_Fallback() { Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromHours(1)); Collection collectionTwo = TwoItemCollection(3, 4, TimeSpan.FromMinutes(4)); Collection collectionThree = TwoItemCollection(5, 6, TimeSpan.FromMinutes(1)); var scheduleItem = new ProgramScheduleItemMultiple { Id = 1, Index = 1, Collection = collectionOne, CollectionId = collectionOne.Id, StartTime = null, PlaybackOrder = PlaybackOrder.Chronological, TailFiller = new FillerPreset { FillerKind = FillerKind.Tail, Collection = collectionTwo, CollectionId = collectionTwo.Id }, FallbackFiller = new FillerPreset { FillerKind = FillerKind.Fallback, Collection = collectionThree, CollectionId = collectionThree.Id }, Count = 3 }; var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator( new List <ProgramScheduleItem> { scheduleItem }, new CollectionEnumeratorState()); var enumerator1 = new ChronologicalMediaCollectionEnumerator( collectionOne.MediaItems, new CollectionEnumeratorState()); var enumerator2 = new ChronologicalMediaCollectionEnumerator( collectionTwo.MediaItems, new CollectionEnumeratorState()); var enumerator3 = new ChronologicalMediaCollectionEnumerator( collectionThree.MediaItems, new CollectionEnumeratorState()); var collectionMediaItems = new Dictionary <CollectionKey, List <MediaItem> > { { CollectionKey.ForScheduleItem(scheduleItem), collectionOne.MediaItems }, { CollectionKey.ForFillerPreset(scheduleItem.TailFiller), collectionTwo.MediaItems }, { CollectionKey.ForFillerPreset(scheduleItem.FallbackFiller), collectionThree.MediaItems } }.ToMap(); PlayoutBuilderState startState = StartState(scheduleItemsEnumerator); var scheduler = new PlayoutModeSchedulerMultiple(collectionMediaItems, new Mock <ILogger>().Object); (PlayoutBuilderState playoutBuilderState, List <PlayoutItem> playoutItems) = scheduler.Schedule( startState, CollectionEnumerators( scheduleItem, enumerator1, scheduleItem.TailFiller, enumerator2, scheduleItem.FallbackFiller, enumerator3), scheduleItem, NextScheduleItem, HardStop(scheduleItemsEnumerator)); playoutBuilderState.CurrentTime.Should().Be(startState.CurrentTime.AddHours(3)); playoutItems.Last().FinishOffset.Should().Be(playoutBuilderState.CurrentTime); playoutBuilderState.NextGuideGroup.Should().Be(4); playoutBuilderState.DurationFinish.IsNone.Should().BeTrue(); playoutBuilderState.InFlood.Should().BeFalse(); playoutBuilderState.MultipleRemaining.IsNone.Should().BeTrue(); playoutBuilderState.InDurationFiller.Should().BeFalse(); playoutBuilderState.ScheduleItemsEnumerator.State.Index.Should().Be(0); enumerator1.State.Index.Should().Be(1); enumerator2.State.Index.Should().Be(0); enumerator3.State.Index.Should().Be(0); playoutItems.Count.Should().Be(3); playoutItems[0].MediaItemId.Should().Be(1); playoutItems[0].StartOffset.Should().Be(startState.CurrentTime); playoutItems[0].GuideGroup.Should().Be(1); playoutItems[0].FillerKind.Should().Be(FillerKind.None); playoutItems[1].MediaItemId.Should().Be(2); playoutItems[1].StartOffset.Should().Be(startState.CurrentTime.AddHours(1)); playoutItems[1].GuideGroup.Should().Be(2); playoutItems[1].FillerKind.Should().Be(FillerKind.None); playoutItems[2].MediaItemId.Should().Be(1); playoutItems[2].StartOffset.Should().Be(startState.CurrentTime.AddHours(2)); playoutItems[2].GuideGroup.Should().Be(3); playoutItems[2].FillerKind.Should().Be(FillerKind.None); }
/// <summary> /// 1. Recreate the collection key -> collection map /// 2. rebuild the collection entries /// 3. call Interceptor.postFlush() /// </summary> protected virtual void PostFlush(ISessionImplementor session) { if (log.IsDebugEnabled) log.Debug("post flush"); IPersistenceContext persistenceContext = session.PersistenceContext; persistenceContext.CollectionsByKey.Clear(); persistenceContext.BatchFetchQueue.ClearSubselects(); //the database has changed now, so the subselect results need to be invalidated ISet keysToRemove = new HashedSet(); IDictionary cEntries = persistenceContext.CollectionEntries; foreach (DictionaryEntry me in cEntries) { CollectionEntry collectionEntry = (CollectionEntry) me.Value; IPersistentCollection persistentCollection = (IPersistentCollection) me.Key; collectionEntry.PostFlush(persistentCollection); if (collectionEntry.LoadedPersister == null) { keysToRemove.Add(persistentCollection); } else { //otherwise recreate the mapping between the collection and its key CollectionKey collectionKey = new CollectionKey(collectionEntry.LoadedPersister, collectionEntry.LoadedKey, session.EntityMode); persistenceContext.CollectionsByKey[collectionKey] = persistentCollection; } } foreach (object key in keysToRemove) { persistenceContext.CollectionEntries.Remove(key); } session.Interceptor.PostFlush((ICollection)persistenceContext.EntitiesByKey.Values); }
public override Tuple <PlayoutBuilderState, List <PlayoutItem> > Schedule( PlayoutBuilderState playoutBuilderState, Dictionary <CollectionKey, IMediaCollectionEnumerator> collectionEnumerators, ProgramScheduleItemFlood scheduleItem, ProgramScheduleItem nextScheduleItem, DateTimeOffset hardStop) { var playoutItems = new List <PlayoutItem>(); PlayoutBuilderState nextState = playoutBuilderState; var willFinishInTime = true; IMediaCollectionEnumerator contentEnumerator = collectionEnumerators[CollectionKey.ForScheduleItem(scheduleItem)]; ProgramScheduleItem peekScheduleItem = nextScheduleItem; while (contentEnumerator.Current.IsSome && nextState.CurrentTime < hardStop && willFinishInTime) { MediaItem mediaItem = contentEnumerator.Current.ValueUnsafe(); // find when we should start this item, based on the current time DateTimeOffset itemStartTime = GetStartTimeAfter(nextState, scheduleItem); TimeSpan itemDuration = DurationForMediaItem(mediaItem); List <MediaChapter> itemChapters = ChaptersForMediaItem(mediaItem); var playoutItem = new PlayoutItem { MediaItemId = mediaItem.Id, Start = itemStartTime.UtcDateTime, Finish = itemStartTime.UtcDateTime + itemDuration, InPoint = TimeSpan.Zero, OutPoint = itemDuration, GuideGroup = nextState.NextGuideGroup, FillerKind = scheduleItem.GuideMode == GuideMode.Filler ? FillerKind.Tail : FillerKind.None, WatermarkId = scheduleItem.WatermarkId, PreferredAudioLanguageCode = scheduleItem.PreferredAudioLanguageCode, PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode, SubtitleMode = scheduleItem.SubtitleMode }; DateTimeOffset peekScheduleItemStart = peekScheduleItem.StartType == StartType.Fixed ? GetStartTimeAfter(nextState with { InFlood = false }, peekScheduleItem) : DateTimeOffset.MaxValue; DateTimeOffset itemEndTimeWithFiller = CalculateEndTimeWithFiller( collectionEnumerators, scheduleItem, itemStartTime, itemDuration, itemChapters); // if the next schedule item is supposed to start during this item, // don't schedule this item and just move on willFinishInTime = peekScheduleItemStart < itemStartTime || peekScheduleItemStart >= itemEndTimeWithFiller; if (willFinishInTime) { playoutItems.AddRange( AddFiller(nextState, collectionEnumerators, scheduleItem, playoutItem, itemChapters)); // LogScheduledItem(scheduleItem, mediaItem, itemStartTime); DateTimeOffset actualEndTime = playoutItems.Max(p => p.FinishOffset); if (Math.Abs((itemEndTimeWithFiller - actualEndTime).TotalSeconds) > 1) { _logger.LogWarning( "Filler prediction failure: predicted {PredictedDuration} doesn't match actual {ActualDuration}", itemEndTimeWithFiller, actualEndTime); // _logger.LogWarning("Playout items: {@PlayoutItems}", playoutItems); } nextState = nextState with { CurrentTime = itemEndTimeWithFiller, InFlood = true, NextGuideGroup = nextState.IncrementGuideGroup }; contentEnumerator.MoveNext(); } } // _logger.LogDebug( // "Advancing to next schedule item after playout mode {PlayoutMode}", // "Flood"); nextState = nextState with { InFlood = nextState.CurrentTime >= hardStop, NextGuideGroup = nextState.DecrementGuideGroup }; nextState.ScheduleItemsEnumerator.MoveNext(); ProgramScheduleItem peekItem = nextScheduleItem; DateTimeOffset peekItemStart = GetStartTimeAfter(nextState, peekItem); if (scheduleItem.TailFiller != null) { (nextState, playoutItems) = AddTailFiller( nextState, collectionEnumerators, scheduleItem, playoutItems, peekItemStart); } if (scheduleItem.FallbackFiller != null) { (nextState, playoutItems) = AddFallbackFiller( nextState, collectionEnumerators, scheduleItem, playoutItems, peekItemStart); } nextState = nextState with { NextGuideGroup = nextState.IncrementGuideGroup }; return(Tuple(nextState, playoutItems)); } }
/// <summary> Add a collection which has no owner loaded</summary> public void AddUnownedCollection(CollectionKey key, IPersistentCollection collection) { if (unownedCollections == null) unownedCollections = new Dictionary<CollectionKey, IPersistentCollection>(8); unownedCollections[key] = collection; }
/// <summary> Add an collection to the cache, with a given collection entry. </summary> /// <param name="coll">The collection for which we are adding an entry.</param> /// <param name="entry">The entry representing the collection. </param> /// <param name="key">The key of the collection's entry. </param> private void AddCollection(IPersistentCollection coll, CollectionEntry entry, object key) { collectionEntries[coll] = entry; CollectionKey collectionKey = new CollectionKey(entry.LoadedPersister, key, session.EntityMode); IPersistentCollection tempObject; collectionsByKey.TryGetValue(collectionKey, out tempObject); collectionsByKey[collectionKey] = coll; IPersistentCollection old = tempObject; if (old != null) { if (old == coll) { throw new AssertionFailure("bug adding collection twice"); } // or should it actually throw an exception? old.UnsetSession(session); collectionEntries.Remove(old); // watch out for a case where old is still referenced // somewhere in the object graph! (which is a user error) } }
private LoadingCollectionEntry GetLoadingCollectionEntry( CollectionKey collectionKey ) { return ( LoadingCollectionEntry ) loadingCollections[ collectionKey ]; }
private async Task <Either <BaseError, PlayoutItemWithPath> > CheckForFallbackFiller( TvContext dbContext, Channel channel, DateTimeOffset now) { // check for channel fallback Option <FillerPreset> maybeFallback = await dbContext.FillerPresets .SelectOneAsync(w => w.Id, w => w.Id == channel.FallbackFillerId); // then check for global fallback if (maybeFallback.IsNone) { maybeFallback = await dbContext.ConfigElements .GetValue <int>(ConfigElementKey.FFmpegGlobalFallbackFillerId) .BindT(fillerId => dbContext.FillerPresets.SelectOneAsync(w => w.Id, w => w.Id == fillerId)); } foreach (FillerPreset fallbackPreset in maybeFallback) { // turn this into a playout item var collectionKey = CollectionKey.ForFillerPreset(fallbackPreset); List <MediaItem> items = await MediaItemsForCollection.Collect( _mediaCollectionRepository, _televisionRepository, _artistRepository, collectionKey); // TODO: shuffle? does it really matter since we loop anyway MediaItem item = items[new Random().Next(items.Count)]; Option <TimeSpan> maybeDuration = await dbContext.PlayoutItems .Filter(pi => pi.Playout.ChannelId == channel.Id) .Filter(pi => pi.Start > now.UtcDateTime) .OrderBy(pi => pi.Start) .FirstOrDefaultAsync() .Map(Optional) .MapT(pi => pi.StartOffset - now); MediaVersion version = item.GetHeadVersion(); version.MediaFiles = await dbContext.MediaFiles .AsNoTracking() .Filter(mf => mf.MediaVersionId == version.Id) .ToListAsync(); version.Streams = await dbContext.MediaStreams .AsNoTracking() .Filter(ms => ms.MediaVersionId == version.Id) .ToListAsync(); DateTimeOffset finish = maybeDuration.Match( // next playout item exists // loop until it starts now.Add, // no next playout item exists // loop for 5 minutes if less than 30s, otherwise play full item () => version.Duration < TimeSpan.FromSeconds(30) ? now.AddMinutes(5) : now.Add(version.Duration)); var playoutItem = new PlayoutItem { MediaItem = item, MediaItemId = item.Id, Start = now.UtcDateTime, Finish = finish.UtcDateTime, FillerKind = FillerKind.Fallback, InPoint = TimeSpan.Zero, OutPoint = version.Duration }; return(await ValidatePlayoutItemPath(playoutItem)); } return(new UnableToLocatePlayoutItem()); }
public void Process(System.Runtime.Serialization.StreamingContext context) { if (string.IsNullOrEmpty(base.Html)) { string _tempFooterPadding = string.Empty; int LevelSize = 0; if (TotalLevels == 1) { LevelSize = CollectionKey.Split(":-:").Length; } else { LevelSize = TotalLevels; } for (int i = 0; i < LevelSize; i++) { _tempFooterPadding += "<td> </td>"; } if (IsMultiLevel) { _tempFooterPadding += "<td> </td>"; } _tempFooterPadding += "<td> </td>";//serial column if (ShowCheckbox) { _tempFooterPadding += "<td> </td>"; } string _tempFooterText = string.Empty; foreach (DVBaseColumn Column in TableColumns) { var ColumnCulture = Column.GetColumnCultureInfo(CultureDetails); if (Column.bVisible) { if ((Column is DVNumericColumn) && (Column as DVNumericColumn).Aggregate) { string _style = string.Empty; if ((Column as DVNumericColumn).Align == Align.Left) { _style = "text-align:left;"; } else if ((Column as DVNumericColumn).Align == Align.Right || (Column as DVNumericColumn).Align == Align.Auto) { _style = "text-align:right;"; } else { _style = "text-align:center;"; } _tempFooterText += "<td style=" + _style + "><b>" + (this.Aggregations[Column.Data].Sum).ToString("N", ColumnCulture.NumberFormat) + "</b></td>"; } else { _tempFooterText += "<td> </td>"; } } } //if (IsAutoGen) // _tempFooterPadding += "<td> </td>"; base.Html = string.Format("<tr class='group-sum' group='{0}'>{1}{2}</tr>", (this.IsMultiLevel) ? base.CurrentLevel.ToString() : 1.ToString(), _tempFooterPadding, _tempFooterText); } }
public override Tuple <PlayoutBuilderState, List <PlayoutItem> > Schedule( PlayoutBuilderState playoutBuilderState, Dictionary <CollectionKey, IMediaCollectionEnumerator> collectionEnumerators, ProgramScheduleItemDuration scheduleItem, ProgramScheduleItem nextScheduleItem, DateTimeOffset hardStop) { var playoutItems = new List <PlayoutItem>(); PlayoutBuilderState nextState = playoutBuilderState; var willFinishInTime = true; Option <DateTimeOffset> durationUntil = None; IMediaCollectionEnumerator contentEnumerator = collectionEnumerators[CollectionKey.ForScheduleItem(scheduleItem)]; while (contentEnumerator.Current.IsSome && nextState.CurrentTime < hardStop && willFinishInTime) { MediaItem mediaItem = contentEnumerator.Current.ValueUnsafe(); // find when we should start this item, based on the current time DateTimeOffset itemStartTime = GetStartTimeAfter(nextState, scheduleItem); // remember when we need to finish this duration item if (nextState.DurationFinish.IsNone) { nextState = nextState with { DurationFinish = itemStartTime + scheduleItem.PlayoutDuration }; durationUntil = nextState.DurationFinish; } TimeSpan itemDuration = DurationForMediaItem(mediaItem); List <MediaChapter> itemChapters = ChaptersForMediaItem(mediaItem); if (itemDuration > scheduleItem.PlayoutDuration) { _logger.LogWarning( "Skipping playout item {Title} with duration {Duration} that is longer than schedule item duration {PlayoutDuration}", PlayoutBuilder.DisplayTitle(mediaItem), itemDuration, scheduleItem.PlayoutDuration); contentEnumerator.MoveNext(); continue; } var playoutItem = new PlayoutItem { MediaItemId = mediaItem.Id, Start = itemStartTime.UtcDateTime, Finish = itemStartTime.UtcDateTime + itemDuration, InPoint = TimeSpan.Zero, OutPoint = itemDuration, GuideGroup = nextState.NextGuideGroup, FillerKind = scheduleItem.GuideMode == GuideMode.Filler ? FillerKind.Tail : FillerKind.None, CustomTitle = scheduleItem.CustomTitle, WatermarkId = scheduleItem.WatermarkId, PreferredAudioLanguageCode = scheduleItem.PreferredAudioLanguageCode, PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode, SubtitleMode = scheduleItem.SubtitleMode }; durationUntil.Do(du => playoutItem.GuideFinish = du.UtcDateTime); DateTimeOffset durationFinish = nextState.DurationFinish.IfNone(SystemTime.MaxValueUtc); DateTimeOffset itemEndTimeWithFiller = CalculateEndTimeWithFiller( collectionEnumerators, scheduleItem, itemStartTime, itemDuration, itemChapters); willFinishInTime = itemStartTime > durationFinish || itemEndTimeWithFiller <= durationFinish; if (willFinishInTime) { // LogScheduledItem(scheduleItem, mediaItem, itemStartTime); playoutItems.AddRange( AddFiller(nextState, collectionEnumerators, scheduleItem, playoutItem, itemChapters)); nextState = nextState with { CurrentTime = itemEndTimeWithFiller, // only bump guide group if we don't have a custom title NextGuideGroup = string.IsNullOrWhiteSpace(scheduleItem.CustomTitle) ? nextState.IncrementGuideGroup : nextState.NextGuideGroup }; contentEnumerator.MoveNext(); } else { TimeSpan durationBlock = itemEndTimeWithFiller - itemStartTime; if (itemEndTimeWithFiller - itemStartTime > scheduleItem.PlayoutDuration) { _logger.LogWarning( "Unable to schedule duration block of {DurationBlock} which is longer than the configured playout duration {PlayoutDuration}", durationBlock, scheduleItem.PlayoutDuration); } nextState = nextState with { DurationFinish = None }; nextState.ScheduleItemsEnumerator.MoveNext(); } } // this is needed when the duration finish exactly matches the hard stop if (nextState.DurationFinish.IsSome && nextState.CurrentTime == nextState.DurationFinish) { nextState = nextState with { DurationFinish = None }; nextState.ScheduleItemsEnumerator.MoveNext(); } if (playoutItems.Select(pi => pi.GuideGroup).Distinct().Count() != 1) { nextState = nextState with { NextGuideGroup = nextState.DecrementGuideGroup }; } foreach (DateTimeOffset nextItemStart in durationUntil) { switch (scheduleItem.TailMode) { case TailMode.Filler: if (scheduleItem.TailFiller != null) { (nextState, playoutItems) = AddTailFiller( nextState, collectionEnumerators, scheduleItem, playoutItems, nextItemStart); } if (scheduleItem.FallbackFiller != null) { (nextState, playoutItems) = AddFallbackFiller( nextState, collectionEnumerators, scheduleItem, playoutItems, nextItemStart); } nextState = nextState with { CurrentTime = nextItemStart }; break; case TailMode.Offline: if (scheduleItem.FallbackFiller != null) { (nextState, playoutItems) = AddFallbackFiller( nextState, collectionEnumerators, scheduleItem, playoutItems, nextItemStart); } nextState = nextState with { CurrentTime = nextItemStart }; break; } } // clear guide finish on all but the last item var all = playoutItems.Filter(pi => pi.FillerKind == FillerKind.None).ToList(); PlayoutItem last = all.OrderBy(pi => pi.FinishOffset).LastOrDefault(); foreach (PlayoutItem item in all.Filter(pi => pi != last)) { item.GuideFinish = null; } nextState = nextState with { NextGuideGroup = nextState.IncrementGuideGroup }; return(Tuple(nextState, playoutItems)); } }
private void AddLoadingCollectionEntry( CollectionKey key, PersistentCollection collection, object id, object resultSetId ) { loadingCollections.Add( key, new LoadingCollectionEntry( key, collection, id, resultSetId ) ); }
/// <summary> /// 1. Recreate the collection key -> collection map /// 2. rebuild the collection entries /// 3. call Interceptor.postFlush() /// </summary> protected virtual void PostFlush(ISessionImplementor session) { if (log.IsDebugEnabled) { log.Debug("post flush"); } IPersistenceContext persistenceContext = session.PersistenceContext; persistenceContext.CollectionsByKey.Clear(); persistenceContext.BatchFetchQueue.ClearSubselects(); //the database has changed now, so the subselect results need to be invalidated // NH Different implementation: In NET an iterator is immutable; // we need something to hold the persistent collection to remove, and it must be less intrusive as possible IDictionary cEntries = persistenceContext.CollectionEntries; List<IPersistentCollection> keysToRemove = new List<IPersistentCollection>(cEntries.Count); foreach (DictionaryEntry me in cEntries) { CollectionEntry collectionEntry = (CollectionEntry) me.Value; IPersistentCollection persistentCollection = (IPersistentCollection) me.Key; collectionEntry.PostFlush(persistentCollection); if (collectionEntry.LoadedPersister == null) { keysToRemove.Add(persistentCollection); } else { //otherwise recreate the mapping between the collection and its key CollectionKey collectionKey = new CollectionKey(collectionEntry.LoadedPersister, collectionEntry.LoadedKey, session.EntityMode); persistenceContext.CollectionsByKey[collectionKey] = persistentCollection; } } foreach (IPersistentCollection key in keysToRemove) { persistenceContext.CollectionEntries.Remove(key); } session.Interceptor.PostFlush((ICollection) persistenceContext.EntitiesByKey.Values); }
/// <summary> /// Retrieve the collection that is being loaded as part of processing this result set. /// </summary> /// <param name="persister">The persister for the collection being requested. </param> /// <param name="key">The key of the collection being requested. </param> /// <returns> The loading collection (see discussion above). </returns> /// <remarks> /// Basically, there are two valid return values from this method:<ul> /// <li>an instance of {@link PersistentCollection} which indicates to /// continue loading the result set row data into that returned collection /// instance; this may be either an instance already associated and in the /// midst of being loaded, or a newly instantiated instance as a matching /// associated collection was not found.</li> /// <li><i>null</i> indicates to ignore the corresponding result set row /// data relating to the requested collection; this indicates that either /// the collection was found to already be associated with the persistence /// context in a fully loaded state, or it was found in a loading state /// associated with another result set processing context.</li> /// </ul> /// </remarks> public IPersistentCollection GetLoadingCollection(ICollectionPersister persister, object key) { CollectionKey collectionKey = new CollectionKey(persister, key); if (log.IsDebugEnabled()) { log.Debug("starting attempt to find loading collection [{0}]", MessageHelper.InfoString(persister.Role, key)); } LoadingCollectionEntry loadingCollectionEntry = loadContexts.LocateLoadingCollectionEntry(collectionKey); if (loadingCollectionEntry == null) { // look for existing collection as part of the persistence context IPersistentCollection collection = loadContexts.PersistenceContext.GetCollection(collectionKey); if (collection != null) { if (collection.WasInitialized) { log.Debug("collection already initialized; ignoring"); return(null); // ignore this row of results! Note the early exit } else { // initialize this collection log.Debug("collection not yet initialized; initializing"); } } else { object owner = loadContexts.PersistenceContext.GetCollectionOwner(key, persister); bool newlySavedEntity = owner != null && loadContexts.PersistenceContext.GetEntry(owner).Status != Status.Loading; if (newlySavedEntity) { // important, to account for newly saved entities in query // todo : some kind of check for new status... log.Debug("owning entity already loaded; ignoring"); return(null); } else { // create one if (log.IsDebugEnabled()) { // Do not log the resultSet as-is, it is an IEnumerable which may get enumerated by loggers. // (Serilog does that.) See #1667. log.Debug("instantiating new collection [key={0}, rs={1}]", key, resultSet.GetType()); } collection = persister.CollectionType.Instantiate(loadContexts.PersistenceContext.Session, persister, key); } } collection.BeforeInitialize(persister, -1); collection.BeginRead(); localLoadingCollectionKeys.Add(collectionKey); loadContexts.RegisterLoadingCollectionXRef(collectionKey, new LoadingCollectionEntry(resultSet, persister, key, collection)); return(collection); } else { if (loadingCollectionEntry.ResultSet == resultSet) { log.Debug("found loading collection bound to current result set processing; reading row"); return(loadingCollectionEntry.Collection); } else { // ignore this row, the collection is in process of // being loaded somewhere further "up" the stack log.Debug("collection is already being initialized; ignoring row"); return(null); } } }
private PersistentCollection GetCollection( CollectionKey key ) { return ( PersistentCollection ) collectionsByKey[ key ]; }
/// <summary> /// Get and remove a collection whose owner is not yet loaded, /// when its owner is being loaded /// </summary> public IPersistentCollection UseUnownedCollection(CollectionKey key) { if (unownedCollections == null) { return null; } else { IPersistentCollection tempObject; if (unownedCollections.TryGetValue(key, out tempObject)) unownedCollections.Remove(key); return tempObject; } }
public void Should_Have_Gap_With_No_Tail_No_Fallback() { Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromMinutes(55)); var scheduleItem = new ProgramScheduleItemMultiple { Id = 1, Index = 1, Collection = collectionOne, CollectionId = collectionOne.Id, StartTime = null, PlaybackOrder = PlaybackOrder.Chronological, TailFiller = null, FallbackFiller = null, Count = 3 }; var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator( new List <ProgramScheduleItem> { scheduleItem }, new CollectionEnumeratorState()); var enumerator = new ChronologicalMediaCollectionEnumerator( collectionOne.MediaItems, new CollectionEnumeratorState()); var collectionMediaItems = new Dictionary <CollectionKey, List <MediaItem> > { { CollectionKey.ForScheduleItem(scheduleItem), collectionOne.MediaItems } }.ToMap(); PlayoutBuilderState startState = StartState(scheduleItemsEnumerator); var scheduler = new PlayoutModeSchedulerMultiple(collectionMediaItems, new Mock <ILogger>().Object); (PlayoutBuilderState playoutBuilderState, List <PlayoutItem> playoutItems) = scheduler.Schedule( startState, CollectionEnumerators(scheduleItem, enumerator), scheduleItem, NextScheduleItem, HardStop(scheduleItemsEnumerator)); playoutBuilderState.CurrentTime.Should().Be(startState.CurrentTime.Add(new TimeSpan(2, 45, 0))); playoutItems.Last().FinishOffset.Should().Be(playoutBuilderState.CurrentTime); playoutBuilderState.NextGuideGroup.Should().Be(4); playoutBuilderState.DurationFinish.IsNone.Should().BeTrue(); playoutBuilderState.InFlood.Should().BeFalse(); playoutBuilderState.MultipleRemaining.IsNone.Should().BeTrue(); playoutBuilderState.InDurationFiller.Should().BeFalse(); playoutBuilderState.ScheduleItemsEnumerator.State.Index.Should().Be(0); enumerator.State.Index.Should().Be(1); playoutItems.Count.Should().Be(3); playoutItems[0].MediaItemId.Should().Be(1); playoutItems[0].StartOffset.Should().Be(startState.CurrentTime); playoutItems[0].GuideGroup.Should().Be(1); playoutItems[0].FillerKind.Should().Be(FillerKind.None); playoutItems[1].MediaItemId.Should().Be(2); playoutItems[1].StartOffset.Should().Be(startState.CurrentTime.AddMinutes(55)); playoutItems[1].GuideGroup.Should().Be(2); playoutItems[1].FillerKind.Should().Be(FillerKind.None); playoutItems[2].MediaItemId.Should().Be(1); playoutItems[2].StartOffset.Should().Be(startState.CurrentTime.Add(new TimeSpan(1, 50, 0))); playoutItems[2].GuideGroup.Should().Be(3); playoutItems[2].FillerKind.Should().Be(FillerKind.None); }
/// <summary> Get the collection instance associated with the <tt>CollectionKey</tt></summary> public IPersistentCollection GetCollection(CollectionKey collectionKey) { IPersistentCollection result; if (collectionsByKey.TryGetValue(collectionKey, out result)) return result; else return null; }
internal LoadingCollectionEntry( CollectionKey key, PersistentCollection collection, object id, object resultSetId ) { this.key = key; this.collection = collection; this.id = id; this.resultSetId = resultSetId; }