Browse Source

allow more collection types and playback orders in blocks (#1554)

pull/1555/head
Jason Dove 2 years ago committed by GitHub
parent
commit
907b8074f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 73
      ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs
  2. 104
      ErsatzTV.Core/Scheduling/BlockScheduling/HistoryDetails.cs
  3. 10
      ErsatzTV/Pages/BlockEditor.razor

73
ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs

@ -25,6 +25,13 @@ public class BlockPlayoutBuilder(
playout.Channel.Number, playout.Channel.Number,
playout.Channel.Name); playout.Channel.Name);
List<PlaybackOrder> allowedPlaybackOrders =
[
PlaybackOrder.Chronological,
PlaybackOrder.SeasonEpisode,
PlaybackOrder.Random
];
DateTimeOffset start = DateTimeOffset.Now; DateTimeOffset start = DateTimeOffset.Now;
int daysToBuild = await configElementRepository.GetValue<int>(ConfigElementKey.PlayoutDaysToBuild) int daysToBuild = await configElementRepository.GetValue<int>(ConfigElementKey.PlayoutDaysToBuild)
@ -58,10 +65,10 @@ public class BlockPlayoutBuilder(
DateTimeOffset currentTime = effectiveBlock.Start; DateTimeOffset currentTime = effectiveBlock.Start;
foreach (BlockItem blockItem in effectiveBlock.Block.Items) foreach (BlockItem blockItem in effectiveBlock.Block.Items.OrderBy(i => i.Index))
{ {
// TODO: support other playback orders // TODO: support other playback orders
if (blockItem.PlaybackOrder is not PlaybackOrder.SeasonEpisode and not PlaybackOrder.Chronological) if (!allowedPlaybackOrders.Contains(blockItem.PlaybackOrder))
{ {
continue; continue;
} }
@ -82,61 +89,27 @@ public class BlockPlayoutBuilder(
var collectionKey = CollectionKey.ForBlockItem(blockItem); var collectionKey = CollectionKey.ForBlockItem(blockItem);
List<MediaItem> collectionItems = collectionMediaItems[collectionKey]; List<MediaItem> collectionItems = collectionMediaItems[collectionKey];
// get enumerator // get enumerator
var enumerator = new SeasonEpisodeMediaCollectionEnumerator(collectionItems, state); IMediaCollectionEnumerator enumerator = blockItem.PlaybackOrder switch
{
PlaybackOrder.Chronological => new ChronologicalMediaCollectionEnumerator(collectionItems, state),
PlaybackOrder.SeasonEpisode => new SeasonEpisodeMediaCollectionEnumerator(collectionItems, state),
_ => new RandomizedMediaCollectionEnumerator(
collectionItems,
new CollectionEnumeratorState { Seed = new Random().Next(), Index = 0 })
};
// seek to the appropriate place in the collection enumerator // seek to the appropriate place in the collection enumerator
foreach (PlayoutHistory history in maybeHistory) foreach (PlayoutHistory history in maybeHistory)
{ {
logger.LogDebug("History is applicable: {When}: {History}", history.When, history.Details); logger.LogDebug("History is applicable: {When}: {History}", history.When, history.Details);
// find next media item HistoryDetails.MoveToNextItem(
HistoryDetails.Details details = collectionItems,
JsonConvert.DeserializeObject<HistoryDetails.Details>(history.Details); history.Details,
if (details.SeasonNumber.HasValue && details.EpisodeNumber.HasValue) enumerator,
{ blockItem.PlaybackOrder);
Option<MediaItem> maybeMatchedItem = Optional(
collectionItems.Find(
ci => ci is Episode e &&
e.EpisodeMetadata.Any(em => em.EpisodeNumber == details.EpisodeNumber.Value) &&
e.Season.SeasonNumber == details.SeasonNumber.Value));
var copy = collectionItems.ToList();
if (maybeMatchedItem.IsNone)
{
var fakeItem = new Episode
{
Season = new Season { SeasonNumber = details.SeasonNumber.Value },
EpisodeMetadata =
[
new EpisodeMetadata
{
EpisodeNumber = details.EpisodeNumber.Value,
ReleaseDate = details.ReleaseDate
}
]
};
copy.Add(fakeItem);
maybeMatchedItem = fakeItem;
}
foreach (MediaItem matchedItem in maybeMatchedItem)
{
IComparer<MediaItem> comparer = blockItem.PlaybackOrder switch
{
PlaybackOrder.Chronological => new ChronologicalMediaComparer(),
_ => new SeasonEpisodeMediaComparer()
};
copy.Sort(comparer);
state.Index = copy.IndexOf(matchedItem);
enumerator.ResetState(state);
enumerator.MoveNext();
}
}
} }
foreach (MediaItem mediaItem in enumerator.Current) foreach (MediaItem mediaItem in enumerator.Current)

104
ErsatzTV.Core/Scheduling/BlockScheduling/HistoryDetails.cs

@ -1,5 +1,6 @@
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Scheduling; using ErsatzTV.Core.Domain.Scheduling;
using ErsatzTV.Core.Interfaces.Scheduling;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace ErsatzTV.Core.Scheduling.BlockScheduling; namespace ErsatzTV.Core.Scheduling.BlockScheduling;
@ -32,12 +33,104 @@ internal static class HistoryDetails
Details details = mediaItem switch Details details = mediaItem switch
{ {
Episode e => ForEpisode(e), Episode e => ForEpisode(e),
Movie m => ForMovie(m),
_ => new Details(mediaItem.Id, null, null, null) _ => new Details(mediaItem.Id, null, null, null)
}; };
return JsonConvert.SerializeObject(details, Formatting.None, JsonSettings); return JsonConvert.SerializeObject(details, Formatting.None, JsonSettings);
} }
public static void MoveToNextItem(
List<MediaItem> collectionItems,
string detailsString,
IMediaCollectionEnumerator enumerator,
PlaybackOrder playbackOrder)
{
if (playbackOrder is PlaybackOrder.Random)
{
return;
}
Option<MediaItem> maybeMatchedItem = Option<MediaItem>.None;
var copy = collectionItems.ToList();
Details details = JsonConvert.DeserializeObject<Details>(detailsString);
if (details.SeasonNumber.HasValue && details.EpisodeNumber.HasValue)
{
int season = details.SeasonNumber.Value;
int episode = details.EpisodeNumber.Value;
maybeMatchedItem = Optional(collectionItems.Find(ci => MatchSeasonAndEpisode(ci, season, episode)));
if (maybeMatchedItem.IsNone)
{
var fakeItem = new Episode
{
Season = new Season { SeasonNumber = season },
EpisodeMetadata =
[
new EpisodeMetadata
{
EpisodeNumber = episode,
ReleaseDate = details.ReleaseDate
}
]
};
copy.Add(fakeItem);
maybeMatchedItem = fakeItem;
}
}
else if (playbackOrder is PlaybackOrder.Chronological && details.ReleaseDate.HasValue)
{
maybeMatchedItem = Optional(collectionItems.Find(ci => MatchReleaseDate(ci, details.ReleaseDate.Value)));
if (maybeMatchedItem.IsNone)
{
var fakeItem = new Movie { MovieMetadata = [new MovieMetadata { ReleaseDate = details.ReleaseDate }] };
copy.Add(fakeItem);
maybeMatchedItem = fakeItem;
}
}
foreach (MediaItem matchedItem in maybeMatchedItem)
{
IComparer<MediaItem> comparer = playbackOrder switch
{
PlaybackOrder.Chronological => new ChronologicalMediaComparer(),
_ => new SeasonEpisodeMediaComparer(),
};
copy.Sort(comparer);
var state = new CollectionEnumeratorState
{
Seed = enumerator.State.Seed,
Index = copy.IndexOf(matchedItem)
};
enumerator.ResetState(state);
enumerator.MoveNext();
}
}
private static bool MatchReleaseDate(MediaItem mediaItem, DateTime releaseDate) =>
mediaItem switch
{
Movie m => m.MovieMetadata.Any(mm => mm.ReleaseDate == releaseDate),
Episode e => e.EpisodeMetadata.Any(em => em.ReleaseDate == releaseDate),
//MusicVideo mv => mv.MusicVideoMetadata.Any(mvm => mvm.ReleaseDate == releaseDate),
OtherVideo ov => ov.OtherVideoMetadata.Any(ovm => ovm.ReleaseDate == releaseDate),
_ => false
};
private static bool MatchSeasonAndEpisode(MediaItem mediaItem, int seasonNumber, int episodeNumber) =>
mediaItem switch
{
Episode e => e.Season.SeasonNumber == seasonNumber &&
e.EpisodeMetadata.Any(em => em.EpisodeNumber == episodeNumber),
_ => false
};
private static Details ForEpisode(Episode e) private static Details ForEpisode(Episode e)
{ {
int? episodeNumber = null; int? episodeNumber = null;
@ -51,5 +144,16 @@ internal static class HistoryDetails
return new Details(e.Id, releaseDate, e.Season.SeasonNumber, episodeNumber); return new Details(e.Id, releaseDate, e.Season.SeasonNumber, episodeNumber);
} }
private static Details ForMovie(Movie m)
{
DateTime? releaseDate = null;
foreach (MovieMetadata movieMetadata in m.MovieMetadata.HeadOrNone())
{
releaseDate = movieMetadata.ReleaseDate;
}
return new Details(m.Id, releaseDate, null, null);
}
public record Details(int? MediaItemId, DateTime? ReleaseDate, int? SeasonNumber, int? EpisodeNumber); public record Details(int? MediaItemId, DateTime? ReleaseDate, int? SeasonNumber, int? EpisodeNumber);
} }

10
ErsatzTV/Pages/BlockEditor.razor

@ -89,12 +89,12 @@
<MudCard> <MudCard>
<MudCardContent> <MudCardContent>
<MudSelect Class="mt-3" Label="Collection Type" @bind-Value="_selectedItem.CollectionType" For="@(() => _selectedItem.CollectionType)"> <MudSelect Class="mt-3" Label="Collection Type" @bind-Value="_selectedItem.CollectionType" For="@(() => _selectedItem.CollectionType)">
@* <MudSelectItem Value="ProgramScheduleItemCollectionType.Collection">Collection</MudSelectItem> *@ <MudSelectItem Value="ProgramScheduleItemCollectionType.Collection">Collection</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.TelevisionShow">Television Show</MudSelectItem> <MudSelectItem Value="ProgramScheduleItemCollectionType.TelevisionShow">Television Show</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.TelevisionSeason">Television Season</MudSelectItem> <MudSelectItem Value="ProgramScheduleItemCollectionType.TelevisionSeason">Television Season</MudSelectItem>
@* <MudSelectItem Value="ProgramScheduleItemCollectionType.Artist">Artist</MudSelectItem> *@ @* <MudSelectItem Value="ProgramScheduleItemCollectionType.Artist">Artist</MudSelectItem> *@
@* <MudSelectItem Value="ProgramScheduleItemCollectionType.MultiCollection">Multi Collection</MudSelectItem> *@ @* <MudSelectItem Value="ProgramScheduleItemCollectionType.MultiCollection">Multi Collection</MudSelectItem> *@
@* <MudSelectItem Value="ProgramScheduleItemCollectionType.SmartCollection">Smart Collection</MudSelectItem> *@ <MudSelectItem Value="ProgramScheduleItemCollectionType.SmartCollection">Smart Collection</MudSelectItem>
</MudSelect> </MudSelect>
@if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Collection) @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Collection)
{ {
@ -180,14 +180,14 @@
case ProgramScheduleItemCollectionType.Collection: case ProgramScheduleItemCollectionType.Collection:
case ProgramScheduleItemCollectionType.SmartCollection: case ProgramScheduleItemCollectionType.SmartCollection:
<MudSelectItem Value="PlaybackOrder.Chronological">Chronological</MudSelectItem> <MudSelectItem Value="PlaybackOrder.Chronological">Chronological</MudSelectItem>
@* <MudSelectItem Value="PlaybackOrder.Random">Random</MudSelectItem> *@ <MudSelectItem Value="PlaybackOrder.Random">Random</MudSelectItem>
@* <MudSelectItem Value="PlaybackOrder.Shuffle">Shuffle</MudSelectItem> *@ @* <MudSelectItem Value="PlaybackOrder.Shuffle">Shuffle</MudSelectItem> *@
@* <MudSelectItem Value="PlaybackOrder.ShuffleInOrder">Shuffle In Order</MudSelectItem> *@ @* <MudSelectItem Value="PlaybackOrder.ShuffleInOrder">Shuffle In Order</MudSelectItem> *@
break; break;
case ProgramScheduleItemCollectionType.TelevisionShow: case ProgramScheduleItemCollectionType.TelevisionShow:
<MudSelectItem Value="PlaybackOrder.Chronological">Chronological</MudSelectItem> <MudSelectItem Value="PlaybackOrder.Chronological">Chronological</MudSelectItem>
<MudSelectItem Value="PlaybackOrder.SeasonEpisode">Season, Episode</MudSelectItem> <MudSelectItem Value="PlaybackOrder.SeasonEpisode">Season, Episode</MudSelectItem>
@* <MudSelectItem Value="PlaybackOrder.Random">Random</MudSelectItem> *@ <MudSelectItem Value="PlaybackOrder.Random">Random</MudSelectItem>
@* <MudSelectItem Value="PlaybackOrder.Shuffle">Shuffle</MudSelectItem> *@ @* <MudSelectItem Value="PlaybackOrder.Shuffle">Shuffle</MudSelectItem> *@
@* <MudSelectItem Value="PlaybackOrder.MultiEpisodeShuffle">Multi-Episode Shuffle</MudSelectItem> *@ @* <MudSelectItem Value="PlaybackOrder.MultiEpisodeShuffle">Multi-Episode Shuffle</MudSelectItem> *@
break; break;
@ -196,7 +196,7 @@
case ProgramScheduleItemCollectionType.FakeCollection: case ProgramScheduleItemCollectionType.FakeCollection:
default: default:
<MudSelectItem Value="PlaybackOrder.Chronological">Chronological</MudSelectItem> <MudSelectItem Value="PlaybackOrder.Chronological">Chronological</MudSelectItem>
@* <MudSelectItem Value="PlaybackOrder.Random">Random</MudSelectItem> *@ <MudSelectItem Value="PlaybackOrder.Random">Random</MudSelectItem>
@* <MudSelectItem Value="PlaybackOrder.Shuffle">Shuffle</MudSelectItem> *@ @* <MudSelectItem Value="PlaybackOrder.Shuffle">Shuffle</MudSelectItem> *@
break; break;
} }

Loading…
Cancel
Save