262 lines
9.5 KiB
C#
262 lines
9.5 KiB
C#
using System.Collections.Immutable;
|
|
using ErsatzTV.Core.Domain;
|
|
using ErsatzTV.Core.Extensions;
|
|
using ErsatzTV.Core.Interfaces.Repositories;
|
|
|
|
namespace ErsatzTV.Core.Scheduling.Engine;
|
|
|
|
public class MarathonHelper(IMediaCollectionRepository mediaCollectionRepository)
|
|
{
|
|
public async Task<Option<PlaylistEnumerator>> GetEnumerator(
|
|
List<MediaItem> mediaItems,
|
|
MarathonGroupBy marathonGroupBy,
|
|
bool marathonShuffleGroups,
|
|
bool marathonShuffleItems,
|
|
Option<int> marathonBatchSize,
|
|
CollectionEnumeratorState state,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
List<IGrouping<GroupKey, MediaItem>> groups = [];
|
|
|
|
PlaybackOrder itemPlaybackOrder;
|
|
|
|
// group by show
|
|
switch (marathonGroupBy)
|
|
{
|
|
case MarathonGroupBy.Show:
|
|
groups.AddRange(mediaItems.GroupBy(MediaItemKeyByShow));
|
|
itemPlaybackOrder = marathonShuffleItems ? PlaybackOrder.Shuffle : PlaybackOrder.SeasonEpisode;
|
|
break;
|
|
case MarathonGroupBy.Season:
|
|
groups.AddRange(mediaItems.GroupBy(MediaItemKeyBySeason));
|
|
itemPlaybackOrder = marathonShuffleItems ? PlaybackOrder.Shuffle : PlaybackOrder.SeasonEpisode;
|
|
break;
|
|
case MarathonGroupBy.Artist:
|
|
groups.AddRange(mediaItems.GroupBy(MediaItemKeyByArtist));
|
|
itemPlaybackOrder = marathonShuffleItems ? PlaybackOrder.Shuffle : PlaybackOrder.Chronological;
|
|
break;
|
|
case MarathonGroupBy.Album:
|
|
groups.AddRange(mediaItems.GroupBy(MediaItemKeyByAlbum));
|
|
itemPlaybackOrder = marathonShuffleItems ? PlaybackOrder.Shuffle : PlaybackOrder.Chronological;
|
|
break;
|
|
case MarathonGroupBy.Director:
|
|
groups.AddRange(mediaItems.GroupBy(MediaItemKeyByDirector));
|
|
itemPlaybackOrder = marathonShuffleItems ? PlaybackOrder.Shuffle : PlaybackOrder.Chronological;
|
|
break;
|
|
default:
|
|
return Option<PlaylistEnumerator>.None;
|
|
}
|
|
|
|
Dictionary<PlaylistItem, List<MediaItem>> itemMap = [];
|
|
|
|
for (var index = 0; index < groups.Count; index++)
|
|
{
|
|
IGrouping<GroupKey, MediaItem> group = groups[index];
|
|
PlaylistItem playlistItem = GroupToPlaylistItem(
|
|
index,
|
|
marathonBatchSize.IsNone,
|
|
itemPlaybackOrder,
|
|
group);
|
|
itemMap.Add(playlistItem, group.ToList());
|
|
}
|
|
|
|
return await PlaylistEnumerator.Create(
|
|
mediaCollectionRepository,
|
|
itemMap,
|
|
state,
|
|
marathonShuffleGroups,
|
|
marathonBatchSize,
|
|
cancellationToken);
|
|
}
|
|
|
|
public async Task<Option<PlaylistContentResult>> GetEnumerator(
|
|
Dictionary<string, List<string>> guids,
|
|
List<string> searches,
|
|
string groupBy,
|
|
bool shuffleGroups,
|
|
PlaybackOrder itemPlaybackOrder,
|
|
bool playAllItems,
|
|
CollectionEnumeratorState state,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var allMediaItems = new List<MediaItem>();
|
|
|
|
// grab items from each show guid
|
|
foreach (string showGuid in guids.SelectMany(g => g.Value.Select(v => $"{g.Key}://{v}")))
|
|
{
|
|
allMediaItems.AddRange(await mediaCollectionRepository.GetShowItemsByShowGuids([showGuid]));
|
|
}
|
|
|
|
// grab items from each search
|
|
foreach (string query in searches)
|
|
{
|
|
allMediaItems.AddRange(await mediaCollectionRepository.GetSmartCollectionItems(query, string.Empty, cancellationToken));
|
|
}
|
|
|
|
List<IGrouping<GroupKey, MediaItem>> groups = [];
|
|
|
|
// group by show
|
|
if (string.Equals(groupBy, "show", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
groups.AddRange(allMediaItems.GroupBy(MediaItemKeyByShow));
|
|
}
|
|
// group by season
|
|
else if (string.Equals(groupBy, "season", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
groups.AddRange(allMediaItems.GroupBy(MediaItemKeyBySeason));
|
|
}
|
|
// group by artist
|
|
else if (string.Equals(groupBy, "artist", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
groups.AddRange(allMediaItems.GroupBy(MediaItemKeyByArtist));
|
|
}
|
|
// group by album
|
|
else if (string.Equals(groupBy, "album", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
groups.AddRange(allMediaItems.GroupBy(MediaItemKeyByAlbum));
|
|
}
|
|
// group by (first) director
|
|
else if (string.Equals(groupBy, "director", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
groups.AddRange(allMediaItems.GroupBy(MediaItemKeyByDirector));
|
|
}
|
|
|
|
Dictionary<PlaylistItem, List<MediaItem>> itemMap = [];
|
|
|
|
for (var index = 0; index < groups.Count; index++)
|
|
{
|
|
IGrouping<GroupKey, MediaItem> group = groups[index];
|
|
PlaylistItem playlistItem = GroupToPlaylistItem(index, playAllItems, itemPlaybackOrder, group);
|
|
itemMap.Add(playlistItem, group.ToList());
|
|
}
|
|
|
|
PlaylistEnumerator enumerator = await PlaylistEnumerator.Create(
|
|
mediaCollectionRepository,
|
|
itemMap,
|
|
state,
|
|
shuffleGroups,
|
|
batchSize: Option<int>.None,
|
|
cancellationToken);
|
|
|
|
return new PlaylistContentResult(
|
|
enumerator,
|
|
itemMap.ToImmutableDictionary(x => CollectionKey.ForPlaylistItem(x.Key), x => x.Value));
|
|
}
|
|
|
|
private static GroupKey MediaItemKeyByShow(MediaItem mediaItem) =>
|
|
mediaItem switch
|
|
{
|
|
Episode e => new GroupKey(
|
|
CollectionType.TelevisionShow,
|
|
null,
|
|
null,
|
|
null,
|
|
e.Season?.ShowId ?? 0),
|
|
_ => new GroupKey(CollectionType.TelevisionShow, null, null, null, 0)
|
|
};
|
|
|
|
private static GroupKey MediaItemKeyBySeason(MediaItem mediaItem) =>
|
|
mediaItem switch
|
|
{
|
|
Episode e => new GroupKey(
|
|
CollectionType.TelevisionSeason,
|
|
null,
|
|
null,
|
|
null,
|
|
e.SeasonId),
|
|
_ => new GroupKey(CollectionType.TelevisionSeason, null, null, null, 0)
|
|
};
|
|
|
|
private static GroupKey MediaItemKeyByArtist(MediaItem mediaItem) =>
|
|
mediaItem switch
|
|
{
|
|
MusicVideo mv => new GroupKey(
|
|
CollectionType.Artist,
|
|
null,
|
|
null,
|
|
null,
|
|
mv.ArtistId),
|
|
_ => new GroupKey(CollectionType.Artist, null, null, null, 0)
|
|
};
|
|
|
|
private static GroupKey MediaItemKeyByAlbum(MediaItem mediaItem) =>
|
|
mediaItem switch
|
|
{
|
|
Song s => new GroupKey(
|
|
CollectionType.Collection,
|
|
s.SongMetadata.HeadOrNone().Map(sm => sm.Album.GetStableHashCode()).IfNone(0),
|
|
null,
|
|
null,
|
|
null),
|
|
MusicVideo mv => new GroupKey(
|
|
CollectionType.Collection,
|
|
mv.MusicVideoMetadata.HeadOrNone()
|
|
.Map(mvm => $"{mv.ArtistId}-${mvm.Album}".GetStableHashCode()).IfNone(0),
|
|
null,
|
|
null,
|
|
null),
|
|
_ => new GroupKey(CollectionType.Collection, 0, null, null, null)
|
|
};
|
|
|
|
private static GroupKey MediaItemKeyByDirector(MediaItem mediaItem) =>
|
|
mediaItem switch
|
|
{
|
|
Movie m => new GroupKey(
|
|
CollectionType.Collection,
|
|
m.MovieMetadata.HeadOrNone().Map(mm => FirstDirectorHashCode(mm.Directors)).IfNone(0),
|
|
null,
|
|
null,
|
|
null),
|
|
Episode e => new GroupKey(
|
|
CollectionType.Collection,
|
|
e.EpisodeMetadata.HeadOrNone().Map(em => FirstDirectorHashCode(em.Directors)).IfNone(0),
|
|
null,
|
|
null,
|
|
null),
|
|
MusicVideo mv => new GroupKey(
|
|
CollectionType.Collection,
|
|
mv.MusicVideoMetadata.HeadOrNone().Map(mvm => FirstDirectorHashCode(mvm.Directors)).IfNone(0),
|
|
null,
|
|
null,
|
|
null),
|
|
OtherVideo ov => new GroupKey(
|
|
CollectionType.Collection,
|
|
ov.OtherVideoMetadata.HeadOrNone().Map(ovm => FirstDirectorHashCode(ovm.Directors)).IfNone(0),
|
|
null,
|
|
null,
|
|
null),
|
|
_ => new GroupKey(CollectionType.Collection, 0, null, null, null)
|
|
};
|
|
|
|
private static int FirstDirectorHashCode(List<Director> directors) =>
|
|
directors.HeadOrNone().Select(director => director.Name.GetStableHashCode()).FirstOrDefault();
|
|
|
|
private static PlaylistItem GroupToPlaylistItem(
|
|
int index,
|
|
bool playAllItems,
|
|
PlaybackOrder playbackOrder,
|
|
IGrouping<GroupKey, MediaItem> group) =>
|
|
new()
|
|
{
|
|
Index = index,
|
|
|
|
CollectionType = group.Key.CollectionType,
|
|
CollectionId = group.Key.CollectionId,
|
|
MultiCollectionId = group.Key.MultiCollectionId,
|
|
SmartCollectionId = group.Key.SmartCollectionId,
|
|
MediaItemId = group.Key.MediaItemId,
|
|
|
|
PlayAll = playAllItems,
|
|
PlaybackOrder = playbackOrder,
|
|
|
|
IncludeInProgramGuide = true
|
|
};
|
|
|
|
private record GroupKey(
|
|
CollectionType CollectionType,
|
|
int? CollectionId,
|
|
int? MultiCollectionId,
|
|
int? SmartCollectionId,
|
|
int? MediaItemId);
|
|
}
|