From 42d2cec545af1145ab29a4be12eedc5a15c9982c Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Fri, 26 Feb 2021 13:38:48 -0600 Subject: [PATCH] fix playout building --- .../Fakes/FakeTelevisionRepository.cs | 4 +- .../Repositories/ITelevisionRepository.cs | 4 +- ErsatzTV.Core/Scheduling/PlayoutBuilder.cs | 29 ++-- .../Repositories/MediaCollectionRepository.cs | 152 +++++++++--------- .../Data/Repositories/PlayoutRepository.cs | 1 + .../Data/Repositories/TelevisionRepository.cs | 69 +++----- 6 files changed, 114 insertions(+), 145 deletions(-) diff --git a/ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs b/ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs index ddc79e45c..74ea5594f 100644 --- a/ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs +++ b/ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs @@ -24,7 +24,7 @@ namespace ErsatzTV.Core.Tests.Fakes public Task> GetPagedShows(int pageNumber, int pageSize) => throw new NotSupportedException(); - public Task> GetShowItems(int televisionShowId) => throw new NotSupportedException(); + public Task> GetShowItems(int showId) => throw new NotSupportedException(); public Task> GetAllSeasons() => throw new NotSupportedException(); @@ -35,7 +35,7 @@ namespace ErsatzTV.Core.Tests.Fakes public Task> GetPagedSeasons(int televisionShowId, int pageNumber, int pageSize) => throw new NotSupportedException(); - public Task> GetSeasonItems(int televisionSeasonId) => throw new NotSupportedException(); + public Task> GetSeasonItems(int seasonId) => throw new NotSupportedException(); public Task> GetEpisode(int televisionEpisodeId) => throw new NotSupportedException(); diff --git a/ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs index c24a8d705..062e6cee5 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs @@ -14,12 +14,12 @@ namespace ErsatzTV.Core.Interfaces.Repositories Task> GetShow(int televisionShowId); Task GetShowCount(); Task> GetPagedShows(int pageNumber, int pageSize); - Task> GetShowItems(int televisionShowId); + Task> GetShowItems(int showId); Task> GetAllSeasons(); Task> GetSeason(int televisionSeasonId); Task GetSeasonCount(int televisionShowId); Task> GetPagedSeasons(int televisionShowId, int pageNumber, int pageSize); - Task> GetSeasonItems(int televisionSeasonId); + Task> GetSeasonItems(int seasonId); Task> GetEpisode(int televisionEpisodeId); Task GetEpisodeCount(int televisionSeasonId); Task> GetPagedEpisodes(int televisionSeasonId, int pageNumber, int pageSize); diff --git a/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs b/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs index f13c4b13a..3414675e4 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs @@ -55,15 +55,15 @@ namespace ErsatzTV.Core.Scheduling { case ProgramScheduleItemCollectionType.Collection: Option> maybeItems = - await _mediaCollectionRepository.GetItems(collectionKey.Id); + await _mediaCollectionRepository.GetItems(collectionKey.CollectionId ?? 0); return Tuple(collectionKey, maybeItems.IfNone(new List())); case ProgramScheduleItemCollectionType.TelevisionShow: List showItems = - await _televisionRepository.GetShowItems(collectionKey.Id); + await _televisionRepository.GetShowItems(collectionKey.MediaItemId ?? 0); return Tuple(collectionKey, showItems.Cast().ToList()); case ProgramScheduleItemCollectionType.TelevisionSeason: List seasonItems = - await _televisionRepository.GetSeasonItems(collectionKey.Id); + await _televisionRepository.GetSeasonItems(collectionKey.MediaItemId ?? 0); return Tuple(collectionKey, seasonItems.Cast().ToList()); default: return Tuple(collectionKey, new List()); @@ -325,7 +325,9 @@ namespace ErsatzTV.Core.Scheduling { Option maybeExisting = playout.ProgramScheduleAnchors .FirstOrDefault( - a => a.CollectionType == collectionKey.CollectionType && a.NewCollectionId == collectionKey.Id); + a => a.CollectionType == collectionKey.CollectionType + && a.NewCollectionId == collectionKey.CollectionId + && a.MediaItemId == collectionKey.MediaItemId); var maybeEnumeratorState = collectionEnumerators.GroupBy(e => e.Key, e => e.Value.State) .ToDictionary(mcs => mcs.Key, mcs => mcs.Head()); @@ -343,7 +345,8 @@ namespace ErsatzTV.Core.Scheduling ProgramSchedule = playout.ProgramSchedule, ProgramScheduleId = playout.ProgramScheduleId, CollectionType = collectionKey.CollectionType, - NewCollectionId = collectionKey.Id, + NewCollectionId = collectionKey.CollectionId, + MediaItemId = collectionKey.MediaItemId, EnumeratorState = maybeEnumeratorState[collectionKey] }); @@ -360,9 +363,10 @@ namespace ErsatzTV.Core.Scheduling { Option maybeAnchor = playout.ProgramScheduleAnchors .FirstOrDefault( - a => a.ProgramScheduleId == playout.ProgramScheduleId && a.CollectionType == - collectionKey.CollectionType - && a.NewCollectionId == collectionKey.Id); + a => a.ProgramScheduleId == playout.ProgramScheduleId + && a.CollectionType == collectionKey.CollectionType + && a.NewCollectionId == collectionKey.CollectionId + && a.MediaItemId == collectionKey.MediaItemId); CollectionEnumeratorState state = maybeAnchor.Match( anchor => anchor.EnumeratorState, @@ -400,17 +404,17 @@ namespace ErsatzTV.Core.Scheduling ProgramScheduleItemCollectionType.Collection => new CollectionKey { CollectionType = item.CollectionType, - Id = item.CollectionId.Value + CollectionId = item.CollectionId }, ProgramScheduleItemCollectionType.TelevisionShow => new CollectionKey { CollectionType = item.CollectionType, - Id = item.MediaItemId.Value + MediaItemId = item.MediaItemId }, ProgramScheduleItemCollectionType.TelevisionSeason => new CollectionKey { CollectionType = item.CollectionType, - Id = item.MediaItemId.Value + MediaItemId = item.MediaItemId }, _ => throw new ArgumentOutOfRangeException(nameof(item)) }; @@ -418,7 +422,8 @@ namespace ErsatzTV.Core.Scheduling private class CollectionKey : Record { public ProgramScheduleItemCollectionType CollectionType { get; set; } - public int Id { get; set; } + public int? CollectionId { get; set; } + public int? MediaItemId { get; set; } } } } diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs index d8ad01f9f..25fa444b3 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Data; using System.Linq; using System.Threading.Tasks; @@ -78,7 +77,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories _dbContext.Collections.ToListAsync(); public Task>> GetItems(int id) => - Get(id).MapT(c => c.MediaItems); + Get(id).MapT(GetItemsForCollection).Bind(x => x.Sequence()); public Task Update(Collection collection) { @@ -93,82 +92,75 @@ namespace ErsatzTV.Infrastructure.Data.Repositories await _dbContext.SaveChangesAsync(); } -// private async Task> GetTelevisionShowItems(SimpleMediaCollection collection) -// { -// var parameters = new { CollectionId = collection.Id }; -// return await _dbConnection -// .QueryAsync( -// @"select tmi.Id, tmi.SeasonId, mi.LibraryPathId, mi.LastWriteTime, mi.Path, mi.Poster, mi.PosterLastWriteTime, -// mi.Statistics_AudioCodec as AudioCodec, mi.Statistics_DisplayAspectRatio as DisplayAspectRatio, mi.Statistics_Duration as Duration, mi.Statistics_Height as Height, mi.Statistics_LastWriteTime as LastWriteTime, mi.Statistics_SampleAspectRatio as SampleAspectRatio, -// mi.Statistics_VideoCodec as VideoCodec, mi.Statistics_VideoScanType as VideoScanType, mi.Statistics_Width as Width, -// tem.TelevisionEpisodeId, tem.Id, tem.Season, tem.Episode, tem.Plot, tem.Aired, tem.Source, tem.LastWriteTime, tem.Title, tem.SortTitle -// from TelevisionEpisode tmi -// inner join MediaItem mi on tmi.Id = mi.Id -// inner join TelevisionEpisodeMetadata tem on tem.TelevisionEpisodeId = tmi.Id -// inner join TelevisionSeason tsn on tsn.Id = tmi.SeasonId -// inner join TelevisionShow ts on ts.Id = tsn.TelevisionShowId -// inner join SimpleMediaCollectionShow s on s.TelevisionShowsId = ts.Id -// where s.SimpleMediaCollectionsId = @CollectionId", -// (episode, statistics, metadata) => -// { -// episode.Statistics = statistics; -// episode.Metadata = metadata; -// return episode; -// }, -// parameters, -// splitOn: "AudioCodec,TelevisionEpisodeId").Map(result => result.ToList()); -// } -// -// private async Task> GetTelevisionSeasonItems(SimpleMediaCollection collection) -// { -// var parameters = new { CollectionId = collection.Id }; -// return await _dbConnection -// .QueryAsync( -// @"select tmi.Id, tmi.SeasonId, mi.LibraryPathId, mi.LastWriteTime, mi.Path, mi.Poster, mi.PosterLastWriteTime, -// mi.Statistics_AudioCodec as AudioCodec, mi.Statistics_DisplayAspectRatio as DisplayAspectRatio, mi.Statistics_Duration as Duration, mi.Statistics_Height as Height, mi.Statistics_LastWriteTime as LastWriteTime, mi.Statistics_SampleAspectRatio as SampleAspectRatio, -// mi.Statistics_VideoCodec as VideoCodec, mi.Statistics_VideoScanType as VideoScanType, mi.Statistics_Width as Width, -// tem.TelevisionEpisodeId, tem.Id, tem.Season, tem.Episode, tem.Plot, tem.Aired, tem.Source, tem.LastWriteTime, tem.Title, tem.SortTitle -// from TelevisionEpisode tmi -// inner join MediaItem mi on tmi.Id = mi.Id -// inner join TelevisionEpisodeMetadata tem on tem.TelevisionEpisodeId = tmi.Id -// inner join TelevisionSeason tsn on tsn.Id = tmi.SeasonId -// inner join SimpleMediaCollectionSeason s on s.TelevisionSeasonsId = tsn.Id -// where s.SimpleMediaCollectionsId = @CollectionId", -// (episode, statistics, metadata) => -// { -// episode.Statistics = statistics; -// episode.Metadata = metadata; -// return episode; -// }, -// parameters, -// splitOn: "AudioCodec,TelevisionEpisodeId").Map(result => result.ToList()); -// } -// -// private async Task> GetTelevisionEpisodeItems(SimpleMediaCollection collection) -// { -// var parameters = new { CollectionId = collection.Id }; -// return await _dbConnection -// .QueryAsync( -// @"select tmi.Id, tmi.SeasonId, mi.LibraryPathId, mi.LastWriteTime, mi.Path, mi.Poster, mi.PosterLastWriteTime, -// mi.Statistics_AudioCodec as AudioCodec, mi.Statistics_DisplayAspectRatio as DisplayAspectRatio, mi.Statistics_Duration as Duration, mi.Statistics_Height as Height, mi.Statistics_LastWriteTime as LastWriteTime, mi.Statistics_SampleAspectRatio as SampleAspectRatio, -// mi.Statistics_VideoCodec as VideoCodec, mi.Statistics_VideoScanType as VideoScanType, mi.Statistics_Width as Width, -// tem.TelevisionEpisodeId, tem.Id, tem.Season, tem.Episode, tem.Plot, tem.Aired, tem.Source, tem.LastWriteTime, tem.Title, tem.SortTitle -// from TelevisionEpisode tmi -// inner join MediaItem mi on tmi.Id = mi.Id -// inner join TelevisionEpisodeMetadata tem on tem.TelevisionEpisodeId = tmi.Id -// inner join SimpleMediaCollectionEpisode s on s.TelevisionEpisodesId = tmi.Id -// where s.SimpleMediaCollectionsId = @CollectionId", -// (episode, statistics, metadata) => -// { -// episode.Statistics = statistics; -// episode.Metadata = metadata; -// return episode; -// }, -// parameters, -// splitOn: "AudioCodec,TelevisionEpisodeId").Map(result => result.ToList()); -// } + private async Task> GetItemsForCollection(Collection collection) + { + var result = new List(); + + result.AddRange(await GetMovieItems(collection)); + result.AddRange(await GetShowItems(collection)); + result.AddRange(await GetSeasonItems(collection)); + result.AddRange(await GetEpisodeItems(collection)); + + return result.Distinct().ToList(); + } + + private async Task> GetMovieItems(Collection collection) + { + IEnumerable ids = await _dbConnection.QueryAsync( + @"SELECT m.Id FROM CollectionItem ci + INNER JOIN Movie m ON m.Id = ci.MediaItemId + WHERE ci.CollectionId = @CollectionId", + new { CollectionId = collection.Id }); + + return await _dbContext.Movies + .Include(m => m.MovieMetadata) + .Filter(m => ids.Contains(m.Id)) + .ToListAsync(); + } + + private async Task> GetShowItems(Collection collection) + { + IEnumerable ids = await _dbConnection.QueryAsync( + @"SELECT Episode.Id FROM CollectionItem ci + INNER JOIN Show ON Show.Id = ci.MediaItemId + INNER JOIN Season ON Season.ShowId = Show.Id + INNER JOIN Episode ON Episode.SeasonId = Season.Id + WHERE ci.CollectionId = @CollectionId", + new { CollectionId = collection.Id }); + + return await _dbContext.Episodes + .Include(e => e.EpisodeMetadata) + .Filter(e => ids.Contains(e.Id)) + .ToListAsync(); + } + + private async Task> GetSeasonItems(Collection collection) + { + IEnumerable ids = await _dbConnection.QueryAsync( + @"SELECT Episode.Id FROM CollectionItem ci + INNER JOIN Season ON Season.Id = ci.MediaItemId + INNER JOIN Episode ON Episode.SeasonId = Season.Id + WHERE ci.CollectionId = @CollectionId", + new { CollectionId = collection.Id }); + + return await _dbContext.Episodes + .Include(e => e.EpisodeMetadata) + .Filter(e => ids.Contains(e.Id)) + .ToListAsync(); + } + + private async Task> GetEpisodeItems(Collection collection) + { + IEnumerable ids = await _dbConnection.QueryAsync( + @"SELECT Episode.Id FROM CollectionItem ci + INNER JOIN Episode ON Episode.Id = ci.MediaItemId + WHERE ci.CollectionId = @CollectionId", + new { CollectionId = collection.Id }); + + return await _dbContext.Episodes + .Include(e => e.EpisodeMetadata) + .Filter(e => ids.Contains(e.Id)) + .ToListAsync(); + } } } diff --git a/ErsatzTV.Infrastructure/Data/Repositories/PlayoutRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/PlayoutRepository.cs index 049bae717..83e579022 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/PlayoutRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/PlayoutRepository.cs @@ -32,6 +32,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories .Include(p => p.Channel) .Include(p => p.Items) .Include(p => p.ProgramScheduleAnchors) + .ThenInclude(a => a.MediaItem) .Include(p => p.ProgramSchedule) .ThenInclude(ps => ps.Items) .ThenInclude(psi => psi.Collection) diff --git a/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs index bafcf8c8f..b54f65bc7 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; using System.Threading.Tasks; +using Dapper; using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; @@ -220,57 +221,27 @@ namespace ErsatzTV.Infrastructure.Data.Repositories }) .ToUnit(); - public async Task> GetShowItems(int televisionShowId) => - // TODO: fix this - new(); + public async Task> GetShowItems(int showId) + { + IEnumerable ids = await _dbConnection.QueryAsync( + @"SELECT Episode.Id FROM Show + INNER JOIN Season ON Season.ShowId = Show.Id + INNER JOIN Episode ON Episode.SeasonId = Season.Id + WHERE Show.Id = @ShowId", + new { ShowId = showId }); - // var parameters = new { ShowId = televisionShowId }; - // return await _dbConnection - // .QueryAsync( - // @"select tmi.Id, tmi.SeasonId, mi.LibraryPathId, mi.LastWriteTime, mi.Path, mi.Poster, mi.PosterLastWriteTime, - // mi.Statistics_AudioCodec as AudioCodec, mi.Statistics_DisplayAspectRatio as DisplayAspectRatio, mi.Statistics_Duration as Duration, mi.Statistics_Height as Height, mi.Statistics_LastWriteTime as LastWriteTime, mi.Statistics_SampleAspectRatio as SampleAspectRatio, - // mi.Statistics_VideoCodec as VideoCodec, mi.Statistics_VideoScanType as VideoScanType, mi.Statistics_Width as Width, - // tem.TelevisionEpisodeId, tem.Id, tem.Season, tem.Episode, tem.Plot, tem.Aired, tem.Source, tem.LastWriteTime, tem.Title, tem.SortTitle - // from TelevisionEpisode tmi - // inner join MediaItem mi on tmi.Id = mi.Id - // inner join TelevisionEpisodeMetadata tem on tem.TelevisionEpisodeId = tmi.Id - // inner join TelevisionSeason tsn on tsn.Id = tmi.SeasonId - // inner join TelevisionShow ts on ts.Id = tsn.TelevisionShowId - // where ts.Id = @ShowId", - // (episode, statistics, metadata) => - // { - // episode.Statistics = statistics; - // episode.Metadata = metadata; - // return episode; - // }, - // parameters, - // splitOn: "AudioCodec,TelevisionEpisodeId").Map(result => result.ToList()); - public async Task> GetSeasonItems(int televisionSeasonId) => - // TODO: fix this - new(); + return await _dbContext.Episodes + .Include(e => e.EpisodeMetadata) + .Filter(e => ids.Contains(e.Id)) + .ToListAsync(); + } + + public Task> GetSeasonItems(int seasonId) => + _dbContext.Episodes + .Include(e => e.EpisodeMetadata) + .Filter(e => e.SeasonId == seasonId) + .ToListAsync(); - // var parameters = new { SeasonId = televisionSeasonId }; - // return await _dbConnection - // .QueryAsync( - // @"select tmi.Id, tmi.SeasonId, mi.LibraryPathId, mi.LastWriteTime, mi.Path, mi.Poster, mi.PosterLastWriteTime, - // mi.Statistics_AudioCodec as AudioCodec, mi.Statistics_DisplayAspectRatio as DisplayAspectRatio, mi.Statistics_Duration as Duration, mi.Statistics_Height as Height, mi.Statistics_LastWriteTime as LastWriteTime, mi.Statistics_SampleAspectRatio as SampleAspectRatio, - // mi.Statistics_VideoCodec as VideoCodec, mi.Statistics_VideoScanType as VideoScanType, mi.Statistics_Width as Width, - // tem.TelevisionEpisodeId, tem.Id, tem.Season, tem.Episode, tem.Plot, tem.Aired, tem.Source, tem.LastWriteTime, tem.Title, tem.SortTitle - // from TelevisionEpisode tmi - // inner join MediaItem mi on tmi.Id = mi.Id - // inner join TelevisionEpisodeMetadata tem on tem.TelevisionEpisodeId = tmi.Id - // inner join TelevisionSeason tsn on tsn.Id = tmi.SeasonId - // where tsn.Id = @SeasonId", - // (episode, statistics, metadata) => - // { - // episode.Statistics = statistics; - // episode.Metadata = metadata; - // return episode; - // }, - // parameters, - // splitOn: "AudioCodec,TelevisionEpisodeId").Map(result => result.ToList()); private async Task> AddSeason( Show show, string path,