diff --git a/ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs b/ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs index 2d1dac84..229e60e1 100644 --- a/ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs +++ b/ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using ErsatzTV.Core; @@ -36,10 +37,29 @@ namespace ErsatzTV.Application.Channels.Commands private async Task> Validate(CreateChannel request) => (ValidateName(request), ValidateNumber(request), await FFmpegProfileMustExist(request)) .Apply( - (name, number, ffmpegProfileId) => new Channel(Guid.NewGuid()) + (name, number, ffmpegProfileId) => { - Name = name, Number = number, FFmpegProfileId = ffmpegProfileId, - StreamingMode = request.StreamingMode + var artwork = new List(); + if (!string.IsNullOrWhiteSpace(request.Logo)) + { + artwork.Add( + new Artwork + { + Path = request.Logo, + ArtworkKind = ArtworkKind.Logo, + DateAdded = DateTime.UtcNow, + DateUpdated = DateTime.UtcNow + }); + } + + return new Channel(Guid.NewGuid()) + { + Name = name, + Number = number, + FFmpegProfileId = ffmpegProfileId, + StreamingMode = request.StreamingMode, + Artwork = artwork + }; }); private Validation ValidateName(CreateChannel createChannel) => diff --git a/ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs b/ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs index e63f59d7..4fd9eacc 100644 --- a/ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs +++ b/ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs @@ -1,4 +1,7 @@ -using System.Threading; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; using System.Threading.Tasks; using ErsatzTV.Core; using ErsatzTV.Core.Domain; @@ -6,6 +9,7 @@ using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; using MediatR; using static ErsatzTV.Application.Channels.Mapper; +using static LanguageExt.Prelude; namespace ErsatzTV.Application.Channels.Commands { @@ -27,7 +31,33 @@ namespace ErsatzTV.Application.Channels.Commands c.Name = update.Name; c.Number = update.Number; c.FFmpegProfileId = update.FFmpegProfileId; - c.Logo = update.Logo; + + if (!string.IsNullOrWhiteSpace(update.Logo)) + { + c.Artwork ??= new List(); + + Option maybeLogo = + Optional(c.Artwork).Flatten().FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Logo); + + maybeLogo.Match( + artwork => + { + artwork.Path = update.Logo; + artwork.DateUpdated = DateTime.UtcNow; + }, + () => + { + var artwork = new Artwork + { + Path = update.Logo, + DateAdded = DateTime.UtcNow, + DateUpdated = DateTime.UtcNow, + ArtworkKind = ArtworkKind.Logo + }; + c.Artwork.Add(artwork); + }); + } + c.StreamingMode = update.StreamingMode; await _channelRepository.Update(c); return ProjectToViewModel(c); diff --git a/ErsatzTV.Application/Channels/Mapper.cs b/ErsatzTV.Application/Channels/Mapper.cs index 4e86ae75..6ad4237c 100644 --- a/ErsatzTV.Application/Channels/Mapper.cs +++ b/ErsatzTV.Application/Channels/Mapper.cs @@ -1,10 +1,22 @@ -using ErsatzTV.Core.Domain; +using System.Linq; +using ErsatzTV.Core.Domain; +using static LanguageExt.Prelude; namespace ErsatzTV.Application.Channels { internal static class Mapper { internal static ChannelViewModel ProjectToViewModel(Channel channel) => - new(channel.Id, channel.Number, channel.Name, channel.FFmpegProfileId, channel.Logo, channel.StreamingMode); + new( + channel.Id, + channel.Number, + channel.Name, + channel.FFmpegProfileId, + GetLogo(channel), + channel.StreamingMode); + + private static string GetLogo(Channel channel) => + Optional(channel.Artwork.FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Logo)) + .Match(a => a.Path, string.Empty); } } diff --git a/ErsatzTV.Application/Images/Commands/SaveImageToDisk.cs b/ErsatzTV.Application/Images/Commands/SaveArtworkToDisk.cs similarity index 54% rename from ErsatzTV.Application/Images/Commands/SaveImageToDisk.cs rename to ErsatzTV.Application/Images/Commands/SaveArtworkToDisk.cs index 68873ff0..653a1db4 100644 --- a/ErsatzTV.Application/Images/Commands/SaveImageToDisk.cs +++ b/ErsatzTV.Application/Images/Commands/SaveArtworkToDisk.cs @@ -1,9 +1,10 @@ using ErsatzTV.Core; +using ErsatzTV.Core.Domain; using LanguageExt; using MediatR; namespace ErsatzTV.Application.Images.Commands { // ReSharper disable once SuggestBaseTypeForParameter - public record SaveImageToDisk(byte[] Buffer) : IRequest>; + public record SaveArtworkToDisk(byte[] Buffer, ArtworkKind ArtworkKind) : IRequest>; } diff --git a/ErsatzTV.Application/Images/Commands/SaveArtworkToDiskHandler.cs b/ErsatzTV.Application/Images/Commands/SaveArtworkToDiskHandler.cs new file mode 100644 index 00000000..a7dd2ab1 --- /dev/null +++ b/ErsatzTV.Application/Images/Commands/SaveArtworkToDiskHandler.cs @@ -0,0 +1,19 @@ +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Interfaces.Images; +using LanguageExt; +using MediatR; + +namespace ErsatzTV.Application.Images.Commands +{ + public class SaveArtworkToDiskHandler : IRequestHandler> + { + private readonly IImageCache _imageCache; + + public SaveArtworkToDiskHandler(IImageCache imageCache) => _imageCache = imageCache; + + public Task> Handle(SaveArtworkToDisk request, CancellationToken cancellationToken) => + _imageCache.SaveArtworkToCache(request.Buffer, request.ArtworkKind); + } +} diff --git a/ErsatzTV.Application/Images/Commands/SaveImageToDiskHandler.cs b/ErsatzTV.Application/Images/Commands/SaveImageToDiskHandler.cs deleted file mode 100644 index 13243443..00000000 --- a/ErsatzTV.Application/Images/Commands/SaveImageToDiskHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using ErsatzTV.Core; -using ErsatzTV.Core.Interfaces.Images; -using LanguageExt; -using MediatR; - -namespace ErsatzTV.Application.Images.Commands -{ - public class SaveImageToDiskHandler : IRequestHandler> - { - private readonly IImageCache _imageCache; - - public SaveImageToDiskHandler(IImageCache imageCache) => _imageCache = imageCache; - - public Task> Handle( - SaveImageToDisk request, - CancellationToken cancellationToken) => _imageCache.SaveImage(request.Buffer); - } -} diff --git a/ErsatzTV.Application/Images/Queries/GetImageContents.cs b/ErsatzTV.Application/Images/Queries/GetImageContents.cs index a1bc6c38..b1ef0ed4 100644 --- a/ErsatzTV.Application/Images/Queries/GetImageContents.cs +++ b/ErsatzTV.Application/Images/Queries/GetImageContents.cs @@ -1,8 +1,10 @@ using ErsatzTV.Core; +using ErsatzTV.Core.Domain; using LanguageExt; using MediatR; namespace ErsatzTV.Application.Images.Queries { - public record GetImageContents(string FileName) : IRequest>; + public record GetImageContents + (string FileName, ArtworkKind ArtworkKind, int? MaxHeight = null) : IRequest>; } diff --git a/ErsatzTV.Application/Images/Queries/GetImageContentsHandler.cs b/ErsatzTV.Application/Images/Queries/GetImageContentsHandler.cs index 6598c4df..636ee093 100644 --- a/ErsatzTV.Application/Images/Queries/GetImageContentsHandler.cs +++ b/ErsatzTV.Application/Images/Queries/GetImageContentsHandler.cs @@ -3,6 +3,8 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using ErsatzTV.Core; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Images; using LanguageExt; using MediatR; using Microsoft.Extensions.Caching.Memory; @@ -13,9 +15,14 @@ namespace ErsatzTV.Application.Images.Queries public class GetImageContentsHandler : IRequestHandler> { private static readonly MimeTypes MimeTypes = new(); + private readonly IImageCache _imageCache; private readonly IMemoryCache _memoryCache; - public GetImageContentsHandler(IMemoryCache memoryCache) => _memoryCache = memoryCache; + public GetImageContentsHandler(IImageCache imageCache, IMemoryCache memoryCache) + { + _imageCache = imageCache; + _memoryCache = memoryCache; + } public async Task> Handle( GetImageContents request, @@ -29,8 +36,25 @@ namespace ErsatzTV.Application.Images.Queries { entry.SlidingExpiration = TimeSpan.FromHours(1); - string fileName = Path.Combine(FileSystemLayout.ImageCacheFolder, request.FileName); + string subfolder = request.FileName.Substring(0, 2); + string baseFolder = request.ArtworkKind switch + { + ArtworkKind.Poster => Path.Combine(FileSystemLayout.PosterCacheFolder, subfolder), + ArtworkKind.Thumbnail => Path.Combine(FileSystemLayout.ThumbnailCacheFolder, subfolder), + ArtworkKind.Logo => Path.Combine(FileSystemLayout.LogoCacheFolder, subfolder), + _ => FileSystemLayout.LegacyImageCacheFolder + }; + + string fileName = Path.Combine(baseFolder, request.FileName); byte[] contents = await File.ReadAllBytesAsync(fileName, cancellationToken); + + if (request.MaxHeight.HasValue) + { + Either resizeResult = await _imageCache + .ResizeImage(contents, request.MaxHeight.Value); + resizeResult.IfRight(result => contents = result); + } + MimeType mimeType = MimeTypes.GetMimeType(contents); return new ImageViewModel(contents, mimeType.Name); }); diff --git a/ErsatzTV.Application/Libraries/Commands/CreateLocalLibraryPath.cs b/ErsatzTV.Application/Libraries/Commands/CreateLocalLibraryPath.cs new file mode 100644 index 00000000..8970f67b --- /dev/null +++ b/ErsatzTV.Application/Libraries/Commands/CreateLocalLibraryPath.cs @@ -0,0 +1,9 @@ +using ErsatzTV.Core; +using LanguageExt; +using MediatR; + +namespace ErsatzTV.Application.Libraries.Commands +{ + public record CreateLocalLibraryPath + (int LibraryId, string Path) : IRequest>; +} diff --git a/ErsatzTV.Application/Libraries/Commands/CreateLocalLibraryPathHandler.cs b/ErsatzTV.Application/Libraries/Commands/CreateLocalLibraryPathHandler.cs new file mode 100644 index 00000000..870da8c0 --- /dev/null +++ b/ErsatzTV.Application/Libraries/Commands/CreateLocalLibraryPathHandler.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using static LanguageExt.Prelude; +using static ErsatzTV.Application.Libraries.Mapper; + +namespace ErsatzTV.Application.Libraries.Commands +{ + public class CreateLocalLibraryPathHandler : IRequestHandler> + { + private readonly ILibraryRepository _libraryRepository; + + public CreateLocalLibraryPathHandler(ILibraryRepository mediaSourceRepository) => + _libraryRepository = mediaSourceRepository; + + public Task> Handle( + CreateLocalLibraryPath request, + CancellationToken cancellationToken) => + Validate(request).MapT(PersistLocalLibraryPath).Bind(v => v.ToEitherAsync()); + + private Task PersistLocalLibraryPath(LibraryPath p) => + _libraryRepository.Add(p).Map(ProjectToViewModel); + + private Task> Validate(CreateLocalLibraryPath request) => + ValidateFolder(request) + .MapT( + folder => + new LibraryPath + { + LibraryId = request.LibraryId, + Path = folder + }); + + private async Task> ValidateFolder(CreateLocalLibraryPath request) + { + List allPaths = await _libraryRepository.GetLocalPaths(request.LibraryId) + .Map(list => list.Map(c => c.Path).ToList()); + + + return Optional(request.Path) + .Filter(folder => allPaths.ForAll(f => !AreSubPaths(f, folder))) + .ToValidation("Path must not belong to another library path"); + } + + private static bool AreSubPaths(string path1, string path2) + { + string one = path1 + Path.DirectorySeparatorChar; + string two = path2 + Path.DirectorySeparatorChar; + return one == two || one.StartsWith(two, StringComparison.OrdinalIgnoreCase) || + two.StartsWith(one, StringComparison.OrdinalIgnoreCase); + } + } +} diff --git a/ErsatzTV.Application/Libraries/Commands/DeleteLocalLibraryPath.cs b/ErsatzTV.Application/Libraries/Commands/DeleteLocalLibraryPath.cs new file mode 100644 index 00000000..d8c72e36 --- /dev/null +++ b/ErsatzTV.Application/Libraries/Commands/DeleteLocalLibraryPath.cs @@ -0,0 +1,7 @@ +using ErsatzTV.Core; +using LanguageExt; + +namespace ErsatzTV.Application.Libraries.Commands +{ + public record DeleteLocalLibraryPath(int LocalLibraryPathId) : MediatR.IRequest>; +} diff --git a/ErsatzTV.Application/Libraries/Commands/DeleteLocalLibraryPathHandler.cs b/ErsatzTV.Application/Libraries/Commands/DeleteLocalLibraryPathHandler.cs new file mode 100644 index 00000000..7fc6d7ee --- /dev/null +++ b/ErsatzTV.Application/Libraries/Commands/DeleteLocalLibraryPathHandler.cs @@ -0,0 +1,34 @@ +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; + +namespace ErsatzTV.Application.Libraries.Commands +{ + public class + DeleteLocalLibraryPathHandler : MediatR.IRequestHandler> + { + private readonly ILibraryRepository _libraryRepository; + + public DeleteLocalLibraryPathHandler(ILibraryRepository libraryRepository) => + _libraryRepository = libraryRepository; + + public Task> Handle( + DeleteLocalLibraryPath request, + CancellationToken cancellationToken) => + MediaSourceMustExist(request) + .MapT(DoDeletion) + .Bind(t => t.ToEitherAsync()); + + private Task DoDeletion(LibraryPath libraryPath) => + _libraryRepository.DeleteLocalPath(libraryPath.Id).ToUnit(); + + private async Task> MediaSourceMustExist(DeleteLocalLibraryPath request) => + (await _libraryRepository.GetPath(request.LocalLibraryPathId)) + .HeadOrNone() + .ToValidation( + $"Local library path {request.LocalLibraryPathId} does not exist."); + } +} diff --git a/ErsatzTV.Application/Libraries/LocalLibraryPathViewModel.cs b/ErsatzTV.Application/Libraries/LocalLibraryPathViewModel.cs new file mode 100644 index 00000000..9c8356bf --- /dev/null +++ b/ErsatzTV.Application/Libraries/LocalLibraryPathViewModel.cs @@ -0,0 +1,4 @@ +namespace ErsatzTV.Application.Libraries +{ + public record LocalLibraryPathViewModel(int Id, int LibraryId, string Path); +} diff --git a/ErsatzTV.Application/Libraries/LocalLibraryViewModel.cs b/ErsatzTV.Application/Libraries/LocalLibraryViewModel.cs new file mode 100644 index 00000000..3e37ecfb --- /dev/null +++ b/ErsatzTV.Application/Libraries/LocalLibraryViewModel.cs @@ -0,0 +1,6 @@ +using ErsatzTV.Core.Domain; + +namespace ErsatzTV.Application.Libraries +{ + public record LocalLibraryViewModel(int Id, string Name, LibraryMediaKind MediaKind); +} diff --git a/ErsatzTV.Application/Libraries/Mapper.cs b/ErsatzTV.Application/Libraries/Mapper.cs new file mode 100644 index 00000000..872a1a16 --- /dev/null +++ b/ErsatzTV.Application/Libraries/Mapper.cs @@ -0,0 +1,13 @@ +using ErsatzTV.Core.Domain; + +namespace ErsatzTV.Application.Libraries +{ + internal static class Mapper + { + public static LocalLibraryViewModel ProjectToViewModel(LocalLibrary library) => + new(library.Id, library.Name, library.MediaKind); + + public static LocalLibraryPathViewModel ProjectToViewModel(LibraryPath libraryPath) => + new(libraryPath.Id, libraryPath.LibraryId, libraryPath.Path); + } +} diff --git a/ErsatzTV.Application/Libraries/Queries/CountMediaItemsByLibraryPath.cs b/ErsatzTV.Application/Libraries/Queries/CountMediaItemsByLibraryPath.cs new file mode 100644 index 00000000..0b3d3b7d --- /dev/null +++ b/ErsatzTV.Application/Libraries/Queries/CountMediaItemsByLibraryPath.cs @@ -0,0 +1,6 @@ +using MediatR; + +namespace ErsatzTV.Application.Libraries.Queries +{ + public record CountMediaItemsByLibraryPath(int LibraryPathId) : IRequest; +} diff --git a/ErsatzTV.Application/Libraries/Queries/CountMediaItemsByLibraryPathHandler.cs b/ErsatzTV.Application/Libraries/Queries/CountMediaItemsByLibraryPathHandler.cs new file mode 100644 index 00000000..4b862af7 --- /dev/null +++ b/ErsatzTV.Application/Libraries/Queries/CountMediaItemsByLibraryPathHandler.cs @@ -0,0 +1,18 @@ +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core.Interfaces.Repositories; +using MediatR; + +namespace ErsatzTV.Application.Libraries.Queries +{ + public class CountMediaItemsByLibraryPathHandler : IRequestHandler + { + private readonly ILibraryRepository _libraryRepository; + + public CountMediaItemsByLibraryPathHandler(ILibraryRepository libraryRepository) => + _libraryRepository = libraryRepository; + + public Task Handle(CountMediaItemsByLibraryPath request, CancellationToken cancellationToken) => + _libraryRepository.CountMediaItemsByPath(request.LibraryPathId); + } +} diff --git a/ErsatzTV.Application/Libraries/Queries/GetLocalLibraries.cs b/ErsatzTV.Application/Libraries/Queries/GetLocalLibraries.cs new file mode 100644 index 00000000..6abe74f5 --- /dev/null +++ b/ErsatzTV.Application/Libraries/Queries/GetLocalLibraries.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; +using MediatR; + +namespace ErsatzTV.Application.Libraries.Queries +{ + public record GetLocalLibraries : IRequest>; +} diff --git a/ErsatzTV.Application/Libraries/Queries/GetLocalLibrariesHandler.cs b/ErsatzTV.Application/Libraries/Queries/GetLocalLibrariesHandler.cs new file mode 100644 index 00000000..0ea7d275 --- /dev/null +++ b/ErsatzTV.Application/Libraries/Queries/GetLocalLibrariesHandler.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using static ErsatzTV.Application.Libraries.Mapper; + +namespace ErsatzTV.Application.Libraries.Queries +{ + public class GetLocalLibrariesHandler : IRequestHandler> + { + private readonly ILibraryRepository _libraryRepository; + + public GetLocalLibrariesHandler(ILibraryRepository libraryRepository) => _libraryRepository = libraryRepository; + + public Task> + Handle(GetLocalLibraries request, CancellationToken cancellationToken) => + _libraryRepository.GetAllLocal().Map(list => list.Map(ProjectToViewModel).ToList()); + } +} diff --git a/ErsatzTV.Application/Libraries/Queries/GetLocalLibraryById.cs b/ErsatzTV.Application/Libraries/Queries/GetLocalLibraryById.cs new file mode 100644 index 00000000..becb8e12 --- /dev/null +++ b/ErsatzTV.Application/Libraries/Queries/GetLocalLibraryById.cs @@ -0,0 +1,7 @@ +using LanguageExt; +using MediatR; + +namespace ErsatzTV.Application.Libraries.Queries +{ + public record GetLocalLibraryById(int LibraryId) : IRequest>; +} diff --git a/ErsatzTV.Application/Libraries/Queries/GetLocalLibraryByIdHandler.cs b/ErsatzTV.Application/Libraries/Queries/GetLocalLibraryByIdHandler.cs new file mode 100644 index 00000000..6e9f111a --- /dev/null +++ b/ErsatzTV.Application/Libraries/Queries/GetLocalLibraryByIdHandler.cs @@ -0,0 +1,22 @@ +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using static ErsatzTV.Application.Libraries.Mapper; + +namespace ErsatzTV.Application.Libraries.Queries +{ + public class GetLocalLibraryByIdHandler : IRequestHandler> + { + private readonly ILibraryRepository _libraryRepository; + + public GetLocalLibraryByIdHandler(ILibraryRepository libraryRepository) => + _libraryRepository = libraryRepository; + + public Task> Handle( + GetLocalLibraryById request, + CancellationToken cancellationToken) => + _libraryRepository.GetLocal(request.LibraryId).MapT(ProjectToViewModel); + } +} diff --git a/ErsatzTV.Application/Libraries/Queries/GetLocalLibraryPaths.cs b/ErsatzTV.Application/Libraries/Queries/GetLocalLibraryPaths.cs new file mode 100644 index 00000000..1bf34612 --- /dev/null +++ b/ErsatzTV.Application/Libraries/Queries/GetLocalLibraryPaths.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; +using MediatR; + +namespace ErsatzTV.Application.Libraries.Queries +{ + public record GetLocalLibraryPaths(int LocalLibraryId) : IRequest>; +} diff --git a/ErsatzTV.Application/Libraries/Queries/GetLocalLibraryPathsHandler.cs b/ErsatzTV.Application/Libraries/Queries/GetLocalLibraryPathsHandler.cs new file mode 100644 index 00000000..517985a7 --- /dev/null +++ b/ErsatzTV.Application/Libraries/Queries/GetLocalLibraryPathsHandler.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using static ErsatzTV.Application.Libraries.Mapper; + +namespace ErsatzTV.Application.Libraries.Queries +{ + public class GetLocalLibraryPathsHandler : IRequestHandler> + { + private readonly ILibraryRepository _libraryRepository; + + public GetLocalLibraryPathsHandler(ILibraryRepository libraryRepository) => + _libraryRepository = libraryRepository; + + public Task> Handle( + GetLocalLibraryPaths request, + CancellationToken cancellationToken) => + _libraryRepository.GetLocalPaths(request.LocalLibraryId) + .Map(list => list.Map(ProjectToViewModel).ToList()); + } +} diff --git a/ErsatzTV.Application/MediaCards/SimpleMediaCollectionCardResultsViewModel.cs b/ErsatzTV.Application/MediaCards/CollectionCardResultsViewModel.cs similarity index 83% rename from ErsatzTV.Application/MediaCards/SimpleMediaCollectionCardResultsViewModel.cs rename to ErsatzTV.Application/MediaCards/CollectionCardResultsViewModel.cs index 0ac4bebb..e84c2dc5 100644 --- a/ErsatzTV.Application/MediaCards/SimpleMediaCollectionCardResultsViewModel.cs +++ b/ErsatzTV.Application/MediaCards/CollectionCardResultsViewModel.cs @@ -2,7 +2,7 @@ namespace ErsatzTV.Application.MediaCards { - public record SimpleMediaCollectionCardResultsViewModel( + public record CollectionCardResultsViewModel( string Name, List MovieCards, List ShowCards, diff --git a/ErsatzTV.Application/MediaCards/Mapper.cs b/ErsatzTV.Application/MediaCards/Mapper.cs index d087d3a4..b4161db1 100644 --- a/ErsatzTV.Application/MediaCards/Mapper.cs +++ b/ErsatzTV.Application/MediaCards/Mapper.cs @@ -1,60 +1,70 @@ using System; using System.Linq; using ErsatzTV.Core.Domain; +using static LanguageExt.Prelude; namespace ErsatzTV.Application.MediaCards { internal static class Mapper { - internal static TelevisionShowCardViewModel ProjectToViewModel(TelevisionShow televisionShow) => + internal static TelevisionShowCardViewModel ProjectToViewModel(ShowMetadata showMetadata) => new( - televisionShow.Id, - televisionShow.Metadata?.Title, - televisionShow.Metadata?.Year.ToString(), - televisionShow.Metadata?.SortTitle, - televisionShow.Poster); + showMetadata.ShowId, + showMetadata.Title, + showMetadata.Year?.ToString(), + showMetadata.SortTitle, + GetPoster(showMetadata)); - internal static TelevisionSeasonCardViewModel ProjectToViewModel(TelevisionSeason televisionSeason) => + internal static TelevisionSeasonCardViewModel ProjectToViewModel(Season season) => new( - televisionSeason.TelevisionShow.Metadata?.Title, - televisionSeason.Id, - televisionSeason.Number, - GetSeasonName(televisionSeason.Number), + season.Show.ShowMetadata.HeadOrNone().Map(m => m.Title).IfNone(string.Empty), + season.Id, + season.SeasonNumber, + GetSeasonName(season.SeasonNumber), string.Empty, - GetSeasonName(televisionSeason.Number), - televisionSeason.Poster, - televisionSeason.Number == 0 ? "S" : televisionSeason.Number.ToString()); + GetSeasonName(season.SeasonNumber), + season.SeasonMetadata.HeadOrNone().Map(GetPoster).IfNone(string.Empty), + season.SeasonNumber == 0 ? "S" : season.SeasonNumber.ToString()); internal static TelevisionEpisodeCardViewModel ProjectToViewModel( - TelevisionEpisodeMediaItem televisionEpisode) => + EpisodeMetadata episodeMetadata) => new( - televisionEpisode.Id, - televisionEpisode.Metadata?.Aired ?? DateTime.MinValue, - televisionEpisode.Season.TelevisionShow.Metadata.Title, - televisionEpisode.Metadata?.Title, - $"Episode {televisionEpisode.Metadata?.Episode}", - televisionEpisode.Metadata?.Episode.ToString(), - televisionEpisode.Poster, - televisionEpisode.Metadata?.Episode.ToString()); - - internal static MovieCardViewModel ProjectToViewModel(MovieMediaItem movie) => + episodeMetadata.EpisodeId, + episodeMetadata.ReleaseDate ?? DateTime.MinValue, + episodeMetadata.Episode.Season.Show.ShowMetadata.HeadOrNone().Map(m => m.Title).IfNone(string.Empty), + episodeMetadata.Title, + $"Episode {episodeMetadata.Episode.EpisodeNumber}", + episodeMetadata.Episode.EpisodeNumber.ToString(), + GetThumbnail(episodeMetadata), + episodeMetadata.Episode.EpisodeNumber.ToString()); + + internal static MovieCardViewModel ProjectToViewModel(MovieMetadata movieMetadata) => new( - movie.Id, - movie.Metadata?.Title, - movie.Metadata?.Year?.ToString(), - movie.Metadata?.SortTitle, - movie.Poster); - - internal static SimpleMediaCollectionCardResultsViewModel - ProjectToViewModel(SimpleMediaCollection collection) => + movieMetadata.MovieId, + movieMetadata.Title, + movieMetadata.Year?.ToString(), + movieMetadata.SortTitle, + GetPoster(movieMetadata)); + + internal static CollectionCardResultsViewModel + ProjectToViewModel(Collection collection) => new( collection.Name, - collection.Movies.Map(ProjectToViewModel).ToList(), - collection.TelevisionShows.Map(ProjectToViewModel).ToList(), - collection.TelevisionSeasons.Map(ProjectToViewModel).ToList(), - collection.TelevisionEpisodes.Map(ProjectToViewModel).ToList()); + collection.MediaItems.OfType().Map(m => ProjectToViewModel(m.MovieMetadata.Head())).ToList(), + collection.MediaItems.OfType().Map(s => ProjectToViewModel(s.ShowMetadata.Head())).ToList(), + collection.MediaItems.OfType().Map(ProjectToViewModel).ToList(), + collection.MediaItems.OfType().Map(e => ProjectToViewModel(e.EpisodeMetadata.Head())) + .ToList()); private static string GetSeasonName(int number) => number == 0 ? "Specials" : $"Season {number}"; + + private static string GetPoster(Metadata metadata) => + Optional(metadata.Artwork.FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Poster)) + .Match(a => a.Path, string.Empty); + + private static string GetThumbnail(Metadata metadata) => + Optional(metadata.Artwork.FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Thumbnail)) + .Match(a => a.Path, string.Empty); } } diff --git a/ErsatzTV.Application/MediaCards/Queries/GetCollectionCards.cs b/ErsatzTV.Application/MediaCards/Queries/GetCollectionCards.cs new file mode 100644 index 00000000..21027629 --- /dev/null +++ b/ErsatzTV.Application/MediaCards/Queries/GetCollectionCards.cs @@ -0,0 +1,8 @@ +using ErsatzTV.Core; +using LanguageExt; +using MediatR; + +namespace ErsatzTV.Application.MediaCards.Queries +{ + public record GetCollectionCards(int Id) : IRequest>; +} diff --git a/ErsatzTV.Application/MediaCards/Queries/GetCollectionCardsHandler.cs b/ErsatzTV.Application/MediaCards/Queries/GetCollectionCardsHandler.cs new file mode 100644 index 00000000..babaf6cc --- /dev/null +++ b/ErsatzTV.Application/MediaCards/Queries/GetCollectionCardsHandler.cs @@ -0,0 +1,26 @@ +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using static ErsatzTV.Application.MediaCards.Mapper; + +namespace ErsatzTV.Application.MediaCards.Queries +{ + public class GetCollectionCardsHandler : IRequestHandler> + { + private readonly IMediaCollectionRepository _collectionRepository; + + public GetCollectionCardsHandler(IMediaCollectionRepository collectionRepository) => + _collectionRepository = collectionRepository; + + public async Task> Handle( + GetCollectionCards request, + CancellationToken cancellationToken) => + (await _collectionRepository.GetCollectionWithItemsUntracked(request.Id)) + .ToEither(BaseError.New("Unable to load collection")) + .Map(ProjectToViewModel); + } +} diff --git a/ErsatzTV.Application/MediaCards/Queries/GetSimpleMediaCollectionCards.cs b/ErsatzTV.Application/MediaCards/Queries/GetSimpleMediaCollectionCards.cs deleted file mode 100644 index d99fc0ea..00000000 --- a/ErsatzTV.Application/MediaCards/Queries/GetSimpleMediaCollectionCards.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErsatzTV.Core; -using LanguageExt; -using MediatR; - -namespace ErsatzTV.Application.MediaCards.Queries -{ - public record GetSimpleMediaCollectionCards - (int Id) : IRequest>; -} diff --git a/ErsatzTV.Application/MediaCards/Queries/GetSimpleMediaCollectionCardsHandler.cs b/ErsatzTV.Application/MediaCards/Queries/GetSimpleMediaCollectionCardsHandler.cs deleted file mode 100644 index 07393be8..00000000 --- a/ErsatzTV.Application/MediaCards/Queries/GetSimpleMediaCollectionCardsHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using ErsatzTV.Core; -using ErsatzTV.Core.Interfaces.Repositories; -using LanguageExt; -using MediatR; -using static ErsatzTV.Application.MediaCards.Mapper; - -namespace ErsatzTV.Application.MediaCards.Queries -{ - public class GetSimpleMediaCollectionCardsHandler : IRequestHandler> - { - private readonly IMediaCollectionRepository _mediaCollectionRepository; - - public GetSimpleMediaCollectionCardsHandler(IMediaCollectionRepository mediaCollectionRepository) => - _mediaCollectionRepository = mediaCollectionRepository; - - public async Task> Handle( - GetSimpleMediaCollectionCards request, - CancellationToken cancellationToken) => - (await _mediaCollectionRepository.GetSimpleMediaCollectionWithItemsUntracked(request.Id)) - .ToEither(BaseError.New("Unable to load collection")) - .Map(ProjectToViewModel); - } -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollection.cs new file mode 100644 index 00000000..ae69c119 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollection.cs @@ -0,0 +1,7 @@ +using ErsatzTV.Core; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public record AddEpisodeToCollection(int CollectionId, int EpisodeId) : MediatR.IRequest>; +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollectionHandler.cs new file mode 100644 index 00000000..00c9abae --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollectionHandler.cs @@ -0,0 +1,50 @@ +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public class + AddEpisodeToCollectionHandler : MediatR.IRequestHandler> + { + private readonly IMediaCollectionRepository _mediaCollectionRepository; + private readonly ITelevisionRepository _televisionRepository; + + public AddEpisodeToCollectionHandler( + IMediaCollectionRepository mediaCollectionRepository, + ITelevisionRepository televisionRepository) + { + _mediaCollectionRepository = mediaCollectionRepository; + _televisionRepository = televisionRepository; + } + + public Task> Handle( + AddEpisodeToCollection request, + CancellationToken cancellationToken) => + Validate(request) + .MapT(_ => ApplyAddTelevisionEpisodeRequest(request)) + .Bind(v => v.ToEitherAsync()); + + private Task ApplyAddTelevisionEpisodeRequest(AddEpisodeToCollection request) => + _mediaCollectionRepository.AddMediaItem(request.CollectionId, request.EpisodeId); + + private async Task> Validate(AddEpisodeToCollection request) => + (await CollectionMustExist(request), await ValidateEpisode(request)) + .Apply((_, _) => Unit.Default); + + private Task> CollectionMustExist(AddEpisodeToCollection request) => + _mediaCollectionRepository.Get(request.CollectionId) + .MapT(_ => Unit.Default) + .Map(v => v.ToValidation("Collection does not exist.")); + + private Task> ValidateEpisode(AddEpisodeToCollection request) => + LoadTelevisionEpisode(request) + .MapT(_ => Unit.Default) + .Map(v => v.ToValidation("Episode does not exist")); + + private Task> LoadTelevisionEpisode(AddEpisodeToCollection request) => + _televisionRepository.GetEpisode(request.EpisodeId).MapT(e => e.Id); + } +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddMovieCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/AddMovieCollection.cs new file mode 100644 index 00000000..e9bc90de --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/AddMovieCollection.cs @@ -0,0 +1,7 @@ +using ErsatzTV.Core; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public record AddMovieToCollection(int CollectionId, int MovieId) : MediatR.IRequest>; +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddMovieToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddMovieToCollectionHandler.cs new file mode 100644 index 00000000..d0f3d521 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/AddMovieToCollectionHandler.cs @@ -0,0 +1,50 @@ +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public class AddMovieToCollectionHandler : MediatR.IRequestHandler> + { + private readonly IMediaCollectionRepository _mediaCollectionRepository; + private readonly IMovieRepository _movieRepository; + + public AddMovieToCollectionHandler( + IMediaCollectionRepository mediaCollectionRepository, + IMovieRepository movieRepository) + { + _mediaCollectionRepository = mediaCollectionRepository; + _movieRepository = movieRepository; + } + + public Task> Handle( + AddMovieToCollection request, + CancellationToken cancellationToken) => + Validate(request) + .MapT(_ => ApplyAddMoviesRequest(request)) + .Bind(v => v.ToEitherAsync()); + + private Task ApplyAddMoviesRequest(AddMovieToCollection request) => + _mediaCollectionRepository.AddMediaItem(request.CollectionId, request.MovieId); + + private async Task> Validate(AddMovieToCollection request) => + (await CollectionMustExist(request), await ValidateMovies(request)) + .Apply((_, _) => Unit.Default); + + private Task> CollectionMustExist(AddMovieToCollection request) => + _mediaCollectionRepository.GetCollectionWithItems(request.CollectionId) + .MapT(_ => Unit.Default) + .Map(v => v.ToValidation("Collection does not exist.")); + + private Task> ValidateMovies(AddMovieToCollection request) => + LoadMovie(request) + .MapT(_ => Unit.Default) + .Map(v => v.ToValidation("Movie does not exist")); + + private Task> LoadMovie(AddMovieToCollection request) => + _movieRepository.GetMovie(request.MovieId); + } +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddMovieToSimpleMediaCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/AddMovieToSimpleMediaCollection.cs deleted file mode 100644 index cacb5f4b..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/AddMovieToSimpleMediaCollection.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErsatzTV.Core; -using LanguageExt; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public record AddMovieToSimpleMediaCollection - (int MediaCollectionId, int MovieId) : MediatR.IRequest>; -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddMovieToSimpleMediaCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddMovieToSimpleMediaCollectionHandler.cs deleted file mode 100644 index a42304b0..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/AddMovieToSimpleMediaCollectionHandler.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using ErsatzTV.Core; -using ErsatzTV.Core.Domain; -using ErsatzTV.Core.Interfaces.Repositories; -using LanguageExt; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public class - AddMovieToSimpleMediaCollectionHandler : MediatR.IRequestHandler> - { - private readonly IMediaCollectionRepository _mediaCollectionRepository; - private readonly IMovieRepository _movieRepository; - - public AddMovieToSimpleMediaCollectionHandler( - IMediaCollectionRepository mediaCollectionRepository, - IMovieRepository movieRepository) - { - _mediaCollectionRepository = mediaCollectionRepository; - _movieRepository = movieRepository; - } - - public Task> Handle( - AddMovieToSimpleMediaCollection request, - CancellationToken cancellationToken) => - Validate(request) - .MapT(ApplyAddMoviesRequest) - .Bind(v => v.ToEitherAsync()); - - private async Task ApplyAddMoviesRequest(RequestParameters parameters) - { - parameters.Collection.Movies.Add(parameters.MovieToAdd); - await _mediaCollectionRepository.Update(parameters.Collection); - - return Unit.Default; - } - - private async Task> - Validate(AddMovieToSimpleMediaCollection request) => - (await SimpleMediaCollectionMustExist(request), await ValidateMovies(request)) - .Apply( - (simpleMediaCollectionToUpdate, movieToAdd) => - new RequestParameters(simpleMediaCollectionToUpdate, movieToAdd)); - - private Task> SimpleMediaCollectionMustExist( - AddMovieToSimpleMediaCollection updateSimpleMediaCollection) => - _mediaCollectionRepository.GetSimpleMediaCollectionWithItems(updateSimpleMediaCollection.MediaCollectionId) - .Map(v => v.ToValidation("SimpleMediaCollection does not exist.")); - - private Task> ValidateMovies( - AddMovieToSimpleMediaCollection request) => - LoadMovie(request) - .Map(v => v.ToValidation("MovieMediaItem does not exist")); - - private Task> LoadMovie(AddMovieToSimpleMediaCollection request) => - _movieRepository.GetMovie(request.MovieId); - - private record RequestParameters(SimpleMediaCollection Collection, MovieMediaItem MovieToAdd); - } -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollection.cs new file mode 100644 index 00000000..e3f795f8 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollection.cs @@ -0,0 +1,7 @@ +using ErsatzTV.Core; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public record AddSeasonToCollection(int CollectionId, int SeasonId) : MediatR.IRequest>; +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollectionHandler.cs new file mode 100644 index 00000000..4812be04 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollectionHandler.cs @@ -0,0 +1,51 @@ +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public class AddSeasonToCollectionHandler : MediatR.IRequestHandler> + { + private readonly IMediaCollectionRepository _mediaCollectionRepository; + private readonly ITelevisionRepository _televisionRepository; + + public AddSeasonToCollectionHandler( + IMediaCollectionRepository mediaCollectionRepository, + ITelevisionRepository televisionRepository) + { + _mediaCollectionRepository = mediaCollectionRepository; + _televisionRepository = televisionRepository; + } + + public Task> Handle( + AddSeasonToCollection request, + CancellationToken cancellationToken) => + Validate(request) + .MapT(_ => ApplyAddTelevisionSeasonRequest(request)) + .Bind(v => v.ToEitherAsync()); + + private async Task ApplyAddTelevisionSeasonRequest(AddSeasonToCollection request) => + await _mediaCollectionRepository.AddMediaItem(request.CollectionId, request.SeasonId); + + private async Task> Validate(AddSeasonToCollection request) => + (await CollectionMustExist(request), await ValidateSeason(request)) + .Apply((_, _) => Unit.Default); + + private Task> CollectionMustExist(AddSeasonToCollection request) => + _mediaCollectionRepository.GetCollectionWithItems(request.CollectionId) + .MapT(_ => Unit.Default) + .Map(v => v.ToValidation("Collection does not exist.")); + + private Task> ValidateSeason(AddSeasonToCollection request) => + LoadTelevisionSeason(request) + .MapT(_ => Unit.Default) + .Map(v => v.ToValidation("Season does not exist")); + + private Task> LoadTelevisionSeason( + AddSeasonToCollection request) => + _televisionRepository.GetSeason(request.SeasonId); + } +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddShowToCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/AddShowToCollection.cs new file mode 100644 index 00000000..48233e6d --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/AddShowToCollection.cs @@ -0,0 +1,7 @@ +using ErsatzTV.Core; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public record AddShowToCollection(int CollectionId, int ShowId) : MediatR.IRequest>; +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddShowToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddShowToCollectionHandler.cs new file mode 100644 index 00000000..9c7e780a --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/AddShowToCollectionHandler.cs @@ -0,0 +1,50 @@ +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public class AddShowToCollectionHandler : MediatR.IRequestHandler> + { + private readonly IMediaCollectionRepository _mediaCollectionRepository; + private readonly ITelevisionRepository _televisionRepository; + + public AddShowToCollectionHandler( + IMediaCollectionRepository mediaCollectionRepository, + ITelevisionRepository televisionRepository) + { + _mediaCollectionRepository = mediaCollectionRepository; + _televisionRepository = televisionRepository; + } + + public Task> Handle( + AddShowToCollection request, + CancellationToken cancellationToken) => + Validate(request) + .MapT(_ => ApplyAddTelevisionShowRequest(request)) + .Bind(v => v.ToEitherAsync()); + + private Task ApplyAddTelevisionShowRequest(AddShowToCollection request) + => _mediaCollectionRepository.AddMediaItem(request.CollectionId, request.ShowId); + + private async Task> Validate(AddShowToCollection request) => + (await CollectionMustExist(request), await ValidateShow(request)) + .Apply((_, _) => Unit.Default); + + private Task> CollectionMustExist(AddShowToCollection request) => + _mediaCollectionRepository.GetCollectionWithItems(request.CollectionId) + .MapT(_ => Unit.Default) + .Map(v => v.ToValidation("Collection does not exist.")); + + private Task> ValidateShow(AddShowToCollection request) => + LoadTelevisionShow(request) + .MapT(_ => Unit.Default) + .Map(v => v.ToValidation("Show does not exist")); + + private Task> LoadTelevisionShow(AddShowToCollection request) => + _televisionRepository.GetShow(request.ShowId); + } +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionEpisodeToSimpleMediaCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionEpisodeToSimpleMediaCollection.cs deleted file mode 100644 index 290d8d17..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionEpisodeToSimpleMediaCollection.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErsatzTV.Core; -using LanguageExt; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public record AddTelevisionEpisodeToSimpleMediaCollection - (int MediaCollectionId, int TelevisionEpisodeId) : MediatR.IRequest>; -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionEpisodeToSimpleMediaCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionEpisodeToSimpleMediaCollectionHandler.cs deleted file mode 100644 index 9604b9ba..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionEpisodeToSimpleMediaCollectionHandler.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using ErsatzTV.Core; -using ErsatzTV.Core.Domain; -using ErsatzTV.Core.Interfaces.Repositories; -using LanguageExt; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public class - AddTelevisionEpisodeToSimpleMediaCollectionHandler : MediatR.IRequestHandler< - AddTelevisionEpisodeToSimpleMediaCollection, - Either> - { - private readonly IMediaCollectionRepository _mediaCollectionRepository; - private readonly ITelevisionRepository _televisionRepository; - - public AddTelevisionEpisodeToSimpleMediaCollectionHandler( - IMediaCollectionRepository mediaCollectionRepository, - ITelevisionRepository televisionRepository) - { - _mediaCollectionRepository = mediaCollectionRepository; - _televisionRepository = televisionRepository; - } - - public Task> Handle( - AddTelevisionEpisodeToSimpleMediaCollection request, - CancellationToken cancellationToken) => - Validate(request) - .MapT(ApplyAddTelevisionEpisodeRequest) - .Bind(v => v.ToEitherAsync()); - - private async Task ApplyAddTelevisionEpisodeRequest(RequestParameters parameters) - { - if (parameters.Collection.TelevisionEpisodes.All(s => s.Id != parameters.EpisodeToAdd.Id)) - { - parameters.Collection.TelevisionEpisodes.Add(parameters.EpisodeToAdd); - await _mediaCollectionRepository.Update(parameters.Collection); - } - - return Unit.Default; - } - - private async Task> - Validate(AddTelevisionEpisodeToSimpleMediaCollection request) => - (await SimpleMediaCollectionMustExist(request), await ValidateEpisode(request)) - .Apply( - (simpleMediaCollectionToUpdate, episode) => - new RequestParameters(simpleMediaCollectionToUpdate, episode)); - - private Task> SimpleMediaCollectionMustExist( - AddTelevisionEpisodeToSimpleMediaCollection updateSimpleMediaCollection) => - _mediaCollectionRepository.GetSimpleMediaCollectionWithItems(updateSimpleMediaCollection.MediaCollectionId) - .Map(v => v.ToValidation("SimpleMediaCollection does not exist.")); - - private Task> ValidateEpisode( - AddTelevisionEpisodeToSimpleMediaCollection request) => - LoadTelevisionEpisode(request) - .Map(v => v.ToValidation("TelevisionEpisode does not exist")); - - private Task> LoadTelevisionEpisode( - AddTelevisionEpisodeToSimpleMediaCollection request) => - _televisionRepository.GetEpisode(request.TelevisionEpisodeId); - - private record RequestParameters(SimpleMediaCollection Collection, TelevisionEpisodeMediaItem EpisodeToAdd); - } -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionSeasonToSimpleMediaCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionSeasonToSimpleMediaCollection.cs deleted file mode 100644 index 54cde04a..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionSeasonToSimpleMediaCollection.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErsatzTV.Core; -using LanguageExt; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public record AddTelevisionSeasonToSimpleMediaCollection - (int MediaCollectionId, int TelevisionSeasonId) : MediatR.IRequest>; -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionSeasonToSimpleMediaCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionSeasonToSimpleMediaCollectionHandler.cs deleted file mode 100644 index fb4ada45..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionSeasonToSimpleMediaCollectionHandler.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using ErsatzTV.Core; -using ErsatzTV.Core.Domain; -using ErsatzTV.Core.Interfaces.Repositories; -using LanguageExt; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public class - AddTelevisionSeasonToSimpleMediaCollectionHandler : MediatR.IRequestHandler< - AddTelevisionSeasonToSimpleMediaCollection, - Either> - { - private readonly IMediaCollectionRepository _mediaCollectionRepository; - private readonly ITelevisionRepository _televisionRepository; - - public AddTelevisionSeasonToSimpleMediaCollectionHandler( - IMediaCollectionRepository mediaCollectionRepository, - ITelevisionRepository televisionRepository) - { - _mediaCollectionRepository = mediaCollectionRepository; - _televisionRepository = televisionRepository; - } - - public Task> Handle( - AddTelevisionSeasonToSimpleMediaCollection request, - CancellationToken cancellationToken) => - Validate(request) - .MapT(ApplyAddTelevisionSeasonRequest) - .Bind(v => v.ToEitherAsync()); - - private async Task ApplyAddTelevisionSeasonRequest(RequestParameters parameters) - { - if (parameters.Collection.TelevisionSeasons.All(s => s.Id != parameters.SeasonToAdd.Id)) - { - parameters.Collection.TelevisionSeasons.Add(parameters.SeasonToAdd); - await _mediaCollectionRepository.Update(parameters.Collection); - } - - return Unit.Default; - } - - private async Task> - Validate(AddTelevisionSeasonToSimpleMediaCollection request) => - (await SimpleMediaCollectionMustExist(request), await ValidateSeason(request)) - .Apply( - (simpleMediaCollectionToUpdate, season) => - new RequestParameters(simpleMediaCollectionToUpdate, season)); - - private Task> SimpleMediaCollectionMustExist( - AddTelevisionSeasonToSimpleMediaCollection updateSimpleMediaCollection) => - _mediaCollectionRepository.GetSimpleMediaCollectionWithItems(updateSimpleMediaCollection.MediaCollectionId) - .Map(v => v.ToValidation("SimpleMediaCollection does not exist.")); - - private Task> ValidateSeason( - AddTelevisionSeasonToSimpleMediaCollection request) => - LoadTelevisionSeason(request) - .Map(v => v.ToValidation("TelevisionSeason does not exist")); - - private Task> LoadTelevisionSeason( - AddTelevisionSeasonToSimpleMediaCollection request) => - _televisionRepository.GetSeason(request.TelevisionSeasonId); - - private record RequestParameters(SimpleMediaCollection Collection, TelevisionSeason SeasonToAdd); - } -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionShowToSimpleMediaCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionShowToSimpleMediaCollection.cs deleted file mode 100644 index faf5eb3f..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionShowToSimpleMediaCollection.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErsatzTV.Core; -using LanguageExt; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public record AddTelevisionShowToSimpleMediaCollection - (int MediaCollectionId, int TelevisionShowId) : MediatR.IRequest>; -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionShowToSimpleMediaCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionShowToSimpleMediaCollectionHandler.cs deleted file mode 100644 index 77197f62..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/AddTelevisionShowToSimpleMediaCollectionHandler.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using ErsatzTV.Core; -using ErsatzTV.Core.Domain; -using ErsatzTV.Core.Interfaces.Repositories; -using LanguageExt; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public class - AddTelevisionShowToSimpleMediaCollectionHandler : MediatR.IRequestHandler< - AddTelevisionShowToSimpleMediaCollection, - Either> - { - private readonly IMediaCollectionRepository _mediaCollectionRepository; - private readonly ITelevisionRepository _televisionRepository; - - public AddTelevisionShowToSimpleMediaCollectionHandler( - IMediaCollectionRepository mediaCollectionRepository, - ITelevisionRepository televisionRepository) - { - _mediaCollectionRepository = mediaCollectionRepository; - _televisionRepository = televisionRepository; - } - - public Task> Handle( - AddTelevisionShowToSimpleMediaCollection request, - CancellationToken cancellationToken) => - Validate(request) - .MapT(ApplyAddTelevisionShowRequest) - .Bind(v => v.ToEitherAsync()); - - private async Task ApplyAddTelevisionShowRequest(RequestParameters parameters) - { - if (parameters.Collection.TelevisionShows.All(s => s.Id != parameters.ShowToAdd.Id)) - { - parameters.Collection.TelevisionShows.Add(parameters.ShowToAdd); - await _mediaCollectionRepository.Update(parameters.Collection); - } - - return Unit.Default; - } - - private async Task> - Validate(AddTelevisionShowToSimpleMediaCollection request) => - (await SimpleMediaCollectionMustExist(request), await ValidateShow(request)) - .Apply( - (simpleMediaCollectionToUpdate, show) => - new RequestParameters(simpleMediaCollectionToUpdate, show)); - - private Task> SimpleMediaCollectionMustExist( - AddTelevisionShowToSimpleMediaCollection updateSimpleMediaCollection) => - _mediaCollectionRepository.GetSimpleMediaCollectionWithItems(updateSimpleMediaCollection.MediaCollectionId) - .Map(v => v.ToValidation("SimpleMediaCollection does not exist.")); - - private Task> ValidateShow( - AddTelevisionShowToSimpleMediaCollection request) => - LoadTelevisionShow(request) - .Map(v => v.ToValidation("TelevisionShow does not exist")); - - private Task> LoadTelevisionShow(AddTelevisionShowToSimpleMediaCollection request) => - _televisionRepository.GetShow(request.TelevisionShowId); - - private record RequestParameters(SimpleMediaCollection Collection, TelevisionShow ShowToAdd); - } -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/CreateCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/CreateCollection.cs new file mode 100644 index 00000000..b4450b08 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/CreateCollection.cs @@ -0,0 +1,8 @@ +using ErsatzTV.Core; +using LanguageExt; +using MediatR; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public record CreateCollection(string Name) : IRequest>; +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/CreateSimpleMediaCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/CreateCollectionHandler.cs similarity index 62% rename from ErsatzTV.Application/MediaCollections/Commands/CreateSimpleMediaCollectionHandler.cs rename to ErsatzTV.Application/MediaCollections/Commands/CreateCollectionHandler.cs index 362fab63..2bb74750 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/CreateSimpleMediaCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/CreateCollectionHandler.cs @@ -12,36 +12,33 @@ using static LanguageExt.Prelude; namespace ErsatzTV.Application.MediaCollections.Commands { - public class CreateSimpleMediaCollectionHandler : IRequestHandler> + public class + CreateCollectionHandler : IRequestHandler> { private readonly IMediaCollectionRepository _mediaCollectionRepository; - public CreateSimpleMediaCollectionHandler(IMediaCollectionRepository mediaCollectionRepository) => + public CreateCollectionHandler(IMediaCollectionRepository mediaCollectionRepository) => _mediaCollectionRepository = mediaCollectionRepository; public Task> Handle( - CreateSimpleMediaCollection request, + CreateCollection request, CancellationToken cancellationToken) => Validate(request).MapT(PersistCollection).Bind(v => v.ToEitherAsync()); - private Task PersistCollection(SimpleMediaCollection c) => + private Task PersistCollection(Collection c) => _mediaCollectionRepository.Add(c).Map(ProjectToViewModel); - private Task> Validate(CreateSimpleMediaCollection request) => + private Task> Validate(CreateCollection request) => ValidateName(request).MapT( - name => new SimpleMediaCollection + name => new Collection { Name = name, - Movies = new List(), - TelevisionShows = new List(), - TelevisionEpisodes = new List(), - TelevisionSeasons = new List() + MediaItems = new List() }); - private async Task> ValidateName(CreateSimpleMediaCollection createCollection) + private async Task> ValidateName(CreateCollection createCollection) { - List allNames = await _mediaCollectionRepository.GetSimpleMediaCollections() + List allNames = await _mediaCollectionRepository.GetAll() .Map(list => list.Map(c => c.Name).ToList()); Validation result1 = createCollection.NotEmpty(c => c.Name) @@ -49,7 +46,7 @@ namespace ErsatzTV.Application.MediaCollections.Commands var result2 = Optional(createCollection.Name) .Filter(name => !allNames.Contains(name)) - .ToValidation("Media collection name must be unique"); + .ToValidation("Collection name must be unique"); return (result1, result2).Apply((_, _) => createCollection.Name); } diff --git a/ErsatzTV.Application/MediaCollections/Commands/CreateSimpleMediaCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/CreateSimpleMediaCollection.cs deleted file mode 100644 index 11cff638..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/CreateSimpleMediaCollection.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErsatzTV.Core; -using LanguageExt; -using MediatR; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public record CreateSimpleMediaCollection - (string Name) : IRequest>; -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/DeleteSimpleMediaCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/DeleteCollection.cs similarity index 57% rename from ErsatzTV.Application/MediaCollections/Commands/DeleteSimpleMediaCollection.cs rename to ErsatzTV.Application/MediaCollections/Commands/DeleteCollection.cs index 0251bbcb..eb8bcbe7 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/DeleteSimpleMediaCollection.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/DeleteCollection.cs @@ -5,5 +5,5 @@ using MediatR; namespace ErsatzTV.Application.MediaCollections.Commands { - public record DeleteSimpleMediaCollection(int SimpleMediaCollectionId) : IRequest>; + public record DeleteCollection(int CollectionId) : IRequest>; } diff --git a/ErsatzTV.Application/MediaCollections/Commands/DeleteSimpleMediaCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/DeleteCollectionHandler.cs similarity index 50% rename from ErsatzTV.Application/MediaCollections/Commands/DeleteSimpleMediaCollectionHandler.cs rename to ErsatzTV.Application/MediaCollections/Commands/DeleteCollectionHandler.cs index 0c7b405d..57e8b2ef 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/DeleteSimpleMediaCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/DeleteCollectionHandler.cs @@ -7,28 +7,27 @@ using MediatR; namespace ErsatzTV.Application.MediaCollections.Commands { - public class - DeleteSimpleMediaCollectionHandler : IRequestHandler> + public class DeleteCollectionHandler : IRequestHandler> { private readonly IMediaCollectionRepository _mediaCollectionRepository; - public DeleteSimpleMediaCollectionHandler(IMediaCollectionRepository mediaCollectionRepository) => + public DeleteCollectionHandler(IMediaCollectionRepository mediaCollectionRepository) => _mediaCollectionRepository = mediaCollectionRepository; public async Task> Handle( - DeleteSimpleMediaCollection request, + DeleteCollection request, CancellationToken cancellationToken) => - (await SimpleMediaCollectionMustExist(request)) + (await CollectionMustExist(request)) .Map(DoDeletion) .ToEither(); private Task DoDeletion(int mediaCollectionId) => _mediaCollectionRepository.Delete(mediaCollectionId); - private async Task> SimpleMediaCollectionMustExist( - DeleteSimpleMediaCollection deleteMediaCollection) => - (await _mediaCollectionRepository.GetSimpleMediaCollection(deleteMediaCollection.SimpleMediaCollectionId)) + private async Task> CollectionMustExist( + DeleteCollection deleteMediaCollection) => + (await _mediaCollectionRepository.Get(deleteMediaCollection.CollectionId)) .ToValidation( - $"SimpleMediaCollection {deleteMediaCollection.SimpleMediaCollectionId} does not exist.") + $"Collection {deleteMediaCollection.CollectionId} does not exist.") .Map(c => c.Id); } } diff --git a/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollection.cs new file mode 100644 index 00000000..29aaa670 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollection.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using ErsatzTV.Core; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public record RemoveItemsFromCollection(int MediaCollectionId) : MediatR.IRequest> + { + public List MediaItemIds { get; set; } = new(); + } +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollectionHandler.cs new file mode 100644 index 00000000..f4341f2b --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollectionHandler.cs @@ -0,0 +1,51 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public class + RemoveItemsFromCollectionHandler : MediatR.IRequestHandler> + { + private readonly IMediaCollectionRepository _mediaCollectionRepository; + + public RemoveItemsFromCollectionHandler( + IMediaCollectionRepository mediaCollectionRepository) => + _mediaCollectionRepository = mediaCollectionRepository; + + public Task> Handle( + RemoveItemsFromCollection request, + CancellationToken cancellationToken) => + Validate(request) + .MapT(collection => ApplyAddTelevisionEpisodeRequest(request, collection)) + .Bind(v => v.ToEitherAsync()); + + private Task ApplyAddTelevisionEpisodeRequest( + RemoveItemsFromCollection request, + Collection collection) + { + var itemsToRemove = collection.MediaItems + .Filter(m => request.MediaItemIds.Contains(m.Id)) + .ToList(); + + itemsToRemove.ForEach(m => collection.MediaItems.Remove(m)); + + return itemsToRemove.Any() + ? _mediaCollectionRepository.Update(collection).ToUnit() + : Task.FromResult(Unit.Default); + } + + private Task> Validate( + RemoveItemsFromCollection request) => + CollectionMustExist(request); + + private Task> CollectionMustExist( + RemoveItemsFromCollection updateCollection) => + _mediaCollectionRepository.GetCollectionWithItems(updateCollection.MediaCollectionId) + .Map(v => v.ToValidation("Collection does not exist.")); + } +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromSimpleMediaCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromSimpleMediaCollection.cs deleted file mode 100644 index a3ea0249..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromSimpleMediaCollection.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using ErsatzTV.Core; -using LanguageExt; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public record RemoveItemsFromSimpleMediaCollection - (int MediaCollectionId) : MediatR.IRequest> - { - public List MovieIds { get; set; } = new(); - public List TelevisionShowIds { get; set; } = new(); - public List TelevisionSeasonIds { get; set; } = new(); - public List TelevisionEpisodeIds { get; set; } = new(); - } -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromSimpleMediaCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromSimpleMediaCollectionHandler.cs deleted file mode 100644 index d7342eba..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromSimpleMediaCollectionHandler.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using ErsatzTV.Core; -using ErsatzTV.Core.Domain; -using ErsatzTV.Core.Interfaces.Repositories; -using LanguageExt; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public class - RemoveItemsFromSimpleMediaCollectionHandler : MediatR.IRequestHandler< - RemoveItemsFromSimpleMediaCollection, - Either> - { - private readonly IMediaCollectionRepository _mediaCollectionRepository; - - public RemoveItemsFromSimpleMediaCollectionHandler( - IMediaCollectionRepository mediaCollectionRepository) => - _mediaCollectionRepository = mediaCollectionRepository; - - public Task> Handle( - RemoveItemsFromSimpleMediaCollection request, - CancellationToken cancellationToken) => - Validate(request) - .MapT(collection => ApplyAddTelevisionEpisodeRequest(request, collection)) - .Bind(v => v.ToEitherAsync()); - - private Task ApplyAddTelevisionEpisodeRequest( - RemoveItemsFromSimpleMediaCollection request, - SimpleMediaCollection collection) - { - var moviesToRemove = collection.Movies - .Filter(m => request.MovieIds.Contains(m.Id)) - .ToList(); - - moviesToRemove.ForEach(m => collection.Movies.Remove(m)); - - var showsToRemove = collection.TelevisionShows - .Filter(s => request.TelevisionShowIds.Contains(s.Id)) - .ToList(); - - showsToRemove.ForEach(s => collection.TelevisionShows.Remove(s)); - - var seasonsToRemove = collection.TelevisionSeasons - .Filter(s => request.TelevisionSeasonIds.Contains(s.Id)) - .ToList(); - - seasonsToRemove.ForEach(s => collection.TelevisionSeasons.Remove(s)); - - var episodesToRemove = collection.TelevisionEpisodes - .Filter(e => request.TelevisionEpisodeIds.Contains(e.Id)) - .ToList(); - - episodesToRemove.ForEach(e => collection.TelevisionEpisodes.Remove(e)); - - if (moviesToRemove.Any() || showsToRemove.Any() || seasonsToRemove.Any() || episodesToRemove.Any()) - { - return _mediaCollectionRepository.Update(collection).ToUnit(); - } - - return Task.FromResult(Unit.Default); - } - - private Task> Validate( - RemoveItemsFromSimpleMediaCollection request) => - SimpleMediaCollectionMustExist(request); - - private Task> SimpleMediaCollectionMustExist( - RemoveItemsFromSimpleMediaCollection updateSimpleMediaCollection) => - _mediaCollectionRepository.GetSimpleMediaCollectionWithItems(updateSimpleMediaCollection.MediaCollectionId) - .Map(v => v.ToValidation("SimpleMediaCollection does not exist.")); - } -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateCollection.cs new file mode 100644 index 00000000..9a2e8c58 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/UpdateCollection.cs @@ -0,0 +1,8 @@ +using ErsatzTV.Core; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public record UpdateCollection + (int CollectionId, string Name) : MediatR.IRequest>; +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionHandler.cs new file mode 100644 index 00000000..063bc8c0 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionHandler.cs @@ -0,0 +1,45 @@ +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public class UpdateCollectionHandler : MediatR.IRequestHandler> + { + private readonly IMediaCollectionRepository _mediaCollectionRepository; + + public UpdateCollectionHandler(IMediaCollectionRepository mediaCollectionRepository) => + _mediaCollectionRepository = mediaCollectionRepository; + + public Task> Handle( + UpdateCollection request, + CancellationToken cancellationToken) => + Validate(request) + .MapT(c => ApplyUpdateRequest(c, request)) + .Bind(v => v.ToEitherAsync()); + + private async Task ApplyUpdateRequest(Collection c, UpdateCollection update) + { + c.Name = update.Name; + await _mediaCollectionRepository.Update(c); + return Unit.Default; + } + + private async Task> + Validate(UpdateCollection request) => + (await CollectionMustExist(request), ValidateName(request)) + .Apply((collectionToUpdate, _) => collectionToUpdate); + + private Task> CollectionMustExist( + UpdateCollection updateCollection) => + _mediaCollectionRepository.Get(updateCollection.CollectionId) + .Map(v => v.ToValidation("Collection does not exist.")); + + private Validation ValidateName(UpdateCollection updateSimpleMediaCollection) => + updateSimpleMediaCollection.NotEmpty(c => c.Name) + .Bind(_ => updateSimpleMediaCollection.NotLongerThan(50)(c => c.Name)); + } +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateSimpleMediaCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateSimpleMediaCollection.cs deleted file mode 100644 index 55ceb9f4..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/UpdateSimpleMediaCollection.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErsatzTV.Core; -using LanguageExt; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public record UpdateSimpleMediaCollection - (int MediaCollectionId, string Name) : MediatR.IRequest>; -} diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateSimpleMediaCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateSimpleMediaCollectionHandler.cs deleted file mode 100644 index cab486ac..00000000 --- a/ErsatzTV.Application/MediaCollections/Commands/UpdateSimpleMediaCollectionHandler.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using ErsatzTV.Core; -using ErsatzTV.Core.Domain; -using ErsatzTV.Core.Interfaces.Repositories; -using LanguageExt; - -namespace ErsatzTV.Application.MediaCollections.Commands -{ - public class - UpdateSimpleMediaCollectionHandler : MediatR.IRequestHandler> - { - private readonly IMediaCollectionRepository _mediaCollectionRepository; - - public UpdateSimpleMediaCollectionHandler(IMediaCollectionRepository mediaCollectionRepository) => - _mediaCollectionRepository = mediaCollectionRepository; - - public Task> Handle( - UpdateSimpleMediaCollection request, - CancellationToken cancellationToken) => - Validate(request) - .MapT(c => ApplyUpdateRequest(c, request)) - .Bind(v => v.ToEitherAsync()); - - private async Task ApplyUpdateRequest(SimpleMediaCollection c, UpdateSimpleMediaCollection update) - { - c.Name = update.Name; - await _mediaCollectionRepository.Update(c); - return Unit.Default; - } - - private async Task> - Validate(UpdateSimpleMediaCollection request) => - (await SimpleMediaCollectionMustExist(request), ValidateName(request)) - .Apply((simpleMediaCollectionToUpdate, _) => simpleMediaCollectionToUpdate); - - private Task> SimpleMediaCollectionMustExist( - UpdateSimpleMediaCollection updateSimpleMediaCollection) => - _mediaCollectionRepository.GetSimpleMediaCollection(updateSimpleMediaCollection.MediaCollectionId) - .Map(v => v.ToValidation("SimpleMediaCollection does not exist.")); - - private Validation ValidateName(UpdateSimpleMediaCollection updateSimpleMediaCollection) => - updateSimpleMediaCollection.NotEmpty(c => c.Name) - .Bind(_ => updateSimpleMediaCollection.NotLongerThan(50)(c => c.Name)); - } -} diff --git a/ErsatzTV.Application/MediaCollections/Mapper.cs b/ErsatzTV.Application/MediaCollections/Mapper.cs index 59217405..9144d129 100644 --- a/ErsatzTV.Application/MediaCollections/Mapper.cs +++ b/ErsatzTV.Application/MediaCollections/Mapper.cs @@ -4,7 +4,7 @@ namespace ErsatzTV.Application.MediaCollections { internal static class Mapper { - internal static MediaCollectionViewModel ProjectToViewModel(MediaCollection mediaCollection) => - new(mediaCollection.Id, mediaCollection.Name); + internal static MediaCollectionViewModel ProjectToViewModel(Collection collection) => + new(collection.Id, collection.Name); } } diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetAllMediaCollections.cs b/ErsatzTV.Application/MediaCollections/Queries/GetAllCollections.cs similarity index 57% rename from ErsatzTV.Application/MediaCollections/Queries/GetAllMediaCollections.cs rename to ErsatzTV.Application/MediaCollections/Queries/GetAllCollections.cs index 678d5a80..92cbd842 100644 --- a/ErsatzTV.Application/MediaCollections/Queries/GetAllMediaCollections.cs +++ b/ErsatzTV.Application/MediaCollections/Queries/GetAllCollections.cs @@ -3,5 +3,5 @@ using MediatR; namespace ErsatzTV.Application.MediaCollections.Queries { - public record GetAllMediaCollections : IRequest>; + public record GetAllCollections : IRequest>; } diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetAllMediaCollectionsHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetAllCollectionsHandler.cs similarity index 71% rename from ErsatzTV.Application/MediaCollections/Queries/GetAllMediaCollectionsHandler.cs rename to ErsatzTV.Application/MediaCollections/Queries/GetAllCollectionsHandler.cs index 5e2a6898..0efde96e 100644 --- a/ErsatzTV.Application/MediaCollections/Queries/GetAllMediaCollectionsHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Queries/GetAllCollectionsHandler.cs @@ -9,15 +9,15 @@ using static ErsatzTV.Application.MediaCollections.Mapper; namespace ErsatzTV.Application.MediaCollections.Queries { - public class GetAllMediaCollectionsHandler : IRequestHandler> + public class GetAllCollectionsHandler : IRequestHandler> { private readonly IMediaCollectionRepository _mediaCollectionRepository; - public GetAllMediaCollectionsHandler(IMediaCollectionRepository mediaCollectionRepository) => + public GetAllCollectionsHandler(IMediaCollectionRepository mediaCollectionRepository) => _mediaCollectionRepository = mediaCollectionRepository; public Task> Handle( - GetAllMediaCollections request, + GetAllCollections request, CancellationToken cancellationToken) => _mediaCollectionRepository.GetAll().Map(list => list.Map(ProjectToViewModel).ToList()); } diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetAllSimpleMediaCollections.cs b/ErsatzTV.Application/MediaCollections/Queries/GetAllSimpleMediaCollections.cs deleted file mode 100644 index e8a9a87f..00000000 --- a/ErsatzTV.Application/MediaCollections/Queries/GetAllSimpleMediaCollections.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Collections.Generic; -using MediatR; - -namespace ErsatzTV.Application.MediaCollections.Queries -{ - public record GetAllSimpleMediaCollections : IRequest>; -} diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetAllSimpleMediaCollectionsHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetAllSimpleMediaCollectionsHandler.cs deleted file mode 100644 index 40b85740..00000000 --- a/ErsatzTV.Application/MediaCollections/Queries/GetAllSimpleMediaCollectionsHandler.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using ErsatzTV.Core.Interfaces.Repositories; -using MediatR; -using static ErsatzTV.Application.MediaCollections.Mapper; - -namespace ErsatzTV.Application.MediaCollections.Queries -{ - public class - GetAllSimpleMediaCollectionsHandler : IRequestHandler> - { - private readonly IMediaCollectionRepository _mediaCollectionRepository; - - public GetAllSimpleMediaCollectionsHandler(IMediaCollectionRepository mediaCollectionRepository) => - _mediaCollectionRepository = mediaCollectionRepository; - - public async Task> Handle( - GetAllSimpleMediaCollections request, - CancellationToken cancellationToken) => - (await _mediaCollectionRepository.GetSimpleMediaCollections()).Map(ProjectToViewModel).ToList(); - } -} diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetCollectionById.cs b/ErsatzTV.Application/MediaCollections/Queries/GetCollectionById.cs new file mode 100644 index 00000000..8d988eec --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Queries/GetCollectionById.cs @@ -0,0 +1,7 @@ +using LanguageExt; +using MediatR; + +namespace ErsatzTV.Application.MediaCollections.Queries +{ + public record GetCollectionById(int Id) : IRequest>; +} diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionByIdHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetCollectionByIdHandler.cs similarity index 66% rename from ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionByIdHandler.cs rename to ErsatzTV.Application/MediaCollections/Queries/GetCollectionByIdHandler.cs index a3a1a070..e6bbb55b 100644 --- a/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionByIdHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Queries/GetCollectionByIdHandler.cs @@ -8,18 +8,18 @@ using static ErsatzTV.Application.MediaCollections.Mapper; namespace ErsatzTV.Application.MediaCollections.Queries { public class - GetSimpleMediaCollectionByIdHandler : IRequestHandler> { private readonly IMediaCollectionRepository _mediaCollectionRepository; - public GetSimpleMediaCollectionByIdHandler(IMediaCollectionRepository mediaCollectionRepository) => + public GetCollectionByIdHandler(IMediaCollectionRepository mediaCollectionRepository) => _mediaCollectionRepository = mediaCollectionRepository; public Task> Handle( - GetSimpleMediaCollectionById request, + GetCollectionById request, CancellationToken cancellationToken) => - _mediaCollectionRepository.GetSimpleMediaCollection(request.Id) + _mediaCollectionRepository.Get(request.Id) .MapT(ProjectToViewModel); } } diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionItems.cs b/ErsatzTV.Application/MediaCollections/Queries/GetCollectionItems.cs similarity index 61% rename from ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionItems.cs rename to ErsatzTV.Application/MediaCollections/Queries/GetCollectionItems.cs index ed0f21f7..35318b83 100644 --- a/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionItems.cs +++ b/ErsatzTV.Application/MediaCollections/Queries/GetCollectionItems.cs @@ -5,5 +5,5 @@ using MediatR; namespace ErsatzTV.Application.MediaCollections.Queries { - public record GetSimpleMediaCollectionItems(int Id) : IRequest>>; + public record GetCollectionItems(int Id) : IRequest>>; } diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionItemsHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetCollectionItemsHandler.cs similarity index 67% rename from ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionItemsHandler.cs rename to ErsatzTV.Application/MediaCollections/Queries/GetCollectionItemsHandler.cs index 92e918fa..11740f0c 100644 --- a/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionItemsHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Queries/GetCollectionItemsHandler.cs @@ -9,18 +9,18 @@ using static ErsatzTV.Application.MediaItems.Mapper; namespace ErsatzTV.Application.MediaCollections.Queries { - public class GetSimpleMediaCollectionItemsHandler : IRequestHandler>> { private readonly IMediaCollectionRepository _mediaCollectionRepository; - public GetSimpleMediaCollectionItemsHandler(IMediaCollectionRepository mediaCollectionRepository) => + public GetCollectionItemsHandler(IMediaCollectionRepository mediaCollectionRepository) => _mediaCollectionRepository = mediaCollectionRepository; public Task>> Handle( - GetSimpleMediaCollectionItems request, + GetCollectionItems request, CancellationToken cancellationToken) => - _mediaCollectionRepository.GetSimpleMediaCollectionItems(request.Id) + _mediaCollectionRepository.GetItems(request.Id) .MapT(mediaItems => mediaItems.Map(ProjectToViewModel)); } } diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionById.cs b/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionById.cs deleted file mode 100644 index 937e5e5c..00000000 --- a/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionById.cs +++ /dev/null @@ -1,7 +0,0 @@ -using LanguageExt; -using MediatR; - -namespace ErsatzTV.Application.MediaCollections.Queries -{ - public record GetSimpleMediaCollectionById(int Id) : IRequest>; -} diff --git a/ErsatzTV.Application/MediaItems/Mapper.cs b/ErsatzTV.Application/MediaItems/Mapper.cs index 59fb7c5f..ef60cf27 100644 --- a/ErsatzTV.Application/MediaItems/Mapper.cs +++ b/ErsatzTV.Application/MediaItems/Mapper.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using ErsatzTV.Core.Domain; namespace ErsatzTV.Application.MediaItems @@ -7,31 +6,28 @@ namespace ErsatzTV.Application.MediaItems internal static class Mapper { internal static MediaItemViewModel ProjectToViewModel(MediaItem mediaItem) => - new( - mediaItem.Id, - mediaItem.MediaSourceId, - mediaItem.Path); + new(mediaItem.Id, mediaItem.LibraryPathId); internal static MediaItemSearchResultViewModel ProjectToSearchViewModel(MediaItem mediaItem) => mediaItem switch { - TelevisionEpisodeMediaItem e => ProjectToSearchViewModel(e), - MovieMediaItem m => ProjectToSearchViewModel(m), + Episode e => ProjectToSearchViewModel(e), + Movie m => ProjectToSearchViewModel(m), _ => throw new ArgumentOutOfRangeException() }; - private static MediaItemSearchResultViewModel ProjectToSearchViewModel(TelevisionEpisodeMediaItem mediaItem) => + private static MediaItemSearchResultViewModel ProjectToSearchViewModel(Episode mediaItem) => new( mediaItem.Id, - GetSourceName(mediaItem.Source), + GetLibraryName(mediaItem), "TV Show", GetDisplayTitle(mediaItem), GetDisplayDuration(mediaItem)); - private static MediaItemSearchResultViewModel ProjectToSearchViewModel(MovieMediaItem mediaItem) => + private static MediaItemSearchResultViewModel ProjectToSearchViewModel(Movie mediaItem) => new( mediaItem.Id, - GetSourceName(mediaItem.Source), + GetLibraryName(mediaItem), "Movie", GetDisplayTitle(mediaItem), GetDisplayDuration(mediaItem)); @@ -40,23 +36,41 @@ namespace ErsatzTV.Application.MediaItems private static string GetDisplayTitle(MediaItem mediaItem) => mediaItem switch { - TelevisionEpisodeMediaItem e => e.Metadata != null - ? $"{e.Metadata.Title} - s{e.Metadata.Season:00}e{e.Metadata.Episode:00}" - : Path.GetFileName(e.Path), - MovieMediaItem m => m.Metadata?.Title ?? Path.GetFileName(m.Path), + Episode e => e.EpisodeMetadata.HeadOrNone() + .Map(em => $"{em.Title} - s{e.Season.SeasonNumber:00}e{e.EpisodeNumber:00}") + .IfNone("[unknown episode]"), + Movie m => m.MovieMetadata.HeadOrNone().Map(mm => mm.Title).IfNone("[unknown movie]"), _ => string.Empty }; - private static string GetDisplayDuration(MediaItem mediaItem) => - string.Format( - mediaItem.Statistics.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}", - mediaItem.Statistics.Duration); - - private static string GetSourceName(MediaSource source) => - source switch + private static string GetDisplayDuration(MediaItem mediaItem) + { + MediaVersion version = mediaItem switch { - LocalMediaSource lms => lms.Folder, - _ => source.Name + Movie m => m.MediaVersions.Head(), + Episode e => e.MediaVersions.Head(), + _ => throw new ArgumentOutOfRangeException(nameof(mediaItem)) }; + + return string.Format( + version.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}", + version.Duration); + } + + // TODO: fix this when search is reimplemented + private static string GetLibraryName(MediaItem item) => + "Library Name"; + + public static NamedMediaItemViewModel ProjectToViewModel(Show show) => + new(show.Id, show.ShowMetadata.HeadOrNone().Map(sm => $"{sm?.Title} ({sm?.Year})").IfNone("???")); + + public static NamedMediaItemViewModel ProjectToViewModel(Season season) => + new(season.Id, $"{ShowTitle(season)} ({SeasonDescription(season)})"); + + private static string ShowTitle(Season season) => + season.Show.ShowMetadata.HeadOrNone().Map(sm => sm.Title).IfNone("???"); + + private static string SeasonDescription(Season season) => + season.SeasonNumber == 0 ? "Specials" : $"Season {season.SeasonNumber}"; } } diff --git a/ErsatzTV.Application/MediaItems/MediaItemViewModel.cs b/ErsatzTV.Application/MediaItems/MediaItemViewModel.cs index 108127c8..b2cc42d2 100644 --- a/ErsatzTV.Application/MediaItems/MediaItemViewModel.cs +++ b/ErsatzTV.Application/MediaItems/MediaItemViewModel.cs @@ -1,4 +1,4 @@ namespace ErsatzTV.Application.MediaItems { - public record MediaItemViewModel(int Id, int MediaSourceId, string Path); + public record MediaItemViewModel(int Id, int LibraryPathId); } diff --git a/ErsatzTV.Application/MediaItems/NamedMediaItemViewModel.cs b/ErsatzTV.Application/MediaItems/NamedMediaItemViewModel.cs new file mode 100644 index 00000000..8af3f337 --- /dev/null +++ b/ErsatzTV.Application/MediaItems/NamedMediaItemViewModel.cs @@ -0,0 +1,4 @@ +namespace ErsatzTV.Application.MediaItems +{ + public record NamedMediaItemViewModel(int MediaItemId, string Name); +} diff --git a/ErsatzTV.Application/MediaSources/Commands/CreateLocalMediaSource.cs b/ErsatzTV.Application/MediaSources/Commands/CreateLocalMediaSource.cs deleted file mode 100644 index 49bb842d..00000000 --- a/ErsatzTV.Application/MediaSources/Commands/CreateLocalMediaSource.cs +++ /dev/null @@ -1,10 +0,0 @@ -using ErsatzTV.Core; -using ErsatzTV.Core.Domain; -using LanguageExt; -using MediatR; - -namespace ErsatzTV.Application.MediaSources.Commands -{ - public record CreateLocalMediaSource - (string Name, MediaType MediaType, string Folder) : IRequest>; -} diff --git a/ErsatzTV.Application/MediaSources/Commands/CreateLocalMediaSourceHandler.cs b/ErsatzTV.Application/MediaSources/Commands/CreateLocalMediaSourceHandler.cs deleted file mode 100644 index da307a55..00000000 --- a/ErsatzTV.Application/MediaSources/Commands/CreateLocalMediaSourceHandler.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using ErsatzTV.Core; -using ErsatzTV.Core.Domain; -using ErsatzTV.Core.Interfaces.Repositories; -using LanguageExt; -using MediatR; -using static ErsatzTV.Application.MediaSources.Mapper; -using static LanguageExt.Prelude; - -namespace ErsatzTV.Application.MediaSources.Commands -{ - public class CreateLocalMediaSourceHandler : IRequestHandler> - { - private readonly IMediaSourceRepository _mediaSourceRepository; - - public CreateLocalMediaSourceHandler(IMediaSourceRepository mediaSourceRepository) => - _mediaSourceRepository = mediaSourceRepository; - - public Task> Handle( - CreateLocalMediaSource request, - CancellationToken cancellationToken) => - Validate(request).MapT(PersistLocalMediaSource).Bind(v => v.ToEitherAsync()); - - private Task PersistLocalMediaSource(LocalMediaSource c) => - _mediaSourceRepository.Add(c).Map(ProjectToViewModel); - - private async Task> Validate(CreateLocalMediaSource request) => - (await ValidateName(request), await ValidateFolder(request)) - .Apply( - (name, folder) => - new LocalMediaSource - { - Name = name, - MediaType = request.MediaType, - Folder = folder - }); - - private async Task> ValidateName(CreateLocalMediaSource request) - { - List allNames = await _mediaSourceRepository.GetAll() - .Map(list => list.Map(c => c.Name).ToList()); - - Validation result1 = request.NotEmpty(c => c.Name) - .Bind(_ => request.NotLongerThan(50)(c => c.Name)); - - var result2 = Optional(request.Name) - .Filter(name => !allNames.Contains(name)) - .ToValidation("Media source name must be unique"); - - return (result1, result2).Apply((_, _) => request.Name); - } - - private async Task> ValidateFolder(CreateLocalMediaSource request) - { - List allFolders = await _mediaSourceRepository.GetAll() - .Map(list => list.OfType().Map(c => c.Folder).ToList()); - - - return Optional(request.Folder) - .Filter(folder => allFolders.ForAll(f => !AreSubPaths(f, folder))) - .ToValidation("Folder must not belong to another media source"); - } - - private static bool AreSubPaths(string path1, string path2) - { - string one = path1 + Path.DirectorySeparatorChar; - string two = path2 + Path.DirectorySeparatorChar; - return one == two || one.StartsWith(two, StringComparison.OrdinalIgnoreCase) || - two.StartsWith(one, StringComparison.OrdinalIgnoreCase); - } - } -} diff --git a/ErsatzTV.Application/MediaSources/Commands/DeleteLocalMediaSource.cs b/ErsatzTV.Application/MediaSources/Commands/DeleteLocalMediaSource.cs deleted file mode 100644 index 8455a4c6..00000000 --- a/ErsatzTV.Application/MediaSources/Commands/DeleteLocalMediaSource.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; -using ErsatzTV.Core; -using LanguageExt; -using MediatR; - -namespace ErsatzTV.Application.MediaSources.Commands -{ - public record DeleteLocalMediaSource(int LocalMediaSourceId) : IRequest>; -} diff --git a/ErsatzTV.Application/MediaSources/Commands/DeleteLocalMediaSourceHandler.cs b/ErsatzTV.Application/MediaSources/Commands/DeleteLocalMediaSourceHandler.cs deleted file mode 100644 index 26eff7b3..00000000 --- a/ErsatzTV.Application/MediaSources/Commands/DeleteLocalMediaSourceHandler.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using ErsatzTV.Core; -using ErsatzTV.Core.Domain; -using ErsatzTV.Core.Interfaces.Repositories; -using LanguageExt; -using MediatR; - -namespace ErsatzTV.Application.MediaSources.Commands -{ - public class - DeleteLocalMediaSourceHandler : IRequestHandler> - { - private readonly IMediaSourceRepository _mediaSourceRepository; - - public DeleteLocalMediaSourceHandler(IMediaSourceRepository mediaSourceRepository) => - _mediaSourceRepository = mediaSourceRepository; - - public async Task> Handle( - DeleteLocalMediaSource request, - CancellationToken cancellationToken) => - (await MediaSourceMustExist(request)) - .Map(DoDeletion) - .ToEither(); - - private async Task DoDeletion(LocalMediaSource mediaSource) => - await _mediaSourceRepository.Delete(mediaSource.Id); - - private async Task> MediaSourceMustExist( - DeleteLocalMediaSource deleteMediaSource) => - (await _mediaSourceRepository.Get(deleteMediaSource.LocalMediaSourceId)) - .OfType() - .HeadOrNone() - .ToValidation( - $"Local media source {deleteMediaSource.LocalMediaSourceId} does not exist."); - } -} diff --git a/ErsatzTV.Application/MediaSources/Commands/ScanLocalLibraryHandler.cs b/ErsatzTV.Application/MediaSources/Commands/ScanLocalLibraryHandler.cs new file mode 100644 index 00000000..81fc46df --- /dev/null +++ b/ErsatzTV.Application/MediaSources/Commands/ScanLocalLibraryHandler.cs @@ -0,0 +1,114 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Locking; +using ErsatzTV.Core.Interfaces.Metadata; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using Microsoft.Extensions.Logging; +using Unit = LanguageExt.Unit; + +namespace ErsatzTV.Application.MediaSources.Commands +{ + public class ScanLocalLibraryHandler : IRequestHandler>, + IRequestHandler> + { + private readonly IConfigElementRepository _configElementRepository; + private readonly IEntityLocker _entityLocker; + private readonly ILibraryRepository _libraryRepository; + private readonly ILogger _logger; + private readonly IMovieFolderScanner _movieFolderScanner; + private readonly ITelevisionFolderScanner _televisionFolderScanner; + + public ScanLocalLibraryHandler( + ILibraryRepository libraryRepository, + IConfigElementRepository configElementRepository, + IMovieFolderScanner movieFolderScanner, + ITelevisionFolderScanner televisionFolderScanner, + IEntityLocker entityLocker, + ILogger logger) + { + _libraryRepository = libraryRepository; + _configElementRepository = configElementRepository; + _movieFolderScanner = movieFolderScanner; + _televisionFolderScanner = televisionFolderScanner; + _entityLocker = entityLocker; + _logger = logger; + } + + public Task> Handle( + ForceScanLocalLibrary request, + CancellationToken cancellationToken) => Handle(request); + + public Task> Handle( + ScanLocalLibraryIfNeeded request, + CancellationToken cancellationToken) => Handle(request); + + private Task> + Handle(IScanLocalLibrary request) => + Validate(request) + .MapT(parameters => PerformScan(parameters).Map(_ => parameters.LocalLibrary.Name)) + .Bind(v => v.ToEitherAsync()); + + private async Task PerformScan(RequestParameters parameters) + { + (LocalLibrary localLibrary, string ffprobePath, bool forceScan) = parameters; + + var lastScan = new DateTimeOffset(localLibrary.LastScan ?? DateTime.MinValue, TimeSpan.Zero); + if (forceScan || lastScan < DateTimeOffset.Now - TimeSpan.FromHours(6)) + { + foreach (LibraryPath libraryPath in localLibrary.Paths) + { + switch (localLibrary.MediaKind) + { + case LibraryMediaKind.Movies: + await _movieFolderScanner.ScanFolder(libraryPath, ffprobePath); + break; + case LibraryMediaKind.Shows: + await _televisionFolderScanner.ScanFolder(libraryPath, ffprobePath); + break; + } + } + + localLibrary.LastScan = DateTime.UtcNow; + await _libraryRepository.UpdateLastScan(localLibrary); + } + else + { + _logger.LogDebug( + "Skipping unforced scan of library {Name}", + localLibrary.Name); + } + + _entityLocker.UnlockLibrary(localLibrary.Id); + return Unit.Default; + } + + private async Task> Validate(IScanLocalLibrary request) => + (await LocalLibraryMustExist(request), await ValidateFFprobePath()) + .Apply( + (library, ffprobePath) => new RequestParameters( + library, + ffprobePath, + request.ForceScan)); + + private Task> LocalLibraryMustExist( + IScanLocalLibrary request) => + _libraryRepository.Get(request.LibraryId) + .Map(maybeLibrary => maybeLibrary.Map(ms => ms as LocalLibrary)) + .Map(v => v.ToValidation($"Local library {request.LibraryId} does not exist.")); + + private Task> ValidateFFprobePath() => + _configElementRepository.GetValue(ConfigElementKey.FFprobePath) + .FilterT(File.Exists) + .Map( + ffprobePath => + ffprobePath.ToValidation("FFprobe path does not exist on the file system")); + + private record RequestParameters(LocalLibrary LocalLibrary, string FFprobePath, bool ForceScan); + } +} diff --git a/ErsatzTV.Application/MediaSources/Commands/ScanLocalMediaSource.cs b/ErsatzTV.Application/MediaSources/Commands/ScanLocalMediaSource.cs index c2d46be0..da4999e1 100644 --- a/ErsatzTV.Application/MediaSources/Commands/ScanLocalMediaSource.cs +++ b/ErsatzTV.Application/MediaSources/Commands/ScanLocalMediaSource.cs @@ -4,18 +4,18 @@ using MediatR; namespace ErsatzTV.Application.MediaSources.Commands { - public interface IScanLocalMediaSource : IRequest>, IBackgroundServiceRequest + public interface IScanLocalLibrary : IRequest>, IBackgroundServiceRequest { - int MediaSourceId { get; } + int LibraryId { get; } bool ForceScan { get; } } - public record ScanLocalMediaSourceIfNeeded(int MediaSourceId) : IScanLocalMediaSource + public record ScanLocalLibraryIfNeeded(int LibraryId) : IScanLocalLibrary { public bool ForceScan => false; } - public record ForceScanLocalMediaSource(int MediaSourceId) : IScanLocalMediaSource + public record ForceScanLocalLibrary(int LibraryId) : IScanLocalLibrary { public bool ForceScan => true; } diff --git a/ErsatzTV.Application/MediaSources/Commands/ScanLocalMediaSourceHandler.cs b/ErsatzTV.Application/MediaSources/Commands/ScanLocalMediaSourceHandler.cs deleted file mode 100644 index 4c387ee8..00000000 --- a/ErsatzTV.Application/MediaSources/Commands/ScanLocalMediaSourceHandler.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using ErsatzTV.Core; -using ErsatzTV.Core.Domain; -using ErsatzTV.Core.Interfaces.Locking; -using ErsatzTV.Core.Interfaces.Metadata; -using ErsatzTV.Core.Interfaces.Repositories; -using LanguageExt; -using MediatR; -using Microsoft.Extensions.Logging; -using Unit = LanguageExt.Unit; - -namespace ErsatzTV.Application.MediaSources.Commands -{ - public class ScanLocalMediaSourceHandler : IRequestHandler>, - IRequestHandler> - { - private readonly IConfigElementRepository _configElementRepository; - private readonly IEntityLocker _entityLocker; - private readonly ILogger _logger; - private readonly IMediaSourceRepository _mediaSourceRepository; - private readonly IMovieFolderScanner _movieFolderScanner; - private readonly ITelevisionFolderScanner _televisionFolderScanner; - - public ScanLocalMediaSourceHandler( - IMediaSourceRepository mediaSourceRepository, - IConfigElementRepository configElementRepository, - IMovieFolderScanner movieFolderScanner, - ITelevisionFolderScanner televisionFolderScanner, - IEntityLocker entityLocker, - ILogger logger) - { - _mediaSourceRepository = mediaSourceRepository; - _configElementRepository = configElementRepository; - _movieFolderScanner = movieFolderScanner; - _televisionFolderScanner = televisionFolderScanner; - _entityLocker = entityLocker; - _logger = logger; - } - - public Task> Handle( - ForceScanLocalMediaSource request, - CancellationToken cancellationToken) => - Handle((IScanLocalMediaSource) request, cancellationToken); - - public Task> Handle( - ScanLocalMediaSourceIfNeeded request, - CancellationToken cancellationToken) => - Handle((IScanLocalMediaSource) request, cancellationToken); - - private Task> - Handle(IScanLocalMediaSource request, CancellationToken cancellationToken) => - Validate(request) - .MapT(parameters => PerformScan(parameters).Map(_ => parameters.LocalMediaSource.Folder)) - .Bind(v => v.ToEitherAsync()); - - private async Task PerformScan(RequestParameters parameters) - { - DateTimeOffset lastScan = parameters.LocalMediaSource.LastScan ?? DateTime.MinValue; - if (parameters.ForceScan || lastScan < DateTimeOffset.Now - TimeSpan.FromHours(6)) - { - switch (parameters.LocalMediaSource.MediaType) - { - case MediaType.Movie: - await _movieFolderScanner.ScanFolder(parameters.LocalMediaSource, parameters.FFprobePath); - break; - case MediaType.TvShow: - await _televisionFolderScanner.ScanFolder(parameters.LocalMediaSource, parameters.FFprobePath); - break; - } - - parameters.LocalMediaSource.LastScan = DateTimeOffset.Now; - await _mediaSourceRepository.Update(parameters.LocalMediaSource); - } - else - { - _logger.LogDebug( - "Skipping unforced scan of media source {Folder}", - parameters.LocalMediaSource.Folder); - } - - _entityLocker.UnlockMediaSource(parameters.LocalMediaSource.Id); - return Unit.Default; - } - - private async Task> Validate(IScanLocalMediaSource request) => - (await LocalMediaSourceMustExist(request), await ValidateFFprobePath()) - .Apply( - (localMediaSource, ffprobePath) => new RequestParameters( - localMediaSource, - ffprobePath, - request.ForceScan)); - - private Task> LocalMediaSourceMustExist( - IScanLocalMediaSource request) => - _mediaSourceRepository.Get(request.MediaSourceId) - .Map(maybeMediaSource => maybeMediaSource.Map(ms => ms as LocalMediaSource)) - .Map(v => v.ToValidation($"Local media source {request.MediaSourceId} does not exist.")); - - private Task> ValidateFFprobePath() => - _configElementRepository.GetValue(ConfigElementKey.FFprobePath) - .FilterT(File.Exists) - .Map( - ffprobePath => - ffprobePath.ToValidation("FFprobe path does not exist on the file system")); - - private record RequestParameters(LocalMediaSource LocalMediaSource, string FFprobePath, bool ForceScan); - } -} diff --git a/ErsatzTV.Application/MediaSources/LocalMediaSourceViewModel.cs b/ErsatzTV.Application/MediaSources/LocalMediaSourceViewModel.cs index ad16412a..96111acf 100644 --- a/ErsatzTV.Application/MediaSources/LocalMediaSourceViewModel.cs +++ b/ErsatzTV.Application/MediaSources/LocalMediaSourceViewModel.cs @@ -1,7 +1,4 @@ -using ErsatzTV.Core.Domain; - -namespace ErsatzTV.Application.MediaSources +namespace ErsatzTV.Application.MediaSources { - public record LocalMediaSourceViewModel(int Id, string Name, string Folder) - : MediaSourceViewModel(Id, Name, MediaSourceType.Local); + public record LocalMediaSourceViewModel(int Id) : MediaSourceViewModel(Id, "Local"); } diff --git a/ErsatzTV.Application/MediaSources/Mapper.cs b/ErsatzTV.Application/MediaSources/Mapper.cs index df68b4e3..e586c663 100644 --- a/ErsatzTV.Application/MediaSources/Mapper.cs +++ b/ErsatzTV.Application/MediaSources/Mapper.cs @@ -1,7 +1,5 @@ using System; -using System.Linq; using ErsatzTV.Core.Domain; -using static LanguageExt.Prelude; namespace ErsatzTV.Application.MediaSources { @@ -10,15 +8,9 @@ namespace ErsatzTV.Application.MediaSources internal static MediaSourceViewModel ProjectToViewModel(MediaSource mediaSource) => mediaSource switch { - LocalMediaSource lms => new LocalMediaSourceViewModel(lms.Id, lms.Name, lms.Folder), - PlexMediaSource pms => ProjectToViewModel(pms), + LocalMediaSource lms => new LocalMediaSourceViewModel(lms.Id), + PlexMediaSource pms => Plex.Mapper.ProjectToViewModel(pms), _ => throw new NotSupportedException($"Unsupported media source {mediaSource.GetType().Name}") }; - - internal static PlexMediaSourceViewModel ProjectToViewModel(PlexMediaSource plexMediaSource) => - new( - plexMediaSource.Id, - plexMediaSource.Name, - Optional(plexMediaSource.Connections.SingleOrDefault(c => c.IsActive)).Match(c => c.Uri, string.Empty)); } } diff --git a/ErsatzTV.Application/MediaSources/MediaSourceViewModel.cs b/ErsatzTV.Application/MediaSources/MediaSourceViewModel.cs index ab44f694..036a23d7 100644 --- a/ErsatzTV.Application/MediaSources/MediaSourceViewModel.cs +++ b/ErsatzTV.Application/MediaSources/MediaSourceViewModel.cs @@ -1,6 +1,4 @@ -using ErsatzTV.Core.Domain; - -namespace ErsatzTV.Application.MediaSources +namespace ErsatzTV.Application.MediaSources { - public record MediaSourceViewModel(int Id, string Name, MediaSourceType SourceType); + public record MediaSourceViewModel(int Id, string Name); } diff --git a/ErsatzTV.Application/MediaSources/PlexMediaSourceViewModel.cs b/ErsatzTV.Application/MediaSources/PlexMediaSourceViewModel.cs deleted file mode 100644 index aa952a24..00000000 --- a/ErsatzTV.Application/MediaSources/PlexMediaSourceViewModel.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErsatzTV.Core.Domain; - -namespace ErsatzTV.Application.MediaSources -{ - public record PlexMediaSourceViewModel(int Id, string Name, string Address) : MediaSourceViewModel( - Id, - Name, - MediaSourceType.Plex); -} diff --git a/ErsatzTV.Application/Movies/Mapper.cs b/ErsatzTV.Application/Movies/Mapper.cs index 4656151f..59e83e29 100644 --- a/ErsatzTV.Application/Movies/Mapper.cs +++ b/ErsatzTV.Application/Movies/Mapper.cs @@ -1,10 +1,20 @@ -using ErsatzTV.Core.Domain; +using System.Linq; +using ErsatzTV.Core.Domain; +using static LanguageExt.Prelude; namespace ErsatzTV.Application.Movies { internal static class Mapper { - internal static MovieViewModel ProjectToViewModel(MovieMediaItem movie) => - new(movie.Metadata.Title, movie.Metadata.Year?.ToString(), movie.Metadata.Plot, movie.Poster); + internal static MovieViewModel ProjectToViewModel(Movie movie) + { + MovieMetadata metadata = Optional(movie.MovieMetadata).Flatten().Head(); + return new MovieViewModel( + metadata.Title, + metadata.Year?.ToString(), + metadata.Plot, + Optional(metadata.Artwork.FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Poster)) + .Match(a => a.Path, string.Empty)); + } } } diff --git a/ErsatzTV.Application/Playouts/Mapper.cs b/ErsatzTV.Application/Playouts/Mapper.cs index f2167f72..c2c9b20e 100644 --- a/ErsatzTV.Application/Playouts/Mapper.cs +++ b/ErsatzTV.Application/Playouts/Mapper.cs @@ -1,4 +1,4 @@ -using System.IO; +using System; using ErsatzTV.Core.Domain; namespace ErsatzTV.Application.Playouts @@ -13,7 +13,10 @@ namespace ErsatzTV.Application.Playouts playout.ProgramSchedulePlayoutType); internal static PlayoutItemViewModel ProjectToViewModel(PlayoutItem playoutItem) => - new(GetDisplayTitle(playoutItem.MediaItem), playoutItem.Start, GetDisplayDuration(playoutItem.MediaItem)); + new( + GetDisplayTitle(playoutItem.MediaItem), + playoutItem.StartOffset, + GetDisplayDuration(playoutItem.MediaItem)); private static PlayoutChannelViewModel Project(Channel channel) => new(channel.Id, channel.Number, channel.Name); @@ -24,16 +27,25 @@ namespace ErsatzTV.Application.Playouts private static string GetDisplayTitle(MediaItem mediaItem) => mediaItem switch { - TelevisionEpisodeMediaItem e => e.Metadata != null - ? $"{e.Metadata.Title} - s{e.Metadata.Season:00}e{e.Metadata.Episode:00}" - : Path.GetFileName(e.Path), - MovieMediaItem m => m.Metadata?.Title ?? Path.GetFileName(m.Path), + Episode e => e.EpisodeMetadata.HeadOrNone() + .Map(em => $"{em.Title} - s{e.Season.SeasonNumber:00}e{e.EpisodeNumber:00}") + .IfNone("[unknown episode]"), + Movie m => m.MovieMetadata.HeadOrNone().Map(mm => mm.Title).IfNone("[unknown movie]"), _ => string.Empty }; - private static string GetDisplayDuration(MediaItem mediaItem) => - string.Format( - mediaItem.Statistics.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}", - mediaItem.Statistics.Duration); + private static string GetDisplayDuration(MediaItem mediaItem) + { + MediaVersion version = mediaItem switch + { + Movie m => m.MediaVersions.Head(), + Episode e => e.MediaVersions.Head(), + _ => throw new ArgumentOutOfRangeException(nameof(mediaItem)) + }; + + return string.Format( + version.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}", + version.Duration); + } } } diff --git a/ErsatzTV.Application/MediaSources/Commands/StartPlexPinFlow.cs b/ErsatzTV.Application/Plex/Commands/StartPlexPinFlow.cs similarity index 72% rename from ErsatzTV.Application/MediaSources/Commands/StartPlexPinFlow.cs rename to ErsatzTV.Application/Plex/Commands/StartPlexPinFlow.cs index 1aa6da31..5c5eebbc 100644 --- a/ErsatzTV.Application/MediaSources/Commands/StartPlexPinFlow.cs +++ b/ErsatzTV.Application/Plex/Commands/StartPlexPinFlow.cs @@ -2,7 +2,7 @@ using LanguageExt; using MediatR; -namespace ErsatzTV.Application.MediaSources.Commands +namespace ErsatzTV.Application.Plex.Commands { public record StartPlexPinFlow : IRequest>; } diff --git a/ErsatzTV.Application/MediaSources/Commands/StartPlexPinFlowHandler.cs b/ErsatzTV.Application/Plex/Commands/StartPlexPinFlowHandler.cs similarity index 96% rename from ErsatzTV.Application/MediaSources/Commands/StartPlexPinFlowHandler.cs rename to ErsatzTV.Application/Plex/Commands/StartPlexPinFlowHandler.cs index 7a3cf296..86989701 100644 --- a/ErsatzTV.Application/MediaSources/Commands/StartPlexPinFlowHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/StartPlexPinFlowHandler.cs @@ -7,7 +7,7 @@ using LanguageExt; using MediatR; using static LanguageExt.Prelude; -namespace ErsatzTV.Application.MediaSources.Commands +namespace ErsatzTV.Application.Plex.Commands { public class StartPlexPinFlowHandler : IRequestHandler> { diff --git a/ErsatzTV.Application/MediaSources/Commands/SynchronizePlexLibraries.cs b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraries.cs similarity index 75% rename from ErsatzTV.Application/MediaSources/Commands/SynchronizePlexLibraries.cs rename to ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraries.cs index 38aba8f5..16bc46a5 100644 --- a/ErsatzTV.Application/MediaSources/Commands/SynchronizePlexLibraries.cs +++ b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraries.cs @@ -1,7 +1,7 @@ using ErsatzTV.Core; using LanguageExt; -namespace ErsatzTV.Application.MediaSources.Commands +namespace ErsatzTV.Application.Plex.Commands { public record SynchronizePlexLibraries(int PlexMediaSourceId) : MediatR.IRequest>; } diff --git a/ErsatzTV.Application/MediaSources/Commands/SynchronizePlexLibrariesHandler.cs b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibrariesHandler.cs similarity index 90% rename from ErsatzTV.Application/MediaSources/Commands/SynchronizePlexLibrariesHandler.cs rename to ErsatzTV.Application/Plex/Commands/SynchronizePlexLibrariesHandler.cs index 398e0ad4..77d241b3 100644 --- a/ErsatzTV.Application/MediaSources/Commands/SynchronizePlexLibrariesHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibrariesHandler.cs @@ -10,7 +10,7 @@ using ErsatzTV.Core.Plex; using LanguageExt; using Microsoft.Extensions.Logging; -namespace ErsatzTV.Application.MediaSources.Commands +namespace ErsatzTV.Application.Plex.Commands { public class SynchronizePlexLibrariesHandler : MediatR.IRequestHandler> @@ -51,7 +51,7 @@ namespace ErsatzTV.Application.MediaSources.Commands private Validation MediaSourceMustHaveActiveConnection( PlexMediaSource plexMediaSource) { - Option maybeConnection = + Option maybeConnection = plexMediaSource.Connections.SingleOrDefault(c => c.IsActive); return maybeConnection.Map(connection => new ConnectionParameters(plexMediaSource, connection)) .ToValidation("Plex media source requires an active connection"); @@ -68,14 +68,14 @@ namespace ErsatzTV.Application.MediaSources.Commands private async Task SynchronizeLibraries(ConnectionParameters connectionParameters) { - Either> maybeLibraries = await _plexServerApiClient.GetLibraries( + Either> maybeLibraries = await _plexServerApiClient.GetLibraries( connectionParameters.ActiveConnection, connectionParameters.PlexServerAuthToken); await maybeLibraries.Match( libraries => { - List existing = connectionParameters.PlexMediaSource.Libraries; + var existing = connectionParameters.PlexMediaSource.Libraries.OfType().ToList(); var toAdd = libraries.Filter(library => existing.All(l => l.Key != library.Key)).ToList(); var toRemove = existing.Filter(library => libraries.All(l => l.Key != library.Key)).ToList(); existing.AddRange(toAdd); @@ -87,7 +87,7 @@ namespace ErsatzTV.Application.MediaSources.Commands { _logger.LogWarning( "Unable to synchronize libraries from plex server {PlexServer}: {Error}", - connectionParameters.PlexMediaSource.Name, + connectionParameters.PlexMediaSource.ServerName, error.Value); return Task.CompletedTask; @@ -98,7 +98,7 @@ namespace ErsatzTV.Application.MediaSources.Commands private record ConnectionParameters( PlexMediaSource PlexMediaSource, - PlexMediaSourceConnection ActiveConnection) + PlexConnection ActiveConnection) { public PlexServerAuthToken PlexServerAuthToken { get; set; } } diff --git a/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryById.cs b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryById.cs new file mode 100644 index 00000000..46e11ab5 --- /dev/null +++ b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryById.cs @@ -0,0 +1,25 @@ +using ErsatzTV.Core; +using LanguageExt; +using MediatR; + +namespace ErsatzTV.Application.Plex.Commands +{ + public interface ISynchronizePlexLibraryById : IRequest>, IBackgroundServiceRequest + { + int PlexMediaSourceId { get; } + int PlexLibraryId { get; } + bool ForceScan { get; } + } + + public record SynchronizePlexLibraryByIdIfNeeded + (int PlexMediaSourceId, int PlexLibraryId) : ISynchronizePlexLibraryById + { + public bool ForceScan => false; + } + + public record ForceSynchronizePlexLibraryById + (int PlexMediaSourceId, int PlexLibraryId) : ISynchronizePlexLibraryById + { + public bool ForceScan => true; + } +} diff --git a/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryByIdHandler.cs b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryByIdHandler.cs new file mode 100644 index 00000000..3d9d82a6 --- /dev/null +++ b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryByIdHandler.cs @@ -0,0 +1,144 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Locking; +using ErsatzTV.Core.Interfaces.Plex; +using ErsatzTV.Core.Interfaces.Repositories; +using ErsatzTV.Core.Plex; +using LanguageExt; +using MediatR; +using Microsoft.Extensions.Logging; +using Unit = LanguageExt.Unit; + +namespace ErsatzTV.Application.Plex.Commands +{ + public class + SynchronizePlexLibraryByIdHandler : IRequestHandler>, + IRequestHandler> + { + private readonly IEntityLocker _entityLocker; + private readonly ILogger _logger; + private readonly IMediaSourceRepository _mediaSourceRepository; + private readonly IPlexMovieLibraryScanner _plexMovieLibraryScanner; + private readonly IPlexSecretStore _plexSecretStore; + + public SynchronizePlexLibraryByIdHandler( + IMediaSourceRepository mediaSourceRepository, + IPlexSecretStore plexSecretStore, + IPlexMovieLibraryScanner plexMovieLibraryScanner, + IEntityLocker entityLocker, + ILogger logger) + { + _mediaSourceRepository = mediaSourceRepository; + _plexSecretStore = plexSecretStore; + _plexMovieLibraryScanner = plexMovieLibraryScanner; + _entityLocker = entityLocker; + _logger = logger; + } + + public Task> Handle( + ForceSynchronizePlexLibraryById request, + CancellationToken cancellationToken) => Handle(request); + + public Task> Handle( + SynchronizePlexLibraryByIdIfNeeded request, + CancellationToken cancellationToken) => Handle(request); + + private Task> + Handle(ISynchronizePlexLibraryById request) => + Validate(request) + .MapT(parameters => Synchronize(parameters).Map(_ => parameters.Library.Name)) + .Bind(v => v.ToEitherAsync()); + + private async Task Synchronize(RequestParameters parameters) + { + var lastScan = new DateTimeOffset(parameters.Library.LastScan ?? DateTime.MinValue, TimeSpan.Zero); + if (parameters.ForceScan || lastScan < DateTimeOffset.Now - TimeSpan.FromHours(6)) + { + switch (parameters.Library.MediaKind) + { + case LibraryMediaKind.Movies: + await _plexMovieLibraryScanner.ScanLibrary( + parameters.ConnectionParameters.ActiveConnection, + parameters.ConnectionParameters.PlexServerAuthToken, + parameters.Library); + break; + case LibraryMediaKind.Shows: + // TODO: plex tv scanner + // await _televisionFolderScanner.ScanFolder(parameters.LocalMediaSource, parameters.FFprobePath); + break; + } + + parameters.Library.LastScan = DateTime.UtcNow; + await _mediaSourceRepository.Update(parameters.Library); + } + else + { + _logger.LogDebug( + "Skipping unforced scan of plex media library {Name}", + parameters.Library.Name); + } + + // _entityLocker.UnlockMediaSource(parameters.MediaSource.Id); + return Unit.Default; + } + + private async Task> Validate(ISynchronizePlexLibraryById request) => + (await ValidateConnection(request), await PlexLibraryMustExist(request)) + .Apply( + (connectionParameters, plexLibrary) => new RequestParameters( + connectionParameters, + plexLibrary, + request.ForceScan + )); + + private Task> ValidateConnection( + ISynchronizePlexLibraryById request) => + PlexMediaSourceMustExist(request) + .BindT(MediaSourceMustHaveActiveConnection) + .BindT(MediaSourceMustHaveToken); + + private Task> PlexMediaSourceMustExist( + ISynchronizePlexLibraryById request) => + _mediaSourceRepository.GetPlex(request.PlexMediaSourceId) + .Map(v => v.ToValidation($"Plex media source {request.PlexMediaSourceId} does not exist.")); + + private Validation MediaSourceMustHaveActiveConnection( + PlexMediaSource plexMediaSource) + { + Option maybeConnection = + plexMediaSource.Connections.SingleOrDefault(c => c.IsActive); + return maybeConnection.Map(connection => new ConnectionParameters(plexMediaSource, connection)) + .ToValidation("Plex media source requires an active connection"); + } + + private async Task> MediaSourceMustHaveToken( + ConnectionParameters connectionParameters) + { + Option maybeToken = await + _plexSecretStore.GetServerAuthToken(connectionParameters.PlexMediaSource.ClientIdentifier); + return maybeToken.Map(token => connectionParameters with { PlexServerAuthToken = token }) + .ToValidation("Plex media source requires a token"); + } + + private Task> PlexLibraryMustExist( + ISynchronizePlexLibraryById request) => + _mediaSourceRepository.GetPlexLibrary(request.PlexLibraryId) + .Map(v => v.ToValidation($"Plex library {request.PlexLibraryId} does not exist.")); + + private record RequestParameters( + ConnectionParameters ConnectionParameters, + PlexLibrary Library, + bool ForceScan); + + private record ConnectionParameters( + PlexMediaSource PlexMediaSource, + PlexConnection ActiveConnection) + { + public PlexServerAuthToken PlexServerAuthToken { get; set; } + } + } +} diff --git a/ErsatzTV.Application/MediaSources/Commands/SynchronizePlexMediaSources.cs b/ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSources.cs similarity index 83% rename from ErsatzTV.Application/MediaSources/Commands/SynchronizePlexMediaSources.cs rename to ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSources.cs index 55d1bab4..c1c7680a 100644 --- a/ErsatzTV.Application/MediaSources/Commands/SynchronizePlexMediaSources.cs +++ b/ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSources.cs @@ -4,7 +4,7 @@ using ErsatzTV.Core.Domain; using LanguageExt; using MediatR; -namespace ErsatzTV.Application.MediaSources.Commands +namespace ErsatzTV.Application.Plex.Commands { public record SynchronizePlexMediaSources : IRequest>>, IPlexBackgroundServiceRequest; diff --git a/ErsatzTV.Application/MediaSources/Commands/SynchronizePlexMediaSourcesHandler.cs b/ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSourcesHandler.cs similarity index 93% rename from ErsatzTV.Application/MediaSources/Commands/SynchronizePlexMediaSourcesHandler.cs rename to ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSourcesHandler.cs index 9cd53fe1..a1e45e26 100644 --- a/ErsatzTV.Application/MediaSources/Commands/SynchronizePlexMediaSourcesHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSourcesHandler.cs @@ -9,7 +9,7 @@ using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; using MediatR; -namespace ErsatzTV.Application.MediaSources.Commands +namespace ErsatzTV.Application.Plex.Commands { public class SynchronizePlexMediaSourcesHandler : IRequestHandler { existing.ProductVersion = server.ProductVersion; - existing.Name = server.Name; + existing.ServerName = server.ServerName; MergeConnections(existing.Connections, server.Connections); if (existing.Connections.Any() && existing.Connections.All(c => !c.IsActive)) { @@ -72,8 +72,8 @@ namespace ErsatzTV.Application.MediaSources.Commands } private void MergeConnections( - List existing, - List incoming) + List existing, + List incoming) { var toAdd = incoming.Filter(connection => existing.All(c => c.Uri != connection.Uri)).ToList(); var toRemove = existing.Filter(connection => incoming.All(c => c.Uri != connection.Uri)).ToList(); diff --git a/ErsatzTV.Application/MediaSources/Commands/TryCompletePlexPinFlow.cs b/ErsatzTV.Application/Plex/Commands/TryCompletePlexPinFlow.cs similarity index 81% rename from ErsatzTV.Application/MediaSources/Commands/TryCompletePlexPinFlow.cs rename to ErsatzTV.Application/Plex/Commands/TryCompletePlexPinFlow.cs index e6d16ea4..97dc6638 100644 --- a/ErsatzTV.Application/MediaSources/Commands/TryCompletePlexPinFlow.cs +++ b/ErsatzTV.Application/Plex/Commands/TryCompletePlexPinFlow.cs @@ -3,7 +3,7 @@ using ErsatzTV.Core.Plex; using LanguageExt; using MediatR; -namespace ErsatzTV.Application.MediaSources.Commands +namespace ErsatzTV.Application.Plex.Commands { public record TryCompletePlexPinFlow(PlexAuthPin AuthPin) : IRequest>, IPlexBackgroundServiceRequest; diff --git a/ErsatzTV.Application/MediaSources/Commands/TryCompletePlexPinFlowHandler.cs b/ErsatzTV.Application/Plex/Commands/TryCompletePlexPinFlowHandler.cs similarity index 96% rename from ErsatzTV.Application/MediaSources/Commands/TryCompletePlexPinFlowHandler.cs rename to ErsatzTV.Application/Plex/Commands/TryCompletePlexPinFlowHandler.cs index 3bdadd98..c8f535b2 100644 --- a/ErsatzTV.Application/MediaSources/Commands/TryCompletePlexPinFlowHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/TryCompletePlexPinFlowHandler.cs @@ -7,7 +7,7 @@ using ErsatzTV.Core.Interfaces.Plex; using LanguageExt; using MediatR; -namespace ErsatzTV.Application.MediaSources.Commands +namespace ErsatzTV.Application.Plex.Commands { public class TryCompletePlexPinFlowHandler : IRequestHandler> { diff --git a/ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferences.cs b/ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferences.cs new file mode 100644 index 00000000..244c15f8 --- /dev/null +++ b/ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferences.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using ErsatzTV.Core; +using LanguageExt; + +namespace ErsatzTV.Application.Plex.Commands +{ + public record UpdatePlexLibraryPreferences + (List Preferences) : MediatR.IRequest>; + + public record PlexLibraryPreference(int Id, bool ShouldSyncItems); +} diff --git a/ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferencesHandler.cs b/ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferencesHandler.cs new file mode 100644 index 00000000..831811b3 --- /dev/null +++ b/ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferencesHandler.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; + +namespace ErsatzTV.Application.Plex.Commands +{ + public class + UpdatePlexLibraryPreferencesHandler : MediatR.IRequestHandler> + { + private readonly IMediaSourceRepository _mediaSourceRepository; + + public UpdatePlexLibraryPreferencesHandler(IMediaSourceRepository mediaSourceRepository) => + _mediaSourceRepository = mediaSourceRepository; + + public async Task> Handle( + UpdatePlexLibraryPreferences request, + CancellationToken cancellationToken) + { + IEnumerable toDisable = request.Preferences.Filter(p => p.ShouldSyncItems == false).Map(p => p.Id); + await _mediaSourceRepository.DisablePlexLibrarySync(toDisable); + + IEnumerable toEnable = request.Preferences.Filter(p => p.ShouldSyncItems).Map(p => p.Id); + await _mediaSourceRepository.EnablePlexLibrarySync(toEnable); + + return Unit.Default; + } + } +} diff --git a/ErsatzTV.Application/Plex/Mapper.cs b/ErsatzTV.Application/Plex/Mapper.cs new file mode 100644 index 00000000..8c80e28f --- /dev/null +++ b/ErsatzTV.Application/Plex/Mapper.cs @@ -0,0 +1,18 @@ +using System.Linq; +using ErsatzTV.Core.Domain; +using static LanguageExt.Prelude; + +namespace ErsatzTV.Application.Plex +{ + internal static class Mapper + { + internal static PlexMediaSourceViewModel ProjectToViewModel(PlexMediaSource plexMediaSource) => + new( + plexMediaSource.Id, + plexMediaSource.ServerName, + Optional(plexMediaSource.Connections.SingleOrDefault(c => c.IsActive)).Match(c => c.Uri, string.Empty)); + + internal static PlexLibraryViewModel ProjectToViewModel(PlexLibrary library) => + new(library.Id, library.Name, library.MediaKind, library.ShouldSyncItems); + } +} diff --git a/ErsatzTV.Application/Plex/PlexLibraryViewModel.cs b/ErsatzTV.Application/Plex/PlexLibraryViewModel.cs new file mode 100644 index 00000000..d0dae98e --- /dev/null +++ b/ErsatzTV.Application/Plex/PlexLibraryViewModel.cs @@ -0,0 +1,6 @@ +using ErsatzTV.Core.Domain; + +namespace ErsatzTV.Application.Plex +{ + public record PlexLibraryViewModel(int Id, string Name, LibraryMediaKind MediaKind, bool ShouldSyncItems); +} diff --git a/ErsatzTV.Application/Plex/PlexMediaSourceViewModel.cs b/ErsatzTV.Application/Plex/PlexMediaSourceViewModel.cs new file mode 100644 index 00000000..a5567d43 --- /dev/null +++ b/ErsatzTV.Application/Plex/PlexMediaSourceViewModel.cs @@ -0,0 +1,6 @@ +using ErsatzTV.Application.MediaSources; + +namespace ErsatzTV.Application.Plex +{ + public record PlexMediaSourceViewModel(int Id, string Name, string Address) : MediaSourceViewModel(Id, Name); +} diff --git a/ErsatzTV.Application/MediaSources/Queries/GetAllPlexMediaSources.cs b/ErsatzTV.Application/Plex/Queries/GetAllPlexMediaSources.cs similarity index 73% rename from ErsatzTV.Application/MediaSources/Queries/GetAllPlexMediaSources.cs rename to ErsatzTV.Application/Plex/Queries/GetAllPlexMediaSources.cs index 74133239..c99fa95f 100644 --- a/ErsatzTV.Application/MediaSources/Queries/GetAllPlexMediaSources.cs +++ b/ErsatzTV.Application/Plex/Queries/GetAllPlexMediaSources.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using MediatR; -namespace ErsatzTV.Application.MediaSources.Queries +namespace ErsatzTV.Application.Plex.Queries { public record GetAllPlexMediaSources : IRequest>; } diff --git a/ErsatzTV.Application/MediaSources/Queries/GetAllPlexMediaSourcesHandler.cs b/ErsatzTV.Application/Plex/Queries/GetAllPlexMediaSourcesHandler.cs similarity index 88% rename from ErsatzTV.Application/MediaSources/Queries/GetAllPlexMediaSourcesHandler.cs rename to ErsatzTV.Application/Plex/Queries/GetAllPlexMediaSourcesHandler.cs index f124bdb2..22761238 100644 --- a/ErsatzTV.Application/MediaSources/Queries/GetAllPlexMediaSourcesHandler.cs +++ b/ErsatzTV.Application/Plex/Queries/GetAllPlexMediaSourcesHandler.cs @@ -5,9 +5,9 @@ using System.Threading.Tasks; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; using MediatR; -using static ErsatzTV.Application.MediaSources.Mapper; +using static ErsatzTV.Application.Plex.Mapper; -namespace ErsatzTV.Application.MediaSources.Queries +namespace ErsatzTV.Application.Plex.Queries { public class GetAllPlexMediaSourcesHandler : IRequestHandler> { diff --git a/ErsatzTV.Application/Plex/Queries/GetPlexLibrariesBySourceId.cs b/ErsatzTV.Application/Plex/Queries/GetPlexLibrariesBySourceId.cs new file mode 100644 index 00000000..11da4bc8 --- /dev/null +++ b/ErsatzTV.Application/Plex/Queries/GetPlexLibrariesBySourceId.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; +using MediatR; + +namespace ErsatzTV.Application.Plex.Queries +{ + public record GetPlexLibrariesBySourceId(int PlexMediaSourceId) : IRequest>; +} diff --git a/ErsatzTV.Application/Plex/Queries/GetPlexLibrariesBySourceIdHandler.cs b/ErsatzTV.Application/Plex/Queries/GetPlexLibrariesBySourceIdHandler.cs new file mode 100644 index 00000000..e73dd612 --- /dev/null +++ b/ErsatzTV.Application/Plex/Queries/GetPlexLibrariesBySourceIdHandler.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using static ErsatzTV.Application.Plex.Mapper; + +namespace ErsatzTV.Application.Plex.Queries +{ + public class + GetPlexLibrariesBySourceIdHandler : IRequestHandler> + { + private readonly IMediaSourceRepository _mediaSourceRepository; + + public GetPlexLibrariesBySourceIdHandler(IMediaSourceRepository mediaSourceRepository) => + _mediaSourceRepository = mediaSourceRepository; + + public Task> Handle( + GetPlexLibrariesBySourceId request, + CancellationToken cancellationToken) => + _mediaSourceRepository.GetPlexLibraries(request.PlexMediaSourceId) + .Map(list => list.Map(ProjectToViewModel).ToList()); + } +} diff --git a/ErsatzTV.Application/Plex/Queries/GetPlexMediaSourceById.cs b/ErsatzTV.Application/Plex/Queries/GetPlexMediaSourceById.cs new file mode 100644 index 00000000..5fdc0504 --- /dev/null +++ b/ErsatzTV.Application/Plex/Queries/GetPlexMediaSourceById.cs @@ -0,0 +1,7 @@ +using LanguageExt; +using MediatR; + +namespace ErsatzTV.Application.Plex.Queries +{ + public record GetPlexMediaSourceById(int PlexMediaSourceId) : IRequest>; +} diff --git a/ErsatzTV.Application/Plex/Queries/GetPlexMediaSourceByIdHandler.cs b/ErsatzTV.Application/Plex/Queries/GetPlexMediaSourceByIdHandler.cs new file mode 100644 index 00000000..8bd9f9f1 --- /dev/null +++ b/ErsatzTV.Application/Plex/Queries/GetPlexMediaSourceByIdHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using static ErsatzTV.Application.Plex.Mapper; + +namespace ErsatzTV.Application.Plex.Queries +{ + public class + GetPlexMediaSourceByIdHandler : IRequestHandler> + { + private readonly IMediaSourceRepository _mediaSourceRepository; + + public GetPlexMediaSourceByIdHandler(IMediaSourceRepository mediaSourceRepository) => + _mediaSourceRepository = mediaSourceRepository; + + public Task> Handle( + GetPlexMediaSourceById request, + CancellationToken cancellationToken) => + _mediaSourceRepository.GetPlex(request.PlexMediaSourceId).MapT(ProjectToViewModel); + } +} diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItem.cs b/ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItem.cs index bb7265c4..9eaf9086 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItem.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItem.cs @@ -12,9 +12,8 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands TimeSpan? StartTime, PlayoutMode PlayoutMode, ProgramScheduleItemCollectionType CollectionType, - int? MediaCollectionId, - int? TelevisionShowId, - int? TelevisionSeasonId, + int? CollectionId, + int? MediaItemId, int? MultipleCount, TimeSpan? PlayoutDuration, bool? OfflineTail) : IRequest>, IProgramScheduleItemRequest; diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/IProgramScheduleItemRequest.cs b/ErsatzTV.Application/ProgramSchedules/Commands/IProgramScheduleItemRequest.cs index be9def8d..f7c1c232 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/IProgramScheduleItemRequest.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/IProgramScheduleItemRequest.cs @@ -7,9 +7,8 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands { TimeSpan? StartTime { get; } ProgramScheduleItemCollectionType CollectionType { get; } - int? MediaCollectionId { get; } - int? TelevisionShowId { get; } - int? TelevisionSeasonId { get; } + int? CollectionId { get; } + int? MediaItemId { get; } PlayoutMode PlayoutMode { get; } int? MultipleCount { get; } TimeSpan? PlayoutDuration { get; } diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs b/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs index f205279d..a6788ca1 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs @@ -60,23 +60,23 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands switch (item.CollectionType) { case ProgramScheduleItemCollectionType.Collection: - if (item.MediaCollectionId is null) + if (item.CollectionId is null) { - return BaseError.New("[MediaCollection] is required for collection type 'Collection'"); + return BaseError.New("[Collection] is required for collection type 'Collection'"); } break; case ProgramScheduleItemCollectionType.TelevisionShow: - if (item.TelevisionShowId is null) + if (item.MediaItemId is null) { - return BaseError.New("[TelevisionShow] is required for collection type 'TelevisionShow'"); + return BaseError.New("[MediaItem] is required for collection type 'TelevisionShow'"); } break; case ProgramScheduleItemCollectionType.TelevisionSeason: - if (item.TelevisionSeasonId is null) + if (item.MediaItemId is null) { - return BaseError.New("[TelevisionSeason] is required for collection type 'TelevisionSeason'"); + return BaseError.New("[MediaItem] is required for collection type 'TelevisionSeason'"); } break; @@ -99,9 +99,8 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands Index = index, StartTime = item.StartTime, CollectionType = item.CollectionType, - MediaCollectionId = item.MediaCollectionId, - TelevisionShowId = item.TelevisionShowId, - TelevisionSeasonId = item.TelevisionSeasonId + CollectionId = item.CollectionId, + MediaItemId = item.MediaItemId }, PlayoutMode.One => new ProgramScheduleItemOne { @@ -109,9 +108,8 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands Index = index, StartTime = item.StartTime, CollectionType = item.CollectionType, - MediaCollectionId = item.MediaCollectionId, - TelevisionShowId = item.TelevisionShowId, - TelevisionSeasonId = item.TelevisionSeasonId + CollectionId = item.CollectionId, + MediaItemId = item.MediaItemId }, PlayoutMode.Multiple => new ProgramScheduleItemMultiple { @@ -119,9 +117,8 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands Index = index, StartTime = item.StartTime, CollectionType = item.CollectionType, - MediaCollectionId = item.MediaCollectionId, - TelevisionShowId = item.TelevisionShowId, - TelevisionSeasonId = item.TelevisionSeasonId, + CollectionId = item.CollectionId, + MediaItemId = item.MediaItemId, Count = item.MultipleCount.GetValueOrDefault() }, PlayoutMode.Duration => new ProgramScheduleItemDuration @@ -130,9 +127,8 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands Index = index, StartTime = item.StartTime, CollectionType = item.CollectionType, - MediaCollectionId = item.MediaCollectionId, - TelevisionShowId = item.TelevisionShowId, - TelevisionSeasonId = item.TelevisionSeasonId, + CollectionId = item.CollectionId, + MediaItemId = item.MediaItemId, PlayoutDuration = item.PlayoutDuration.GetValueOrDefault(), OfflineTail = item.OfflineTail.GetValueOrDefault() }, diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItems.cs b/ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItems.cs index 50c7a18e..2e9a37c4 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItems.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItems.cs @@ -13,9 +13,8 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands TimeSpan? StartTime, PlayoutMode PlayoutMode, ProgramScheduleItemCollectionType CollectionType, - int? MediaCollectionId, - int? TelevisionShowId, - int? TelevisionSeasonId, + int? CollectionId, + int? MediaItemId, int? MultipleCount, TimeSpan? PlayoutDuration, bool? OfflineTail) : IProgramScheduleItemRequest; diff --git a/ErsatzTV.Application/ProgramSchedules/Mapper.cs b/ErsatzTV.Application/ProgramSchedules/Mapper.cs index 9d8ea1e6..6a9e01e1 100644 --- a/ErsatzTV.Application/ProgramSchedules/Mapper.cs +++ b/ErsatzTV.Application/ProgramSchedules/Mapper.cs @@ -18,15 +18,15 @@ namespace ErsatzTV.Application.ProgramSchedules duration.StartType, duration.StartTime, duration.CollectionType, - duration.MediaCollection != null - ? MediaCollections.Mapper.ProjectToViewModel(duration.MediaCollection) - : null, - duration.TelevisionShow != null - ? Television.Mapper.ProjectToViewModel(duration.TelevisionShow) - : null, - duration.TelevisionSeason != null - ? Television.Mapper.ProjectToViewModel(duration.TelevisionSeason) + duration.Collection != null + ? MediaCollections.Mapper.ProjectToViewModel(duration.Collection) : null, + duration.MediaItem switch + { + Show show => MediaItems.Mapper.ProjectToViewModel(show), + Season season => MediaItems.Mapper.ProjectToViewModel(season), + _ => null + }, duration.PlayoutDuration, duration.OfflineTail), ProgramScheduleItemFlood flood => @@ -36,15 +36,15 @@ namespace ErsatzTV.Application.ProgramSchedules flood.StartType, flood.StartTime, flood.CollectionType, - flood.MediaCollection != null - ? MediaCollections.Mapper.ProjectToViewModel(flood.MediaCollection) - : null, - flood.TelevisionShow != null - ? Television.Mapper.ProjectToViewModel(flood.TelevisionShow) + flood.Collection != null + ? MediaCollections.Mapper.ProjectToViewModel(flood.Collection) : null, - flood.TelevisionSeason != null - ? Television.Mapper.ProjectToViewModel(flood.TelevisionSeason) - : null), + flood.MediaItem switch + { + Show show => MediaItems.Mapper.ProjectToViewModel(show), + Season season => MediaItems.Mapper.ProjectToViewModel(season), + _ => null + }), ProgramScheduleItemMultiple multiple => new ProgramScheduleItemMultipleViewModel( multiple.Id, @@ -52,15 +52,15 @@ namespace ErsatzTV.Application.ProgramSchedules multiple.StartType, multiple.StartTime, multiple.CollectionType, - multiple.MediaCollection != null - ? MediaCollections.Mapper.ProjectToViewModel(multiple.MediaCollection) - : null, - multiple.TelevisionShow != null - ? Television.Mapper.ProjectToViewModel(multiple.TelevisionShow) - : null, - multiple.TelevisionSeason != null - ? Television.Mapper.ProjectToViewModel(multiple.TelevisionSeason) + multiple.Collection != null + ? MediaCollections.Mapper.ProjectToViewModel(multiple.Collection) : null, + multiple.MediaItem switch + { + Show show => MediaItems.Mapper.ProjectToViewModel(show), + Season season => MediaItems.Mapper.ProjectToViewModel(season), + _ => null + }, multiple.Count), ProgramScheduleItemOne one => new ProgramScheduleItemOneViewModel( @@ -69,13 +69,15 @@ namespace ErsatzTV.Application.ProgramSchedules one.StartType, one.StartTime, one.CollectionType, - one.MediaCollection != null - ? MediaCollections.Mapper.ProjectToViewModel(one.MediaCollection) + one.Collection != null + ? MediaCollections.Mapper.ProjectToViewModel(one.Collection) : null, - one.TelevisionShow != null ? Television.Mapper.ProjectToViewModel(one.TelevisionShow) : null, - one.TelevisionSeason != null - ? Television.Mapper.ProjectToViewModel(one.TelevisionSeason) - : null), + one.MediaItem switch + { + Show show => MediaItems.Mapper.ProjectToViewModel(show), + Season season => MediaItems.Mapper.ProjectToViewModel(season), + _ => null + }), _ => throw new NotSupportedException( $"Unsupported program schedule item type {programScheduleItem.GetType().Name}") }; diff --git a/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemDurationViewModel.cs b/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemDurationViewModel.cs index 6071ffc9..e21fecb9 100644 --- a/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemDurationViewModel.cs +++ b/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemDurationViewModel.cs @@ -1,6 +1,6 @@ using System; using ErsatzTV.Application.MediaCollections; -using ErsatzTV.Application.Television; +using ErsatzTV.Application.MediaItems; using ErsatzTV.Core.Domain; namespace ErsatzTV.Application.ProgramSchedules @@ -13,9 +13,8 @@ namespace ErsatzTV.Application.ProgramSchedules StartType startType, TimeSpan? startTime, ProgramScheduleItemCollectionType collectionType, - MediaCollectionViewModel mediaCollection, - TelevisionShowViewModel televisionShow, - TelevisionSeasonViewModel televisionSeason, + MediaCollectionViewModel collection, + NamedMediaItemViewModel mediaItem, TimeSpan playoutDuration, bool offlineTail) : base( id, @@ -24,9 +23,8 @@ namespace ErsatzTV.Application.ProgramSchedules startTime, PlayoutMode.Duration, collectionType, - mediaCollection, - televisionShow, - televisionSeason) + collection, + mediaItem) { PlayoutDuration = playoutDuration; OfflineTail = offlineTail; diff --git a/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemFloodViewModel.cs b/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemFloodViewModel.cs index e1f13b32..8698da9a 100644 --- a/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemFloodViewModel.cs +++ b/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemFloodViewModel.cs @@ -1,6 +1,6 @@ using System; using ErsatzTV.Application.MediaCollections; -using ErsatzTV.Application.Television; +using ErsatzTV.Application.MediaItems; using ErsatzTV.Core.Domain; namespace ErsatzTV.Application.ProgramSchedules @@ -13,18 +13,16 @@ namespace ErsatzTV.Application.ProgramSchedules StartType startType, TimeSpan? startTime, ProgramScheduleItemCollectionType collectionType, - MediaCollectionViewModel mediaCollection, - TelevisionShowViewModel televisionShow, - TelevisionSeasonViewModel televisionSeason) : base( + MediaCollectionViewModel collection, + NamedMediaItemViewModel mediaItem) : base( id, index, startType, startTime, PlayoutMode.Flood, collectionType, - mediaCollection, - televisionShow, - televisionSeason) + collection, + mediaItem) { } } diff --git a/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemMultipleViewModel.cs b/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemMultipleViewModel.cs index 4106836f..a7d161c5 100644 --- a/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemMultipleViewModel.cs +++ b/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemMultipleViewModel.cs @@ -1,6 +1,6 @@ using System; using ErsatzTV.Application.MediaCollections; -using ErsatzTV.Application.Television; +using ErsatzTV.Application.MediaItems; using ErsatzTV.Core.Domain; namespace ErsatzTV.Application.ProgramSchedules @@ -13,9 +13,8 @@ namespace ErsatzTV.Application.ProgramSchedules StartType startType, TimeSpan? startTime, ProgramScheduleItemCollectionType collectionType, - MediaCollectionViewModel mediaCollection, - TelevisionShowViewModel televisionShow, - TelevisionSeasonViewModel televisionSeason, + MediaCollectionViewModel collection, + NamedMediaItemViewModel mediaItem, int count) : base( id, index, @@ -23,9 +22,8 @@ namespace ErsatzTV.Application.ProgramSchedules startTime, PlayoutMode.Multiple, collectionType, - mediaCollection, - televisionShow, - televisionSeason) => + collection, + mediaItem) => Count = count; public int Count { get; } diff --git a/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemOneViewModel.cs b/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemOneViewModel.cs index e77e5566..011e4743 100644 --- a/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemOneViewModel.cs +++ b/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemOneViewModel.cs @@ -1,6 +1,6 @@ using System; using ErsatzTV.Application.MediaCollections; -using ErsatzTV.Application.Television; +using ErsatzTV.Application.MediaItems; using ErsatzTV.Core.Domain; namespace ErsatzTV.Application.ProgramSchedules @@ -13,18 +13,16 @@ namespace ErsatzTV.Application.ProgramSchedules StartType startType, TimeSpan? startTime, ProgramScheduleItemCollectionType collectionType, - MediaCollectionViewModel mediaCollection, - TelevisionShowViewModel televisionShow, - TelevisionSeasonViewModel televisionSeason) : base( + MediaCollectionViewModel collection, + NamedMediaItemViewModel mediaItem) : base( id, index, startType, startTime, PlayoutMode.One, collectionType, - mediaCollection, - televisionShow, - televisionSeason) + collection, + mediaItem) { } } diff --git a/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemViewModel.cs b/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemViewModel.cs index dc7392ab..367a7ee6 100644 --- a/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemViewModel.cs +++ b/ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemViewModel.cs @@ -1,6 +1,6 @@ using System; using ErsatzTV.Application.MediaCollections; -using ErsatzTV.Application.Television; +using ErsatzTV.Application.MediaItems; using ErsatzTV.Core.Domain; namespace ErsatzTV.Application.ProgramSchedules @@ -12,16 +12,16 @@ namespace ErsatzTV.Application.ProgramSchedules TimeSpan? StartTime, PlayoutMode PlayoutMode, ProgramScheduleItemCollectionType CollectionType, - MediaCollectionViewModel MediaCollection, - TelevisionShowViewModel TelevisionShow, - TelevisionSeasonViewModel TelevisionSeason) + MediaCollectionViewModel Collection, + NamedMediaItemViewModel MediaItem) { public string Name => CollectionType switch { - ProgramScheduleItemCollectionType.Collection => MediaCollection?.Name, - ProgramScheduleItemCollectionType.TelevisionShow => $"{TelevisionShow?.Title} ({TelevisionShow?.Year})", - ProgramScheduleItemCollectionType.TelevisionSeason => - $"{TelevisionSeason?.Title} ({TelevisionSeason?.Plot})", + ProgramScheduleItemCollectionType.Collection => Collection?.Name, + ProgramScheduleItemCollectionType + .TelevisionShow => MediaItem?.Name, // $"{TelevisionShow?.Title} ({TelevisionShow?.Year})", + ProgramScheduleItemCollectionType + .TelevisionSeason => MediaItem?.Name, // $"{TelevisionSeason?.Title} ({TelevisionSeason?.Plot})", _ => string.Empty }; } diff --git a/ErsatzTV.Application/Television/Mapper.cs b/ErsatzTV.Application/Television/Mapper.cs index 983e973d..77aec13b 100644 --- a/ErsatzTV.Application/Television/Mapper.cs +++ b/ErsatzTV.Application/Television/Mapper.cs @@ -1,28 +1,43 @@ -using ErsatzTV.Core.Domain; +using System.Linq; +using ErsatzTV.Core.Domain; +using static LanguageExt.Prelude; namespace ErsatzTV.Application.Television { internal static class Mapper { - internal static TelevisionShowViewModel ProjectToViewModel(TelevisionShow show) => - new(show.Id, show.Metadata.Title, show.Metadata.Year?.ToString(), show.Metadata.Plot, show.Poster); + internal static TelevisionShowViewModel ProjectToViewModel(Show show) => + new( + show.Id, + show.ShowMetadata.HeadOrNone().Map(m => m.Title).IfNone(string.Empty), + show.ShowMetadata.HeadOrNone().Map(m => m.Year?.ToString()).IfNone(string.Empty), + show.ShowMetadata.HeadOrNone().Map(m => m.Plot).IfNone(string.Empty), + show.ShowMetadata.HeadOrNone().Map(GetPoster).IfNone(string.Empty)); - internal static TelevisionSeasonViewModel ProjectToViewModel(TelevisionSeason season) => + internal static TelevisionSeasonViewModel ProjectToViewModel(Season season) => new( season.Id, - season.TelevisionShowId, - season.TelevisionShow.Metadata.Title, - season.TelevisionShow.Metadata.Year?.ToString(), - season.Number == 0 ? "Specials" : $"Season {season.Number}", - season.Poster); + season.ShowId, + season.Show.ShowMetadata.HeadOrNone().Map(m => m.Title).IfNone(string.Empty), + season.Show.ShowMetadata.HeadOrNone().Map(m => m.Year?.ToString()).IfNone(string.Empty), + season.SeasonNumber == 0 ? "Specials" : $"Season {season.SeasonNumber}", + season.SeasonMetadata.HeadOrNone().Map(GetPoster).IfNone(string.Empty)); - internal static TelevisionEpisodeViewModel ProjectToViewModel(TelevisionEpisodeMediaItem episode) => + internal static TelevisionEpisodeViewModel ProjectToViewModel(Episode episode) => new( - episode.Season.TelevisionShowId, + episode.Season.ShowId, episode.SeasonId, - episode.Metadata.Episode, - episode.Metadata.Title, - episode.Metadata.Plot, - episode.Poster); + episode.EpisodeNumber, + episode.EpisodeMetadata.HeadOrNone().Map(m => m.Title).IfNone(string.Empty), + episode.EpisodeMetadata.HeadOrNone().Map(m => m.Plot).IfNone(string.Empty), + episode.EpisodeMetadata.HeadOrNone().Map(GetThumbnail).IfNone(string.Empty)); + + private static string GetPoster(Metadata metadata) => + Optional(metadata.Artwork.FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Poster)) + .Match(a => a.Path, string.Empty); + + private static string GetThumbnail(Metadata metadata) => + Optional(metadata.Artwork.FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Thumbnail)) + .Match(a => a.Path, string.Empty); } } diff --git a/ErsatzTV.Application/Television/Queries/GetAllTelevisionSeasons.cs b/ErsatzTV.Application/Television/Queries/GetAllTelevisionSeasons.cs index 303c8aac..07b5940d 100644 --- a/ErsatzTV.Application/Television/Queries/GetAllTelevisionSeasons.cs +++ b/ErsatzTV.Application/Television/Queries/GetAllTelevisionSeasons.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using ErsatzTV.Application.MediaItems; using MediatR; namespace ErsatzTV.Application.Television.Queries { - public record GetAllTelevisionSeasons : IRequest>; + public record GetAllTelevisionSeasons : IRequest>; } diff --git a/ErsatzTV.Application/Television/Queries/GetAllTelevisionSeasonsHandler.cs b/ErsatzTV.Application/Television/Queries/GetAllTelevisionSeasonsHandler.cs index 5c0668b3..8f490ec8 100644 --- a/ErsatzTV.Application/Television/Queries/GetAllTelevisionSeasonsHandler.cs +++ b/ErsatzTV.Application/Television/Queries/GetAllTelevisionSeasonsHandler.cs @@ -2,22 +2,23 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using ErsatzTV.Application.MediaItems; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; using MediatR; -using static ErsatzTV.Application.Television.Mapper; +using static ErsatzTV.Application.MediaItems.Mapper; namespace ErsatzTV.Application.Television.Queries { public class - GetAllTelevisionSeasonsHandler : IRequestHandler> + GetAllTelevisionSeasonsHandler : IRequestHandler> { private readonly ITelevisionRepository _televisionRepository; public GetAllTelevisionSeasonsHandler(ITelevisionRepository televisionRepository) => _televisionRepository = televisionRepository; - public Task> Handle( + public Task> Handle( GetAllTelevisionSeasons request, CancellationToken cancellationToken) => _televisionRepository.GetAllSeasons().Map(list => list.Map(ProjectToViewModel).ToList()); diff --git a/ErsatzTV.Application/Television/Queries/GetAllTelevisionShows.cs b/ErsatzTV.Application/Television/Queries/GetAllTelevisionShows.cs index 87f91eb1..aac8e200 100644 --- a/ErsatzTV.Application/Television/Queries/GetAllTelevisionShows.cs +++ b/ErsatzTV.Application/Television/Queries/GetAllTelevisionShows.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using ErsatzTV.Application.MediaItems; using MediatR; namespace ErsatzTV.Application.Television.Queries { - public record GetAllTelevisionShows : IRequest>; + public record GetAllTelevisionShows : IRequest>; } diff --git a/ErsatzTV.Application/Television/Queries/GetAllTelevisionShowsHandler.cs b/ErsatzTV.Application/Television/Queries/GetAllTelevisionShowsHandler.cs index 09c73e8a..608befc3 100644 --- a/ErsatzTV.Application/Television/Queries/GetAllTelevisionShowsHandler.cs +++ b/ErsatzTV.Application/Television/Queries/GetAllTelevisionShowsHandler.cs @@ -2,21 +2,22 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using ErsatzTV.Application.MediaItems; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; using MediatR; -using static ErsatzTV.Application.Television.Mapper; +using static ErsatzTV.Application.MediaItems.Mapper; namespace ErsatzTV.Application.Television.Queries { - public class GetAllTelevisionShowsHandler : IRequestHandler> + public class GetAllTelevisionShowsHandler : IRequestHandler> { private readonly ITelevisionRepository _televisionRepository; public GetAllTelevisionShowsHandler(ITelevisionRepository televisionRepository) => _televisionRepository = televisionRepository; - public Task> Handle( + public Task> Handle( GetAllTelevisionShows request, CancellationToken cancellationToken) => _televisionRepository.GetAllShows().Map(list => list.Map(ProjectToViewModel).ToList()); diff --git a/ErsatzTV.CommandLine/Commands/Schedules/ScheduleAddItemCommand.cs b/ErsatzTV.CommandLine/Commands/Schedules/ScheduleAddItemCommand.cs index 1a76f901..9e1c0704 100644 --- a/ErsatzTV.CommandLine/Commands/Schedules/ScheduleAddItemCommand.cs +++ b/ErsatzTV.CommandLine/Commands/Schedules/ScheduleAddItemCommand.cs @@ -97,7 +97,7 @@ namespace ErsatzTV.CommandLine.Commands.Schedules () => { _logger.LogError( - "Unable to locate media collection {MediaCollection}", + "Unable to locate collection {Collection}", CollectionName); return Task.CompletedTask; }); diff --git a/ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsServiceTests.cs b/ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsServiceTests.cs index b4c4ae7f..5237e37d 100644 --- a/ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsServiceTests.cs +++ b/ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsServiceTests.cs @@ -15,15 +15,6 @@ namespace ErsatzTV.Core.Tests.FFmpeg public CalculateSettings() => _calculator = new FFmpegPlaybackSettingsCalculator(); - private static PlayoutItem EmptyPlayoutItem() => - new() - { - MediaItem = new MediaItem - { - Statistics = new MediaItemStatistics() - } - }; - [Test] public void Should_UseSpecifiedThreadCount_ForTransportStream() { @@ -32,7 +23,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - EmptyPlayoutItem(), + new MediaVersion(), + DateTimeOffset.Now, DateTimeOffset.Now); actual.ThreadCount.Should().Be(7); @@ -46,7 +38,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.HttpLiveStreaming, ffmpegProfile, - EmptyPlayoutItem(), + new MediaVersion(), + DateTimeOffset.Now, DateTimeOffset.Now); actual.ThreadCount.Should().Be(7); @@ -60,7 +53,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - EmptyPlayoutItem(), + new MediaVersion(), + DateTimeOffset.Now, DateTimeOffset.Now); string[] expected = { "+genpts", "+discardcorrupt", "+igndts" }; @@ -76,7 +70,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.HttpLiveStreaming, ffmpegProfile, - EmptyPlayoutItem(), + new MediaVersion(), + DateTimeOffset.Now, DateTimeOffset.Now); string[] expected = { "+genpts", "+discardcorrupt", "+igndts" }; @@ -92,7 +87,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - EmptyPlayoutItem(), + new MediaVersion(), + DateTimeOffset.Now, DateTimeOffset.Now); actual.RealtimeOutput.Should().BeTrue(); @@ -106,7 +102,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.HttpLiveStreaming, ffmpegProfile, - EmptyPlayoutItem(), + new MediaVersion(), + DateTimeOffset.Now, DateTimeOffset.Now); actual.RealtimeOutput.Should().BeTrue(); @@ -118,13 +115,12 @@ namespace ErsatzTV.Core.Tests.FFmpeg DateTimeOffset now = DateTimeOffset.Now; FFmpegProfile ffmpegProfile = TestProfile(); - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.Start = now; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + new MediaVersion(), + now, now.AddMinutes(5)); actual.StreamSeek.IsSome.Should().BeTrue(); @@ -137,13 +133,12 @@ namespace ErsatzTV.Core.Tests.FFmpeg DateTimeOffset now = DateTimeOffset.Now; FFmpegProfile ffmpegProfile = TestProfile(); - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.Start = now; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.HttpLiveStreaming, ffmpegProfile, - playoutItem, + new MediaVersion(), + now, now.AddMinutes(5)); actual.StreamSeek.IsSome.Should().BeTrue(); @@ -154,12 +149,12 @@ namespace ErsatzTV.Core.Tests.FFmpeg public void ShouldNot_SetScaledSize_When_NotNormalizingResolution_ForTransportStream() { FFmpegProfile ffmpegProfile = TestProfile() with { NormalizeResolution = false }; - PlayoutItem playoutItem = EmptyPlayoutItem(); FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + new MediaVersion(), + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -174,15 +169,14 @@ namespace ErsatzTV.Core.Tests.FFmpeg Resolution = new Resolution { Width = 1920, Height = 1080 } }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1920; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic + // not anamorphic + var version = new MediaVersion { Width = 1920, Height = 1080, SampleAspectRatio = "1:1" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -197,15 +191,14 @@ namespace ErsatzTV.Core.Tests.FFmpeg Resolution = new Resolution { Width = 1920, Height = 1080 } }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1918; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic + // not anamorphic + var version = new MediaVersion { Width = 1918, Height = 1080, SampleAspectRatio = "1:1" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -220,15 +213,14 @@ namespace ErsatzTV.Core.Tests.FFmpeg Resolution = new Resolution { Width = 1920, Height = 1080 } }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1920; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic + // not anamorphic + var version = new MediaVersion { Width = 1920, Height = 1080, SampleAspectRatio = "1:1" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -244,15 +236,14 @@ namespace ErsatzTV.Core.Tests.FFmpeg Resolution = new Resolution { Width = 1920, Height = 1080 } }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1918; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic + // not anamorphic + var version = new MediaVersion { Width = 1918, Height = 1080, SampleAspectRatio = "1:1" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -268,15 +259,14 @@ namespace ErsatzTV.Core.Tests.FFmpeg Resolution = new Resolution { Width = 1920, Height = 1080 } }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1918; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic + // not anamorphic + var version = new MediaVersion { Width = 1918, Height = 1080, SampleAspectRatio = "1:1" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.HttpLiveStreaming, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -294,15 +284,14 @@ namespace ErsatzTV.Core.Tests.FFmpeg VideoCodec = "testCodec" }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1918; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic + // not anamorphic + var version = new MediaVersion { Width = 1918, Height = 1080, SampleAspectRatio = "1:1" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -322,16 +311,15 @@ namespace ErsatzTV.Core.Tests.FFmpeg VideoCodec = "testCodec" }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1920; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic - playoutItem.MediaItem.Statistics.VideoCodec = "mpeg2video"; + // not anamorphic + var version = new MediaVersion + { Width = 1920, Height = 1080, SampleAspectRatio = "1:1", VideoCodec = "mpeg2video" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -351,16 +339,15 @@ namespace ErsatzTV.Core.Tests.FFmpeg VideoCodec = "testCodec" }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1920; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic - playoutItem.MediaItem.Statistics.VideoCodec = "mpeg2video"; + // not anamorphic + var version = new MediaVersion + { Width = 1920, Height = 1080, SampleAspectRatio = "1:1", VideoCodec = "mpeg2video" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.HttpLiveStreaming, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -379,16 +366,15 @@ namespace ErsatzTV.Core.Tests.FFmpeg VideoCodec = "libx264" }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1920; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic - playoutItem.MediaItem.Statistics.VideoCodec = "libx264"; + // not anamorphic + var version = new MediaVersion + { Width = 1920, Height = 1080, SampleAspectRatio = "1:1", VideoCodec = "libx264" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -408,16 +394,15 @@ namespace ErsatzTV.Core.Tests.FFmpeg VideoCodec = "libx264" }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1920; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic - playoutItem.MediaItem.Statistics.VideoCodec = "mpeg2video"; + // not anamorphic + var version = new MediaVersion + { Width = 1920, Height = 1080, SampleAspectRatio = "1:1", VideoCodec = "mpeg2video" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -436,15 +421,14 @@ namespace ErsatzTV.Core.Tests.FFmpeg VideoBitrate = 2525 }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1918; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic + // not anamorphic + var version = new MediaVersion { Width = 1918, Height = 1080, SampleAspectRatio = "1:1" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -463,16 +447,15 @@ namespace ErsatzTV.Core.Tests.FFmpeg VideoBitrate = 2525 }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1920; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic - playoutItem.MediaItem.Statistics.VideoCodec = "mpeg2video"; + // not anamorphic + var version = new MediaVersion + { Width = 1920, Height = 1080, SampleAspectRatio = "1:1", VideoCodec = "mpeg2video" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -491,15 +474,14 @@ namespace ErsatzTV.Core.Tests.FFmpeg VideoBufferSize = 2525 }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1918; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic + // not anamorphic + var version = new MediaVersion { Width = 1918, Height = 1080, SampleAspectRatio = "1:1" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -519,16 +501,15 @@ namespace ErsatzTV.Core.Tests.FFmpeg VideoBufferSize = 2525 }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.Width = 1920; - playoutItem.MediaItem.Statistics.Height = 1080; - playoutItem.MediaItem.Statistics.SampleAspectRatio = "1:1"; // not anamorphic - playoutItem.MediaItem.Statistics.VideoCodec = "mpeg2video"; + // not anamorphic + var version = new MediaVersion + { Width = 1920, Height = 1080, SampleAspectRatio = "1:1", VideoCodec = "mpeg2video" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.ScaledSize.IsNone.Should().BeTrue(); @@ -545,13 +526,13 @@ namespace ErsatzTV.Core.Tests.FFmpeg AudioCodec = "aac" }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.AudioCodec = "aac"; + var version = new MediaVersion { AudioCodec = "aac" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.AudioCodec.Should().Be("copy"); @@ -566,13 +547,13 @@ namespace ErsatzTV.Core.Tests.FFmpeg AudioCodec = "aac" }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.AudioCodec = "ac3"; + var version = new MediaVersion { AudioCodec = "ac3" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.AudioCodec.Should().Be("copy"); @@ -587,13 +568,13 @@ namespace ErsatzTV.Core.Tests.FFmpeg AudioCodec = "aac" }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.AudioCodec = "ac3"; + var version = new MediaVersion { AudioCodec = "ac3" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.AudioCodec.Should().Be("aac"); @@ -608,13 +589,13 @@ namespace ErsatzTV.Core.Tests.FFmpeg AudioCodec = "aac" }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.AudioCodec = "ac3"; + var version = new MediaVersion { AudioCodec = "ac3" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.HttpLiveStreaming, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.AudioCodec.Should().Be("copy"); @@ -629,13 +610,13 @@ namespace ErsatzTV.Core.Tests.FFmpeg AudioBitrate = 2424 }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.AudioCodec = "ac3"; + var version = new MediaVersion { AudioCodec = "ac3" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.AudioBitrate.IfNone(0).Should().Be(2424); @@ -650,13 +631,13 @@ namespace ErsatzTV.Core.Tests.FFmpeg AudioBufferSize = 2424 }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.AudioCodec = "ac3"; + var version = new MediaVersion { AudioCodec = "ac3" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.AudioBufferSize.IfNone(0).Should().Be(2424); @@ -673,13 +654,13 @@ namespace ErsatzTV.Core.Tests.FFmpeg AudioChannels = 6 }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.AudioCodec = "ac3"; + var version = new MediaVersion { AudioCodec = "ac3" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.AudioChannels.IsNone.Should().BeTrue(); @@ -696,13 +677,13 @@ namespace ErsatzTV.Core.Tests.FFmpeg AudioSampleRate = 48 }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.AudioCodec = "ac3"; + var version = new MediaVersion { AudioCodec = "ac3" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.AudioSampleRate.IsNone.Should().BeTrue(); @@ -718,13 +699,13 @@ namespace ErsatzTV.Core.Tests.FFmpeg AudioChannels = 6 }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.AudioCodec = "ac3"; + var version = new MediaVersion { AudioCodec = "ac3" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.AudioChannels.IfNone(0).Should().Be(6); @@ -740,13 +721,13 @@ namespace ErsatzTV.Core.Tests.FFmpeg AudioSampleRate = 48 }; - PlayoutItem playoutItem = EmptyPlayoutItem(); - playoutItem.MediaItem.Statistics.AudioCodec = "ac3"; + var version = new MediaVersion { AudioCodec = "ac3" }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, ffmpegProfile, - playoutItem, + version, + DateTimeOffset.Now, DateTimeOffset.Now); actual.AudioSampleRate.IfNone(0).Should().Be(48); diff --git a/ErsatzTV.Core.Tests/Fakes/FakeLocalFileSystem.cs b/ErsatzTV.Core.Tests/Fakes/FakeLocalFileSystem.cs index 0e28632c..172c7391 100644 --- a/ErsatzTV.Core.Tests/Fakes/FakeLocalFileSystem.cs +++ b/ErsatzTV.Core.Tests/Fakes/FakeLocalFileSystem.cs @@ -41,8 +41,8 @@ namespace ErsatzTV.Core.Tests.Fakes .Map(f => f.LastWriteTime) .IfNone(DateTime.MinValue); - public bool IsMediaSourceAccessible(LocalMediaSource localMediaSource) => - _files.Any(f => f.Path.StartsWith(localMediaSource.Folder)); + public bool IsLibraryPathAccessible(LibraryPath libraryPath) => + _files.Any(f => f.Path.StartsWith(libraryPath.Path + Path.DirectorySeparatorChar)); public IEnumerable ListSubdirectories(string folder) => _folders.Map(f => f.Path).Filter(f => f.StartsWith(folder) && Directory.GetParent(f)?.FullName == folder); @@ -54,6 +54,9 @@ namespace ErsatzTV.Core.Tests.Fakes public Task ReadAllBytes(string path) => TestBytes.AsTask(); + public Unit CopyFile(string source, string destination) => + Unit.Default; + private static List Split(DirectoryInfo path) { var result = new List(); diff --git a/ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs b/ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs index 62142b4b..8a94581a 100644 --- a/ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs +++ b/ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs @@ -11,34 +11,26 @@ namespace ErsatzTV.Core.Tests.Fakes { public class FakeMediaCollectionRepository : IMediaCollectionRepository { - private readonly Map> _data; + private readonly Map> _data; - public FakeMediaCollectionRepository(Map> data) => _data = data; + public FakeMediaCollectionRepository(Map> data) => _data = data; - public Task Add(SimpleMediaCollection collection) => throw new NotSupportedException(); + public Task Add(Collection collection) => throw new NotSupportedException(); + public Task AddMediaItem(int collectionId, int mediaItemId) => throw new NotSupportedException(); - public Task> Get(int id) => throw new NotSupportedException(); + public Task> Get(int id) => throw new NotSupportedException(); - public Task> GetSimpleMediaCollection(int id) => - throw new NotSupportedException(); + public Task> GetCollectionWithItems(int id) => throw new NotSupportedException(); - public Task> GetSimpleMediaCollectionWithItems(int id) => - throw new NotSupportedException(); + public Task> GetCollectionWithItemsUntracked(int id) => throw new NotSupportedException(); - public Task> GetSimpleMediaCollectionWithItemsUntracked(int id) => - throw new NotSupportedException(); + public Task> GetAll() => throw new NotSupportedException(); - public Task> GetSimpleMediaCollections() => throw new NotSupportedException(); + public Task>> GetItems(int id) => Some(_data[id].ToList()).AsTask(); - public Task> GetAll() => throw new NotSupportedException(); + public Task Update(Collection collection) => throw new NotSupportedException(); - public Task>> GetItems(int id) => Some(_data[id].OfType().ToList()).AsTask(); - - public Task>> GetSimpleMediaCollectionItems(int id) => - throw new NotSupportedException(); - - public Task Update(SimpleMediaCollection collection) => throw new NotSupportedException(); - - public Task Delete(int mediaCollectionId) => throw new NotSupportedException(); + public Task Delete(int collectionId) => throw new NotSupportedException(); + public Task>> GetSimpleMediaCollectionItems(int id) => throw new NotSupportedException(); } } diff --git a/ErsatzTV.Core.Tests/Fakes/FakeMovieWithPath.cs b/ErsatzTV.Core.Tests/Fakes/FakeMovieWithPath.cs new file mode 100644 index 00000000..f6d096ee --- /dev/null +++ b/ErsatzTV.Core.Tests/Fakes/FakeMovieWithPath.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using ErsatzTV.Core.Domain; + +namespace ErsatzTV.Core.Tests.Fakes +{ + public class FakeMovieWithPath : Movie + { + public FakeMovieWithPath(string path) + { + Path = path; + + MediaVersions = new List + { + new() + { + MediaFiles = new List + { + new() { Path = path } + } + } + }; + } + + public string Path { get; } + } +} diff --git a/ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs b/ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs index 002c86c7..6eda1708 100644 --- a/ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs +++ b/ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs @@ -9,70 +9,59 @@ namespace ErsatzTV.Core.Tests.Fakes { public class FakeTelevisionRepository : ITelevisionRepository { - public Task Update(TelevisionShow show) => throw new NotSupportedException(); + public Task Update(Show show) => throw new NotSupportedException(); - public Task Update(TelevisionSeason season) => throw new NotSupportedException(); + public Task Update(Season season) => throw new NotSupportedException(); - public Task Update(TelevisionEpisodeMediaItem episode) => throw new NotSupportedException(); + public Task Update(Episode episode) => throw new NotSupportedException(); - public Task> GetAllShows() => throw new NotSupportedException(); + public Task> GetAllShows() => throw new NotSupportedException(); - public Task> GetShow(int televisionShowId) => throw new NotSupportedException(); + public Task> GetShow(int showId) => throw new NotSupportedException(); public Task GetShowCount() => throw new NotSupportedException(); - public Task> GetPagedShows(int pageNumber, int pageSize) => + 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(); + public Task> GetAllSeasons() => throw new NotSupportedException(); - public Task> GetSeason(int televisionSeasonId) => throw new NotSupportedException(); + public Task> GetSeason(int seasonId) => throw new NotSupportedException(); - public Task GetSeasonCount(int televisionShowId) => throw new NotSupportedException(); + public Task GetSeasonCount(int showId) => throw new NotSupportedException(); - public Task> GetPagedSeasons(int televisionShowId, int pageNumber, int pageSize) => + 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(); + public Task> GetEpisode(int episodeId) => throw new NotSupportedException(); - public Task GetEpisodeCount(int televisionSeasonId) => throw new NotSupportedException(); + public Task GetEpisodeCount(int seasonId) => throw new NotSupportedException(); + + public Task> GetPagedEpisodes(int seasonId, int pageNumber, int pageSize) => + throw new NotSupportedException(); - public Task> GetPagedEpisodes( - int televisionSeasonId, - int pageNumber, - int pageSize) => throw new NotSupportedException(); + public Task> GetShowByMetadata(int libraryPathId, ShowMetadata metadata) => + throw new NotSupportedException(); - public Task> GetShowByPath(int mediaSourceId, string path) => + public Task> + AddShow(int libraryPathId, string showFolder, ShowMetadata metadata) => throw new NotSupportedException(); - public Task> GetShowByMetadata(TelevisionShowMetadata metadata) => + public Task> GetOrAddSeason(Show show, int libraryPathId, int seasonNumber) => throw new NotSupportedException(); - public Task> AddShow( - int localMediaSourceId, - string showFolder, - TelevisionShowMetadata metadata) => throw new NotSupportedException(); + public Task> GetOrAddEpisode(Season season, LibraryPath libraryPath, string path) => + throw new NotSupportedException(); - public Task> GetOrAddSeason( - TelevisionShow show, - string path, - int seasonNumber) => throw new NotSupportedException(); + public Task DeleteEmptyShows() => throw new NotSupportedException(); - public Task> GetOrAddEpisode( - TelevisionSeason season, - int mediaSourceId, - string path) => throw new NotSupportedException(); + public Task> GetShowByPath(int mediaSourceId, string path) => throw new NotSupportedException(); public Task DeleteMissingSources(int localMediaSourceId, List allFolders) => throw new NotSupportedException(); - - public Task DeleteEmptyShows() => throw new NotSupportedException(); } } diff --git a/ErsatzTV.Core.Tests/Metadata/FallbackMetadataProviderTests.cs b/ErsatzTV.Core.Tests/Metadata/FallbackMetadataProviderTests.cs index 6d1a5ad6..dc036508 100644 --- a/ErsatzTV.Core.Tests/Metadata/FallbackMetadataProviderTests.cs +++ b/ErsatzTV.Core.Tests/Metadata/FallbackMetadataProviderTests.cs @@ -1,4 +1,6 @@ -using ErsatzTV.Core.Domain; +using System; +using System.Collections.Generic; +using ErsatzTV.Core.Domain; using ErsatzTV.Core.Metadata; using FluentAssertions; using NUnit.Framework; @@ -37,13 +39,26 @@ namespace ErsatzTV.Core.Tests.Metadata 2)] public void GetFallbackMetadata_ShouldHandleVariousFormats(string path, string title, int season, int episode) { - TelevisionEpisodeMetadata metadata = FallbackMetadataProvider.GetFallbackMetadata( - new TelevisionEpisodeMediaItem - { Path = path, Source = new LocalMediaSource { MediaType = MediaType.TvShow } }); + (EpisodeMetadata metadata, int episodeNumber) = FallbackMetadataProvider.GetFallbackMetadata( + new Episode + { + LibraryPath = new LibraryPath(), + MediaVersions = new List + { + new() + { + MediaFiles = new List + { + new() { Path = path } + } + } + } + }); metadata.Title.Should().Be(title); - metadata.Season.Should().Be(season); - metadata.Episode.Should().Be(episode); + // TODO: how can we test season number? do we need to? + // metadata.Season.Should().Be(season); + episodeNumber.Should().Be(episode); } } } diff --git a/ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs b/ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs index 8f0e75df..a27e6e84 100644 --- a/ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs +++ b/ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -40,18 +41,23 @@ namespace ErsatzTV.Core.Tests.Metadata public void SetUp() { _movieRepository = new Mock(); - _movieRepository.Setup(x => x.GetOrAdd(It.IsAny(), It.IsAny())) + _movieRepository.Setup(x => x.GetOrAdd(It.IsAny(), It.IsAny())) .Returns( - (int _, string path) => - Right(new MovieMediaItem { Path = path }).AsTask()); + (LibraryPath _, string path) => Right(new FakeMovieWithPath(path)).AsTask()); _localStatisticsProvider = new Mock(); _localMetadataProvider = new Mock(); + // fallback metadata adds metadata to a movie, so we need to replicate that here + _localMetadataProvider.Setup(x => x.RefreshFallbackMetadata(It.IsAny())) + .Returns( + (MediaItem mediaItem) => + { + ((Movie) mediaItem).MovieMetadata = new List { new() }; + return Unit.Default.AsTask(); + }); + _imageCache = new Mock(); - _imageCache.Setup( - x => x.ResizeAndSaveImage(FakeLocalFileSystem.TestBytes, It.IsAny(), It.IsAny())) - .Returns(Right("poster").AsTask()); } private Mock _movieRepository; @@ -65,9 +71,9 @@ namespace ErsatzTV.Core.Tests.Metadata MovieFolderScanner service = GetService( new FakeFileEntry(Path.Combine(FakeRoot, Path.Combine("Movie (2020)", "Movie (2020).mkv"))) ); - var source = new LocalMediaSource { Folder = BadFakeRoot }; + var libraryPath = new LibraryPath { Path = BadFakeRoot }; - Either result = await service.ScanFolder(source, FFprobePath); + Either result = await service.ScanFolder(libraryPath, FFprobePath); result.IsLeft.Should().BeTrue(); result.IfLeft(error => error.Should().BeOfType()); @@ -83,23 +89,23 @@ namespace ErsatzTV.Core.Tests.Metadata Path.Combine("Movie (2020)", $"Movie (2020){videoExtension}")); MovieFolderScanner service = GetService( - new FakeFileEntry(moviePath) + new FakeFileEntry(moviePath) { LastWriteTime = DateTime.Now } ); - var source = new LocalMediaSource { Id = 1, Folder = FakeRoot }; + var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; - Either result = await service.ScanFolder(source, FFprobePath); + Either result = await service.ScanFolder(libraryPath, FFprobePath); result.IsRight.Should().BeTrue(); - _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); - _movieRepository.Verify(x => x.GetOrAdd(1, moviePath), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(libraryPath, moviePath), Times.Once); _localStatisticsProvider.Verify( - x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), + x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), Times.Once); _localMetadataProvider.Verify( - x => x.RefreshFallbackMetadata(It.Is(i => i.Path == moviePath)), + x => x.RefreshFallbackMetadata(It.Is(i => i.Path == moviePath)), Times.Once); } @@ -115,24 +121,24 @@ namespace ErsatzTV.Core.Tests.Metadata string metadataPath = Path.ChangeExtension(moviePath, "nfo"); MovieFolderScanner service = GetService( - new FakeFileEntry(moviePath), + new FakeFileEntry(moviePath) { LastWriteTime = DateTime.Now }, new FakeFileEntry(metadataPath) ); - var source = new LocalMediaSource { Id = 1, Folder = FakeRoot }; + var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; - Either result = await service.ScanFolder(source, FFprobePath); + Either result = await service.ScanFolder(libraryPath, FFprobePath); result.IsRight.Should().BeTrue(); - _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); - _movieRepository.Verify(x => x.GetOrAdd(1, moviePath), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(libraryPath, moviePath), Times.Once); _localStatisticsProvider.Verify( - x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), + x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), Times.Once); _localMetadataProvider.Verify( - x => x.RefreshSidecarMetadata(It.Is(i => i.Path == moviePath), metadataPath), + x => x.RefreshSidecarMetadata(It.Is(i => i.Path == moviePath), metadataPath), Times.Once); } @@ -148,24 +154,24 @@ namespace ErsatzTV.Core.Tests.Metadata string metadataPath = Path.Combine(Path.GetDirectoryName(moviePath) ?? string.Empty, "movie.nfo"); MovieFolderScanner service = GetService( - new FakeFileEntry(moviePath), + new FakeFileEntry(moviePath) { LastWriteTime = DateTime.Now }, new FakeFileEntry(metadataPath) ); - var source = new LocalMediaSource { Id = 1, Folder = FakeRoot }; + var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; - Either result = await service.ScanFolder(source, FFprobePath); + Either result = await service.ScanFolder(libraryPath, FFprobePath); result.IsRight.Should().BeTrue(); - _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); - _movieRepository.Verify(x => x.GetOrAdd(1, moviePath), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(libraryPath, moviePath), Times.Once); _localStatisticsProvider.Verify( - x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), + x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), Times.Once); _localMetadataProvider.Verify( - x => x.RefreshSidecarMetadata(It.Is(i => i.Path == moviePath), metadataPath), + x => x.RefreshSidecarMetadata(It.Is(i => i.Path == moviePath), metadataPath), Times.Once); } @@ -185,28 +191,28 @@ namespace ErsatzTV.Core.Tests.Metadata $"poster.{imageExtension}"); MovieFolderScanner service = GetService( - new FakeFileEntry(moviePath), - new FakeFileEntry(posterPath) + new FakeFileEntry(moviePath) { LastWriteTime = DateTime.Now }, + new FakeFileEntry(posterPath) { LastWriteTime = DateTime.Now } ); - var source = new LocalMediaSource { Id = 1, Folder = FakeRoot }; + var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; - Either result = await service.ScanFolder(source, FFprobePath); + Either result = await service.ScanFolder(libraryPath, FFprobePath); result.IsRight.Should().BeTrue(); - _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); - _movieRepository.Verify(x => x.GetOrAdd(1, moviePath), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(libraryPath, moviePath), Times.Once); _localStatisticsProvider.Verify( - x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), + x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), Times.Once); _localMetadataProvider.Verify( - x => x.RefreshFallbackMetadata(It.Is(i => i.Path == moviePath)), + x => x.RefreshFallbackMetadata(It.Is(i => i.Path == moviePath)), Times.Once); _imageCache.Verify( - x => x.ResizeAndSaveImage(FakeLocalFileSystem.TestBytes, It.IsAny(), It.IsAny()), + x => x.CopyArtworkToCache(posterPath, ArtworkKind.Poster), Times.Once); } @@ -226,28 +232,28 @@ namespace ErsatzTV.Core.Tests.Metadata $"Movie (2020)-poster.{imageExtension}"); MovieFolderScanner service = GetService( - new FakeFileEntry(moviePath), - new FakeFileEntry(posterPath) + new FakeFileEntry(moviePath) { LastWriteTime = DateTime.Now }, + new FakeFileEntry(posterPath) { LastWriteTime = DateTime.Now } ); - var source = new LocalMediaSource { Id = 1, Folder = FakeRoot }; + var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; - Either result = await service.ScanFolder(source, FFprobePath); + Either result = await service.ScanFolder(libraryPath, FFprobePath); result.IsRight.Should().BeTrue(); - _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); - _movieRepository.Verify(x => x.GetOrAdd(1, moviePath), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(libraryPath, moviePath), Times.Once); _localStatisticsProvider.Verify( - x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), + x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), Times.Once); _localMetadataProvider.Verify( - x => x.RefreshFallbackMetadata(It.Is(i => i.Path == moviePath)), + x => x.RefreshFallbackMetadata(It.Is(i => i.Path == moviePath)), Times.Once); _imageCache.Verify( - x => x.ResizeAndSaveImage(FakeLocalFileSystem.TestBytes, It.IsAny(), It.IsAny()), + x => x.CopyArtworkToCache(posterPath, ArtworkKind.Poster), Times.Once); } @@ -263,27 +269,27 @@ namespace ErsatzTV.Core.Tests.Metadata Path.Combine("Movie (2020)", $"Movie (2020){videoExtension}")); MovieFolderScanner service = GetService( - new FakeFileEntry(moviePath), + new FakeFileEntry(moviePath) { LastWriteTime = DateTime.Now }, new FakeFileEntry( Path.Combine( Path.GetDirectoryName(moviePath) ?? string.Empty, $"Movie (2020)-{extraFile}{videoExtension}")) ); - var source = new LocalMediaSource { Id = 1, Folder = FakeRoot }; + var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; - Either result = await service.ScanFolder(source, FFprobePath); + Either result = await service.ScanFolder(libraryPath, FFprobePath); result.IsRight.Should().BeTrue(); - _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); - _movieRepository.Verify(x => x.GetOrAdd(1, moviePath), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(libraryPath, moviePath), Times.Once); _localStatisticsProvider.Verify( - x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), + x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), Times.Once); _localMetadataProvider.Verify( - x => x.RefreshFallbackMetadata(It.Is(i => i.Path == moviePath)), + x => x.RefreshFallbackMetadata(It.Is(i => i.Path == moviePath)), Times.Once); } @@ -299,27 +305,27 @@ namespace ErsatzTV.Core.Tests.Metadata Path.Combine("Movie (2020)", $"Movie (2020){videoExtension}")); MovieFolderScanner service = GetService( - new FakeFileEntry(moviePath), + new FakeFileEntry(moviePath) { LastWriteTime = DateTime.Now }, new FakeFileEntry( Path.Combine( Path.GetDirectoryName(moviePath) ?? string.Empty, Path.Combine(extraFolder, $"Movie (2020){videoExtension}"))) ); - var source = new LocalMediaSource { Id = 1, Folder = FakeRoot }; + var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; - Either result = await service.ScanFolder(source, FFprobePath); + Either result = await service.ScanFolder(libraryPath, FFprobePath); result.IsRight.Should().BeTrue(); - _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); - _movieRepository.Verify(x => x.GetOrAdd(1, moviePath), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(libraryPath, moviePath), Times.Once); _localStatisticsProvider.Verify( - x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), + x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), Times.Once); _localMetadataProvider.Verify( - x => x.RefreshFallbackMetadata(It.Is(i => i.Path == moviePath)), + x => x.RefreshFallbackMetadata(It.Is(i => i.Path == moviePath)), Times.Once); } @@ -333,30 +339,27 @@ namespace ErsatzTV.Core.Tests.Metadata Path.Combine("Movie (2020)", $"Movie (2020){videoExtension}")); MovieFolderScanner service = GetService( - new FakeFileEntry(moviePath) + new FakeFileEntry(moviePath) { LastWriteTime = DateTime.Now } ); - var source = new LocalMediaSource { Id = 1, Folder = FakeRoot }; + var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; - Either result = await service.ScanFolder(source, FFprobePath); + Either result = await service.ScanFolder(libraryPath, FFprobePath); result.IsRight.Should().BeTrue(); - _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); - _movieRepository.Verify(x => x.GetOrAdd(1, moviePath), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(libraryPath, moviePath), Times.Once); _localStatisticsProvider.Verify( - x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), + x => x.RefreshStatistics(FFprobePath, It.Is(i => i.Path == moviePath)), Times.Once); _localMetadataProvider.Verify( - x => x.RefreshFallbackMetadata(It.Is(i => i.Path == moviePath)), + x => x.RefreshFallbackMetadata(It.Is(i => i.Path == moviePath)), Times.Once); } private MovieFolderScanner GetService(params FakeFileEntry[] files) => - // var mockImageCache = new Mock(); - // mockImageCache.Setup(i => i.ResizeAndSaveImage(It.IsAny(), It.IsAny(), It.IsAny())) - // .Returns(Right("image").AsTask()); new( new FakeLocalFileSystem(new List(files)), _movieRepository.Object, diff --git a/ErsatzTV.Core.Tests/Scheduling/ChronologicalContentTests.cs b/ErsatzTV.Core.Tests/Scheduling/ChronologicalContentTests.cs index c0022aa6..06d60304 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ChronologicalContentTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ChronologicalContentTests.cs @@ -16,7 +16,7 @@ namespace ErsatzTV.Core.Tests.Scheduling public void Episodes_Should_Sort_By_Aired() { List contents = Episodes(10); - var state = new MediaCollectionEnumeratorState(); + var state = new CollectionEnumeratorState(); var chronologicalContent = new ChronologicalMediaCollectionEnumerator(contents, state); @@ -32,7 +32,7 @@ namespace ErsatzTV.Core.Tests.Scheduling public void State_Index_Should_Increment() { List contents = Episodes(10); - var state = new MediaCollectionEnumeratorState(); + var state = new CollectionEnumeratorState(); var chronologicalContent = new ChronologicalMediaCollectionEnumerator(contents, state); @@ -47,7 +47,7 @@ namespace ErsatzTV.Core.Tests.Scheduling public void State_Should_Impact_Iterator_Start() { List contents = Episodes(10); - var state = new MediaCollectionEnumeratorState { Index = 5 }; + var state = new CollectionEnumeratorState { Index = 5 }; var chronologicalContent = new ChronologicalMediaCollectionEnumerator(contents, state); @@ -62,12 +62,15 @@ namespace ErsatzTV.Core.Tests.Scheduling private static List Episodes(int count) => Range(1, count).Map( - i => (MediaItem) new TelevisionEpisodeMediaItem + i => (MediaItem) new Episode { Id = i, - Metadata = new TelevisionEpisodeMetadata + EpisodeMetadata = new List { - Aired = new DateTime(2020, 1, i) + new() + { + ReleaseDate = new DateTime(2020, 1, i) + } } }) .Reverse() diff --git a/ErsatzTV.Core.Tests/Scheduling/PlayoutBuilderTests.cs b/ErsatzTV.Core.Tests/Scheduling/PlayoutBuilderTests.cs index acee730a..34984daf 100644 --- a/ErsatzTV.Core.Tests/Scheduling/PlayoutBuilderTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/PlayoutBuilderTests.cs @@ -40,7 +40,7 @@ namespace ErsatzTV.Core.Tests.Scheduling [Test] public async Task InitialFlood_Should_StartAtMidnight() { - var mediaItems = new List + var mediaItems = new List { TestMovie(1, TimeSpan.FromHours(6), DateTime.Today) }; @@ -52,14 +52,14 @@ namespace ErsatzTV.Core.Tests.Scheduling Playout result = await builder.BuildPlayoutItems(playout, start, finish); result.Items.Count.Should().Be(1); - result.Items.Head().Start.TimeOfDay.Should().Be(TimeSpan.Zero); - result.Items.Head().Finish.TimeOfDay.Should().Be(TimeSpan.FromHours(6)); + result.Items.Head().StartOffset.TimeOfDay.Should().Be(TimeSpan.Zero); + result.Items.Head().FinishOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(6)); } [Test] public async Task InitialFlood_Should_StartAtMidnight_With_LateStart() { - var mediaItems = new List + var mediaItems = new List { TestMovie(1, TimeSpan.FromHours(6), DateTime.Today) }; @@ -71,15 +71,15 @@ namespace ErsatzTV.Core.Tests.Scheduling Playout result = await builder.BuildPlayoutItems(playout, start, finish); result.Items.Count.Should().Be(2); - result.Items[0].Start.TimeOfDay.Should().Be(TimeSpan.Zero); - result.Items[1].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(6)); - result.Items[1].Finish.TimeOfDay.Should().Be(TimeSpan.FromHours(12)); + result.Items[0].StartOffset.TimeOfDay.Should().Be(TimeSpan.Zero); + result.Items[1].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(6)); + result.Items[1].FinishOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(12)); } [Test] public async Task ChronologicalContent_Should_CreateChronologicalItems() { - var mediaItems = new List + var mediaItems = new List { TestMovie(1, TimeSpan.FromHours(1), new DateTime(2020, 1, 1)), TestMovie(2, TimeSpan.FromHours(1), new DateTime(2020, 2, 1)) @@ -92,20 +92,20 @@ namespace ErsatzTV.Core.Tests.Scheduling Playout result = await builder.BuildPlayoutItems(playout, start, finish); result.Items.Count.Should().Be(4); - result.Items[0].Start.TimeOfDay.Should().Be(TimeSpan.Zero); + result.Items[0].StartOffset.TimeOfDay.Should().Be(TimeSpan.Zero); result.Items[0].MediaItemId.Should().Be(1); - result.Items[1].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(1)); + result.Items[1].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(1)); result.Items[1].MediaItemId.Should().Be(2); - result.Items[2].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(2)); + result.Items[2].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(2)); result.Items[2].MediaItemId.Should().Be(1); - result.Items[3].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(3)); + result.Items[3].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(3)); result.Items[3].MediaItemId.Should().Be(2); } [Test] public async Task ChronologicalFlood_Should_AnchorAndMaintainExistingPlayout() { - var mediaItems = new List + var mediaItems = new List { TestMovie(1, TimeSpan.FromHours(6), DateTime.Today), TestMovie(2, TimeSpan.FromHours(6), DateTime.Today.AddHours(1)) @@ -120,7 +120,7 @@ namespace ErsatzTV.Core.Tests.Scheduling result.Items.Count.Should().Be(1); result.Items.Head().MediaItemId.Should().Be(1); - result.Anchor.NextStart.Should().Be(DateTime.Today.AddHours(6)); + result.Anchor.NextStartOffset.Should().Be(DateTime.Today.AddHours(6)); result.ProgramScheduleAnchors.Count.Should().Be(1); result.ProgramScheduleAnchors.Head().EnumeratorState.Index.Should().Be(1); @@ -131,10 +131,10 @@ namespace ErsatzTV.Core.Tests.Scheduling Playout result2 = await builder.BuildPlayoutItems(playout, start2, finish2); result2.Items.Count.Should().Be(2); - result2.Items.Last().Start.TimeOfDay.Should().Be(TimeSpan.FromHours(6)); + result2.Items.Last().StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(6)); result2.Items.Last().MediaItemId.Should().Be(2); - result2.Anchor.NextStart.Should().Be(DateTime.Today.AddHours(12)); + result2.Anchor.NextStartOffset.Should().Be(DateTime.Today.AddHours(12)); result2.ProgramScheduleAnchors.Count.Should().Be(1); result2.ProgramScheduleAnchors.Head().EnumeratorState.Index.Should().Be(0); } @@ -142,7 +142,7 @@ namespace ErsatzTV.Core.Tests.Scheduling [Test] public async Task ChronologicalFlood_Should_AnchorAndReturnNewPlayoutItems() { - var mediaItems = new List + var mediaItems = new List { TestMovie(1, TimeSpan.FromHours(6), DateTime.Today), TestMovie(2, TimeSpan.FromHours(6), DateTime.Today.AddHours(1)) @@ -157,7 +157,7 @@ namespace ErsatzTV.Core.Tests.Scheduling result.Items.Count.Should().Be(1); result.Items.Head().MediaItemId.Should().Be(1); - result.Anchor.NextStart.Should().Be(DateTime.Today.AddHours(6)); + result.Anchor.NextStartOffset.Should().Be(DateTime.Today.AddHours(6)); result.ProgramScheduleAnchors.Count.Should().Be(1); result.ProgramScheduleAnchors.Head().EnumeratorState.Index.Should().Be(1); @@ -167,12 +167,12 @@ namespace ErsatzTV.Core.Tests.Scheduling Playout result2 = await builder.BuildPlayoutItems(playout, start2, finish2); result2.Items.Count.Should().Be(3); - result2.Items[1].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(6)); + result2.Items[1].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(6)); result2.Items[1].MediaItemId.Should().Be(2); - result2.Items[2].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(12)); + result2.Items[2].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(12)); result2.Items[2].MediaItemId.Should().Be(1); - result2.Anchor.NextStart.Should().Be(DateTime.Today.AddHours(18)); + result2.Anchor.NextStartOffset.Should().Be(DateTime.Today.AddHours(18)); result2.ProgramScheduleAnchors.Count.Should().Be(1); result2.ProgramScheduleAnchors.Head().EnumeratorState.Index.Should().Be(1); } @@ -180,7 +180,7 @@ namespace ErsatzTV.Core.Tests.Scheduling [Test] public async Task ShuffleFloodRebuild_Should_IgnoreAnchors() { - var mediaItems = new List + var mediaItems = new List { TestMovie(1, TimeSpan.FromHours(1), DateTime.Today), TestMovie(2, TimeSpan.FromHours(1), DateTime.Today.AddHours(1)), @@ -197,7 +197,7 @@ namespace ErsatzTV.Core.Tests.Scheduling Playout result = await builder.BuildPlayoutItems(playout, start, finish); result.Items.Count.Should().Be(6); - result.Anchor.NextStart.Should().Be(DateTime.Today.AddHours(6)); + result.Anchor.NextStartOffset.Should().Be(DateTime.Today.AddHours(6)); result.ProgramScheduleAnchors.Count.Should().Be(1); result.ProgramScheduleAnchors.Head().EnumeratorState.Index.Should().Be(0); @@ -210,7 +210,7 @@ namespace ErsatzTV.Core.Tests.Scheduling Playout result2 = await builder.BuildPlayoutItems(playout, start2, finish2, true); result2.Items.Count.Should().Be(6); - result2.Anchor.NextStart.Should().Be(DateTime.Today.AddHours(6)); + result2.Anchor.NextStartOffset.Should().Be(DateTime.Today.AddHours(6)); result2.ProgramScheduleAnchors.Count.Should().Be(1); result2.ProgramScheduleAnchors.Head().EnumeratorState.Index.Should().Be(0); @@ -223,7 +223,7 @@ namespace ErsatzTV.Core.Tests.Scheduling [Test] public async Task ShuffleFlood_Should_MaintainRandomSeed() { - var mediaItems = new List + var mediaItems = new List { TestMovie(1, TimeSpan.FromHours(1), DateTime.Today), TestMovie(2, TimeSpan.FromHours(1), DateTime.Today.AddHours(1)), @@ -255,22 +255,22 @@ namespace ErsatzTV.Core.Tests.Scheduling [Test] public async Task FloodContent_Should_FloodAroundFixedContent_One() { - var floodCollection = new SimpleMediaCollection + var floodCollection = new Collection { Id = 1, Name = "Flood Items", - Movies = new List + MediaItems = new List { TestMovie(1, TimeSpan.FromHours(1), new DateTime(2020, 1, 1)), TestMovie(2, TimeSpan.FromHours(1), new DateTime(2020, 2, 1)) } }; - var fixedCollection = new SimpleMediaCollection + var fixedCollection = new Collection { Id = 2, Name = "Fixed Items", - Movies = new List + MediaItems = new List { TestMovie(3, TimeSpan.FromHours(2), new DateTime(2020, 1, 1)) } @@ -278,23 +278,23 @@ namespace ErsatzTV.Core.Tests.Scheduling var fakeRepository = new FakeMediaCollectionRepository( Map( - (floodCollection.Id, floodCollection.Movies.ToList()), - (fixedCollection.Id, fixedCollection.Movies.ToList()))); + (floodCollection.Id, floodCollection.MediaItems.ToList()), + (fixedCollection.Id, fixedCollection.MediaItems.ToList()))); var items = new List { new ProgramScheduleItemFlood { Index = 1, - MediaCollection = floodCollection, - MediaCollectionId = floodCollection.Id, + Collection = floodCollection, + CollectionId = floodCollection.Id, StartTime = null }, new ProgramScheduleItemOne { Index = 2, - MediaCollection = fixedCollection, - MediaCollectionId = fixedCollection.Id, + Collection = fixedCollection, + CollectionId = fixedCollection.Id, StartTime = TimeSpan.FromHours(3) } }; @@ -318,37 +318,37 @@ namespace ErsatzTV.Core.Tests.Scheduling Playout result = await builder.BuildPlayoutItems(playout, start, finish); result.Items.Count.Should().Be(5); - result.Items[0].Start.TimeOfDay.Should().Be(TimeSpan.Zero); + result.Items[0].StartOffset.TimeOfDay.Should().Be(TimeSpan.Zero); result.Items[0].MediaItemId.Should().Be(1); - result.Items[1].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(1)); + result.Items[1].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(1)); result.Items[1].MediaItemId.Should().Be(2); - result.Items[2].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(2)); + result.Items[2].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(2)); result.Items[2].MediaItemId.Should().Be(1); - result.Items[3].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(3)); + result.Items[3].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(3)); result.Items[3].MediaItemId.Should().Be(3); - result.Items[4].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(5)); + result.Items[4].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(5)); result.Items[4].MediaItemId.Should().Be(2); } [Test] public async Task FloodContent_Should_FloodAroundFixedContent_Multiple() { - var floodCollection = new SimpleMediaCollection + var floodCollection = new Collection { Id = 1, Name = "Flood Items", - Movies = new List + MediaItems = new List { TestMovie(1, TimeSpan.FromHours(1), new DateTime(2020, 1, 1)), TestMovie(2, TimeSpan.FromHours(1), new DateTime(2020, 2, 1)) } }; - var fixedCollection = new SimpleMediaCollection + var fixedCollection = new Collection { Id = 2, Name = "Fixed Items", - Movies = new List + MediaItems = new List { TestMovie(3, TimeSpan.FromHours(2), new DateTime(2020, 1, 1)), TestMovie(4, TimeSpan.FromHours(1), new DateTime(2020, 1, 2)) @@ -357,23 +357,23 @@ namespace ErsatzTV.Core.Tests.Scheduling var fakeRepository = new FakeMediaCollectionRepository( Map( - (floodCollection.Id, floodCollection.Movies.ToList()), - (fixedCollection.Id, fixedCollection.Movies.ToList()))); + (floodCollection.Id, floodCollection.MediaItems.ToList()), + (fixedCollection.Id, fixedCollection.MediaItems.ToList()))); var items = new List { new ProgramScheduleItemFlood { Index = 1, - MediaCollection = floodCollection, - MediaCollectionId = floodCollection.Id, + Collection = floodCollection, + CollectionId = floodCollection.Id, StartTime = null }, new ProgramScheduleItemMultiple { Index = 2, - MediaCollection = fixedCollection, - MediaCollectionId = fixedCollection.Id, + Collection = fixedCollection, + CollectionId = fixedCollection.Id, StartTime = TimeSpan.FromHours(3), Count = 2 } @@ -399,41 +399,41 @@ namespace ErsatzTV.Core.Tests.Scheduling result.Items.Count.Should().Be(6); - result.Items[0].Start.TimeOfDay.Should().Be(TimeSpan.Zero); + result.Items[0].StartOffset.TimeOfDay.Should().Be(TimeSpan.Zero); result.Items[0].MediaItemId.Should().Be(1); - result.Items[1].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(1)); + result.Items[1].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(1)); result.Items[1].MediaItemId.Should().Be(2); - result.Items[2].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(2)); + result.Items[2].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(2)); result.Items[2].MediaItemId.Should().Be(1); - result.Items[3].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(3)); + result.Items[3].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(3)); result.Items[3].MediaItemId.Should().Be(3); - result.Items[4].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(5)); + result.Items[4].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(5)); result.Items[4].MediaItemId.Should().Be(4); - result.Items[5].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(6)); + result.Items[5].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(6)); result.Items[5].MediaItemId.Should().Be(2); } [Test] public async Task FloodContent_Should_FloodAroundFixedContent_DurationWithoutOfflineTail() { - var floodCollection = new SimpleMediaCollection + var floodCollection = new Collection { Id = 1, Name = "Flood Items", - Movies = new List + MediaItems = new List { TestMovie(1, TimeSpan.FromHours(1), new DateTime(2020, 1, 1)), TestMovie(2, TimeSpan.FromHours(1), new DateTime(2020, 2, 1)) } }; - var fixedCollection = new SimpleMediaCollection + var fixedCollection = new Collection { Id = 2, Name = "Fixed Items", - Movies = new List + MediaItems = new List { TestMovie(3, TimeSpan.FromHours(0.75), new DateTime(2020, 1, 1)), TestMovie(4, TimeSpan.FromHours(1.5), new DateTime(2020, 1, 2)) @@ -442,23 +442,23 @@ namespace ErsatzTV.Core.Tests.Scheduling var fakeRepository = new FakeMediaCollectionRepository( Map( - (floodCollection.Id, floodCollection.Movies.ToList()), - (fixedCollection.Id, fixedCollection.Movies.ToList()))); + (floodCollection.Id, floodCollection.MediaItems.ToList()), + (fixedCollection.Id, fixedCollection.MediaItems.ToList()))); var items = new List { new ProgramScheduleItemFlood { Index = 1, - MediaCollection = floodCollection, - MediaCollectionId = floodCollection.Id, + Collection = floodCollection, + CollectionId = floodCollection.Id, StartTime = null }, new ProgramScheduleItemDuration { Index = 2, - MediaCollection = fixedCollection, - MediaCollectionId = fixedCollection.Id, + Collection = fixedCollection, + CollectionId = fixedCollection.Id, StartTime = TimeSpan.FromHours(2), PlayoutDuration = TimeSpan.FromHours(2), OfflineTail = false // immediately continue @@ -485,44 +485,44 @@ namespace ErsatzTV.Core.Tests.Scheduling result.Items.Count.Should().Be(7); - result.Items[0].Start.TimeOfDay.Should().Be(TimeSpan.Zero); + result.Items[0].StartOffset.TimeOfDay.Should().Be(TimeSpan.Zero); result.Items[0].MediaItemId.Should().Be(1); - result.Items[1].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(1)); + result.Items[1].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(1)); result.Items[1].MediaItemId.Should().Be(2); - result.Items[2].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(2)); + result.Items[2].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(2)); result.Items[2].MediaItemId.Should().Be(3); - result.Items[3].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(2.75)); + result.Items[3].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(2.75)); result.Items[3].MediaItemId.Should().Be(1); - result.Items[4].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(3.75)); + result.Items[4].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(3.75)); result.Items[4].MediaItemId.Should().Be(2); - result.Items[5].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(4.75)); + result.Items[5].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(4.75)); result.Items[5].MediaItemId.Should().Be(1); - result.Items[6].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(5.75)); + result.Items[6].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(5.75)); result.Items[6].MediaItemId.Should().Be(2); } [Test] public async Task MultipleContent_Should_WrapAroundDynamicContent_DurationWithoutOfflineTail() { - var multipleCollection = new SimpleMediaCollection + var multipleCollection = new Collection { Id = 1, Name = "Multiple Items", - Movies = new List + MediaItems = new List { TestMovie(1, TimeSpan.FromHours(1), new DateTime(2020, 1, 1)), TestMovie(2, TimeSpan.FromHours(1), new DateTime(2020, 2, 1)) } }; - var dynamicCollection = new SimpleMediaCollection + var dynamicCollection = new Collection { Id = 2, Name = "Dynamic Items", - Movies = new List + MediaItems = new List { TestMovie(3, TimeSpan.FromHours(0.75), new DateTime(2020, 1, 1)), TestMovie(4, TimeSpan.FromHours(1.5), new DateTime(2020, 1, 2)) @@ -531,24 +531,24 @@ namespace ErsatzTV.Core.Tests.Scheduling var fakeRepository = new FakeMediaCollectionRepository( Map( - (multipleCollection.Id, multipleCollection.Movies.ToList()), - (dynamicCollection.Id, dynamicCollection.Movies.ToList()))); + (multipleCollection.Id, multipleCollection.MediaItems.ToList()), + (dynamicCollection.Id, dynamicCollection.MediaItems.ToList()))); var items = new List { new ProgramScheduleItemMultiple { Index = 1, - MediaCollection = multipleCollection, - MediaCollectionId = multipleCollection.Id, + Collection = multipleCollection, + CollectionId = multipleCollection.Id, StartTime = null, Count = 2 }, new ProgramScheduleItemDuration { Index = 2, - MediaCollection = dynamicCollection, - MediaCollectionId = dynamicCollection.Id, + Collection = dynamicCollection, + CollectionId = dynamicCollection.Id, StartTime = null, PlayoutDuration = TimeSpan.FromHours(2), OfflineTail = false // immediately continue @@ -575,20 +575,20 @@ namespace ErsatzTV.Core.Tests.Scheduling result.Items.Count.Should().Be(6); - result.Items[0].Start.TimeOfDay.Should().Be(TimeSpan.Zero); + result.Items[0].StartOffset.TimeOfDay.Should().Be(TimeSpan.Zero); result.Items[0].MediaItemId.Should().Be(1); - result.Items[1].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(1)); + result.Items[1].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(1)); result.Items[1].MediaItemId.Should().Be(2); - result.Items[2].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(2)); + result.Items[2].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(2)); result.Items[2].MediaItemId.Should().Be(3); - result.Items[3].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(2.75)); + result.Items[3].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(2.75)); result.Items[3].MediaItemId.Should().Be(1); - result.Items[4].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(3.75)); + result.Items[4].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(3.75)); result.Items[4].MediaItemId.Should().Be(2); - result.Items[5].Start.TimeOfDay.Should().Be(TimeSpan.FromHours(4.75)); + result.Items[5].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(4.75)); result.Items[5].MediaItemId.Should().Be(4); } @@ -598,29 +598,32 @@ namespace ErsatzTV.Core.Tests.Scheduling return now - now.TimeOfDay + TimeSpan.FromHours(hours); } - private static ProgramScheduleItem Flood(MediaCollection mediaCollection) => + private static ProgramScheduleItem Flood(Collection mediaCollection) => new ProgramScheduleItemFlood { Index = 1, - MediaCollection = mediaCollection, - MediaCollectionId = mediaCollection.Id, + Collection = mediaCollection, + CollectionId = mediaCollection.Id, StartTime = null }; - private static MovieMediaItem TestMovie(int id, TimeSpan duration, DateTime aired) => + private static Movie TestMovie(int id, TimeSpan duration, DateTime aired) => new() { Id = id, - Metadata = new MovieMetadata { Premiered = aired }, - Statistics = new MediaItemStatistics { Duration = duration } + MovieMetadata = new List { new() { ReleaseDate = aired } }, + MediaVersions = new List + { + new() { Duration = duration } + } }; - private TestData TestDataFloodForItems(List mediaItems, PlaybackOrder playbackOrder) + private TestData TestDataFloodForItems(List mediaItems, PlaybackOrder playbackOrder) { - var mediaCollection = new SimpleMediaCollection + var mediaCollection = new Collection { Id = 1, - Movies = mediaItems + MediaItems = mediaItems }; var collectionRepo = new FakeMediaCollectionRepository(Map((mediaCollection.Id, mediaItems))); diff --git a/ErsatzTV.Core.Tests/Scheduling/RandomizedContentTests.cs b/ErsatzTV.Core.Tests/Scheduling/RandomizedContentTests.cs index 13f3d8f0..c4832ebb 100644 --- a/ErsatzTV.Core.Tests/Scheduling/RandomizedContentTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/RandomizedContentTests.cs @@ -26,7 +26,7 @@ namespace ErsatzTV.Core.Tests.Scheduling { List contents = Episodes(10); - var state = new MediaCollectionEnumeratorState(); + var state = new CollectionEnumeratorState(); var randomizedContent = new RandomizedMediaCollectionEnumerator(contents, state); @@ -47,7 +47,7 @@ namespace ErsatzTV.Core.Tests.Scheduling public void State_Index_Should_Increment() { List contents = Episodes(10); - var state = new MediaCollectionEnumeratorState(); + var state = new CollectionEnumeratorState(); var randomizedContent = new RandomizedMediaCollectionEnumerator(contents, state); @@ -63,7 +63,7 @@ namespace ErsatzTV.Core.Tests.Scheduling public void State_Should_Impact_Iterator_Start() { List contents = Episodes(10); - var state = new MediaCollectionEnumeratorState { Index = 5, Seed = KnownSeed }; + var state = new CollectionEnumeratorState { Index = 5, Seed = KnownSeed }; var randomizedContent = new RandomizedMediaCollectionEnumerator(contents, state); @@ -80,12 +80,15 @@ namespace ErsatzTV.Core.Tests.Scheduling private static List Episodes(int count) => Range(1, count).Map( - i => (MediaItem) new TelevisionEpisodeMediaItem + i => (MediaItem) new Episode { Id = i, - Metadata = new TelevisionEpisodeMetadata + EpisodeMetadata = new List { - Aired = new DateTime(2020, 1, i) + new() + { + ReleaseDate = new DateTime(2020, 1, i) + } } }) .Reverse() diff --git a/ErsatzTV.Core.Tests/Scheduling/ShuffledContentTests.cs b/ErsatzTV.Core.Tests/Scheduling/ShuffledContentTests.cs index eeeaef98..9240de89 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ShuffledContentTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ShuffledContentTests.cs @@ -20,7 +20,7 @@ namespace ErsatzTV.Core.Tests.Scheduling { List contents = Episodes(10); - var state = new MediaCollectionEnumeratorState(); + var state = new CollectionEnumeratorState(); var shuffledContent = new ShuffledMediaCollectionEnumerator(contents, state); @@ -40,7 +40,7 @@ namespace ErsatzTV.Core.Tests.Scheduling public void State_Index_Should_Increment() { List contents = Episodes(10); - var state = new MediaCollectionEnumeratorState(); + var state = new CollectionEnumeratorState(); var shuffledContent = new ShuffledMediaCollectionEnumerator(contents, state); @@ -55,7 +55,7 @@ namespace ErsatzTV.Core.Tests.Scheduling public void State_Should_Impact_Iterator_Start() { List contents = Episodes(10); - var state = new MediaCollectionEnumeratorState { Index = 5, Seed = MagicSeed }; + var state = new CollectionEnumeratorState { Index = 5, Seed = MagicSeed }; var shuffledContent = new ShuffledMediaCollectionEnumerator(contents, state); @@ -70,12 +70,15 @@ namespace ErsatzTV.Core.Tests.Scheduling private static List Episodes(int count) => Range(1, count).Map( - i => (MediaItem) new TelevisionEpisodeMediaItem + i => (MediaItem) new Episode { Id = i, - Metadata = new TelevisionEpisodeMetadata + EpisodeMetadata = new List { - Aired = new DateTime(2020, 1, i) + new() + { + ReleaseDate = new DateTime(2020, 1, i) + } } }) .Reverse() diff --git a/ErsatzTV.Core/Domain/Channel.cs b/ErsatzTV.Core/Domain/Channel.cs index c12b8fad..8bae22e2 100644 --- a/ErsatzTV.Core/Domain/Channel.cs +++ b/ErsatzTV.Core/Domain/Channel.cs @@ -6,17 +6,16 @@ namespace ErsatzTV.Core.Domain public class Channel { public Channel(Guid uniqueId) => UniqueId = uniqueId; - public int Id { get; set; } public Guid UniqueId { get; init; } public int Number { get; set; } public string Name { get; set; } - public string Logo { get; set; } public int FFmpegProfileId { get; set; } public FFmpegProfile FFmpegProfile { get; set; } public StreamingMode StreamingMode { get; set; } - public List Playouts { get; set; } + + public List Artwork { get; set; } // public SourceMode Mode { get; set; } } } diff --git a/ErsatzTV.Core/Domain/Collection/Collection.cs b/ErsatzTV.Core/Domain/Collection/Collection.cs new file mode 100644 index 00000000..9f1dc292 --- /dev/null +++ b/ErsatzTV.Core/Domain/Collection/Collection.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace ErsatzTV.Core.Domain +{ + public class Collection + { + public int Id { get; set; } + public string Name { get; set; } + public List MediaItems { get; set; } + public List CollectionItems { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/Collection/CollectionItem.cs b/ErsatzTV.Core/Domain/Collection/CollectionItem.cs new file mode 100644 index 00000000..4581d124 --- /dev/null +++ b/ErsatzTV.Core/Domain/Collection/CollectionItem.cs @@ -0,0 +1,10 @@ +namespace ErsatzTV.Core.Domain +{ + public class CollectionItem + { + public int CollectionId { get; set; } + public Collection Collection { get; set; } + public int MediaItemId { get; set; } + public MediaItem MediaItem { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/MediaCollectionEnumeratorState.cs b/ErsatzTV.Core/Domain/CollectionEnumeratorState.cs similarity index 72% rename from ErsatzTV.Core/Domain/MediaCollectionEnumeratorState.cs rename to ErsatzTV.Core/Domain/CollectionEnumeratorState.cs index 9c0013f9..9f843f19 100644 --- a/ErsatzTV.Core/Domain/MediaCollectionEnumeratorState.cs +++ b/ErsatzTV.Core/Domain/CollectionEnumeratorState.cs @@ -1,6 +1,6 @@ namespace ErsatzTV.Core.Domain { - public class MediaCollectionEnumeratorState + public class CollectionEnumeratorState { public int Seed { get; set; } public int Index { get; set; } diff --git a/ErsatzTV.Core/Domain/Library/Library.cs b/ErsatzTV.Core/Domain/Library/Library.cs new file mode 100644 index 00000000..5344357b --- /dev/null +++ b/ErsatzTV.Core/Domain/Library/Library.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace ErsatzTV.Core.Domain +{ + public abstract class Library + { + public int Id { get; set; } + public string Name { get; set; } + public LibraryMediaKind MediaKind { get; set; } + public DateTime? LastScan { get; set; } + + public int MediaSourceId { get; set; } + public MediaSource MediaSource { get; set; } + + public List Paths { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/Library/LibraryMediaKind.cs b/ErsatzTV.Core/Domain/Library/LibraryMediaKind.cs new file mode 100644 index 00000000..4ac8aab6 --- /dev/null +++ b/ErsatzTV.Core/Domain/Library/LibraryMediaKind.cs @@ -0,0 +1,8 @@ +namespace ErsatzTV.Core.Domain +{ + public enum LibraryMediaKind + { + Movies = 1, + Shows = 2 + } +} diff --git a/ErsatzTV.Core/Domain/Library/LibraryPath.cs b/ErsatzTV.Core/Domain/Library/LibraryPath.cs new file mode 100644 index 00000000..074d7fa7 --- /dev/null +++ b/ErsatzTV.Core/Domain/Library/LibraryPath.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace ErsatzTV.Core.Domain +{ + public class LibraryPath + { + public int Id { get; set; } + public string Path { get; set; } + + public int LibraryId { get; set; } + public Library Library { get; set; } + + public List MediaItems { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/Library/LocalLibrary.cs b/ErsatzTV.Core/Domain/Library/LocalLibrary.cs new file mode 100644 index 00000000..787f0df5 --- /dev/null +++ b/ErsatzTV.Core/Domain/Library/LocalLibrary.cs @@ -0,0 +1,6 @@ +namespace ErsatzTV.Core.Domain +{ + public class LocalLibrary : Library + { + } +} diff --git a/ErsatzTV.Core/Domain/Library/PlexLibrary.cs b/ErsatzTV.Core/Domain/Library/PlexLibrary.cs new file mode 100644 index 00000000..bbd35c13 --- /dev/null +++ b/ErsatzTV.Core/Domain/Library/PlexLibrary.cs @@ -0,0 +1,8 @@ +namespace ErsatzTV.Core.Domain +{ + public class PlexLibrary : Library + { + public string Key { get; set; } + public bool ShouldSyncItems { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/LocalMediaSource.cs b/ErsatzTV.Core/Domain/LocalMediaSource.cs deleted file mode 100644 index 23cfcdc9..00000000 --- a/ErsatzTV.Core/Domain/LocalMediaSource.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ErsatzTV.Core.Domain -{ - public class LocalMediaSource : MediaSource - { - public LocalMediaSource() => SourceType = MediaSourceType.Local; - - public MediaType MediaType { get; set; } - public string Folder { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/LocalTelevisionShowSource.cs b/ErsatzTV.Core/Domain/LocalTelevisionShowSource.cs deleted file mode 100644 index 05515809..00000000 --- a/ErsatzTV.Core/Domain/LocalTelevisionShowSource.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace ErsatzTV.Core.Domain -{ - public class LocalTelevisionShowSource : TelevisionShowSource - { - public int MediaSourceId { get; set; } - public LocalMediaSource MediaSource { get; set; } - public string Path { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/MediaCollection.cs b/ErsatzTV.Core/Domain/MediaCollection.cs deleted file mode 100644 index 67d13016..00000000 --- a/ErsatzTV.Core/Domain/MediaCollection.cs +++ /dev/null @@ -1,10 +0,0 @@ -using LanguageExt; - -namespace ErsatzTV.Core.Domain -{ - public abstract class MediaCollection : Record - { - public int Id { get; set; } - public string Name { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/MediaItem.cs b/ErsatzTV.Core/Domain/MediaItem.cs deleted file mode 100644 index a17b305e..00000000 --- a/ErsatzTV.Core/Domain/MediaItem.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using ErsatzTV.Core.Interfaces.Domain; - -namespace ErsatzTV.Core.Domain -{ - public class MediaItem : IHasAPoster - { - public int Id { get; set; } - public int MediaSourceId { get; set; } - public MediaSource Source { get; set; } - public MediaItemStatistics Statistics { get; set; } - public DateTime? LastWriteTime { get; set; } - public string Path { get; set; } - public string Poster { get; set; } - public DateTime? PosterLastWriteTime { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/MediaItem/Episode.cs b/ErsatzTV.Core/Domain/MediaItem/Episode.cs new file mode 100644 index 00000000..2fcc5f00 --- /dev/null +++ b/ErsatzTV.Core/Domain/MediaItem/Episode.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace ErsatzTV.Core.Domain +{ + public class Episode : MediaItem + { + public int EpisodeNumber { get; set; } + public int SeasonId { get; set; } + public Season Season { get; set; } + public List EpisodeMetadata { get; set; } + public List MediaVersions { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/MediaItem/MediaFile.cs b/ErsatzTV.Core/Domain/MediaItem/MediaFile.cs new file mode 100644 index 00000000..5673ec66 --- /dev/null +++ b/ErsatzTV.Core/Domain/MediaItem/MediaFile.cs @@ -0,0 +1,11 @@ +namespace ErsatzTV.Core.Domain +{ + public class MediaFile + { + public int Id { get; set; } + public string Path { get; set; } + + public int MediaVersionId { get; set; } + public MediaVersion MediaVersion { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/MediaItem/MediaItem.cs b/ErsatzTV.Core/Domain/MediaItem/MediaItem.cs new file mode 100644 index 00000000..b1451a49 --- /dev/null +++ b/ErsatzTV.Core/Domain/MediaItem/MediaItem.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace ErsatzTV.Core.Domain +{ + public class MediaItem + { + public int Id { get; set; } + public int LibraryPathId { get; set; } + public LibraryPath LibraryPath { get; set; } + public List Collections { get; set; } + public List CollectionItems { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/MediaItemStatistics.cs b/ErsatzTV.Core/Domain/MediaItem/MediaVersion.cs similarity index 53% rename from ErsatzTV.Core/Domain/MediaItemStatistics.cs rename to ErsatzTV.Core/Domain/MediaItem/MediaVersion.cs index 56b0f59c..8373841d 100644 --- a/ErsatzTV.Core/Domain/MediaItemStatistics.cs +++ b/ErsatzTV.Core/Domain/MediaItem/MediaVersion.cs @@ -1,17 +1,24 @@ using System; +using System.Collections.Generic; using ErsatzTV.Core.Interfaces.FFmpeg; namespace ErsatzTV.Core.Domain { - public record MediaItemStatistics : IDisplaySize + public class MediaVersion : IDisplaySize { - public DateTime? LastWriteTime { get; set; } + public int Id { get; set; } + public string Name { get; set; } + + public List MediaFiles { get; set; } + public TimeSpan Duration { get; set; } public string SampleAspectRatio { get; set; } public string DisplayAspectRatio { get; set; } public string VideoCodec { get; set; } public string AudioCodec { get; set; } - public VideoScanType VideoScanType { get; set; } + public VideoScanKind VideoScanKind { get; set; } + public DateTime DateAdded { get; set; } + public DateTime DateUpdated { get; set; } public int Width { get; set; } public int Height { get; set; } } diff --git a/ErsatzTV.Core/Domain/MediaItem/Movie.cs b/ErsatzTV.Core/Domain/MediaItem/Movie.cs new file mode 100644 index 00000000..39e1f329 --- /dev/null +++ b/ErsatzTV.Core/Domain/MediaItem/Movie.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace ErsatzTV.Core.Domain +{ + public class Movie : MediaItem + { + public List MovieMetadata { get; set; } + public List MediaVersions { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/MediaItem/PlexMediaItemPart.cs b/ErsatzTV.Core/Domain/MediaItem/PlexMediaItemPart.cs new file mode 100644 index 00000000..83b803bd --- /dev/null +++ b/ErsatzTV.Core/Domain/MediaItem/PlexMediaItemPart.cs @@ -0,0 +1,12 @@ +namespace ErsatzTV.Core.Domain +{ + public class PlexMediaItemPart + { + public int Id { get; set; } + public int PlexId { get; set; } + public string Key { get; set; } + public int Duration { get; set; } + public string File { get; set; } + public int Size { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/MediaItem/PlexMovie.cs b/ErsatzTV.Core/Domain/MediaItem/PlexMovie.cs new file mode 100644 index 00000000..326ced9d --- /dev/null +++ b/ErsatzTV.Core/Domain/MediaItem/PlexMovie.cs @@ -0,0 +1,8 @@ +namespace ErsatzTV.Core.Domain +{ + public class PlexMovie : Movie + { + public string Key { get; set; } + public PlexMediaItemPart Part { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/MediaItem/Season.cs b/ErsatzTV.Core/Domain/MediaItem/Season.cs new file mode 100644 index 00000000..6eea4638 --- /dev/null +++ b/ErsatzTV.Core/Domain/MediaItem/Season.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace ErsatzTV.Core.Domain +{ + public class Season : MediaItem + { + public int SeasonNumber { get; set; } + public int ShowId { get; set; } + public Show Show { get; set; } + + public List Episodes { get; set; } + public List SeasonMetadata { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/MediaItem/Show.cs b/ErsatzTV.Core/Domain/MediaItem/Show.cs new file mode 100644 index 00000000..230e6f3d --- /dev/null +++ b/ErsatzTV.Core/Domain/MediaItem/Show.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace ErsatzTV.Core.Domain +{ + public class Show : MediaItem + { + public List Seasons { get; set; } + public List ShowMetadata { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/VideoScanType.cs b/ErsatzTV.Core/Domain/MediaItem/VideoScanKind.cs similarity index 79% rename from ErsatzTV.Core/Domain/VideoScanType.cs rename to ErsatzTV.Core/Domain/MediaItem/VideoScanKind.cs index 443ce6f0..812061ac 100644 --- a/ErsatzTV.Core/Domain/VideoScanType.cs +++ b/ErsatzTV.Core/Domain/MediaItem/VideoScanKind.cs @@ -1,6 +1,6 @@ namespace ErsatzTV.Core.Domain { - public enum VideoScanType + public enum VideoScanKind { Unknown = 0, Progressive = 1, diff --git a/ErsatzTV.Core/Domain/MediaItemMetadata.cs b/ErsatzTV.Core/Domain/MediaItemMetadata.cs deleted file mode 100644 index 4d46e894..00000000 --- a/ErsatzTV.Core/Domain/MediaItemMetadata.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ErsatzTV.Core.Domain -{ - public class MediaItemMetadata - { - public MetadataSource Source { get; set; } - public DateTime? LastWriteTime { get; set; } - public string Title { get; set; } - public string SortTitle { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/MediaSource.cs b/ErsatzTV.Core/Domain/MediaSource.cs deleted file mode 100644 index dd81b6af..00000000 --- a/ErsatzTV.Core/Domain/MediaSource.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ErsatzTV.Core.Domain -{ - public abstract class MediaSource - { - public int Id { get; set; } - public MediaSourceType SourceType { get; set; } - public string Name { get; set; } - public DateTimeOffset? LastScan { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/MediaSource/LocalMediaSource.cs b/ErsatzTV.Core/Domain/MediaSource/LocalMediaSource.cs new file mode 100644 index 00000000..ad0c4642 --- /dev/null +++ b/ErsatzTV.Core/Domain/MediaSource/LocalMediaSource.cs @@ -0,0 +1,6 @@ +namespace ErsatzTV.Core.Domain +{ + public class LocalMediaSource : MediaSource + { + } +} diff --git a/ErsatzTV.Core/Domain/MediaSource/MediaSource.cs b/ErsatzTV.Core/Domain/MediaSource/MediaSource.cs new file mode 100644 index 00000000..62340ebd --- /dev/null +++ b/ErsatzTV.Core/Domain/MediaSource/MediaSource.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace ErsatzTV.Core.Domain +{ + public abstract class MediaSource + { + public int Id { get; set; } + + public List Libraries { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/PlexMediaSourceConnection.cs b/ErsatzTV.Core/Domain/MediaSource/PlexConnection.cs similarity index 53% rename from ErsatzTV.Core/Domain/PlexMediaSourceConnection.cs rename to ErsatzTV.Core/Domain/MediaSource/PlexConnection.cs index 9083a5b4..18313b8f 100644 --- a/ErsatzTV.Core/Domain/PlexMediaSourceConnection.cs +++ b/ErsatzTV.Core/Domain/MediaSource/PlexConnection.cs @@ -1,9 +1,11 @@ namespace ErsatzTV.Core.Domain { - public class PlexMediaSourceConnection + public class PlexConnection { public int Id { get; set; } public bool IsActive { get; set; } public string Uri { get; set; } + public int PlexMediaSourceId { get; set; } + public PlexMediaSource PlexMediaSource { get; set; } } } diff --git a/ErsatzTV.Core/Domain/PlexMediaSource.cs b/ErsatzTV.Core/Domain/MediaSource/PlexMediaSource.cs similarity index 56% rename from ErsatzTV.Core/Domain/PlexMediaSource.cs rename to ErsatzTV.Core/Domain/MediaSource/PlexMediaSource.cs index ef882cce..4206c93f 100644 --- a/ErsatzTV.Core/Domain/PlexMediaSource.cs +++ b/ErsatzTV.Core/Domain/MediaSource/PlexMediaSource.cs @@ -4,13 +4,11 @@ namespace ErsatzTV.Core.Domain { public class PlexMediaSource : MediaSource { - public PlexMediaSource() => SourceType = MediaSourceType.Plex; + public string ServerName { get; set; } public string ProductVersion { get; set; } - public string ClientIdentifier { get; set; } // public bool IsOwned { get; set; } - public List Connections { get; set; } - public List Libraries { get; set; } + public List Connections { get; set; } } } diff --git a/ErsatzTV.Core/Domain/MediaSourceType.cs b/ErsatzTV.Core/Domain/MediaSourceType.cs deleted file mode 100644 index 05406efa..00000000 --- a/ErsatzTV.Core/Domain/MediaSourceType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ErsatzTV.Core.Domain -{ - public enum MediaSourceType - { - None = 0, - Local = 1, - Plex = 2, - Jellyfin = 3 - } -} diff --git a/ErsatzTV.Core/Domain/MediaType.cs b/ErsatzTV.Core/Domain/MediaType.cs deleted file mode 100644 index 49d20821..00000000 --- a/ErsatzTV.Core/Domain/MediaType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace ErsatzTV.Core.Domain -{ - public enum MediaType - { - Other = 0, - TvShow = 1, - Movie = 2 - } -} diff --git a/ErsatzTV.Core/Domain/Metadata/Artwork.cs b/ErsatzTV.Core/Domain/Metadata/Artwork.cs new file mode 100644 index 00000000..674a1599 --- /dev/null +++ b/ErsatzTV.Core/Domain/Metadata/Artwork.cs @@ -0,0 +1,13 @@ +using System; + +namespace ErsatzTV.Core.Domain +{ + public class Artwork + { + public int Id { get; set; } + public string Path { get; set; } + public ArtworkKind ArtworkKind { get; set; } + public DateTime DateAdded { get; set; } + public DateTime DateUpdated { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/Metadata/ArtworkKind.cs b/ErsatzTV.Core/Domain/Metadata/ArtworkKind.cs new file mode 100644 index 00000000..800c4d49 --- /dev/null +++ b/ErsatzTV.Core/Domain/Metadata/ArtworkKind.cs @@ -0,0 +1,9 @@ +namespace ErsatzTV.Core.Domain +{ + public enum ArtworkKind + { + Poster = 0, + Thumbnail = 1, + Logo = 2 + } +} diff --git a/ErsatzTV.Core/Domain/Metadata/EpisodeMetadata.cs b/ErsatzTV.Core/Domain/Metadata/EpisodeMetadata.cs new file mode 100644 index 00000000..61fe29c3 --- /dev/null +++ b/ErsatzTV.Core/Domain/Metadata/EpisodeMetadata.cs @@ -0,0 +1,11 @@ +namespace ErsatzTV.Core.Domain +{ + public class EpisodeMetadata : Metadata + { + public string Outline { get; set; } + public string Plot { get; set; } + public string Tagline { get; set; } + public int EpisodeId { get; set; } + public Episode Episode { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/Metadata/Metadata.cs b/ErsatzTV.Core/Domain/Metadata/Metadata.cs new file mode 100644 index 00000000..e63538ff --- /dev/null +++ b/ErsatzTV.Core/Domain/Metadata/Metadata.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace ErsatzTV.Core.Domain +{ + public class Metadata + { + public int Id { get; set; } + public MetadataKind MetadataKind { get; set; } + public string Title { get; set; } + public string OriginalTitle { get; set; } + public string SortTitle { get; set; } + public int? Year { get; set; } + public DateTime? ReleaseDate { get; set; } + public DateTime DateAdded { get; set; } + public DateTime DateUpdated { get; set; } + public List Artwork { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/Metadata/MetadataKind.cs b/ErsatzTV.Core/Domain/Metadata/MetadataKind.cs new file mode 100644 index 00000000..d480e9b5 --- /dev/null +++ b/ErsatzTV.Core/Domain/Metadata/MetadataKind.cs @@ -0,0 +1,9 @@ +namespace ErsatzTV.Core.Domain +{ + public enum MetadataKind + { + Fallback = 0, + Sidecar = 1, + External + } +} diff --git a/ErsatzTV.Core/Domain/Metadata/MovieMetadata.cs b/ErsatzTV.Core/Domain/Metadata/MovieMetadata.cs new file mode 100644 index 00000000..6c57e1dc --- /dev/null +++ b/ErsatzTV.Core/Domain/Metadata/MovieMetadata.cs @@ -0,0 +1,11 @@ +namespace ErsatzTV.Core.Domain +{ + public class MovieMetadata : Metadata + { + public string Outline { get; set; } + public string Plot { get; set; } + public string Tagline { get; set; } + public int MovieId { get; set; } + public Movie Movie { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/Metadata/SeasonMetadata.cs b/ErsatzTV.Core/Domain/Metadata/SeasonMetadata.cs new file mode 100644 index 00000000..19e3619a --- /dev/null +++ b/ErsatzTV.Core/Domain/Metadata/SeasonMetadata.cs @@ -0,0 +1,9 @@ +namespace ErsatzTV.Core.Domain +{ + public class SeasonMetadata : Metadata + { + public string Outline { get; set; } + public int SeasonId { get; set; } + public Season Season { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/Metadata/ShowMetadata.cs b/ErsatzTV.Core/Domain/Metadata/ShowMetadata.cs new file mode 100644 index 00000000..855ba29d --- /dev/null +++ b/ErsatzTV.Core/Domain/Metadata/ShowMetadata.cs @@ -0,0 +1,11 @@ +namespace ErsatzTV.Core.Domain +{ + public class ShowMetadata : Metadata + { + public string Outline { get; set; } + public string Plot { get; set; } + public string Tagline { get; set; } + public int ShowId { get; set; } + public Show Show { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/MovieMediaItem.cs b/ErsatzTV.Core/Domain/MovieMediaItem.cs deleted file mode 100644 index 9b2d44fe..00000000 --- a/ErsatzTV.Core/Domain/MovieMediaItem.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace ErsatzTV.Core.Domain -{ - public class MovieMediaItem : MediaItem - { - public int MetadataId { get; set; } - public MovieMetadata Metadata { get; set; } - public List SimpleMediaCollections { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/MovieMetadata.cs b/ErsatzTV.Core/Domain/MovieMetadata.cs deleted file mode 100644 index 3c4009ee..00000000 --- a/ErsatzTV.Core/Domain/MovieMetadata.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace ErsatzTV.Core.Domain -{ - public class MovieMetadata : MediaItemMetadata - { - public int Id { get; set; } - public int MovieId { get; set; } - public MovieMediaItem Movie { get; set; } - public int? Year { get; set; } - public DateTime? Premiered { get; set; } - public string Plot { get; set; } - public string Outline { get; set; } - public string Tagline { get; set; } - public string ContentRating { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/PlayoutAnchor.cs b/ErsatzTV.Core/Domain/PlayoutAnchor.cs index 6b99ba12..a5ae3a59 100644 --- a/ErsatzTV.Core/Domain/PlayoutAnchor.cs +++ b/ErsatzTV.Core/Domain/PlayoutAnchor.cs @@ -8,6 +8,8 @@ namespace ErsatzTV.Core.Domain public ProgramScheduleItem NextScheduleItem { get; set; } - public DateTimeOffset NextStart { get; set; } + public DateTime NextStart { get; set; } + + public DateTimeOffset NextStartOffset => new DateTimeOffset(NextStart, TimeSpan.Zero).ToLocalTime(); } } diff --git a/ErsatzTV.Core/Domain/PlayoutItem.cs b/ErsatzTV.Core/Domain/PlayoutItem.cs index 805db3c5..3982dddb 100644 --- a/ErsatzTV.Core/Domain/PlayoutItem.cs +++ b/ErsatzTV.Core/Domain/PlayoutItem.cs @@ -7,9 +7,12 @@ namespace ErsatzTV.Core.Domain public int Id { get; set; } public int MediaItemId { get; set; } public MediaItem MediaItem { get; set; } - public DateTimeOffset Start { get; set; } - public DateTimeOffset Finish { get; set; } + public DateTime Start { get; set; } + public DateTime Finish { get; set; } public int PlayoutId { get; set; } public Playout Playout { get; set; } + + public DateTimeOffset StartOffset => new DateTimeOffset(Start, TimeSpan.Zero).ToLocalTime(); + public DateTimeOffset FinishOffset => new DateTimeOffset(Finish, TimeSpan.Zero).ToLocalTime(); } } diff --git a/ErsatzTV.Core/Domain/PlayoutProgramScheduleAnchor.cs b/ErsatzTV.Core/Domain/PlayoutProgramScheduleAnchor.cs index d7cea705..4f7ec584 100644 --- a/ErsatzTV.Core/Domain/PlayoutProgramScheduleAnchor.cs +++ b/ErsatzTV.Core/Domain/PlayoutProgramScheduleAnchor.cs @@ -8,7 +8,10 @@ public int ProgramScheduleId { get; set; } public ProgramSchedule ProgramSchedule { get; set; } public ProgramScheduleItemCollectionType CollectionType { get; set; } - public int CollectionId { get; set; } - public MediaCollectionEnumeratorState EnumeratorState { get; set; } + public int? CollectionId { get; set; } + public Collection Collection { get; set; } + public int? MediaItemId { get; set; } + public MediaItem MediaItem { get; set; } + public CollectionEnumeratorState EnumeratorState { get; set; } } } diff --git a/ErsatzTV.Core/Domain/PlexMediaSourceLibrary.cs b/ErsatzTV.Core/Domain/PlexMediaSourceLibrary.cs deleted file mode 100644 index 204ffe0e..00000000 --- a/ErsatzTV.Core/Domain/PlexMediaSourceLibrary.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ErsatzTV.Core.Domain -{ - public class PlexMediaSourceLibrary - { - public int Id { get; set; } - public string Key { get; init; } - public string Name { get; init; } - public MediaType MediaType { get; init; } - } -} diff --git a/ErsatzTV.Core/Domain/ProgramScheduleItem.cs b/ErsatzTV.Core/Domain/ProgramScheduleItem.cs index ecc12d02..deb069d0 100644 --- a/ErsatzTV.Core/Domain/ProgramScheduleItem.cs +++ b/ErsatzTV.Core/Domain/ProgramScheduleItem.cs @@ -9,13 +9,11 @@ namespace ErsatzTV.Core.Domain public StartType StartType => StartTime.HasValue ? StartType.Fixed : StartType.Dynamic; public TimeSpan? StartTime { get; set; } public ProgramScheduleItemCollectionType CollectionType { get; set; } - public int? MediaCollectionId { get; set; } - public MediaCollection MediaCollection { get; set; } - public int? TelevisionShowId { get; set; } - public TelevisionShow TelevisionShow { get; set; } - public int? TelevisionSeasonId { get; set; } - public TelevisionSeason TelevisionSeason { get; set; } public int ProgramScheduleId { get; set; } public ProgramSchedule ProgramSchedule { get; set; } + public int? CollectionId { get; set; } + public Collection Collection { get; set; } + public int? MediaItemId { get; set; } + public MediaItem MediaItem { get; set; } } } diff --git a/ErsatzTV.Core/Domain/SimpleMediaCollection.cs b/ErsatzTV.Core/Domain/SimpleMediaCollection.cs deleted file mode 100644 index f475a4ad..00000000 --- a/ErsatzTV.Core/Domain/SimpleMediaCollection.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; - -namespace ErsatzTV.Core.Domain -{ - public class SimpleMediaCollection : MediaCollection - { - public List Movies { get; set; } - public List TelevisionShows { get; set; } - public List TelevisionSeasons { get; set; } - public List TelevisionEpisodes { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/TelevisionEpisodeMediaItem.cs b/ErsatzTV.Core/Domain/TelevisionEpisodeMediaItem.cs deleted file mode 100644 index bd2d66bc..00000000 --- a/ErsatzTV.Core/Domain/TelevisionEpisodeMediaItem.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; - -namespace ErsatzTV.Core.Domain -{ - public class TelevisionEpisodeMediaItem : MediaItem - { - public int SeasonId { get; set; } - public TelevisionSeason Season { get; set; } - public TelevisionEpisodeMetadata Metadata { get; set; } - public List SimpleMediaCollections { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/TelevisionEpisodeMetadata.cs b/ErsatzTV.Core/Domain/TelevisionEpisodeMetadata.cs deleted file mode 100644 index 9b210e4e..00000000 --- a/ErsatzTV.Core/Domain/TelevisionEpisodeMetadata.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace ErsatzTV.Core.Domain -{ - public class TelevisionEpisodeMetadata : MediaItemMetadata - { - public int Id { get; set; } - public int TelevisionEpisodeId { get; set; } - public TelevisionEpisodeMediaItem TelevisionEpisode { get; set; } - public int Season { get; set; } - public int Episode { get; set; } - public string Plot { get; set; } - public DateTime? Aired { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/TelevisionSeason.cs b/ErsatzTV.Core/Domain/TelevisionSeason.cs deleted file mode 100644 index 009bbf3c..00000000 --- a/ErsatzTV.Core/Domain/TelevisionSeason.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using ErsatzTV.Core.Interfaces.Domain; - -namespace ErsatzTV.Core.Domain -{ - public class TelevisionSeason : IHasAPoster - { - public int Id { get; set; } - public int TelevisionShowId { get; set; } - public TelevisionShow TelevisionShow { get; set; } - public int Number { get; set; } - public List Episodes { get; set; } - public List SimpleMediaCollections { get; set; } - public string Path { get; set; } - public string Poster { get; set; } - public DateTime? PosterLastWriteTime { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/TelevisionShow.cs b/ErsatzTV.Core/Domain/TelevisionShow.cs deleted file mode 100644 index e50793d2..00000000 --- a/ErsatzTV.Core/Domain/TelevisionShow.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace ErsatzTV.Core.Domain -{ - public class TelevisionShow - { - public int Id { get; set; } - public List Sources { get; set; } - public TelevisionShowMetadata Metadata { get; set; } - public List Seasons { get; set; } - public string Poster { get; set; } - public DateTime? PosterLastWriteTime { get; set; } - public List SimpleMediaCollections { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/TelevisionShowMetadata.cs b/ErsatzTV.Core/Domain/TelevisionShowMetadata.cs deleted file mode 100644 index 914de83d..00000000 --- a/ErsatzTV.Core/Domain/TelevisionShowMetadata.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace ErsatzTV.Core.Domain -{ - public class TelevisionShowMetadata - { - public int Id { get; set; } - public int TelevisionShowId { get; set; } - public TelevisionShow TelevisionShow { get; set; } - public MetadataSource Source { get; set; } - public DateTime? LastWriteTime { get; set; } - public string Title { get; set; } - public string SortTitle { get; set; } - public int? Year { get; set; } - public string Plot { get; set; } - } -} diff --git a/ErsatzTV.Core/Domain/TelevisionShowSource.cs b/ErsatzTV.Core/Domain/TelevisionShowSource.cs deleted file mode 100644 index 43f7890a..00000000 --- a/ErsatzTV.Core/Domain/TelevisionShowSource.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace ErsatzTV.Core.Domain -{ - public class TelevisionShowSource - { - public int Id { get; set; } - public int TelevisionShowId { get; set; } - public TelevisionShow TelevisionShow { get; set; } - } -} diff --git a/ErsatzTV.Core/ErsatzTV.Core.csproj.DotSettings b/ErsatzTV.Core/ErsatzTV.Core.csproj.DotSettings new file mode 100644 index 00000000..4184bb16 --- /dev/null +++ b/ErsatzTV.Core/ErsatzTV.Core.csproj.DotSettings @@ -0,0 +1,7 @@ + + True + True + True + True + True + True \ No newline at end of file diff --git a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs index 479b906f..d9e05778 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs @@ -44,7 +44,8 @@ namespace ErsatzTV.Core.FFmpeg public FFmpegPlaybackSettings CalculateSettings( StreamingMode streamingMode, FFmpegProfile ffmpegProfile, - PlayoutItem playoutItem, + MediaVersion version, + DateTimeOffset start, DateTimeOffset now) { var result = new FFmpegPlaybackSettings @@ -53,9 +54,9 @@ namespace ErsatzTV.Core.FFmpeg FormatFlags = CommonFormatFlags }; - if (now != playoutItem.Start) + if (now != start) { - result.StreamSeek = now - playoutItem.Start; + result.StreamSeek = now - start; } switch (streamingMode) @@ -66,24 +67,23 @@ namespace ErsatzTV.Core.FFmpeg result.Deinterlace = false; break; case StreamingMode.TransportStream: - if (NeedToScale(ffmpegProfile, playoutItem.MediaItem.Statistics)) + if (NeedToScale(ffmpegProfile, version)) { - IDisplaySize scaledSize = CalculateScaledSize(ffmpegProfile, playoutItem.MediaItem.Statistics); - if (!scaledSize.IsSameSizeAs(playoutItem.MediaItem.Statistics)) + IDisplaySize scaledSize = CalculateScaledSize(ffmpegProfile, version); + if (!scaledSize.IsSameSizeAs(version)) { - result.ScaledSize = Some( - CalculateScaledSize(ffmpegProfile, playoutItem.MediaItem.Statistics)); + result.ScaledSize = Some(CalculateScaledSize(ffmpegProfile, version)); } } - IDisplaySize sizeAfterScaling = result.ScaledSize.IfNone(playoutItem.MediaItem.Statistics); + IDisplaySize sizeAfterScaling = result.ScaledSize.IfNone(version); if (!sizeAfterScaling.IsSameSizeAs(ffmpegProfile.Resolution)) { result.PadToDesiredResolution = true; } if (result.ScaledSize.IsSome || result.PadToDesiredResolution || - NeedToNormalizeVideoCodec(ffmpegProfile, playoutItem.MediaItem.Statistics)) + NeedToNormalizeVideoCodec(ffmpegProfile, version)) { result.VideoCodec = ffmpegProfile.VideoCodec; result.VideoBitrate = ffmpegProfile.VideoBitrate; @@ -94,7 +94,7 @@ namespace ErsatzTV.Core.FFmpeg result.VideoCodec = "copy"; } - if (NeedToNormalizeAudioCodec(ffmpegProfile, playoutItem.MediaItem.Statistics)) + if (NeedToNormalizeAudioCodec(ffmpegProfile, version)) { result.AudioCodec = ffmpegProfile.AudioCodec; result.AudioBitrate = ffmpegProfile.AudioBitrate; @@ -104,7 +104,7 @@ namespace ErsatzTV.Core.FFmpeg { result.AudioChannels = ffmpegProfile.AudioChannels; result.AudioSampleRate = ffmpegProfile.AudioSampleRate; - result.AudioDuration = playoutItem.MediaItem.Statistics.Duration; + result.AudioDuration = version.Duration; } } else @@ -112,7 +112,7 @@ namespace ErsatzTV.Core.FFmpeg result.AudioCodec = "copy"; } - if (playoutItem.MediaItem.Statistics.VideoScanType == VideoScanType.Interlaced) + if (version.VideoScanKind == VideoScanKind.Interlaced) { result.Deinterlace = true; } @@ -132,35 +132,35 @@ namespace ErsatzTV.Core.FFmpeg AudioCodec = ffmpegProfile.AudioCodec }; - private static bool NeedToScale(FFmpegProfile ffmpegProfile, MediaItemStatistics statistics) => + private static bool NeedToScale(FFmpegProfile ffmpegProfile, MediaVersion version) => ffmpegProfile.NormalizeResolution && - IsIncorrectSize(ffmpegProfile.Resolution, statistics) || - IsTooLarge(ffmpegProfile.Resolution, statistics) || - IsOddSize(statistics); + IsIncorrectSize(ffmpegProfile.Resolution, version) || + IsTooLarge(ffmpegProfile.Resolution, version) || + IsOddSize(version); - private static bool IsIncorrectSize(IDisplaySize desiredResolution, MediaItemStatistics statistics) => - IsAnamorphic(statistics) || - statistics.Width != desiredResolution.Width || - statistics.Height != desiredResolution.Height; + private static bool IsIncorrectSize(IDisplaySize desiredResolution, MediaVersion version) => + IsAnamorphic(version) || + version.Width != desiredResolution.Width || + version.Height != desiredResolution.Height; - private static bool IsTooLarge(IDisplaySize desiredResolution, IDisplaySize mediaSize) => - mediaSize.Height > desiredResolution.Height || - mediaSize.Width > desiredResolution.Width; + private static bool IsTooLarge(IDisplaySize desiredResolution, IDisplaySize displaySize) => + displaySize.Height > desiredResolution.Height || + displaySize.Width > desiredResolution.Width; - private static bool IsOddSize(IDisplaySize displaySize) => - displaySize.Height % 2 == 1 || displaySize.Width % 2 == 1; + private static bool IsOddSize(MediaVersion version) => + version.Height % 2 == 1 || version.Width % 2 == 1; - private static bool NeedToNormalizeVideoCodec(FFmpegProfile ffmpegProfile, MediaItemStatistics statistics) => - ffmpegProfile.NormalizeVideoCodec && ffmpegProfile.VideoCodec != statistics.VideoCodec; + private static bool NeedToNormalizeVideoCodec(FFmpegProfile ffmpegProfile, MediaVersion version) => + ffmpegProfile.NormalizeVideoCodec && ffmpegProfile.VideoCodec != version.VideoCodec; - private static bool NeedToNormalizeAudioCodec(FFmpegProfile ffmpegProfile, MediaItemStatistics statistics) => - ffmpegProfile.NormalizeAudioCodec && ffmpegProfile.AudioCodec != statistics.AudioCodec; + private static bool NeedToNormalizeAudioCodec(FFmpegProfile ffmpegProfile, MediaVersion version) => + ffmpegProfile.NormalizeAudioCodec && ffmpegProfile.AudioCodec != version.AudioCodec; - private static IDisplaySize CalculateScaledSize(FFmpegProfile ffmpegProfile, MediaItemStatistics statistics) + private static IDisplaySize CalculateScaledSize(FFmpegProfile ffmpegProfile, MediaVersion version) { - IDisplaySize sarSize = SARSize(statistics); - int p = statistics.Width * sarSize.Width; - int q = statistics.Height * sarSize.Height; + IDisplaySize sarSize = SARSize(version); + int p = version.Width * sarSize.Width; + int q = version.Height * sarSize.Height; int g = Gcd(q, p); p = p / g; q = q / g; @@ -194,29 +194,29 @@ namespace ErsatzTV.Core.FFmpeg return a | b; } - private static bool IsAnamorphic(MediaItemStatistics statistics) + private static bool IsAnamorphic(MediaVersion version) { - if (statistics.SampleAspectRatio == "1:1") + if (version.SampleAspectRatio == "1:1") { return false; } - if (statistics.SampleAspectRatio != "0:1") + if (version.SampleAspectRatio != "0:1") { return true; } - if (statistics.DisplayAspectRatio == "0:1") + if (version.DisplayAspectRatio == "0:1") { return false; } - return statistics.DisplayAspectRatio != $"{statistics.Width}:{statistics.Height}"; + return version.DisplayAspectRatio != $"{version.Width}:{version.Height}"; } - private static IDisplaySize SARSize(MediaItemStatistics statistics) + private static IDisplaySize SARSize(MediaVersion version) { - string[] split = statistics.SampleAspectRatio.Split(":"); + string[] split = version.SampleAspectRatio.Split(":"); return new DisplaySize(int.Parse(split[0]), int.Parse(split[1])); } } diff --git a/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs b/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs index 99fd9ad8..90f45c50 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs @@ -18,19 +18,29 @@ namespace ErsatzTV.Core.FFmpeg PlayoutItem item, DateTimeOffset now) { + MediaVersion version = item.MediaItem switch + { + Movie m => m.MediaVersions.Head(), + Episode e => e.MediaVersions.Head(), + _ => throw new ArgumentOutOfRangeException(nameof(item)) + }; + FFmpegPlaybackSettings playbackSettings = _playbackSettingsCalculator.CalculateSettings( channel.StreamingMode, channel.FFmpegProfile, - item, + version, + item.StartOffset, now); + MediaFile file = version.MediaFiles.Head(); + FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath) .WithThreads(playbackSettings.ThreadCount) .WithQuiet() .WithFormatFlags(playbackSettings.FormatFlags) .WithRealtimeOutput(playbackSettings.RealtimeOutput) .WithSeek(playbackSettings.StreamSeek) - .WithInput(item.MediaItem.Path); + .WithInput(file.Path); playbackSettings.ScaledSize.Match( scaledSize => @@ -76,7 +86,7 @@ namespace ErsatzTV.Core.FFmpeg return builder.WithPlaybackArgs(playbackSettings) .WithMetadata(channel) .WithFormat("mpegts") - .WithDuration(item.Start + item.MediaItem.Statistics.Duration - now) + .WithDuration(item.Start + version.Duration - now) .WithPipe() .Build(); } diff --git a/ErsatzTV.Core/FileSystemLayout.cs b/ErsatzTV.Core/FileSystemLayout.cs index 258c619c..f78453ec 100644 --- a/ErsatzTV.Core/FileSystemLayout.cs +++ b/ErsatzTV.Core/FileSystemLayout.cs @@ -15,8 +15,14 @@ namespace ErsatzTV.Core public static readonly string LogDatabasePath = Path.Combine(AppDataFolder, "logs.sqlite3"); - public static readonly string ImageCacheFolder = Path.Combine(AppDataFolder, "cache", "images"); + public static readonly string LegacyImageCacheFolder = Path.Combine(AppDataFolder, "cache", "images"); public static readonly string PlexSecretsPath = Path.Combine(AppDataFolder, "plex-secrets.json"); + + public static readonly string ArtworkCacheFolder = Path.Combine(AppDataFolder, "cache", "artwork"); + + public static readonly string PosterCacheFolder = Path.Combine(ArtworkCacheFolder, "posters"); + public static readonly string ThumbnailCacheFolder = Path.Combine(ArtworkCacheFolder, "thumbnails"); + public static readonly string LogoCacheFolder = Path.Combine(ArtworkCacheFolder, "logos"); } } diff --git a/ErsatzTV.Core/Interfaces/Domain/IHasAPoster.cs b/ErsatzTV.Core/Interfaces/Domain/IHasAPoster.cs deleted file mode 100644 index 72007f6e..00000000 --- a/ErsatzTV.Core/Interfaces/Domain/IHasAPoster.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace ErsatzTV.Core.Interfaces.Domain -{ - public interface IHasAPoster - { - string Path { get; set; } - string Poster { get; set; } - DateTime? PosterLastWriteTime { get; set; } - } -} diff --git a/ErsatzTV.Core/Interfaces/Images/IImageCache.cs b/ErsatzTV.Core/Interfaces/Images/IImageCache.cs index 5d031a05..5b8737d8 100644 --- a/ErsatzTV.Core/Interfaces/Images/IImageCache.cs +++ b/ErsatzTV.Core/Interfaces/Images/IImageCache.cs @@ -1,11 +1,13 @@ using System.Threading.Tasks; +using ErsatzTV.Core.Domain; using LanguageExt; namespace ErsatzTV.Core.Interfaces.Images { public interface IImageCache { - Task> ResizeAndSaveImage(byte[] imageBuffer, int? height, int? width); - Task> SaveImage(byte[] imageBuffer); + Task> ResizeImage(byte[] imageBuffer, int height); + Task> SaveArtworkToCache(byte[] imageBuffer, ArtworkKind artworkKind); + string CopyArtworkToCache(string path, ArtworkKind artworkKind); } } diff --git a/ErsatzTV.Core/Interfaces/Locking/IEntityLocker.cs b/ErsatzTV.Core/Interfaces/Locking/IEntityLocker.cs index e51ccf70..fc867654 100644 --- a/ErsatzTV.Core/Interfaces/Locking/IEntityLocker.cs +++ b/ErsatzTV.Core/Interfaces/Locking/IEntityLocker.cs @@ -4,9 +4,9 @@ namespace ErsatzTV.Core.Interfaces.Locking { public interface IEntityLocker { - event EventHandler OnMediaSourceChanged; - bool LockMediaSource(int mediaSourceId); - bool UnlockMediaSource(int mediaSourceId); - bool IsMediaSourceLocked(int mediaSourceId); + event EventHandler OnLibraryChanged; + bool LockLibrary(int libraryId); + bool UnlockLibrary(int libraryId); + bool IsLibraryLocked(int libraryId); } } diff --git a/ErsatzTV.Core/Interfaces/Metadata/IFallbackMetadataProvider.cs b/ErsatzTV.Core/Interfaces/Metadata/IFallbackMetadataProvider.cs index 4325b1fd..52125561 100644 --- a/ErsatzTV.Core/Interfaces/Metadata/IFallbackMetadataProvider.cs +++ b/ErsatzTV.Core/Interfaces/Metadata/IFallbackMetadataProvider.cs @@ -4,6 +4,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata { public interface IFallbackMetadataProvider { - TelevisionShowMetadata GetFallbackMetadataForShow(string showFolder); + ShowMetadata GetFallbackMetadataForShow(string showFolder); } } diff --git a/ErsatzTV.Core/Interfaces/Metadata/ILocalFileSystem.cs b/ErsatzTV.Core/Interfaces/Metadata/ILocalFileSystem.cs index 4a698e0d..e28155ac 100644 --- a/ErsatzTV.Core/Interfaces/Metadata/ILocalFileSystem.cs +++ b/ErsatzTV.Core/Interfaces/Metadata/ILocalFileSystem.cs @@ -2,16 +2,18 @@ using System.Collections.Generic; using System.Threading.Tasks; using ErsatzTV.Core.Domain; +using LanguageExt; namespace ErsatzTV.Core.Interfaces.Metadata { public interface ILocalFileSystem { DateTime GetLastWriteTime(string path); - bool IsMediaSourceAccessible(LocalMediaSource localMediaSource); + bool IsLibraryPathAccessible(LibraryPath libraryPath); IEnumerable ListSubdirectories(string folder); IEnumerable ListFiles(string folder); bool FileExists(string path); Task ReadAllBytes(string path); + Unit CopyFile(string source, string destination); } } diff --git a/ErsatzTV.Core/Interfaces/Metadata/ILocalMetadataProvider.cs b/ErsatzTV.Core/Interfaces/Metadata/ILocalMetadataProvider.cs index 317e3588..c4f29c4f 100644 --- a/ErsatzTV.Core/Interfaces/Metadata/ILocalMetadataProvider.cs +++ b/ErsatzTV.Core/Interfaces/Metadata/ILocalMetadataProvider.cs @@ -6,10 +6,10 @@ namespace ErsatzTV.Core.Interfaces.Metadata { public interface ILocalMetadataProvider { - Task GetMetadataForShow(string showFolder); + Task GetMetadataForShow(string showFolder); Task RefreshSidecarMetadata(MediaItem mediaItem, string path); - Task RefreshSidecarMetadata(TelevisionShow televisionShow, string showFolder); + Task RefreshSidecarMetadata(Show televisionShow, string showFolder); Task RefreshFallbackMetadata(MediaItem mediaItem); - Task RefreshFallbackMetadata(TelevisionShow televisionShow, string showFolder); + Task RefreshFallbackMetadata(Show televisionShow, string showFolder); } } diff --git a/ErsatzTV.Core/Interfaces/Metadata/IMovieFolderScanner.cs b/ErsatzTV.Core/Interfaces/Metadata/IMovieFolderScanner.cs index cf43a87d..0c01b009 100644 --- a/ErsatzTV.Core/Interfaces/Metadata/IMovieFolderScanner.cs +++ b/ErsatzTV.Core/Interfaces/Metadata/IMovieFolderScanner.cs @@ -6,6 +6,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata { public interface IMovieFolderScanner { - Task> ScanFolder(LocalMediaSource localMediaSource, string ffprobePath); + Task> ScanFolder(LibraryPath libraryPath, string ffprobePath); } } diff --git a/ErsatzTV.Core/Interfaces/Metadata/ITelevisionFolderScanner.cs b/ErsatzTV.Core/Interfaces/Metadata/ITelevisionFolderScanner.cs index b000216b..c32b7705 100644 --- a/ErsatzTV.Core/Interfaces/Metadata/ITelevisionFolderScanner.cs +++ b/ErsatzTV.Core/Interfaces/Metadata/ITelevisionFolderScanner.cs @@ -6,6 +6,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata { public interface ITelevisionFolderScanner { - Task ScanFolder(LocalMediaSource localMediaSource, string ffprobePath); + Task> ScanFolder(LibraryPath libraryPath, string ffprobePath); } } diff --git a/ErsatzTV.Core/Interfaces/Plex/IPlexMovieLibraryScanner.cs b/ErsatzTV.Core/Interfaces/Plex/IPlexMovieLibraryScanner.cs new file mode 100644 index 00000000..f802b334 --- /dev/null +++ b/ErsatzTV.Core/Interfaces/Plex/IPlexMovieLibraryScanner.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Plex; +using LanguageExt; + +namespace ErsatzTV.Core.Interfaces.Plex +{ + public interface IPlexMovieLibraryScanner + { + Task> ScanLibrary( + PlexConnection connection, + PlexServerAuthToken token, + PlexLibrary plexMediaSourceLibrary); + } +} diff --git a/ErsatzTV.Core/Interfaces/Plex/IPlexServerApiClient.cs b/ErsatzTV.Core/Interfaces/Plex/IPlexServerApiClient.cs index e933d331..87e650db 100644 --- a/ErsatzTV.Core/Interfaces/Plex/IPlexServerApiClient.cs +++ b/ErsatzTV.Core/Interfaces/Plex/IPlexServerApiClient.cs @@ -8,8 +8,13 @@ namespace ErsatzTV.Core.Interfaces.Plex { public interface IPlexServerApiClient { - Task>> GetLibraries( - PlexMediaSourceConnection connection, + Task>> GetLibraries( + PlexConnection connection, + PlexServerAuthToken token); + + Task>> GetLibraryContents( + PlexLibrary library, + PlexConnection connection, PlexServerAuthToken token); } } diff --git a/ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs new file mode 100644 index 00000000..8ecca06f --- /dev/null +++ b/ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using ErsatzTV.Core.Domain; +using LanguageExt; + +namespace ErsatzTV.Core.Interfaces.Repositories +{ + public interface ILibraryRepository + { + Task Add(LibraryPath libraryPath); + Task> Get(int libraryId); + Task> GetLocal(int libraryId); + Task> GetAllLocal(); + Task UpdateLastScan(Library library); + Task> GetLocalPaths(int libraryId); + Task> GetPath(int libraryPathId); + Task CountMediaItemsByPath(int libraryPathId); + Task DeleteLocalPath(int libraryPathId); + } +} diff --git a/ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs index 4dd81dfb..248fa076 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs @@ -7,16 +7,14 @@ namespace ErsatzTV.Core.Interfaces.Repositories { public interface IMediaCollectionRepository { - Task Add(SimpleMediaCollection collection); - Task> Get(int id); - Task> GetSimpleMediaCollection(int id); - Task> GetSimpleMediaCollectionWithItems(int id); - Task> GetSimpleMediaCollectionWithItemsUntracked(int id); - Task> GetSimpleMediaCollections(); - Task> GetAll(); + Task Add(Collection collection); + Task AddMediaItem(int collectionId, int mediaItemId); + Task> Get(int id); + Task> GetCollectionWithItems(int id); + Task> GetCollectionWithItemsUntracked(int id); + Task> GetAll(); Task>> GetItems(int id); - Task>> GetSimpleMediaCollectionItems(int id); - Task Update(SimpleMediaCollection collection); - Task Delete(int mediaCollectionId); + Task Update(Collection collection); + Task Delete(int collectionId); } } diff --git a/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs index e7a3f595..3828cc08 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs @@ -11,11 +11,16 @@ namespace ErsatzTV.Core.Interfaces.Repositories Task Add(PlexMediaSource plexMediaSource); Task> GetAll(); Task> GetAllPlex(); + Task> GetPlexLibraries(int plexMediaSourceId); + Task> GetPlexLibrary(int plexLibraryId); Task> Get(int id); Task> GetPlex(int id); Task CountMediaItems(int id); Task Update(LocalMediaSource localMediaSource); Task Update(PlexMediaSource plexMediaSource); + Task Update(PlexLibrary plexMediaSourceLibrary); Task Delete(int id); + Task DisablePlexLibrarySync(IEnumerable libraryIds); + Task EnablePlexLibrarySync(IEnumerable libraryIds); } } diff --git a/ErsatzTV.Core/Interfaces/Repositories/IMovieRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IMovieRepository.cs index 08371123..e7bd50a7 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IMovieRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IMovieRepository.cs @@ -7,10 +7,11 @@ namespace ErsatzTV.Core.Interfaces.Repositories { public interface IMovieRepository { - Task> GetMovie(int movieId); - Task> GetOrAdd(int mediaSourceId, string path); - Task Update(MovieMediaItem movie); + Task> GetMovie(int movieId); + Task> GetOrAdd(LibraryPath libraryPath, string path); + Task> GetOrAdd(PlexLibrary library, PlexMovie item); + Task Update(Movie movie); Task GetMovieCount(); - Task> GetPagedMovies(int pageNumber, int pageSize); + Task> GetPagedMovies(int pageNumber, int pageSize); } } diff --git a/ErsatzTV.Core/Interfaces/Repositories/IPlayoutRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IPlayoutRepository.cs index 354b1733..b4643065 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IPlayoutRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IPlayoutRepository.cs @@ -13,7 +13,6 @@ namespace ErsatzTV.Core.Interfaces.Repositories Task> GetFull(int id); Task> GetPlayoutItem(int channelId, DateTimeOffset now); Task> GetPlayoutItems(int playoutId); - Task> GetPlayoutIdsForMediaItems(Seq mediaItems); Task> GetAll(); Task Update(Playout playout); Task Delete(int playoutId); diff --git a/ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs index 88d7ebca..9ac405b8 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs @@ -7,41 +7,26 @@ namespace ErsatzTV.Core.Interfaces.Repositories { public interface ITelevisionRepository { - Task Update(TelevisionShow show); - Task Update(TelevisionSeason season); - Task Update(TelevisionEpisodeMediaItem episode); - Task> GetAllShows(); - Task> GetShow(int televisionShowId); + Task Update(Show show); + Task Update(Season season); + Task Update(Episode episode); + Task> GetAllShows(); + Task> GetShow(int showId); Task GetShowCount(); - Task> GetPagedShows(int pageNumber, int pageSize); - Task> GetShowItems(int televisionShowId); - Task> GetAllSeasons(); - Task> GetSeason(int televisionSeasonId); - Task GetSeasonCount(int televisionShowId); - Task> GetPagedSeasons(int televisionShowId, int pageNumber, int pageSize); - Task> GetSeasonItems(int televisionSeasonId); - Task> GetEpisode(int televisionEpisodeId); - Task GetEpisodeCount(int televisionSeasonId); - Task> GetPagedEpisodes(int televisionSeasonId, int pageNumber, int pageSize); - Task> GetShowByPath(int mediaSourceId, string path); - Task> GetShowByMetadata(TelevisionShowMetadata metadata); - - Task> AddShow( - int localMediaSourceId, - string showFolder, - TelevisionShowMetadata metadata); - - Task> GetOrAddSeason( - TelevisionShow show, - string path, - int seasonNumber); - - Task> GetOrAddEpisode( - TelevisionSeason season, - int mediaSourceId, - string path); - - Task DeleteMissingSources(int localMediaSourceId, List allFolders); + Task> GetPagedShows(int pageNumber, int pageSize); + Task> GetShowItems(int showId); + Task> GetAllSeasons(); + Task> GetSeason(int seasonId); + Task GetSeasonCount(int showId); + Task> GetPagedSeasons(int televisionShowId, int pageNumber, int pageSize); + Task> GetSeasonItems(int seasonId); + Task> GetEpisode(int episodeId); + Task GetEpisodeCount(int seasonId); + Task> GetPagedEpisodes(int seasonId, int pageNumber, int pageSize); + Task> GetShowByMetadata(int libraryPathId, ShowMetadata metadata); + Task> AddShow(int libraryPathId, string showFolder, ShowMetadata metadata); + Task> GetOrAddSeason(Show show, int libraryPathId, int seasonNumber); + Task> GetOrAddEpisode(Season season, LibraryPath libraryPath, string path); Task DeleteEmptyShows(); } } diff --git a/ErsatzTV.Core/Interfaces/Scheduling/IMediaCollectionEnumerator.cs b/ErsatzTV.Core/Interfaces/Scheduling/IMediaCollectionEnumerator.cs index 7d36941a..a45b92d4 100644 --- a/ErsatzTV.Core/Interfaces/Scheduling/IMediaCollectionEnumerator.cs +++ b/ErsatzTV.Core/Interfaces/Scheduling/IMediaCollectionEnumerator.cs @@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Interfaces.Scheduling { public interface IMediaCollectionEnumerator { - MediaCollectionEnumeratorState State { get; } + CollectionEnumeratorState State { get; } Option Current { get; } void MoveNext(); } diff --git a/ErsatzTV.Core/Iptv/ChannelGuide.cs b/ErsatzTV.Core/Iptv/ChannelGuide.cs index 55d37171..ece075db 100644 --- a/ErsatzTV.Core/Iptv/ChannelGuide.cs +++ b/ErsatzTV.Core/Iptv/ChannelGuide.cs @@ -41,11 +41,13 @@ namespace ErsatzTV.Core.Iptv xml.WriteEndElement(); // display-name xml.WriteStartElement("icon"); - xml.WriteAttributeString( - "src", - !string.IsNullOrWhiteSpace(channel.Logo) - ? $"{_scheme}://{_host}/iptv/images/{channel.Logo}" - : $"{_scheme}://{_host}/images/ersatztv-500.png"); + string logo = Optional(channel.Artwork).Flatten() + .Filter(a => a.ArtworkKind == ArtworkKind.Logo) + .HeadOrNone() + .Match( + artwork => $"{_scheme}://{_host}/iptv/logos/{artwork.Path}", + () => $"{_scheme}://{_host}/images/ersatztv-500.png"); + xml.WriteAttributeString("src", logo); xml.WriteEndElement(); // icon xml.WriteEndElement(); // channel @@ -55,26 +57,27 @@ namespace ErsatzTV.Core.Iptv { foreach (PlayoutItem playoutItem in channel.Playouts.Collect(p => p.Items).OrderBy(i => i.Start)) { - string start = playoutItem.Start.ToString("yyyyMMddHHmmss zzz").Replace(":", string.Empty); - string stop = playoutItem.Finish.ToString("yyyyMMddHHmmss zzz").Replace(":", string.Empty); + string start = playoutItem.StartOffset.ToString("yyyyMMddHHmmss zzz").Replace(":", string.Empty); + string stop = playoutItem.FinishOffset.ToString("yyyyMMddHHmmss zzz").Replace(":", string.Empty); string title = playoutItem.MediaItem switch { - MovieMediaItem m => m.Metadata?.Title ?? m.Path, - TelevisionEpisodeMediaItem e => e.Metadata?.Title ?? e.Path, + Movie m => m.MovieMetadata.HeadOrNone().Map(mm => mm.Title).IfNone("[unknown movie]"), + Episode e => e.EpisodeMetadata.HeadOrNone().Map(em => em.Title).IfNone("[unknown episode]"), _ => "[unknown]" }; string description = playoutItem.MediaItem switch { - MovieMediaItem m => m.Metadata?.Plot, - TelevisionEpisodeMediaItem e => e.Metadata?.Plot, + Movie m => m.MovieMetadata.HeadOrNone().Map(mm => mm.Plot).IfNone(string.Empty), + Episode e => e.EpisodeMetadata.HeadOrNone().Map(em => em.Plot).IfNone(string.Empty), _ => string.Empty }; string contentRating = playoutItem.MediaItem switch { - MovieMediaItem m => m.Metadata?.ContentRating, + // TODO: re-implement content rating + // Movie m => m.MovieMetadata.HeadOrNone().Map(mm => mm.ContentRating).IfNone(string.Empty), _ => string.Empty }; @@ -95,10 +98,10 @@ namespace ErsatzTV.Core.Iptv xml.WriteAttributeString("lang", "en"); xml.WriteEndElement(); // sub-title - if (playoutItem.MediaItem is TelevisionEpisodeMediaItem episode) + if (playoutItem.MediaItem is Episode episode) { - int s = Optional(episode.Metadata?.Season).IfNone(0); - int e = Optional(episode.Metadata?.Episode).IfNone(0); + int s = Optional(episode.Season?.SeasonNumber).IfNone(0); + int e = episode.EpisodeNumber; if (s > 0 && e > 0) { xml.WriteStartElement("episode-num"); diff --git a/ErsatzTV.Core/Iptv/ChannelPlaylist.cs b/ErsatzTV.Core/Iptv/ChannelPlaylist.cs index 933635f0..de6ac636 100644 --- a/ErsatzTV.Core/Iptv/ChannelPlaylist.cs +++ b/ErsatzTV.Core/Iptv/ChannelPlaylist.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text; using ErsatzTV.Core.Domain; +using static LanguageExt.Prelude; namespace ErsatzTV.Core.Iptv { @@ -26,9 +27,12 @@ namespace ErsatzTV.Core.Iptv sb.AppendLine($"#EXTM3U url-tvg=\"{xmltv}\" x-tvg-url=\"{xmltv}\""); foreach (Channel channel in _channels) { - string logo = !string.IsNullOrWhiteSpace(channel.Logo) - ? $"{_scheme}://{_host}/iptv/images/{channel.Logo}" - : $"{_scheme}://{_host}/images/ersatztv-500.png"; + string logo = Optional(channel.Artwork).Flatten() + .Filter(a => a.ArtworkKind == ArtworkKind.Logo) + .HeadOrNone() + .Match( + artwork => $"{_scheme}://{_host}/iptv/logos/{artwork.Path}", + () => $"{_scheme}://{_host}/images/ersatztv-500.png"); string shortUniqueId = Convert.ToBase64String(channel.UniqueId.ToByteArray()) .TrimEnd('=') @@ -42,7 +46,7 @@ namespace ErsatzTV.Core.Iptv }; sb.AppendLine( - $"#EXTINF:0 tvg-id=\"{channel.Number}\" CUID=\"{shortUniqueId}\" tvg-chno=\"{channel.Number}\" tvg-name=\"{channel.Name}\" tvg-logo=\"{logo}\" group-title=\"ErsatzTV\", {channel.Name}"); + $"#EXTINF:0 tvg-id=\"{channel.Number}\" channel-id=\"{shortUniqueId}\" channel-number=\"{channel.Number}\" CUID=\"{shortUniqueId}\" tvg-chno=\"{channel.Number}\" tvg-name=\"{channel.Name}\" tvg-logo=\"{logo}\" group-title=\"ErsatzTV\", {channel.Name}"); sb.AppendLine($"{_scheme}://{_host}/iptv/channel/{channel.Number}.{format}"); } diff --git a/ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs b/ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs index 726708c4..27ae51eb 100644 --- a/ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs +++ b/ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs @@ -3,57 +3,39 @@ using System.IO; using System.Text.RegularExpressions; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Metadata; +using static LanguageExt.Prelude; namespace ErsatzTV.Core.Metadata { public class FallbackMetadataProvider : IFallbackMetadataProvider { - public TelevisionShowMetadata GetFallbackMetadataForShow(string showFolder) + public ShowMetadata GetFallbackMetadataForShow(string showFolder) { string fileName = Path.GetFileName(showFolder); - var metadata = new TelevisionShowMetadata - { Source = MetadataSource.Fallback, Title = fileName ?? showFolder }; + var metadata = new ShowMetadata + { MetadataKind = MetadataKind.Fallback, Title = fileName ?? showFolder }; return GetTelevisionShowMetadata(fileName, metadata); } - public static TelevisionEpisodeMetadata GetFallbackMetadata(TelevisionEpisodeMediaItem mediaItem) + public static Tuple GetFallbackMetadata(Episode episode) { - string fileName = Path.GetFileName(mediaItem.Path); - var metadata = new TelevisionEpisodeMetadata - { Source = MetadataSource.Fallback, Title = fileName ?? mediaItem.Path }; - - if (fileName != null) - { - if (!(mediaItem.Source is LocalMediaSource)) - { - return metadata; - } - - return GetEpisodeMetadata(fileName, metadata); - } - - return metadata; + string path = episode.MediaVersions.Head().MediaFiles.Head().Path; + string fileName = Path.GetFileName(path); + var metadata = new EpisodeMetadata + { MetadataKind = MetadataKind.Fallback, Title = fileName ?? path }; + return fileName != null ? GetEpisodeMetadata(fileName, metadata) : Tuple(metadata, 0); } - public static MovieMetadata GetFallbackMetadata(MovieMediaItem mediaItem) + public static MovieMetadata GetFallbackMetadata(Movie movie) { - string fileName = Path.GetFileName(mediaItem.Path); - var metadata = new MovieMetadata { Source = MetadataSource.Fallback, Title = fileName ?? mediaItem.Path }; + string path = movie.MediaVersions.Head().MediaFiles.Head().Path; + string fileName = Path.GetFileName(path); + var metadata = new MovieMetadata { MetadataKind = MetadataKind.Fallback, Title = fileName ?? path }; - if (fileName != null) - { - if (!(mediaItem.Source is LocalMediaSource)) - { - return metadata; - } - - return GetMovieMetadata(fileName, metadata); - } - - return metadata; + return fileName != null ? GetMovieMetadata(fileName, metadata) : metadata; } - private static TelevisionEpisodeMetadata GetEpisodeMetadata(string fileName, TelevisionEpisodeMetadata metadata) + private static Tuple GetEpisodeMetadata(string fileName, EpisodeMetadata metadata) { try { @@ -62,8 +44,7 @@ namespace ErsatzTV.Core.Metadata if (match.Success) { metadata.Title = match.Groups[1].Value; - metadata.Season = int.Parse(match.Groups[2].Value); - metadata.Episode = int.Parse(match.Groups[3].Value); + return Tuple(metadata, int.Parse(match.Groups[3].Value)); } } catch (Exception) @@ -71,7 +52,7 @@ namespace ErsatzTV.Core.Metadata // ignored } - return metadata; + return Tuple(metadata, 0); } private static MovieMetadata GetMovieMetadata(string fileName, MovieMetadata metadata) @@ -84,6 +65,7 @@ namespace ErsatzTV.Core.Metadata { metadata.Title = match.Groups[1].Value; metadata.Year = int.Parse(match.Groups[2].Value); + metadata.ReleaseDate = new DateTime(int.Parse(match.Groups[2].Value), 1, 1); } } catch (Exception) @@ -94,9 +76,9 @@ namespace ErsatzTV.Core.Metadata return metadata; } - private static TelevisionShowMetadata GetTelevisionShowMetadata( + private static ShowMetadata GetTelevisionShowMetadata( string fileName, - TelevisionShowMetadata metadata) + ShowMetadata metadata) { try { @@ -106,6 +88,7 @@ namespace ErsatzTV.Core.Metadata { metadata.Title = match.Groups[1].Value; metadata.Year = int.Parse(match.Groups[2].Value); + metadata.ReleaseDate = new DateTime(int.Parse(match.Groups[2].Value), 1, 1); } } catch (Exception) diff --git a/ErsatzTV.Core/Metadata/LocalFileSystem.cs b/ErsatzTV.Core/Metadata/LocalFileSystem.cs index 3042e8d1..25f89298 100644 --- a/ErsatzTV.Core/Metadata/LocalFileSystem.cs +++ b/ErsatzTV.Core/Metadata/LocalFileSystem.cs @@ -4,6 +4,7 @@ using System.IO; using System.Threading.Tasks; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Metadata; +using LanguageExt; using static LanguageExt.Prelude; namespace ErsatzTV.Core.Metadata @@ -13,8 +14,8 @@ namespace ErsatzTV.Core.Metadata public DateTime GetLastWriteTime(string path) => Try(File.GetLastWriteTimeUtc(path)).IfFail(() => DateTime.MinValue); - public bool IsMediaSourceAccessible(LocalMediaSource localMediaSource) => - Directory.Exists(localMediaSource.Folder); + public bool IsLibraryPathAccessible(LibraryPath libraryPath) => + Directory.Exists(libraryPath.Path); public IEnumerable ListSubdirectories(string folder) => Try(Directory.EnumerateDirectories(folder)).IfFail(new List()); @@ -24,5 +25,18 @@ namespace ErsatzTV.Core.Metadata public bool FileExists(string path) => File.Exists(path); public Task ReadAllBytes(string path) => File.ReadAllBytesAsync(path); + + public Unit CopyFile(string source, string destination) + { + string directory = Path.GetDirectoryName(destination) ?? string.Empty; + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + File.Copy(source, destination, true); + + return Unit.Default; + } } } diff --git a/ErsatzTV.Core/Metadata/LocalFolderScanner.cs b/ErsatzTV.Core/Metadata/LocalFolderScanner.cs index 323aafb0..c5d6b2b3 100644 --- a/ErsatzTV.Core/Metadata/LocalFolderScanner.cs +++ b/ErsatzTV.Core/Metadata/LocalFolderScanner.cs @@ -2,18 +2,21 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Threading.Tasks; using ErsatzTV.Core.Domain; -using ErsatzTV.Core.Interfaces.Domain; using ErsatzTV.Core.Interfaces.Images; using ErsatzTV.Core.Interfaces.Metadata; using LanguageExt; using Microsoft.Extensions.Logging; +using static LanguageExt.Prelude; namespace ErsatzTV.Core.Metadata { public abstract class LocalFolderScanner { + private static readonly SHA1CryptoServiceProvider Crypto; + public static readonly List VideoFileExtensions = new() { ".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".ogg", ".mp4", @@ -46,6 +49,8 @@ namespace ErsatzTV.Core.Metadata private readonly ILocalStatisticsProvider _localStatisticsProvider; private readonly ILogger _logger; + static LocalFolderScanner() => Crypto = new SHA1CryptoServiceProvider(); + protected LocalFolderScanner( ILocalFileSystem localFileSystem, ILocalStatisticsProvider localStatisticsProvider, @@ -63,11 +68,18 @@ namespace ErsatzTV.Core.Metadata { try { - if (mediaItem.Statistics is null || - (mediaItem.Statistics.LastWriteTime ?? DateTime.MinValue) < - _localFileSystem.GetLastWriteTime(mediaItem.Path)) + MediaVersion version = mediaItem switch + { + Movie m => m.MediaVersions.Head(), + Episode e => e.MediaVersions.Head(), + _ => throw new ArgumentOutOfRangeException(nameof(mediaItem)) + }; + + string path = version.MediaFiles.Head().Path; + + if (version.DateUpdated < _localFileSystem.GetLastWriteTime(path)) { - _logger.LogDebug("Refreshing {Attribute} for {Path}", "Statistics", mediaItem.Path); + _logger.LogDebug("Refreshing {Attribute} for {Path}", "Statistics", path); await _localStatisticsProvider.RefreshStatistics(ffprobePath, mediaItem); } @@ -79,30 +91,47 @@ namespace ErsatzTV.Core.Metadata } } - protected async Task SavePosterToDisk( - T show, - string posterPath, - Func> update, - int height = 220) where T : IHasAPoster + protected bool RefreshArtwork(string artworkFile, Domain.Metadata metadata, ArtworkKind artworkKind) { - byte[] originalBytes = await _localFileSystem.ReadAllBytes(posterPath); - Either maybeHash = await _imageCache.ResizeAndSaveImage(originalBytes, height, null); - await maybeHash.Match( - hash => - { - show.Poster = hash; - show.PosterLastWriteTime = _localFileSystem.GetLastWriteTime(posterPath); - return update(show); - }, - error => - { - _logger.LogWarning("Unable to save poster to disk from {Path}: {Error}", posterPath, error.Value); - return Task.CompletedTask; - }); - } + DateTime lastWriteTime = _localFileSystem.GetLastWriteTime(artworkFile); + + metadata.Artwork ??= new List(); - protected Task> SavePosterToDisk(string posterPath, int height = 220) => - _localFileSystem.ReadAllBytes(posterPath) - .Bind(bytes => _imageCache.ResizeAndSaveImage(bytes, height, null)); + Option maybePoster = + Optional(metadata.Artwork).Flatten().FirstOrDefault(a => a.ArtworkKind == artworkKind); + + bool shouldRefresh = maybePoster.Match( + artwork => artwork.DateUpdated < lastWriteTime, + true); + + if (shouldRefresh) + { + _logger.LogDebug("Refreshing {Attribute} from {Path}", artworkKind, artworkFile); + string cacheName = _imageCache.CopyArtworkToCache(artworkFile, artworkKind); + + maybePoster.Match( + artwork => + { + artwork.Path = cacheName; + artwork.DateUpdated = lastWriteTime; + }, + () => + { + var artwork = new Artwork + { + Path = cacheName, + DateAdded = DateTime.UtcNow, + DateUpdated = lastWriteTime, + ArtworkKind = artworkKind + }; + + metadata.Artwork.Add(artwork); + }); + + return true; + } + + return false; + } } } diff --git a/ErsatzTV.Core/Metadata/LocalMetadataProvider.cs b/ErsatzTV.Core/Metadata/LocalMetadataProvider.cs index 01cdb407..cd99a9f7 100644 --- a/ErsatzTV.Core/Metadata/LocalMetadataProvider.cs +++ b/ErsatzTV.Core/Metadata/LocalMetadataProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using System.Xml.Serialization; @@ -37,94 +38,132 @@ namespace ErsatzTV.Core.Metadata _logger = logger; } - public Task GetMetadataForShow(string showFolder) + public async Task GetMetadataForShow(string showFolder) { string nfoFileName = Path.Combine(showFolder, "tvshow.nfo"); - return Optional(_localFileSystem.FileExists(nfoFileName)) - .Filter(identity).AsTask() - .Bind(_ => LoadTelevisionShowMetadata(nfoFileName)) - .IfNoneAsync(() => _fallbackMetadataProvider.GetFallbackMetadataForShow(showFolder).AsTask()) - .Map( - m => - { - m.SortTitle = GetSortTitle(m.Title); - return m; - }); + Option maybeMetadata = None; + if (_localFileSystem.FileExists(nfoFileName)) + { + maybeMetadata = await LoadTelevisionShowMetadata(nfoFileName); + } + + return maybeMetadata.IfNone( + () => + { + ShowMetadata metadata = _fallbackMetadataProvider.GetFallbackMetadataForShow(showFolder); + metadata.SortTitle = GetSortTitle(metadata.Title); + return metadata; + }); } public Task RefreshSidecarMetadata(MediaItem mediaItem, string path) => mediaItem switch { - TelevisionEpisodeMediaItem e => LoadMetadata(e, path) + Episode e => LoadMetadata(e, path) .Bind(maybeMetadata => maybeMetadata.IfSomeAsync(metadata => ApplyMetadataUpdate(e, metadata))), - MovieMediaItem m => LoadMetadata(m, path) + Movie m => LoadMetadata(m, path) .Bind(maybeMetadata => maybeMetadata.IfSomeAsync(metadata => ApplyMetadataUpdate(m, metadata))), _ => Task.FromResult(Unit.Default) }; - public Task RefreshSidecarMetadata(TelevisionShow televisionShow, string showFolder) => + public Task RefreshSidecarMetadata(Show televisionShow, string showFolder) => LoadMetadata(televisionShow, showFolder).Bind( maybeMetadata => maybeMetadata.IfSomeAsync(metadata => ApplyMetadataUpdate(televisionShow, metadata))); public Task RefreshFallbackMetadata(MediaItem mediaItem) => mediaItem switch { - TelevisionEpisodeMediaItem e => ApplyMetadataUpdate(e, FallbackMetadataProvider.GetFallbackMetadata(e)) + Episode e => ApplyMetadataUpdate(e, FallbackMetadataProvider.GetFallbackMetadata(e)) .ToUnit(), - MovieMediaItem m => ApplyMetadataUpdate(m, FallbackMetadataProvider.GetFallbackMetadata(m)).ToUnit(), + Movie m => ApplyMetadataUpdate(m, FallbackMetadataProvider.GetFallbackMetadata(m)).ToUnit(), _ => Task.FromResult(Unit.Default) }; - public Task RefreshFallbackMetadata(TelevisionShow televisionShow, string showFolder) => + public Task RefreshFallbackMetadata(Show televisionShow, string showFolder) => ApplyMetadataUpdate(televisionShow, _fallbackMetadataProvider.GetFallbackMetadataForShow(showFolder)) .ToUnit(); - private async Task ApplyMetadataUpdate(TelevisionEpisodeMediaItem mediaItem, TelevisionEpisodeMetadata metadata) + private async Task ApplyMetadataUpdate(Episode episode, Tuple metadataEpisodeNumber) { - mediaItem.Metadata ??= new TelevisionEpisodeMetadata { TelevisionEpisodeId = mediaItem.Id }; - mediaItem.Metadata.Source = metadata.Source; - mediaItem.Metadata.LastWriteTime = metadata.LastWriteTime; - mediaItem.Metadata.Title = metadata.Title; - mediaItem.Metadata.SortTitle = GetSortTitle(metadata.Title); - mediaItem.Metadata.Season = metadata.Season; - mediaItem.Metadata.Episode = metadata.Episode; - mediaItem.Metadata.Plot = metadata.Plot; - mediaItem.Metadata.Aired = metadata.Aired; - - await _televisionRepository.Update(mediaItem); + (EpisodeMetadata metadata, int episodeNumber) = metadataEpisodeNumber; + episode.EpisodeNumber = episodeNumber; + Optional(episode.EpisodeMetadata).Flatten().HeadOrNone().Match( + existing => + { + existing.Outline = metadata.Outline; + existing.Plot = metadata.Plot; + existing.Tagline = metadata.Tagline; + existing.Title = metadata.Title; + existing.DateAdded = metadata.DateAdded; + existing.DateUpdated = metadata.DateUpdated; + existing.MetadataKind = metadata.MetadataKind; + existing.OriginalTitle = metadata.OriginalTitle; + existing.ReleaseDate = metadata.ReleaseDate; + existing.Year = metadata.Year; + existing.SortTitle = metadata.SortTitle ?? GetSortTitle(metadata.Title); + }, + () => + { + metadata.SortTitle ??= GetSortTitle(metadata.Title); + episode.EpisodeMetadata = new List { metadata }; + }); + + await _televisionRepository.Update(episode); } - private async Task ApplyMetadataUpdate(MovieMediaItem mediaItem, MovieMetadata metadata) + private async Task ApplyMetadataUpdate(Movie movie, MovieMetadata metadata) { - mediaItem.Metadata ??= new MovieMetadata(); - mediaItem.Metadata.Source = metadata.Source; - mediaItem.Metadata.LastWriteTime = metadata.LastWriteTime; - mediaItem.Metadata.Title = metadata.Title; - mediaItem.Metadata.SortTitle = GetSortTitle(metadata.Title); - mediaItem.Metadata.Year = metadata.Year; - mediaItem.Metadata.Premiered = metadata.Premiered; - mediaItem.Metadata.Plot = metadata.Plot; - mediaItem.Metadata.Outline = metadata.Outline; - mediaItem.Metadata.Tagline = metadata.Tagline; - mediaItem.Metadata.ContentRating = metadata.ContentRating; - - await _mediaItemRepository.Update(mediaItem); + Optional(movie.MovieMetadata).Flatten().HeadOrNone().Match( + existing => + { + existing.Outline = metadata.Outline; + existing.Plot = metadata.Plot; + existing.Tagline = metadata.Tagline; + existing.Title = metadata.Title; + existing.DateAdded = metadata.DateAdded; + existing.DateUpdated = metadata.DateUpdated; + existing.MetadataKind = metadata.MetadataKind; + existing.OriginalTitle = metadata.OriginalTitle; + existing.ReleaseDate = metadata.ReleaseDate; + existing.Year = metadata.Year; + existing.SortTitle = metadata.SortTitle ?? GetSortTitle(metadata.Title); + }, + () => + { + metadata.SortTitle ??= GetSortTitle(metadata.Title); + movie.MovieMetadata = new List { metadata }; + }); + + await _mediaItemRepository.Update(movie); } - private async Task ApplyMetadataUpdate(TelevisionShow televisionShow, TelevisionShowMetadata metadata) + private async Task ApplyMetadataUpdate(Show show, ShowMetadata metadata) { - televisionShow.Metadata ??= new TelevisionShowMetadata(); - televisionShow.Metadata.Source = metadata.Source; - televisionShow.Metadata.LastWriteTime = metadata.LastWriteTime; - televisionShow.Metadata.Title = metadata.Title; - televisionShow.Metadata.Plot = metadata.Plot; - televisionShow.Metadata.Year = metadata.Year; - televisionShow.Metadata.SortTitle = GetSortTitle(metadata.Title); - - await _televisionRepository.Update(televisionShow); + Optional(show.ShowMetadata).Flatten().HeadOrNone().Match( + existing => + { + existing.Outline = metadata.Outline; + existing.Plot = metadata.Plot; + existing.Tagline = metadata.Tagline; + existing.Title = metadata.Title; + existing.DateAdded = metadata.DateAdded; + existing.DateUpdated = metadata.DateUpdated; + existing.MetadataKind = metadata.MetadataKind; + existing.OriginalTitle = metadata.OriginalTitle; + existing.ReleaseDate = metadata.ReleaseDate; + existing.Year = metadata.Year; + existing.SortTitle = metadata.SortTitle ?? GetSortTitle(metadata.Title); + }, + () => + { + metadata.SortTitle ??= GetSortTitle(metadata.Title); + show.ShowMetadata = new List { metadata }; + }); + + await _televisionRepository.Update(show); } - private async Task> LoadMetadata(MovieMediaItem mediaItem, string nfoFileName) + private async Task> LoadMetadata(Movie mediaItem, string nfoFileName) { if (nfoFileName == null || !File.Exists(nfoFileName)) { @@ -132,18 +171,10 @@ namespace ErsatzTV.Core.Metadata return None; } - if (!(mediaItem.Source is LocalMediaSource)) - { - _logger.LogDebug("Media source {Name} is not a local media source", mediaItem.Source.Name); - return None; - } - return await LoadMovieMetadata(mediaItem, nfoFileName); } - private async Task> LoadMetadata( - TelevisionEpisodeMediaItem mediaItem, - string nfoFileName) + private async Task>> LoadMetadata(Episode mediaItem, string nfoFileName) { if (nfoFileName == null || !File.Exists(nfoFileName)) { @@ -151,18 +182,10 @@ namespace ErsatzTV.Core.Metadata return None; } - if (!(mediaItem.Source is LocalMediaSource)) - { - _logger.LogDebug("Media source {Name} is not a local media source", mediaItem.Source.Name); - return None; - } - return await LoadEpisodeMetadata(mediaItem, nfoFileName); } - private async Task> LoadMetadata( - TelevisionShow televisionShow, - string nfoFileName) + private async Task> LoadMetadata(Show televisionShow, string nfoFileName) { if (nfoFileName == null || !File.Exists(nfoFileName)) { @@ -173,20 +196,23 @@ namespace ErsatzTV.Core.Metadata return await LoadTelevisionShowMetadata(nfoFileName); } - private async Task> LoadTelevisionShowMetadata(string nfoFileName) + private async Task> LoadTelevisionShowMetadata(string nfoFileName) { try { await using FileStream fileStream = File.Open(nfoFileName, FileMode.Open, FileAccess.Read); Option maybeNfo = TvShowSerializer.Deserialize(fileStream) as TvShowNfo; - return maybeNfo.Match>( - nfo => new TelevisionShowMetadata + return maybeNfo.Match>( + nfo => new ShowMetadata { - Source = MetadataSource.Sidecar, - LastWriteTime = File.GetLastWriteTimeUtc(nfoFileName), + MetadataKind = MetadataKind.Sidecar, + DateUpdated = File.GetLastWriteTimeUtc(nfoFileName), Title = nfo.Title, Plot = nfo.Plot, - Year = nfo.Year + Outline = nfo.Outline, + Tagline = nfo.Tagline, + Year = nfo.Year, + ReleaseDate = GetAired(nfo.Premiered) ?? new DateTime(nfo.Year, 1, 1) }, None); } @@ -197,35 +223,35 @@ namespace ErsatzTV.Core.Metadata } } - private async Task> LoadEpisodeMetadata( - TelevisionEpisodeMediaItem mediaItem, - string nfoFileName) + private async Task>> LoadEpisodeMetadata(Episode episode, string nfoFileName) { try { await using FileStream fileStream = File.Open(nfoFileName, FileMode.Open, FileAccess.Read); Option maybeNfo = EpisodeSerializer.Deserialize(fileStream) as TvShowEpisodeNfo; - return maybeNfo.Match>( - nfo => new TelevisionEpisodeMetadata + return maybeNfo.Match>>( + nfo => { - Source = MetadataSource.Sidecar, - LastWriteTime = File.GetLastWriteTimeUtc(nfoFileName), - Title = nfo.Title, - Aired = GetAired(nfo.Aired), - Episode = nfo.Episode, - Season = nfo.Season, - Plot = nfo.Plot + var metadata = new EpisodeMetadata + { + MetadataKind = MetadataKind.Sidecar, + DateUpdated = File.GetLastWriteTimeUtc(nfoFileName), + Title = nfo.Title, + ReleaseDate = GetAired(nfo.Aired), + Plot = nfo.Plot + }; + return Tuple(metadata, nfo.Episode); }, None); } catch (Exception ex) { _logger.LogDebug(ex, "Failed to read TV episode nfo metadata from {Path}", nfoFileName); - return FallbackMetadataProvider.GetFallbackMetadata(mediaItem); + return FallbackMetadataProvider.GetFallbackMetadata(episode); } } - private async Task> LoadMovieMetadata(MovieMediaItem mediaItem, string nfoFileName) + private async Task> LoadMovieMetadata(Movie mediaItem, string nfoFileName) { try { @@ -234,15 +260,14 @@ namespace ErsatzTV.Core.Metadata return maybeNfo.Match>( nfo => new MovieMetadata { - Source = MetadataSource.Sidecar, - LastWriteTime = File.GetLastWriteTimeUtc(nfoFileName), + MetadataKind = MetadataKind.Sidecar, + DateUpdated = File.GetLastWriteTimeUtc(nfoFileName), Title = nfo.Title, Year = nfo.Year, - Premiered = nfo.Premiered, + ReleaseDate = nfo.Premiered, Plot = nfo.Plot, Outline = nfo.Outline, - Tagline = nfo.Tagline, - ContentRating = nfo.ContentRating + Tagline = nfo.Tagline }, None); } @@ -324,6 +349,15 @@ namespace ErsatzTV.Core.Metadata [XmlElement("plot")] public string Plot { get; set; } + + [XmlElement("outline")] + public string Outline { get; set; } + + [XmlElement("tagline")] + public string Tagline { get; set; } + + [XmlElement("premiered")] + public string Premiered { get; set; } } [XmlRoot("episodedetails")] diff --git a/ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs b/ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs index 9be90b17..94de1705 100644 --- a/ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs +++ b/ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs @@ -33,42 +33,49 @@ namespace ErsatzTV.Core.Metadata { try { - FFprobe ffprobe = await GetProbeOutput(ffprobePath, mediaItem); - MediaItemStatistics statistics = ProjectToMediaItemStatistics(ffprobe); - return await ApplyStatisticsUpdate(mediaItem, statistics); + string filePath = mediaItem switch + { + Movie m => m.MediaVersions.Head().MediaFiles.Head().Path, + Episode e => e.MediaVersions.Head().MediaFiles.Head().Path, + _ => throw new ArgumentOutOfRangeException(nameof(mediaItem)) + }; + + FFprobe ffprobe = await GetProbeOutput(ffprobePath, filePath); + MediaVersion version = ProjectToMediaVersion(ffprobe); + return await ApplyVersionUpdate(mediaItem, version, filePath); } catch (Exception ex) { - _logger.LogWarning(ex, "Failed to refresh statistics for media item at {Path}", mediaItem.Path); + _logger.LogWarning(ex, "Failed to refresh statistics for media item {Id}", mediaItem.Id); return false; } } - private async Task ApplyStatisticsUpdate( - MediaItem mediaItem, - MediaItemStatistics statistics) + private async Task ApplyVersionUpdate(MediaItem mediaItem, MediaVersion version, string filePath) { - if (mediaItem.Statistics == null) + MediaVersion mediaItemVersion = mediaItem switch { - mediaItem.Statistics = new MediaItemStatistics(); - } + Movie m => m.MediaVersions.Head(), + Episode e => e.MediaVersions.Head(), + _ => throw new ArgumentOutOfRangeException(nameof(mediaItem)) + }; - bool durationChange = mediaItem.Statistics.Duration != statistics.Duration; + bool durationChange = mediaItemVersion.Duration != version.Duration; - mediaItem.Statistics.LastWriteTime = _localFileSystem.GetLastWriteTime(mediaItem.Path); - mediaItem.Statistics.Duration = statistics.Duration; - mediaItem.Statistics.AudioCodec = statistics.AudioCodec; - mediaItem.Statistics.SampleAspectRatio = statistics.SampleAspectRatio; - mediaItem.Statistics.DisplayAspectRatio = statistics.DisplayAspectRatio; - mediaItem.Statistics.Width = statistics.Width; - mediaItem.Statistics.Height = statistics.Height; - mediaItem.Statistics.VideoCodec = statistics.VideoCodec; - mediaItem.Statistics.VideoScanType = statistics.VideoScanType; + mediaItemVersion.DateUpdated = _localFileSystem.GetLastWriteTime(filePath); + mediaItemVersion.Duration = version.Duration; + mediaItemVersion.AudioCodec = version.AudioCodec; + mediaItemVersion.SampleAspectRatio = version.SampleAspectRatio; + mediaItemVersion.DisplayAspectRatio = version.DisplayAspectRatio; + mediaItemVersion.Width = version.Width; + mediaItemVersion.Height = version.Height; + mediaItemVersion.VideoCodec = version.VideoCodec; + mediaItemVersion.VideoScanKind = version.VideoScanKind; return await _mediaItemRepository.Update(mediaItem) && durationChange; } - private Task GetProbeOutput(string ffprobePath, MediaItem mediaItem) + private Task GetProbeOutput(string ffprobePath, string filePath) { var startInfo = new ProcessStartInfo { @@ -85,7 +92,7 @@ namespace ErsatzTV.Core.Metadata startInfo.ArgumentList.Add("-show_format"); startInfo.ArgumentList.Add("-show_streams"); startInfo.ArgumentList.Add("-i"); - startInfo.ArgumentList.Add(mediaItem.Path); + startInfo.ArgumentList.Add(filePath); var probe = new Process { @@ -101,7 +108,7 @@ namespace ErsatzTV.Core.Metadata }); } - private MediaItemStatistics ProjectToMediaItemStatistics(FFprobe probeOutput) => + private MediaVersion ProjectToMediaVersion(FFprobe probeOutput) => Optional(probeOutput) .Filter(json => json?.format != null && json.streams != null) .ToValidation("Unable to parse ffprobe output") @@ -111,41 +118,35 @@ namespace ErsatzTV.Core.Metadata { var duration = TimeSpan.FromSeconds(double.Parse(json.format.duration)); - var statistics = new MediaItemStatistics { Duration = duration }; + var version = new MediaVersion { Name = "Main", Duration = duration }; FFprobeStream audioStream = json.streams.FirstOrDefault(s => s.codec_type == "audio"); if (audioStream != null) { - statistics = statistics with - { - AudioCodec = audioStream.codec_name - }; + version.AudioCodec = audioStream.codec_name; } FFprobeStream videoStream = json.streams.FirstOrDefault(s => s.codec_type == "video"); if (videoStream != null) { - statistics = statistics with - { - SampleAspectRatio = videoStream.sample_aspect_ratio, - DisplayAspectRatio = videoStream.display_aspect_ratio, - Width = videoStream.width, - Height = videoStream.height, - VideoCodec = videoStream.codec_name, - VideoScanType = ScanTypeFromFieldOrder(videoStream.field_order) - }; + version.SampleAspectRatio = videoStream.sample_aspect_ratio; + version.DisplayAspectRatio = videoStream.display_aspect_ratio; + version.Width = videoStream.width; + version.Height = videoStream.height; + version.VideoCodec = videoStream.codec_name; + version.VideoScanKind = ScanKindFromFieldOrder(videoStream.field_order); } - return statistics; + return version; }, - _ => new MediaItemStatistics()); + _ => new MediaVersion { Name = "Main" }); - private VideoScanType ScanTypeFromFieldOrder(string fieldOrder) => + private VideoScanKind ScanKindFromFieldOrder(string fieldOrder) => fieldOrder?.ToLowerInvariant() switch { - var x when x == "tt" || x == "bb" || x == "tb" || x == "bt" => VideoScanType.Interlaced, - "progressive" => VideoScanType.Progressive, - _ => VideoScanType.Unknown + var x when x == "tt" || x == "bb" || x == "tb" || x == "bt" => VideoScanKind.Interlaced, + "progressive" => VideoScanKind.Progressive, + _ => VideoScanKind.Unknown }; // ReSharper disable InconsistentNaming diff --git a/ErsatzTV.Core/Metadata/MovieFolderScanner.cs b/ErsatzTV.Core/Metadata/MovieFolderScanner.cs index 0c402e75..7a40a2dd 100644 --- a/ErsatzTV.Core/Metadata/MovieFolderScanner.cs +++ b/ErsatzTV.Core/Metadata/MovieFolderScanner.cs @@ -37,15 +37,15 @@ namespace ErsatzTV.Core.Metadata _logger = logger; } - public async Task> ScanFolder(LocalMediaSource localMediaSource, string ffprobePath) + public async Task> ScanFolder(LibraryPath libraryPath, string ffprobePath) { - if (!_localFileSystem.IsMediaSourceAccessible(localMediaSource)) + if (!_localFileSystem.IsLibraryPathAccessible(libraryPath)) { return new MediaSourceInaccessible(); } var folderQueue = new Queue(); - foreach (string folder in _localFileSystem.ListSubdirectories(localMediaSource.Folder).OrderBy(identity)) + foreach (string folder in _localFileSystem.ListSubdirectories(libraryPath.Path).OrderBy(identity)) { folderQueue.Enqueue(folder); } @@ -73,10 +73,10 @@ namespace ErsatzTV.Core.Metadata foreach (string file in allFiles.OrderBy(identity)) { + // TODO: optimize dbcontext use here, do we need tracking? can we make partial updates with dapper? // TODO: figure out how to rebuild playlists - Either x = await _movieRepository.GetOrAdd(localMediaSource.Id, file); - - Either maybeMovie = await x.AsTask() + Either maybeMovie = await _movieRepository + .GetOrAdd(libraryPath, file) .BindT(movie => UpdateStatistics(movie, ffprobePath).MapT(_ => movie)) .BindT(UpdateMetadata) .BindT(UpdatePoster); @@ -89,16 +89,19 @@ namespace ErsatzTV.Core.Metadata return Unit.Default; } - private async Task> UpdateMetadata(MovieMediaItem movie) + private async Task> UpdateMetadata(Movie movie) { try { await LocateNfoFile(movie).Match( async nfoFile => { - if (movie.Metadata == null || movie.Metadata.Source == MetadataSource.Fallback || - (movie.Metadata.LastWriteTime ?? DateTime.MinValue) < - _localFileSystem.GetLastWriteTime(nfoFile)) + bool shouldUpdate = Optional(movie.MovieMetadata).Flatten().HeadOrNone().Match( + m => m.MetadataKind == MetadataKind.Fallback || + m.DateUpdated < _localFileSystem.GetLastWriteTime(nfoFile), + true); + + if (shouldUpdate) { _logger.LogDebug("Refreshing {Attribute} from {Path}", "Sidecar Metadata", nfoFile); await _localMetadataProvider.RefreshSidecarMetadata(movie, nfoFile); @@ -106,9 +109,10 @@ namespace ErsatzTV.Core.Metadata }, async () => { - if (movie.Metadata == null) + if (!Optional(movie.MovieMetadata).Flatten().Any()) { - _logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", movie.Path); + string path = movie.MediaVersions.Head().MediaFiles.Head().Path; + _logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", path); await _localMetadataProvider.RefreshFallbackMetadata(movie); } }); @@ -121,19 +125,17 @@ namespace ErsatzTV.Core.Metadata } } - private async Task> UpdatePoster(MovieMediaItem movie) + private async Task> UpdatePoster(Movie movie) { try { await LocatePoster(movie).IfSomeAsync( async posterFile => { - if (string.IsNullOrWhiteSpace(movie.Poster) || - (movie.PosterLastWriteTime ?? DateTime.MinValue) < - _localFileSystem.GetLastWriteTime(posterFile)) + MovieMetadata metadata = movie.MovieMetadata.Head(); + if (RefreshArtwork(posterFile, metadata, ArtworkKind.Poster)) { - _logger.LogDebug("Refreshing {Attribute} from {Path}", "Poster", posterFile); - await SavePosterToDisk(movie, posterFile, _movieRepository.Update, 440); + await _movieRepository.Update(movie); } }); @@ -145,20 +147,22 @@ namespace ErsatzTV.Core.Metadata } } - private Option LocateNfoFile(MovieMediaItem movie) + private Option LocateNfoFile(Movie movie) { - string movieAsNfo = Path.ChangeExtension(movie.Path, "nfo"); - string movieNfo = Path.Combine(Path.GetDirectoryName(movie.Path) ?? string.Empty, "movie.nfo"); + string path = movie.MediaVersions.Head().MediaFiles.Head().Path; + string movieAsNfo = Path.ChangeExtension(path, "nfo"); + string movieNfo = Path.Combine(Path.GetDirectoryName(path) ?? string.Empty, "movie.nfo"); return Seq.create(movieAsNfo, movieNfo) .Filter(s => _localFileSystem.FileExists(s)) .HeadOrNone(); } - private Option LocatePoster(MovieMediaItem movie) + private Option LocatePoster(Movie movie) { - string folder = Path.GetDirectoryName(movie.Path) ?? string.Empty; + string path = movie.MediaVersions.Head().MediaFiles.Head().Path; + string folder = Path.GetDirectoryName(path) ?? string.Empty; IEnumerable possibleMoviePosters = ImageFileExtensions.Collect( - ext => new[] { $"poster.{ext}", Path.GetFileNameWithoutExtension(movie.Path) + $"-poster.{ext}" }) + ext => new[] { $"poster.{ext}", Path.GetFileNameWithoutExtension(path) + $"-poster.{ext}" }) .Map(f => Path.Combine(folder, f)); Option result = possibleMoviePosters.Filter(p => _localFileSystem.FileExists(p)).HeadOrNone(); return result; diff --git a/ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs b/ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs index d1fe84f2..1e7227a9 100644 --- a/ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs +++ b/ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Errors; using ErsatzTV.Core.Interfaces.Images; using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Repositories; @@ -37,88 +39,65 @@ namespace ErsatzTV.Core.Metadata _logger = logger; } - public async Task ScanFolder(LocalMediaSource localMediaSource, string ffprobePath) + public async Task> ScanFolder(LibraryPath libraryPath, string ffprobePath) { - if (!_localFileSystem.IsMediaSourceAccessible(localMediaSource)) + if (!_localFileSystem.IsLibraryPathAccessible(libraryPath)) { - _logger.LogWarning( - "Media source is not accessible or missing; skipping scan of {Folder}", - localMediaSource.Folder); - return Unit.Default; + return new MediaSourceInaccessible(); } - var allShowFolders = _localFileSystem.ListSubdirectories(localMediaSource.Folder) + var allShowFolders = _localFileSystem.ListSubdirectories(libraryPath.Path) .Filter(ShouldIncludeFolder) .OrderBy(identity) .ToList(); foreach (string showFolder in allShowFolders) { - // TODO: check all sources for latest metadata? - Either maybeShow = - await FindOrCreateShow(localMediaSource.Id, showFolder) + Either maybeShow = + await FindOrCreateShow(libraryPath.Id, showFolder) .BindT(show => UpdateMetadataForShow(show, showFolder)) .BindT(show => UpdatePosterForShow(show, showFolder)); await maybeShow.Match( - show => ScanSeasons(localMediaSource, ffprobePath, show, showFolder), + show => ScanSeasons(libraryPath, ffprobePath, show, showFolder), _ => Task.FromResult(Unit.Default)); } - await _televisionRepository.DeleteMissingSources(localMediaSource.Id, allShowFolders); await _televisionRepository.DeleteEmptyShows(); return Unit.Default; } - private async Task> FindOrCreateShow( - int localMediaSourceId, + private async Task> FindOrCreateShow( + int libraryPathId, string showFolder) { - Option maybeShowByPath = - await _televisionRepository.GetShowByPath(localMediaSourceId, showFolder); - return await maybeShowByPath.Match( - show => Right(show).AsTask(), - async () => - { - TelevisionShowMetadata metadata = await _localMetadataProvider.GetMetadataForShow(showFolder); - Option maybeShow = await _televisionRepository.GetShowByMetadata(metadata); - return await maybeShow.Match( - async show => - { - show.Sources.Add( - new LocalTelevisionShowSource - { - MediaSourceId = localMediaSourceId, - Path = showFolder, - TelevisionShow = show - }); - await _televisionRepository.Update(show); - return Right(show); - }, - async () => await _televisionRepository.AddShow(localMediaSourceId, showFolder, metadata)); - }); + ShowMetadata metadata = await _localMetadataProvider.GetMetadataForShow(showFolder); + Option maybeShow = await _televisionRepository.GetShowByMetadata(libraryPathId, metadata); + return await maybeShow.Match( + show => Right(show).AsTask(), + async () => await _televisionRepository.AddShow(libraryPathId, showFolder, metadata)); } private async Task ScanSeasons( - LocalMediaSource localMediaSource, + LibraryPath libraryPath, string ffprobePath, - TelevisionShow show, - string showPath) + Show show, + string showFolder) { - foreach (string seasonFolder in _localFileSystem.ListSubdirectories(showPath).Filter(ShouldIncludeFolder) + foreach (string seasonFolder in _localFileSystem.ListSubdirectories(showFolder).Filter(ShouldIncludeFolder) .OrderBy(identity)) { Option maybeSeasonNumber = SeasonNumberForFolder(seasonFolder); await maybeSeasonNumber.IfSomeAsync( async seasonNumber => { - Either maybeSeason = await _televisionRepository - .GetOrAddSeason(show, seasonFolder, seasonNumber) - .BindT(UpdatePoster); + Either maybeSeason = await _televisionRepository + .GetOrAddSeason(show, libraryPath.Id, seasonNumber) + .BindT(season => UpdatePoster(season, seasonFolder)); await maybeSeason.Match( - season => ScanEpisodes(localMediaSource, ffprobePath, season), + season => ScanEpisodes(libraryPath, ffprobePath, season, seasonFolder), _ => Task.FromResult(Unit.Default)); }); } @@ -127,16 +106,17 @@ namespace ErsatzTV.Core.Metadata } private async Task ScanEpisodes( - LocalMediaSource localMediaSource, + LibraryPath libraryPath, string ffprobePath, - TelevisionSeason season) + Season season, + string seasonPath) { - foreach (string file in _localFileSystem.ListFiles(season.Path) + foreach (string file in _localFileSystem.ListFiles(seasonPath) .Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f))).OrderBy(identity)) { // TODO: figure out how to rebuild playlists - Either maybeEpisode = await _televisionRepository - .GetOrAddEpisode(season, localMediaSource.Id, file) + Either maybeEpisode = await _televisionRepository + .GetOrAddEpisode(season, libraryPath, file) .BindT(episode => UpdateStatistics(episode, ffprobePath).MapT(_ => episode)) .BindT(UpdateMetadata) .BindT(UpdateThumbnail); @@ -148,8 +128,8 @@ namespace ErsatzTV.Core.Metadata return Unit.Default; } - private async Task> UpdateMetadataForShow( - TelevisionShow show, + private async Task> UpdateMetadataForShow( + Show show, string showFolder) { try @@ -157,9 +137,12 @@ namespace ErsatzTV.Core.Metadata await LocateNfoFileForShow(showFolder).Match( async nfoFile => { - if (show.Metadata == null || show.Metadata.Source == MetadataSource.Fallback || - (show.Metadata.LastWriteTime ?? DateTime.MinValue) < - _localFileSystem.GetLastWriteTime(nfoFile)) + bool shouldUpdate = Optional(show.ShowMetadata).Flatten().HeadOrNone().Match( + m => m.MetadataKind == MetadataKind.Fallback || + m.DateUpdated < _localFileSystem.GetLastWriteTime(nfoFile), + true); + + if (shouldUpdate) { _logger.LogDebug("Refreshing {Attribute} from {Path}", "Sidecar Metadata", nfoFile); await _localMetadataProvider.RefreshSidecarMetadata(show, nfoFile); @@ -167,7 +150,7 @@ namespace ErsatzTV.Core.Metadata }, async () => { - if (show.Metadata == null) + if (!Optional(show.ShowMetadata).Flatten().Any()) { _logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", showFolder); await _localMetadataProvider.RefreshFallbackMetadata(show, showFolder); @@ -182,17 +165,20 @@ namespace ErsatzTV.Core.Metadata } } - private async Task> UpdateMetadata( - TelevisionEpisodeMediaItem episode) + private async Task> UpdateMetadata( + Episode episode) { try { await LocateNfoFile(episode).Match( async nfoFile => { - if (episode.Metadata == null || episode.Metadata.Source == MetadataSource.Fallback || - (episode.Metadata.LastWriteTime ?? DateTime.MinValue) < - _localFileSystem.GetLastWriteTime(nfoFile)) + bool shouldUpdate = Optional(episode.EpisodeMetadata).Flatten().HeadOrNone().Match( + m => m.MetadataKind == MetadataKind.Fallback || + m.DateUpdated < _localFileSystem.GetLastWriteTime(nfoFile), + true); + + if (shouldUpdate) { _logger.LogDebug("Refreshing {Attribute} from {Path}", "Sidecar Metadata", nfoFile); await _localMetadataProvider.RefreshSidecarMetadata(episode, nfoFile); @@ -200,9 +186,10 @@ namespace ErsatzTV.Core.Metadata }, async () => { - if (episode.Metadata == null) + if (!Optional(episode.EpisodeMetadata).Flatten().Any()) { - _logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", episode.Path); + string path = episode.MediaVersions.Head().MediaFiles.Head().Path; + _logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", path); await _localMetadataProvider.RefreshFallbackMetadata(episode); } }); @@ -215,8 +202,8 @@ namespace ErsatzTV.Core.Metadata } } - private async Task> UpdatePosterForShow( - TelevisionShow show, + private async Task> UpdatePosterForShow( + Show show, string showFolder) { try @@ -224,27 +211,10 @@ namespace ErsatzTV.Core.Metadata await LocatePosterForShow(showFolder).IfSomeAsync( async posterFile => { - if (string.IsNullOrWhiteSpace(show.Poster) || - (show.PosterLastWriteTime ?? DateTime.MinValue) < - _localFileSystem.GetLastWriteTime(posterFile)) + ShowMetadata metadata = show.ShowMetadata.Head(); + if (RefreshArtwork(posterFile, metadata, ArtworkKind.Poster)) { - _logger.LogDebug("Refreshing {Attribute} from {Path}", "Poster", posterFile); - Either maybePoster = await SavePosterToDisk(posterFile, 440); - await maybePoster.Match( - poster => - { - show.Poster = poster; - show.PosterLastWriteTime = _localFileSystem.GetLastWriteTime(posterFile); - return _televisionRepository.Update(show); - }, - error => - { - _logger.LogWarning( - "Unable to save poster to disk from {Path}: {Error}", - posterFile, - error.Value); - return Task.CompletedTask; - }); + await _televisionRepository.Update(show); } }); @@ -256,19 +226,24 @@ namespace ErsatzTV.Core.Metadata } } - private async Task> UpdatePoster(TelevisionSeason season) + private async Task> UpdatePoster(Season season, string seasonFolder) { try { - await LocatePoster(season).IfSomeAsync( + await LocatePoster(season, seasonFolder).IfSomeAsync( async posterFile => { - if (string.IsNullOrWhiteSpace(season.Poster) || - (season.PosterLastWriteTime ?? DateTime.MinValue) < - _localFileSystem.GetLastWriteTime(posterFile)) + season.SeasonMetadata ??= new List(); + if (!season.SeasonMetadata.Any()) { - _logger.LogDebug("Refreshing {Attribute} from {Path}", "Poster", posterFile); - await SavePosterToDisk(season, posterFile, _televisionRepository.Update, 440); + season.SeasonMetadata.Add(new SeasonMetadata { SeasonId = season.Id }); + } + + SeasonMetadata metadata = season.SeasonMetadata.Head(); + + if (RefreshArtwork(posterFile, metadata, ArtworkKind.Poster)) + { + await _televisionRepository.Update(season); } }); @@ -280,20 +255,17 @@ namespace ErsatzTV.Core.Metadata } } - private async Task> UpdateThumbnail( - TelevisionEpisodeMediaItem episode) + private async Task> UpdateThumbnail(Episode episode) { try { await LocateThumbnail(episode).IfSomeAsync( async posterFile => { - if (string.IsNullOrWhiteSpace(episode.Poster) || - (episode.PosterLastWriteTime ?? DateTime.MinValue) < - _localFileSystem.GetLastWriteTime(posterFile)) + EpisodeMetadata metadata = episode.EpisodeMetadata.Head(); + if (RefreshArtwork(posterFile, metadata, ArtworkKind.Thumbnail)) { - _logger.LogDebug("Refreshing {Attribute} from {Path}", "Thumbnail", posterFile); - await SavePosterToDisk(episode, posterFile, _televisionRepository.Update); + await _televisionRepository.Update(episode); } }); @@ -309,9 +281,12 @@ namespace ErsatzTV.Core.Metadata Optional(Path.Combine(showFolder, "tvshow.nfo")) .Filter(s => _localFileSystem.FileExists(s)); - private Option LocateNfoFile(TelevisionEpisodeMediaItem episode) => - Optional(Path.ChangeExtension(episode.Path, "nfo")) + private Option LocateNfoFile(Episode episode) + { + string path = episode.MediaVersions.Head().MediaFiles.Head().Path; + return Optional(Path.ChangeExtension(path, "nfo")) .Filter(s => _localFileSystem.FileExists(s)); + } private Option LocatePosterForShow(string showFolder) => ImageFileExtensions @@ -320,20 +295,21 @@ namespace ErsatzTV.Core.Metadata .Filter(s => _localFileSystem.FileExists(s)) .HeadOrNone(); - private Option LocatePoster(TelevisionSeason season) + private Option LocatePoster(Season season, string seasonFolder) { - string folder = Path.GetDirectoryName(season.Path) ?? string.Empty; + string folder = Path.GetDirectoryName(seasonFolder) ?? string.Empty; return ImageFileExtensions - .Map(ext => Path.Combine(folder, $"season{season.Number:00}-poster.{ext}")) + .Map(ext => Path.Combine(folder, $"season{season.SeasonNumber:00}-poster.{ext}")) .Filter(s => _localFileSystem.FileExists(s)) .HeadOrNone(); } - private Option LocateThumbnail(TelevisionEpisodeMediaItem episode) + private Option LocateThumbnail(Episode episode) { - string folder = Path.GetDirectoryName(episode.Path) ?? string.Empty; + string path = episode.MediaVersions.Head().MediaFiles.Head().Path; + string folder = Path.GetDirectoryName(path) ?? string.Empty; return ImageFileExtensions - .Map(ext => Path.GetFileNameWithoutExtension(episode.Path) + $"-thumb.{ext}") + .Map(ext => Path.GetFileNameWithoutExtension(path) + $"-thumb.{ext}") .Map(f => Path.Combine(folder, f)) .Filter(f => _localFileSystem.FileExists(f)) .HeadOrNone(); diff --git a/ErsatzTV.Core/Plex/PlexMediaEntry.cs b/ErsatzTV.Core/Plex/PlexMediaEntry.cs new file mode 100644 index 00000000..cfe06245 --- /dev/null +++ b/ErsatzTV.Core/Plex/PlexMediaEntry.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace ErsatzTV.Core.Plex +{ + public class PlexMediaEntry + { + public int Id { get; set; } + public int Duration { get; set; } + public int Bitrate { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public double AspectRatio { get; set; } + public int AudioChannels { get; set; } + public string AudioCodec { get; set; } + public string VideoCodec { get; set; } + public string Container { get; set; } + public string VideoFrameRate { get; set; } + public List Part { get; set; } + } +} diff --git a/ErsatzTV.Core/Plex/PlexMetadataEntry.cs b/ErsatzTV.Core/Plex/PlexMetadataEntry.cs new file mode 100644 index 00000000..f5089fce --- /dev/null +++ b/ErsatzTV.Core/Plex/PlexMetadataEntry.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace ErsatzTV.Core.Plex +{ + public class PlexMetadataEntry + { + public string Key { get; set; } + public string Title { get; set; } + public string Summary { get; set; } + public int Year { get; set; } + public string Tagline { get; set; } + public string Thumb { get; set; } + public string Art { get; set; } + public string OriginallyAvailableAt { get; set; } + public int AddedAt { get; set; } + public int UpdatedAt { get; set; } + public List Media { get; set; } + } +} diff --git a/ErsatzTV.Core/Plex/PlexMovieLibraryScanner.cs b/ErsatzTV.Core/Plex/PlexMovieLibraryScanner.cs new file mode 100644 index 00000000..cba58716 --- /dev/null +++ b/ErsatzTV.Core/Plex/PlexMovieLibraryScanner.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Plex; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using Microsoft.Extensions.Logging; +using static LanguageExt.Prelude; + +namespace ErsatzTV.Core.Plex +{ + public class PlexMovieLibraryScanner : IPlexMovieLibraryScanner + { + private readonly ILogger _logger; + private readonly IMovieRepository _movieRepository; + private readonly IPlexServerApiClient _plexServerApiClient; + + public PlexMovieLibraryScanner( + IPlexServerApiClient plexServerApiClient, + IMovieRepository movieRepository, + ILogger logger) + { + _plexServerApiClient = plexServerApiClient; + _movieRepository = movieRepository; + _logger = logger; + } + + public async Task> ScanLibrary( + PlexConnection connection, + PlexServerAuthToken token, + PlexLibrary plexMediaSourceLibrary) + { + Either> entries = await _plexServerApiClient.GetLibraryContents( + plexMediaSourceLibrary, + connection, + token); + + await entries.Match( + async movieEntries => + { + foreach (PlexMovie entry in movieEntries) + { + // TODO: optimize dbcontext use here, do we need tracking? can we make partial updates with dapper? + // TODO: figure out how to rebuild playlists + Either maybeMovie = await _movieRepository + .GetOrAdd(plexMediaSourceLibrary, entry) + .BindT(UpdateIfNeeded); + + maybeMovie.IfLeft( + error => _logger.LogWarning( + "Error processing plex movie at {Key}: {Error}", + entry.Key, + error.Value)); + } + }, + error => + { + _logger.LogWarning( + "Error synchronizing plex library {Path}: {Error}", + plexMediaSourceLibrary.Name, + error.Value); + + return Task.CompletedTask; + }); + + // need plex media item model that can be used to lookup by unique id (metadata key?) + + return Unit.Default; + } + + private Task> UpdateIfNeeded(PlexMovie plexMovie) => + // .BindT(movie => UpdateStatistics(movie, ffprobePath).MapT(_ => movie)) + // .BindT(UpdateMetadata) + // .BindT(UpdatePoster); + Right(plexMovie).AsTask(); + } +} diff --git a/ErsatzTV.Core/Plex/PlexPartEntry.cs b/ErsatzTV.Core/Plex/PlexPartEntry.cs new file mode 100644 index 00000000..bd6b9073 --- /dev/null +++ b/ErsatzTV.Core/Plex/PlexPartEntry.cs @@ -0,0 +1,11 @@ +namespace ErsatzTV.Core.Plex +{ + public class PlexPartEntry + { + public int Id { get; set; } + public string Key { get; set; } + public int Duration { get; set; } + public string File { get; set; } + public int Size { get; set; } + } +} diff --git a/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs b/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs index 1bf7eeda..adc3b028 100644 --- a/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs @@ -14,18 +14,18 @@ namespace ErsatzTV.Core.Scheduling public ChronologicalMediaCollectionEnumerator( IEnumerable mediaItems, - MediaCollectionEnumeratorState state) + CollectionEnumeratorState state) { _sortedMediaItems = mediaItems.OrderBy(identity, new ChronologicalComparer()).ToList(); - State = new MediaCollectionEnumeratorState { Seed = state.Seed }; + State = new CollectionEnumeratorState { Seed = state.Seed }; while (State.Index < state.Index) { MoveNext(); } } - public MediaCollectionEnumeratorState State { get; } + public CollectionEnumeratorState State { get; } public Option Current => _sortedMediaItems.Any() ? _sortedMediaItems[State.Index] : None; @@ -42,15 +42,23 @@ namespace ErsatzTV.Core.Scheduling DateTime date1 = x switch { - TelevisionEpisodeMediaItem e => e.Metadata?.Aired ?? DateTime.MaxValue, - MovieMediaItem m => m.Metadata?.Premiered ?? DateTime.MaxValue, + Episode e => e.EpisodeMetadata.HeadOrNone().Match( + em => em.ReleaseDate ?? DateTime.MaxValue, + () => DateTime.MaxValue), + Movie m => m.MovieMetadata.HeadOrNone().Match( + mm => mm.ReleaseDate ?? DateTime.MaxValue, + () => DateTime.MaxValue), _ => DateTime.MaxValue }; DateTime date2 = y switch { - TelevisionEpisodeMediaItem e => e.Metadata?.Aired ?? DateTime.MaxValue, - MovieMediaItem m => m.Metadata?.Premiered ?? DateTime.MaxValue, + Episode e => e.EpisodeMetadata.HeadOrNone().Match( + em => em.ReleaseDate ?? DateTime.MaxValue, + () => DateTime.MaxValue), + Movie m => m.MovieMetadata.HeadOrNone().Match( + mm => mm.ReleaseDate ?? DateTime.MaxValue, + () => DateTime.MaxValue), _ => DateTime.MaxValue }; @@ -61,13 +69,13 @@ namespace ErsatzTV.Core.Scheduling int season1 = x switch { - TelevisionEpisodeMediaItem e => e.Metadata?.Season ?? int.MaxValue, + Episode e => e.Season?.SeasonNumber ?? int.MaxValue, _ => int.MaxValue }; int season2 = y switch { - TelevisionEpisodeMediaItem e => e.Metadata?.Season ?? int.MaxValue, + Episode e => e.Season?.SeasonNumber ?? int.MaxValue, _ => int.MaxValue }; @@ -78,13 +86,13 @@ namespace ErsatzTV.Core.Scheduling int episode1 = x switch { - TelevisionEpisodeMediaItem e => e.Metadata?.Episode ?? int.MaxValue, + Episode e => e.EpisodeNumber, _ => int.MaxValue }; int episode2 = y switch { - TelevisionEpisodeMediaItem e => e.Metadata?.Episode ?? int.MaxValue, + Episode e => e.EpisodeNumber, _ => int.MaxValue }; diff --git a/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs b/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs index 16d70a2c..b92d381e 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading.Tasks; using ErsatzTV.Core.Domain; @@ -55,15 +54,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); + List showItems = + await _televisionRepository.GetShowItems(collectionKey.MediaItemId ?? 0); return Tuple(collectionKey, showItems.Cast().ToList()); case ProgramScheduleItemCollectionType.TelevisionSeason: - List seasonItems = - await _televisionRepository.GetSeasonItems(collectionKey.Id); + List seasonItems = + await _televisionRepository.GetSeasonItems(collectionKey.MediaItemId ?? 0); return Tuple(collectionKey, seasonItems.Cast().ToList()); default: return Tuple(collectionKey, new List()); @@ -145,14 +144,21 @@ namespace ErsatzTV.Core.Scheduling DisplayTitle(mediaItem), itemStartTime); + MediaVersion version = mediaItem switch + { + Movie m => m.MediaVersions.Head(), + Episode e => e.MediaVersions.Head(), + _ => throw new ArgumentOutOfRangeException(nameof(mediaItem)) + }; + var playoutItem = new PlayoutItem { MediaItemId = mediaItem.Id, - Start = itemStartTime, - Finish = itemStartTime + mediaItem.Statistics.Duration + Start = itemStartTime.UtcDateTime, + Finish = itemStartTime.UtcDateTime + version.Duration }; - currentTime = itemStartTime + mediaItem.Statistics.Duration; + currentTime = itemStartTime + version.Duration; enumerator.MoveNext(); playout.Items.Add(playoutItem); @@ -187,6 +193,13 @@ namespace ErsatzTV.Core.Scheduling enumerator.Current.Do( peekMediaItem => { + MediaVersion peekVersion = peekMediaItem switch + { + Movie m => m.MediaVersions.Head(), + Episode e => e.MediaVersions.Head(), + _ => throw new ArgumentOutOfRangeException(nameof(peekMediaItem)) + }; + ProgramScheduleItem peekScheduleItem = sortedScheduleItems[(index + 1) % sortedScheduleItems.Count]; DateTimeOffset peekScheduleItemStart = @@ -198,7 +211,7 @@ namespace ErsatzTV.Core.Scheduling // is after, we need to move on to the next schedule item // eventually, spots probably have to fit in this gap bool willNotFinishInTime = currentTime <= peekScheduleItemStart && - currentTime + peekMediaItem.Statistics.Duration > + currentTime + peekVersion.Duration > peekScheduleItemStart; if (willNotFinishInTime) { @@ -213,6 +226,13 @@ namespace ErsatzTV.Core.Scheduling enumerator.Current.Do( peekMediaItem => { + MediaVersion peekVersion = peekMediaItem switch + { + Movie m => m.MediaVersions.Head(), + Episode e => e.MediaVersions.Head(), + _ => throw new ArgumentOutOfRangeException(nameof(peekMediaItem)) + }; + // remember when we need to finish this duration item if (durationFinish.IsNone) { @@ -221,7 +241,7 @@ namespace ErsatzTV.Core.Scheduling bool willNotFinishInTime = currentTime <= durationFinish.IfNone(DateTime.MinValue) && - currentTime + peekMediaItem.Statistics.Duration > + currentTime + peekVersion.Duration > durationFinish.IfNone(DateTime.MinValue); if (willNotFinishInTime) { @@ -250,7 +270,7 @@ namespace ErsatzTV.Core.Scheduling { NextScheduleItem = nextScheduleItem, NextScheduleItemId = nextScheduleItem.Id, - NextStart = GetStartTimeAfter(nextScheduleItem, currentTime) + NextStart = GetStartTimeAfter(nextScheduleItem, currentTime).UtcDateTime }; // build program schedule anchors @@ -306,7 +326,7 @@ namespace ErsatzTV.Core.Scheduling } TimeSpan startTime = item.StartTime.GetValueOrDefault(); - DateTime result = start.Date + startTime; + DateTimeOffset result = start.Date + startTime; // need to wrap to the next day if appropriate return start.TimeOfDay > startTime ? result.AddDays(1) : result; case StartType.Dynamic: @@ -325,7 +345,9 @@ namespace ErsatzTV.Core.Scheduling { Option maybeExisting = playout.ProgramScheduleAnchors .FirstOrDefault( - a => a.CollectionType == collectionKey.CollectionType && a.CollectionId == collectionKey.Id); + a => a.CollectionType == collectionKey.CollectionType + && a.CollectionId == 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 +365,8 @@ namespace ErsatzTV.Core.Scheduling ProgramSchedule = playout.ProgramSchedule, ProgramScheduleId = playout.ProgramScheduleId, CollectionType = collectionKey.CollectionType, - CollectionId = collectionKey.Id, + CollectionId = collectionKey.CollectionId, + MediaItemId = collectionKey.MediaItemId, EnumeratorState = maybeEnumeratorState[collectionKey] }); @@ -360,13 +383,14 @@ namespace ErsatzTV.Core.Scheduling { Option maybeAnchor = playout.ProgramScheduleAnchors .FirstOrDefault( - a => a.ProgramScheduleId == playout.ProgramScheduleId && a.CollectionType == - collectionKey.CollectionType - && a.CollectionId == collectionKey.Id); + a => a.ProgramScheduleId == playout.ProgramScheduleId + && a.CollectionType == collectionKey.CollectionType + && a.CollectionId == collectionKey.CollectionId + && a.MediaItemId == collectionKey.MediaItemId); - MediaCollectionEnumeratorState state = maybeAnchor.Match( + CollectionEnumeratorState state = maybeAnchor.Match( anchor => anchor.EnumeratorState, - () => new MediaCollectionEnumeratorState { Seed = Random.Next(), Index = 0 }); + () => new CollectionEnumeratorState { Seed = Random.Next(), Index = 0 }); switch (playout.ProgramSchedule.MediaCollectionPlaybackOrder) { @@ -385,10 +409,12 @@ namespace ErsatzTV.Core.Scheduling private static string DisplayTitle(MediaItem mediaItem) => mediaItem switch { - TelevisionEpisodeMediaItem e => e.Metadata != null - ? $"{e.Metadata.Title} - s{e.Metadata.Season:00}e{e.Metadata.Episode:00}" - : Path.GetFileName(e.Path), - MovieMediaItem m => m.Metadata?.Title ?? Path.GetFileName(m.Path), + Episode e => e.EpisodeMetadata.Any() && e.Season != null + ? $"{e.EpisodeMetadata.Head().Title} - s{e.Season.SeasonNumber:00}e{e.EpisodeNumber:00}" + : "[unknown episode]", + Movie m => m.MovieMetadata.HeadOrNone().Match( + mm => mm.Title ?? string.Empty, + () => "[unknown movie]"), _ => string.Empty }; @@ -398,17 +424,17 @@ namespace ErsatzTV.Core.Scheduling ProgramScheduleItemCollectionType.Collection => new CollectionKey { CollectionType = item.CollectionType, - Id = item.MediaCollectionId.Value + CollectionId = item.CollectionId }, ProgramScheduleItemCollectionType.TelevisionShow => new CollectionKey { CollectionType = item.CollectionType, - Id = item.TelevisionShowId.Value + MediaItemId = item.MediaItemId }, ProgramScheduleItemCollectionType.TelevisionSeason => new CollectionKey { CollectionType = item.CollectionType, - Id = item.TelevisionSeasonId.Value + MediaItemId = item.MediaItemId }, _ => throw new ArgumentOutOfRangeException(nameof(item)) }; @@ -416,7 +442,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.Core/Scheduling/RandomizedMediaCollectionEnumerator.cs b/ErsatzTV.Core/Scheduling/RandomizedMediaCollectionEnumerator.cs index 660c7210..9d866d4e 100644 --- a/ErsatzTV.Core/Scheduling/RandomizedMediaCollectionEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/RandomizedMediaCollectionEnumerator.cs @@ -14,12 +14,12 @@ namespace ErsatzTV.Core.Scheduling private readonly Random _random; private int _index; - public RandomizedMediaCollectionEnumerator(IList mediaItems, MediaCollectionEnumeratorState state) + public RandomizedMediaCollectionEnumerator(IList mediaItems, CollectionEnumeratorState state) { _mediaItems = mediaItems; _random = new Random(state.Seed); - State = new MediaCollectionEnumeratorState { Seed = state.Seed }; + State = new CollectionEnumeratorState { Seed = state.Seed }; // we want to move at least once so we start with a random item and not the first // because _index defaults to 0 while (State.Index <= state.Index) @@ -28,7 +28,7 @@ namespace ErsatzTV.Core.Scheduling } } - public MediaCollectionEnumeratorState State { get; } + public CollectionEnumeratorState State { get; } public Option Current => _mediaItems.Any() ? _mediaItems[_index] : None; diff --git a/ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs b/ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs index a81bae44..d51ca9fb 100644 --- a/ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs @@ -15,20 +15,20 @@ namespace ErsatzTV.Core.Scheduling private IList _shuffled; - public ShuffledMediaCollectionEnumerator(IList mediaItems, MediaCollectionEnumeratorState state) + public ShuffledMediaCollectionEnumerator(IList mediaItems, CollectionEnumeratorState state) { _mediaItems = mediaItems; _random = new Random(state.Seed); _shuffled = Shuffle(_mediaItems, _random); - State = new MediaCollectionEnumeratorState { Seed = state.Seed }; + State = new CollectionEnumeratorState { Seed = state.Seed }; while (State.Index < state.Index) { MoveNext(); } } - public MediaCollectionEnumeratorState State { get; } + public CollectionEnumeratorState State { get; } public Option Current => _shuffled.Any() ? _shuffled[State.Index % _mediaItems.Count] : None; diff --git a/ErsatzTV.Infrastructure/Data/Configurations/ChannelConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/ChannelConfiguration.cs index 62e0c458..13b01e70 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/ChannelConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/ChannelConfiguration.cs @@ -8,6 +8,8 @@ namespace ErsatzTV.Infrastructure.Data.Configurations { public void Configure(EntityTypeBuilder builder) { + builder.ToTable("Channel"); + builder.HasIndex(c => c.Number) .IsUnique(); @@ -15,6 +17,10 @@ namespace ErsatzTV.Infrastructure.Data.Configurations .WithOne(p => p.Channel) .HasForeignKey(p => p.ChannelId) .OnDelete(DeleteBehavior.Cascade); + + builder.HasMany(c => c.Artwork) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); } } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/Collection/CollectionConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/Collection/CollectionConfiguration.cs new file mode 100644 index 00000000..c389a3fc --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/Collection/CollectionConfiguration.cs @@ -0,0 +1,27 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class CollectionConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Collection"); + + builder.HasMany(c => c.MediaItems) + .WithMany(m => m.Collections) + .UsingEntity( + j => j.HasOne(ci => ci.MediaItem) + .WithMany(mi => mi.CollectionItems) + .HasForeignKey(ci => ci.MediaItemId) + .OnDelete(DeleteBehavior.Cascade), + j => j.HasOne(ci => ci.Collection) + .WithMany(c => c.CollectionItems) + .HasForeignKey(ci => ci.CollectionId) + .OnDelete(DeleteBehavior.Cascade), + j => j.HasKey(ci => new { ci.CollectionId, ci.MediaItemId })); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/ConfigElementConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/ConfigElementConfiguration.cs index ca5e3bea..0f1da93b 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/ConfigElementConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/ConfigElementConfiguration.cs @@ -6,8 +6,12 @@ namespace ErsatzTV.Infrastructure.Data.Configurations { public class ConfigElementConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) => + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("ConfigElement"); + builder.HasIndex(ce => ce.Key) .IsUnique(); + } } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/FFmpegProfileConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/FFmpegProfileConfiguration.cs new file mode 100644 index 00000000..bcfbf503 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/FFmpegProfileConfiguration.cs @@ -0,0 +1,11 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class FFmpegProfileConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) => builder.ToTable("FFmpegProfile"); + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/Library/LibraryConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/Library/LibraryConfiguration.cs new file mode 100644 index 00000000..3cc2a3ab --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/Library/LibraryConfiguration.cs @@ -0,0 +1,12 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class LibraryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) => + builder.ToTable("Library"); + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/Library/LibraryPathConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/Library/LibraryPathConfiguration.cs new file mode 100644 index 00000000..3238aca1 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/Library/LibraryPathConfiguration.cs @@ -0,0 +1,19 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class LibraryPathConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("LibraryPath"); + + builder.HasMany(p => p.MediaItems) + .WithOne(i => i.LibraryPath) + .HasForeignKey(i => i.LibraryPathId) + .OnDelete(DeleteBehavior.Cascade); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/Library/LocalLibraryConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/Library/LocalLibraryConfiguration.cs new file mode 100644 index 00000000..bf2edef9 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/Library/LocalLibraryConfiguration.cs @@ -0,0 +1,12 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class LocalLibraryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) => + builder.ToTable("LocalLibrary"); + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/Library/PlexLibraryConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/Library/PlexLibraryConfiguration.cs new file mode 100644 index 00000000..777887f7 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/Library/PlexLibraryConfiguration.cs @@ -0,0 +1,12 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class PlexLibraryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) => + builder.ToTable("PlexLibrary"); + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaCollectionConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaCollectionConfiguration.cs deleted file mode 100644 index 1b4fec1b..00000000 --- a/ErsatzTV.Infrastructure/Data/Configurations/MediaCollectionConfiguration.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ErsatzTV.Core.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace ErsatzTV.Infrastructure.Data.Configurations -{ - public class MediaCollectionConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) => - builder.HasIndex(c => c.Name).IsUnique(); - } -} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/EpisodeConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/EpisodeConfiguration.cs new file mode 100644 index 00000000..b541896d --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/EpisodeConfiguration.cs @@ -0,0 +1,23 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class EpisodeConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Episode"); + + builder.HasMany(e => e.EpisodeMetadata) + .WithOne(m => m.Episode) + .HasForeignKey(m => m.EpisodeId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasMany(e => e.MediaVersions) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/MediaFileConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/MediaFileConfiguration.cs new file mode 100644 index 00000000..1b7e189b --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/MediaFileConfiguration.cs @@ -0,0 +1,17 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class MediaFileConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("MediaFile"); + + builder.HasIndex(f => f.Path) + .IsUnique(); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaItemConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/MediaItemConfiguration.cs similarity index 72% rename from ErsatzTV.Infrastructure/Data/Configurations/MediaItemConfiguration.cs rename to ErsatzTV.Infrastructure/Data/Configurations/MediaItem/MediaItemConfiguration.cs index dcb5df39..d9a06624 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/MediaItemConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/MediaItemConfiguration.cs @@ -6,10 +6,6 @@ namespace ErsatzTV.Infrastructure.Data.Configurations { public class MediaItemConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("MediaItems"); - builder.OwnsOne(c => c.Statistics).WithOwner(); - } + public void Configure(EntityTypeBuilder builder) => builder.ToTable("MediaItem"); } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/MediaVersionConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/MediaVersionConfiguration.cs new file mode 100644 index 00000000..2cd0e35e --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/MediaVersionConfiguration.cs @@ -0,0 +1,19 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class MediaVersionConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("MediaVersion"); + + builder.HasMany(v => v.MediaFiles) + .WithOne(f => f.MediaVersion) + .HasForeignKey(f => f.MediaVersionId) + .OnDelete(DeleteBehavior.Cascade); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/MovieConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/MovieConfiguration.cs new file mode 100644 index 00000000..3d1eb66d --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/MovieConfiguration.cs @@ -0,0 +1,23 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class MovieConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Movie"); + + builder.HasMany(m => m.MovieMetadata) + .WithOne(m => m.Movie) + .HasForeignKey(m => m.MovieId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasMany(m => m.MediaVersions) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMovieMediaItemConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMovieMediaItemConfiguration.cs new file mode 100644 index 00000000..ea5269f1 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMovieMediaItemConfiguration.cs @@ -0,0 +1,11 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class PlexMovieMediaItemConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) => builder.ToTable("PlexMovie"); + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/SeasonConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/SeasonConfiguration.cs new file mode 100644 index 00000000..31aa6006 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/SeasonConfiguration.cs @@ -0,0 +1,24 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class SeasonConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Season"); + + builder.HasMany(s => s.Episodes) + .WithOne(e => e.Season) + .HasForeignKey(e => e.SeasonId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasMany(s => s.SeasonMetadata) + .WithOne(s => s.Season) + .HasForeignKey(s => s.SeasonId) + .OnDelete(DeleteBehavior.Cascade); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/ShowConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/ShowConfiguration.cs new file mode 100644 index 00000000..52195bf2 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/ShowConfiguration.cs @@ -0,0 +1,24 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class ShowConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Show"); + + builder.HasMany(s => s.Seasons) + .WithOne(s => s.Show) + .HasForeignKey(s => s.ShowId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasMany(s => s.ShowMetadata) + .WithOne(s => s.Show) + .HasForeignKey(s => s.ShowId) + .OnDelete(DeleteBehavior.Cascade); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/LocalMediaSourceConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaSource/LocalMediaSourceConfiguration.cs similarity index 87% rename from ErsatzTV.Infrastructure/Data/Configurations/LocalMediaSourceConfiguration.cs rename to ErsatzTV.Infrastructure/Data/Configurations/MediaSource/LocalMediaSourceConfiguration.cs index 2132f6d0..9ffbced3 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/LocalMediaSourceConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaSource/LocalMediaSourceConfiguration.cs @@ -7,6 +7,6 @@ namespace ErsatzTV.Infrastructure.Data.Configurations public class LocalMediaSourceConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) => - builder.ToTable("LocalMediaSources"); + builder.ToTable("LocalMediaSource"); } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaSourceConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaSource/MediaSourceConfiguration.cs similarity index 56% rename from ErsatzTV.Infrastructure/Data/Configurations/MediaSourceConfiguration.cs rename to ErsatzTV.Infrastructure/Data/Configurations/MediaSource/MediaSourceConfiguration.cs index debf17ce..92d55e5b 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/MediaSourceConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaSource/MediaSourceConfiguration.cs @@ -6,7 +6,14 @@ namespace ErsatzTV.Infrastructure.Data.Configurations { public class MediaSourceConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) => - builder.HasIndex(ms => ms.Name).IsUnique(); + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("MediaSource"); + + builder.HasMany(s => s.Libraries) + .WithOne(l => l.MediaSource) + .HasForeignKey(l => l.MediaSourceId) + .OnDelete(DeleteBehavior.Cascade); + } } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaSource/PlexConnectionConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaSource/PlexConnectionConfiguration.cs new file mode 100644 index 00000000..d0a10050 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaSource/PlexConnectionConfiguration.cs @@ -0,0 +1,12 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class PlexConnectionConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) => + builder.ToTable("PlexConnection"); + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/PlexMediaSourceConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaSource/PlexMediaSourceConfiguration.cs similarity index 55% rename from ErsatzTV.Infrastructure/Data/Configurations/PlexMediaSourceConfiguration.cs rename to ErsatzTV.Infrastructure/Data/Configurations/MediaSource/PlexMediaSourceConfiguration.cs index 42ec3fdc..990521c7 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/PlexMediaSourceConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaSource/PlexMediaSourceConfiguration.cs @@ -6,6 +6,14 @@ namespace ErsatzTV.Infrastructure.Data.Configurations { public class PlexMediaSourceConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) => builder.ToTable("PlexMediaSources"); + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("PlexMediaSource"); + + builder.HasMany(s => s.Connections) + .WithOne(c => c.PlexMediaSource) + .HasForeignKey(c => c.PlexMediaSourceId) + .OnDelete(DeleteBehavior.Cascade); + } } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/Metadata/ArtworkConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/Metadata/ArtworkConfiguration.cs new file mode 100644 index 00000000..f27fa291 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/Metadata/ArtworkConfiguration.cs @@ -0,0 +1,11 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class ArtworkConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) => builder.ToTable("Artwork"); + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/Metadata/EpisodeMetadataConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/Metadata/EpisodeMetadataConfiguration.cs new file mode 100644 index 00000000..04ae2d4f --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/Metadata/EpisodeMetadataConfiguration.cs @@ -0,0 +1,18 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class EpisodeMetadataConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("EpisodeMetadata"); + + builder.HasMany(em => em.Artwork) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MovieMetadataConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/Metadata/MovieMetadataConfiguration.cs similarity index 71% rename from ErsatzTV.Infrastructure/Data/Configurations/MovieMetadataConfiguration.cs rename to ErsatzTV.Infrastructure/Data/Configurations/Metadata/MovieMetadataConfiguration.cs index 70a6ffdb..bc08274b 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/MovieMetadataConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/Metadata/MovieMetadataConfiguration.cs @@ -6,7 +6,13 @@ namespace ErsatzTV.Infrastructure.Data.Configurations { public class MovieMetadataConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) => + public void Configure(EntityTypeBuilder builder) + { builder.ToTable("MovieMetadata"); + + builder.HasMany(mm => mm.Artwork) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + } } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/Metadata/SeasonMetadataConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/Metadata/SeasonMetadataConfiguration.cs new file mode 100644 index 00000000..5584a0e4 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/Metadata/SeasonMetadataConfiguration.cs @@ -0,0 +1,18 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class SeasonMetadataConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("SeasonMetadata"); + + builder.HasMany(em => em.Artwork) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/Metadata/ShowMetadataConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/Metadata/ShowMetadataConfiguration.cs new file mode 100644 index 00000000..68515436 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/Metadata/ShowMetadataConfiguration.cs @@ -0,0 +1,18 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class ShowMetadataConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("ShowMetadata"); + + builder.HasMany(sm => sm.Artwork) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MovieMediaItemConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MovieMediaItemConfiguration.cs deleted file mode 100644 index b1a5ca66..00000000 --- a/ErsatzTV.Infrastructure/Data/Configurations/MovieMediaItemConfiguration.cs +++ /dev/null @@ -1,19 +0,0 @@ -using ErsatzTV.Core.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace ErsatzTV.Infrastructure.Data.Configurations -{ - public class MovieMediaItemConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("Movies"); - - builder.HasOne(i => i.Metadata) - .WithOne(m => m.Movie) - .HasForeignKey(m => m.MovieId) - .OnDelete(DeleteBehavior.Cascade); - } - } -} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/PlayoutConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/PlayoutConfiguration.cs index 4f38fbb4..e39f981d 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/PlayoutConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/PlayoutConfiguration.cs @@ -8,6 +8,8 @@ namespace ErsatzTV.Infrastructure.Data.Configurations { public void Configure(EntityTypeBuilder builder) { + builder.ToTable("Playout"); + builder.HasMany(p => p.Items) .WithOne(pi => pi.Playout) .HasForeignKey(pi => pi.PlayoutId) diff --git a/ErsatzTV.Infrastructure/Data/Configurations/PlayoutItemConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/PlayoutItemConfiguration.cs new file mode 100644 index 00000000..f1498923 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/PlayoutItemConfiguration.cs @@ -0,0 +1,20 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class PlayoutItemConfiguration : IEntityTypeConfiguration + + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("PlayoutItem"); + + builder.HasOne(pi => pi.MediaItem) + .WithMany() + .HasForeignKey(pi => pi.MediaItemId) + .OnDelete(DeleteBehavior.Cascade); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/PlayoutProgramScheduleAnchorConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/PlayoutProgramScheduleAnchorConfiguration.cs index 8585e920..fe05203b 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/PlayoutProgramScheduleAnchorConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/PlayoutProgramScheduleAnchorConfiguration.cs @@ -6,8 +6,24 @@ namespace ErsatzTV.Infrastructure.Data.Configurations { public class PlayoutProgramScheduleAnchorConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) => + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("PlayoutProgramScheduleAnchor"); + builder.OwnsOne(a => a.EnumeratorState) .WithOwner(); + + builder.HasOne(i => i.Collection) + .WithMany() + .HasForeignKey(i => i.CollectionId) + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(false); + + builder.HasOne(i => i.MediaItem) + .WithMany() + .HasForeignKey(i => i.MediaItemId) + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(false); + } } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/PlexMediaSourceConnectionConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/PlexMediaSourceConnectionConfiguration.cs deleted file mode 100644 index 01850c86..00000000 --- a/ErsatzTV.Infrastructure/Data/Configurations/PlexMediaSourceConnectionConfiguration.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ErsatzTV.Core.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace ErsatzTV.Infrastructure.Data.Configurations -{ - public class PlexMediaSourceConnectionConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) => - builder.ToTable("PlexMediaSourceConnections"); - } -} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/PlexMediaSourceLibraryConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/PlexMediaSourceLibraryConfiguration.cs deleted file mode 100644 index aca02c0f..00000000 --- a/ErsatzTV.Infrastructure/Data/Configurations/PlexMediaSourceLibraryConfiguration.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ErsatzTV.Core.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace ErsatzTV.Infrastructure.Data.Configurations -{ - public class PlexMediaSourceLibraryConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) => - builder.ToTable("PlexMediaSourceLibraries"); - } -} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleConfiguration.cs index f9008782..e93a3a3c 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleConfiguration.cs @@ -8,6 +8,8 @@ namespace ErsatzTV.Infrastructure.Data.Configurations { public void Configure(EntityTypeBuilder builder) { + builder.ToTable("ProgramSchedule"); + builder.HasIndex(ps => ps.Name) .IsUnique(); @@ -18,7 +20,8 @@ namespace ErsatzTV.Infrastructure.Data.Configurations builder.HasMany(ps => ps.Playouts) .WithOne(p => p.ProgramSchedule) - .HasForeignKey(p => p.ProgramScheduleId); + .HasForeignKey(p => p.ProgramScheduleId) + .OnDelete(DeleteBehavior.Cascade); } } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemConfiguration.cs index f3c72b35..0e8a1d90 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemConfiguration.cs @@ -8,21 +8,18 @@ namespace ErsatzTV.Infrastructure.Data.Configurations { public void Configure(EntityTypeBuilder builder) { - builder.ToTable("ProgramScheduleItems"); + builder.ToTable("ProgramScheduleItem"); - builder.HasOne(i => i.MediaCollection) + builder.HasOne(i => i.Collection) .WithMany() - .HasForeignKey(i => i.MediaCollectionId) + .HasForeignKey(i => i.CollectionId) + .OnDelete(DeleteBehavior.Cascade) .IsRequired(false); - builder.HasOne(i => i.TelevisionShow) + builder.HasOne(i => i.MediaItem) .WithMany() - .HasForeignKey(i => i.TelevisionShowId) - .IsRequired(false); - - builder.HasOne(i => i.TelevisionSeason) - .WithMany() - .HasForeignKey(i => i.TelevisionSeasonId) + .HasForeignKey(i => i.MediaItemId) + .OnDelete(DeleteBehavior.Cascade) .IsRequired(false); } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemDurationConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemDurationConfiguration.cs index 7587586c..cdf510dd 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemDurationConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemDurationConfiguration.cs @@ -7,6 +7,6 @@ namespace ErsatzTV.Infrastructure.Data.Configurations public class ProgramScheduleItemDurationConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) => - builder.ToTable("ProgramScheduleDurationItems"); + builder.ToTable("ProgramScheduleDurationItem"); } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemFloodConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemFloodConfiguration.cs index c127ab4e..c58329ec 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemFloodConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemFloodConfiguration.cs @@ -7,6 +7,6 @@ namespace ErsatzTV.Infrastructure.Data.Configurations public class ProgramScheduleItemFloodConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) => - builder.ToTable("ProgramScheduleFloodItems"); + builder.ToTable("ProgramScheduleFloodItem"); } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemMultipleConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemMultipleConfiguration.cs index acc44951..c2117e5b 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemMultipleConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemMultipleConfiguration.cs @@ -7,6 +7,6 @@ namespace ErsatzTV.Infrastructure.Data.Configurations public class ProgramScheduleItemMultipleConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) => - builder.ToTable("ProgramScheduleMultipleItems"); + builder.ToTable("ProgramScheduleMultipleItem"); } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemOneConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemOneConfiguration.cs index 5ef4b164..20376f3c 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemOneConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemOneConfiguration.cs @@ -7,6 +7,6 @@ namespace ErsatzTV.Infrastructure.Data.Configurations public class ProgramScheduleItemOneConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) => - builder.ToTable("ProgramScheduleOneItems"); + builder.ToTable("ProgramScheduleOneItem"); } } diff --git a/ErsatzTV.Infrastructure/Data/Configurations/ResolutionConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/ResolutionConfiguration.cs new file mode 100644 index 00000000..49c35ab9 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/ResolutionConfiguration.cs @@ -0,0 +1,11 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class ResolutionConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) => builder.ToTable("Resolution"); + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/SimpleMediaCollectionConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/SimpleMediaCollectionConfiguration.cs deleted file mode 100644 index 5f79f77a..00000000 --- a/ErsatzTV.Infrastructure/Data/Configurations/SimpleMediaCollectionConfiguration.cs +++ /dev/null @@ -1,30 +0,0 @@ -using ErsatzTV.Core.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace ErsatzTV.Infrastructure.Data.Configurations -{ - public class SimpleMediaCollectionConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("SimpleMediaCollections"); - - builder.HasMany(c => c.Movies) - .WithMany(m => m.SimpleMediaCollections) - .UsingEntity(join => join.ToTable("SimpleMediaCollectionMovies")); - - builder.HasMany(c => c.TelevisionShows) - .WithMany(s => s.SimpleMediaCollections) - .UsingEntity(join => join.ToTable("SimpleMediaCollectionShows")); - - builder.HasMany(c => c.TelevisionSeasons) - .WithMany(s => s.SimpleMediaCollections) - .UsingEntity(join => join.ToTable("SimpleMediaCollectionSeasons")); - - builder.HasMany(c => c.TelevisionEpisodes) - .WithMany(e => e.SimpleMediaCollections) - .UsingEntity(join => join.ToTable("SimpleMediaCollectionEpisodes")); - } - } -} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/TelevisionEpisodeMediaItemConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/TelevisionEpisodeMediaItemConfiguration.cs deleted file mode 100644 index 829316bb..00000000 --- a/ErsatzTV.Infrastructure/Data/Configurations/TelevisionEpisodeMediaItemConfiguration.cs +++ /dev/null @@ -1,19 +0,0 @@ -using ErsatzTV.Core.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace ErsatzTV.Infrastructure.Data.Configurations -{ - public class TelevisionEpisodeMediaItemConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("TelevisionEpisodes"); - - builder.HasOne(i => i.Metadata) - .WithOne(m => m.TelevisionEpisode) - .HasForeignKey(m => m.TelevisionEpisodeId) - .OnDelete(DeleteBehavior.Cascade); - } - } -} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/TelevisionEpisodeMetadataConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/TelevisionEpisodeMetadataConfiguration.cs deleted file mode 100644 index 08c84c14..00000000 --- a/ErsatzTV.Infrastructure/Data/Configurations/TelevisionEpisodeMetadataConfiguration.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ErsatzTV.Core.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace ErsatzTV.Infrastructure.Data.Configurations -{ - public class TelevisionEpisodeMetadataConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) => - builder.ToTable("TelevisionEpisodeMetadata"); - } -} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/TelevisionSeasonConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/TelevisionSeasonConfiguration.cs deleted file mode 100644 index 8ebc89e8..00000000 --- a/ErsatzTV.Infrastructure/Data/Configurations/TelevisionSeasonConfiguration.cs +++ /dev/null @@ -1,19 +0,0 @@ -using ErsatzTV.Core.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace ErsatzTV.Infrastructure.Data.Configurations -{ - public class TelevisionSeasonConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("TelevisionSeasons"); - - builder.HasMany(season => season.Episodes) - .WithOne(episode => episode.Season) - .HasForeignKey(episode => episode.SeasonId) - .OnDelete(DeleteBehavior.Cascade); - } - } -} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/TelevisionShowConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/TelevisionShowConfiguration.cs deleted file mode 100644 index 75464a52..00000000 --- a/ErsatzTV.Infrastructure/Data/Configurations/TelevisionShowConfiguration.cs +++ /dev/null @@ -1,27 +0,0 @@ -using ErsatzTV.Core.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace ErsatzTV.Infrastructure.Data.Configurations -{ - public class TelevisionShowConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("TelevisionShows"); - - builder.HasOne(show => show.Metadata) - .WithOne(metadata => metadata.TelevisionShow) - .HasForeignKey(metadata => metadata.TelevisionShowId) - .OnDelete(DeleteBehavior.Cascade); - - builder.HasMany(show => show.Seasons) - .WithOne(season => season.TelevisionShow); - - builder.HasMany(show => show.Sources) - .WithOne(source => source.TelevisionShow) - .HasForeignKey(source => source.TelevisionShowId) - .OnDelete(DeleteBehavior.Cascade); - } - } -} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/TelevisionShowMetadataConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/TelevisionShowMetadataConfiguration.cs deleted file mode 100644 index 145dbc1c..00000000 --- a/ErsatzTV.Infrastructure/Data/Configurations/TelevisionShowMetadataConfiguration.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ErsatzTV.Core.Domain; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace ErsatzTV.Infrastructure.Data.Configurations -{ - public class TelevisionShowMetadataConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) => - builder.ToTable("TelevisionShowMetadata"); - } -} diff --git a/ErsatzTV.Infrastructure/Data/Repositories/ChannelRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/ChannelRepository.cs index a7c6f5dd..00c8b727 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/ChannelRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/ChannelRepository.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; @@ -22,27 +23,39 @@ namespace ErsatzTV.Infrastructure.Data.Repositories } public Task> Get(int id) => - _dbContext.Channels.SingleOrDefaultAsync(c => c.Id == id).Map(Optional); + _dbContext.Channels + .Include(c => c.Artwork) + .OrderBy(c => c.Id) + .SingleOrDefaultAsync(c => c.Id == id) + .Map(Optional); public Task> GetByNumber(int number) => _dbContext.Channels .Include(c => c.FFmpegProfile) .ThenInclude(p => p.Resolution) + .OrderBy(c => c.Number) .SingleOrDefaultAsync(c => c.Number == number) .Map(Optional); - public Task> GetAll() => _dbContext.Channels.ToListAsync(); + public Task> GetAll() => + _dbContext.Channels + .Include(c => c.Artwork) + .ToListAsync(); public Task> GetAllForGuide() => _dbContext.Channels .Include(c => c.Playouts) .ThenInclude(p => p.Items) .ThenInclude(i => i.MediaItem) - .ThenInclude(i => (i as TelevisionEpisodeMediaItem).Metadata) + .ThenInclude(i => (i as Episode).EpisodeMetadata) + .Include(c => c.Playouts) + .ThenInclude(p => p.Items) + .ThenInclude(i => i.MediaItem) + .ThenInclude(i => (i as Episode).Season) .Include(c => c.Playouts) .ThenInclude(p => p.Items) .ThenInclude(i => i.MediaItem) - .ThenInclude(i => (i as MovieMediaItem).Metadata) + .ThenInclude(i => (i as Movie).MovieMetadata) .ToListAsync(); public async Task Update(Channel channel) diff --git a/ErsatzTV.Infrastructure/Data/Repositories/ConfigElementRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/ConfigElementRepository.cs index 3ab5ba96..8f7c2c16 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/ConfigElementRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/ConfigElementRepository.cs @@ -1,9 +1,11 @@ using System; +using System.Linq; using System.Threading.Tasks; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; using Microsoft.EntityFrameworkCore; +using static LanguageExt.Prelude; namespace ErsatzTV.Infrastructure.Data.Repositories { @@ -20,8 +22,11 @@ namespace ErsatzTV.Infrastructure.Data.Repositories return configElement; } - public async Task> Get(ConfigElementKey key) => - await _dbContext.ConfigElements.SingleOrDefaultAsync(ce => ce.Key == key.Key); + public Task> Get(ConfigElementKey key) => + _dbContext.ConfigElements + .OrderBy(ce => ce.Key) + .SingleOrDefaultAsync(ce => ce.Key == key.Key) + .Map(Optional); public Task> GetValue(ConfigElementKey key) => Get(key).MapT(ce => (T) Convert.ChangeType(ce.Value, typeof(T))); diff --git a/ErsatzTV.Infrastructure/Data/Repositories/FFmpegProfileRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/FFmpegProfileRepository.cs index 6d9b8df1..e4f29bda 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/FFmpegProfileRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/FFmpegProfileRepository.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; @@ -23,6 +24,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories public async Task> Get(int id) => await _dbContext.FFmpegProfiles .Include(p => p.Resolution) + .OrderBy(p => p.Id) .SingleOrDefaultAsync(p => p.Id == id); public Task> GetAll() => diff --git a/ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs new file mode 100644 index 00000000..340c88e9 --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using Dapper; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using Microsoft.EntityFrameworkCore; +using static LanguageExt.Prelude; + +namespace ErsatzTV.Infrastructure.Data.Repositories +{ + public class LibraryRepository : ILibraryRepository + { + private readonly IDbConnection _dbConnection; + private readonly IDbContextFactory _dbContextFactory; + + public LibraryRepository(IDbContextFactory dbContextFactory, IDbConnection dbConnection) + { + _dbContextFactory = dbContextFactory; + _dbConnection = dbConnection; + } + + public async Task Add(LibraryPath libraryPath) + { + await using TvContext context = _dbContextFactory.CreateDbContext(); + await context.LibraryPaths.AddAsync(libraryPath); + await context.SaveChangesAsync(); + return libraryPath; + } + + public Task> Get(int libraryId) + { + using TvContext context = _dbContextFactory.CreateDbContext(); + return context.Libraries + .Include(l => l.Paths) + .OrderBy(l => l.Id) + .SingleOrDefaultAsync(l => l.Id == libraryId) + .Map(Optional); + } + + public Task> GetLocal(int libraryId) + { + using TvContext context = _dbContextFactory.CreateDbContext(); + return context.LocalLibraries + .OrderBy(l => l.Id) + .SingleOrDefaultAsync(l => l.Id == libraryId) + .Map(Optional); + } + + public Task> GetAllLocal() + { + using TvContext context = _dbContextFactory.CreateDbContext(); + return context.LocalLibraries.ToListAsync(); + } + + public Task UpdateLastScan(Library library) => _dbConnection.ExecuteAsync( + "UPDATE Library SET LastScan = @LastScan WHERE Id = @Id", + new { library.LastScan, library.Id }).ToUnit(); + + public Task> GetLocalPaths(int libraryId) + { + using TvContext context = _dbContextFactory.CreateDbContext(); + return context.LocalLibraries + .Include(l => l.Paths) + .OrderBy(l => l.Id) + .SingleOrDefaultAsync(l => l.Id == libraryId) + .Map(Optional) + .Match(l => l.Paths, () => new List()); + } + + public Task> GetPath(int libraryPathId) + { + using TvContext context = _dbContextFactory.CreateDbContext(); + return context.LibraryPaths + .OrderBy(lp => lp.Id) + .SingleOrDefaultAsync(lp => lp.Id == libraryPathId) + .Map(Optional); + } + + public Task CountMediaItemsByPath(int libraryPathId) => + _dbConnection.QuerySingleAsync( + @"SELECT COUNT(*) FROM MediaItem WHERE LibraryPathId = @LibraryPathId", + new { LibraryPathId = libraryPathId }); + + public async Task DeleteLocalPath(int libraryPathId) + { + await using TvContext context = _dbContextFactory.CreateDbContext(); + LibraryPath libraryPath = await context.LibraryPaths.FindAsync(libraryPathId); + context.LibraryPaths.Remove(libraryPath); + await context.SaveChangesAsync(); + } + } +} diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs index 8dfa21a6..33b8f7cf 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; @@ -23,174 +22,191 @@ namespace ErsatzTV.Infrastructure.Data.Repositories _dbConnection = dbConnection; } - public async Task Add(SimpleMediaCollection collection) + public async Task Add(Collection collection) { - await _dbContext.SimpleMediaCollections.AddAsync(collection); + await _dbContext.Collections.AddAsync(collection); await _dbContext.SaveChangesAsync(); return collection; } - public Task> Get(int id) => - _dbContext.MediaCollections.SingleOrDefaultAsync(c => c.Id == id).Map(Optional); - public Task> GetSimpleMediaCollection(int id) => - _dbContext.SimpleMediaCollections + public async Task AddMediaItem(int collectionId, int mediaItemId) + { + Option maybeCollection = await _dbContext.Collections + .Include(c => c.MediaItems) + .OrderBy(c => c.Id) + .SingleOrDefaultAsync(c => c.Id == collectionId) + .Map(Optional); + + await maybeCollection.IfSomeAsync( + async collection => + { + if (collection.MediaItems.All(i => i.Id != mediaItemId)) + { + Option maybeMediaItem = await _dbContext.MediaItems + .OrderBy(i => i.Id) + .SingleOrDefaultAsync(i => i.Id == mediaItemId) + .Map(Optional); + + await maybeMediaItem.IfSomeAsync( + async mediaItem => + { + collection.MediaItems.Add(mediaItem); + await _dbContext.SaveChangesAsync(); + }); + } + }); + + return Unit.Default; + } + + public Task> Get(int id) => + _dbContext.Collections + .OrderBy(c => c.Id) .SingleOrDefaultAsync(c => c.Id == id) .Map(Optional); - public Task> GetSimpleMediaCollectionWithItems(int id) => - _dbContext.SimpleMediaCollections - .Include(s => s.Movies) - .ThenInclude(m => m.Source) - .Include(s => s.TelevisionShows) - .ThenInclude(s => s.Metadata) - .Include(s => s.TelevisionSeasons) - .Include(s => s.TelevisionEpisodes) - .ThenInclude(s => s.Metadata) + public Task> GetCollectionWithItems(int id) => + _dbContext.Collections + .Include(c => c.MediaItems) + .ThenInclude(i => i.LibraryPath) + .Include(c => c.MediaItems) + .ThenInclude(i => (i as Movie).MovieMetadata) + .Include(c => c.MediaItems) + .ThenInclude(i => (i as Show).ShowMetadata) + .Include(c => c.MediaItems) + .ThenInclude(i => (i as Season).Show) + .ThenInclude(s => s.ShowMetadata) + .Include(c => c.MediaItems) + .ThenInclude(i => (i as Episode).EpisodeMetadata) + .Include(c => c.MediaItems) + .ThenInclude(i => (i as Episode).Season) + .ThenInclude(s => s.Show) + .ThenInclude(s => s.ShowMetadata) + .OrderBy(c => c.Id) .SingleOrDefaultAsync(c => c.Id == id) .Map(Optional); - public Task> GetSimpleMediaCollectionWithItemsUntracked(int id) => - _dbContext.SimpleMediaCollections + public Task> GetCollectionWithItemsUntracked(int id) => + _dbContext.Collections .AsNoTracking() - .Include(s => s.Movies) - .ThenInclude(i => i.Source) - .Include(s => s.Movies) - .ThenInclude(m => m.Metadata) - .Include(s => s.TelevisionShows) - .ThenInclude(s => s.Metadata) - .Include(s => s.TelevisionSeasons) - .ThenInclude(s => s.TelevisionShow) - .ThenInclude(s => s.Metadata) - .Include(s => s.TelevisionEpisodes) - .ThenInclude(s => s.Metadata) - .Include(s => s.TelevisionEpisodes) - .ThenInclude(e => e.Season) - .ThenInclude(s => s.TelevisionShow) - .ThenInclude(s => s.Metadata) + .Include(c => c.MediaItems) + .ThenInclude(i => i.LibraryPath) + .Include(c => c.MediaItems) + .ThenInclude(i => (i as Movie).MovieMetadata) + .ThenInclude(mm => mm.Artwork) + .Include(c => c.MediaItems) + .ThenInclude(i => (i as Show).ShowMetadata) + .ThenInclude(sm => sm.Artwork) + .Include(c => c.MediaItems) + .ThenInclude(i => (i as Season).SeasonMetadata) + .ThenInclude(sm => sm.Artwork) + .Include(c => c.MediaItems) + .ThenInclude(i => (i as Season).Show) + .ThenInclude(s => s.ShowMetadata) + .Include(c => c.MediaItems) + .ThenInclude(i => (i as Episode).EpisodeMetadata) + .ThenInclude(em => em.Artwork) + .Include(c => c.MediaItems) + .ThenInclude(i => (i as Episode).Season) + .ThenInclude(s => s.Show) + .ThenInclude(s => s.ShowMetadata) + .OrderBy(c => c.Id) .SingleOrDefaultAsync(c => c.Id == id) .Map(Optional); - public Task> GetSimpleMediaCollections() => - _dbContext.SimpleMediaCollections.ToListAsync(); - - public Task> GetAll() => - _dbContext.MediaCollections.ToListAsync(); + public Task> GetAll() => + _dbContext.Collections.ToListAsync(); public Task>> GetItems(int id) => - Get(id).MapT( - collection => collection switch - { - SimpleMediaCollection s => SimpleItems(s), - _ => throw new NotSupportedException($"Unsupported collection type {collection.GetType().Name}") - }).Bind(x => x.Sequence()); + Get(id).MapT(GetItemsForCollection).Bind(x => x.Sequence()); - public Task>> GetSimpleMediaCollectionItems(int id) => - GetSimpleMediaCollection(id).MapT(SimpleItems).Bind(x => x.Sequence()); - - public Task Update(SimpleMediaCollection collection) + public Task Update(Collection collection) { - _dbContext.SimpleMediaCollections.Update(collection); + _dbContext.Collections.Update(collection); return _dbContext.SaveChangesAsync(); } - public async Task Delete(int mediaCollectionId) + public async Task Delete(int collectionId) { - MediaCollection mediaCollection = await _dbContext.MediaCollections.FindAsync(mediaCollectionId); - _dbContext.MediaCollections.Remove(mediaCollection); + Collection mediaCollection = await _dbContext.Collections.FindAsync(collectionId); + _dbContext.Collections.Remove(mediaCollection); await _dbContext.SaveChangesAsync(); } - private async Task> SimpleItems(SimpleMediaCollection collection) + private async Task> GetItemsForCollection(Collection collection) { var result = new List(); - await _dbContext.Entry(collection).Collection(c => c.Movies).LoadAsync(); - result.AddRange(collection.Movies); - - result.AddRange(await GetTelevisionShowItems(collection)); - result.AddRange(await GetTelevisionSeasonItems(collection)); - result.AddRange(await GetTelevisionEpisodeItems(collection)); + 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> GetTelevisionShowItems(SimpleMediaCollection collection) + private async Task> GetMovieItems(Collection collection) { - var parameters = new { CollectionId = collection.Id }; - return await _dbConnection - .QueryAsync( - @"select tmi.Id, tmi.SeasonId, mi.MediaSourceId, 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 TelevisionEpisodes tmi -inner join MediaItems mi on tmi.Id = mi.Id -inner join TelevisionEpisodeMetadata tem on tem.TelevisionEpisodeId = tmi.Id -inner join TelevisionSeasons tsn on tsn.Id = tmi.SeasonId -inner join TelevisionShows ts on ts.Id = tsn.TelevisionShowId -inner join SimpleMediaCollectionShows 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()); + 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) + .Include(m => m.MediaVersions) + .Filter(m => ids.Contains(m.Id)) + .ToListAsync(); } - private async Task> GetTelevisionSeasonItems(SimpleMediaCollection collection) + private async Task> GetShowItems(Collection collection) { - var parameters = new { CollectionId = collection.Id }; - return await _dbConnection - .QueryAsync( - @"select tmi.Id, tmi.SeasonId, mi.MediaSourceId, 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 TelevisionEpisodes tmi -inner join MediaItems mi on tmi.Id = mi.Id -inner join TelevisionEpisodeMetadata tem on tem.TelevisionEpisodeId = tmi.Id -inner join TelevisionSeasons tsn on tsn.Id = tmi.SeasonId -inner join SimpleMediaCollectionSeasons 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()); + 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) + .Include(e => e.MediaVersions) + .Filter(e => ids.Contains(e.Id)) + .ToListAsync(); } - private async Task> GetTelevisionEpisodeItems(SimpleMediaCollection collection) + private async Task> GetSeasonItems(Collection collection) { - var parameters = new { CollectionId = collection.Id }; - return await _dbConnection - .QueryAsync( - @"select tmi.Id, tmi.SeasonId, mi.MediaSourceId, 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 TelevisionEpisodes tmi -inner join MediaItems mi on tmi.Id = mi.Id -inner join TelevisionEpisodeMetadata tem on tem.TelevisionEpisodeId = tmi.Id -inner join SimpleMediaCollectionEpisodes 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()); + 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) + .Include(e => e.MediaVersions) + .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) + .Include(e => e.MediaVersions) + .Filter(e => ids.Contains(e.Id)) + .ToListAsync(); } } } diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs index add440fd..7df821fa 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs @@ -17,32 +17,33 @@ namespace ErsatzTV.Infrastructure.Data.Repositories public Task> Get(int id) => _dbContext.MediaItems - .Include(i => i.Source) + .Include(i => i.LibraryPath) + .OrderBy(i => i.Id) .SingleOrDefaultAsync(i => i.Id == id) .Map(Optional); public Task> GetAll() => _dbContext.MediaItems.ToListAsync(); - public Task> Search(string searchString) - { - IQueryable episodeData = - from c in _dbContext.TelevisionEpisodeMediaItems.Include(c => c.Source) select c; - - if (!string.IsNullOrEmpty(searchString)) - { - episodeData = episodeData.Where(c => EF.Functions.Like(c.Metadata.Title, $"%{searchString}%")); - } - - IQueryable movieData = - from c in _dbContext.MovieMediaItems.Include(c => c.Source) select c; - - if (!string.IsNullOrEmpty(searchString)) - { - movieData = movieData.Where(c => EF.Functions.Like(c.Metadata.Title, $"%{searchString}%")); - } - - return episodeData.OfType().Concat(movieData.OfType()).ToListAsync(); - } + public Task> Search(string searchString) => + // TODO: fix this when we need to search + // IQueryable episodeData = + // from c in _dbContext.TelevisionEpisodeMediaItems.Include(c => c.LibraryPath) select c; + // + // if (!string.IsNullOrEmpty(searchString)) + // { + // episodeData = episodeData.Where(c => EF.Functions.Like(c.Metadata.Title, $"%{searchString}%")); + // } + // + // IQueryable movieData = + // from c in _dbContext.Movies.Include(c => c.LibraryPath) select c; + // + // // if (!string.IsNullOrEmpty(searchString)) + // // { + // // movieData = movieData.Where(c => EF.Functions.Like(c.Metadata.Title, $"%{searchString}%")); + // // } + // + // return episodeData.OfType().Concat(movieData.OfType()).ToListAsync(); + new List().AsTask(); public async Task Update(MediaItem mediaItem) { diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs index d1462f4d..b012e461 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; +using System.Data; using System.Linq; using System.Threading.Tasks; +using Dapper; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; @@ -11,70 +13,134 @@ namespace ErsatzTV.Infrastructure.Data.Repositories { public class MediaSourceRepository : IMediaSourceRepository { - private readonly TvContext _dbContext; + private readonly IDbConnection _dbConnection; + private readonly IDbContextFactory _dbContextFactory; - public MediaSourceRepository(TvContext dbContext) => _dbContext = dbContext; + public MediaSourceRepository( + IDbContextFactory dbContextFactory, + IDbConnection dbConnection) + { + _dbContextFactory = dbContextFactory; + _dbConnection = dbConnection; + } public async Task Add(LocalMediaSource localMediaSource) { - await _dbContext.LocalMediaSources.AddAsync(localMediaSource); - await _dbContext.SaveChangesAsync(); + await using TvContext context = _dbContextFactory.CreateDbContext(); + await context.LocalMediaSources.AddAsync(localMediaSource); + await context.SaveChangesAsync(); return localMediaSource; } public async Task Add(PlexMediaSource plexMediaSource) { - await _dbContext.PlexMediaSources.AddAsync(plexMediaSource); - await _dbContext.SaveChangesAsync(); + await using TvContext context = _dbContextFactory.CreateDbContext(); + await context.PlexMediaSources.AddAsync(plexMediaSource); + await context.SaveChangesAsync(); return plexMediaSource; } public async Task> GetAll() { - List all = await _dbContext.MediaSources.ToListAsync(); + await using TvContext context = _dbContextFactory.CreateDbContext(); + List all = await context.MediaSources.ToListAsync(); foreach (PlexMediaSource plex in all.OfType()) { - await _dbContext.Entry(plex).Collection(p => p.Connections).LoadAsync(); + await context.Entry(plex).Collection(p => p.Connections).LoadAsync(); } return all; } - public Task> GetAllPlex() => - _dbContext.PlexMediaSources + public Task> GetAllPlex() + { + using TvContext context = _dbContextFactory.CreateDbContext(); + return context.PlexMediaSources .Include(p => p.Connections) .ToListAsync(); + } + + public Task> GetPlexLibraries(int plexMediaSourceId) + { + using TvContext context = _dbContextFactory.CreateDbContext(); + return context.PlexLibraries + .Filter(l => l.MediaSourceId == plexMediaSourceId) + .ToListAsync(); + } - public Task> Get(int id) => - _dbContext.MediaSources + public Task> GetPlexLibrary(int plexLibraryId) + { + using TvContext context = _dbContextFactory.CreateDbContext(); + return context.PlexLibraries + .OrderBy(l => l.Id) // https://github.com/dotnet/efcore/issues/22579 + .SingleOrDefaultAsync(l => l.Id == plexLibraryId) + .Map(Optional); + } + + public Task> Get(int id) + { + using TvContext context = _dbContextFactory.CreateDbContext(); + return context.MediaSources + .OrderBy(s => s.Id) // https://github.com/dotnet/efcore/issues/22579 .SingleOrDefaultAsync(s => s.Id == id) .Map(Optional); + } - public Task> GetPlex(int id) => - _dbContext.PlexMediaSources + public Task> GetPlex(int id) + { + using TvContext context = _dbContextFactory.CreateDbContext(); + return context.PlexMediaSources .Include(p => p.Connections) .Include(p => p.Libraries) + .OrderBy(s => s.Id) // https://github.com/dotnet/efcore/issues/22579 .SingleOrDefaultAsync(p => p.Id == id) .Map(Optional); + } - public Task CountMediaItems(int id) => - _dbContext.MediaItems.CountAsync(i => i.MediaSourceId == id); + public Task CountMediaItems(int id) + { + using TvContext context = _dbContextFactory.CreateDbContext(); + return context.MediaItems + .CountAsync(i => i.LibraryPath.Library.MediaSourceId == id); + } - public Task Update(LocalMediaSource localMediaSource) => - _dbContext.LocalMediaSources.Update(localMediaSource) - .AsTask() - .Bind(_ => _dbContext.SaveChangesAsync()); + public async Task Update(LocalMediaSource localMediaSource) + { + await using TvContext context = _dbContextFactory.CreateDbContext(); + context.LocalMediaSources.Update(localMediaSource); + await context.SaveChangesAsync(); + } - public Task Update(PlexMediaSource plexMediaSource) => - _dbContext.PlexMediaSources.Update(plexMediaSource) - .AsTask() - .Bind(_ => _dbContext.SaveChangesAsync()); + public async Task Update(PlexMediaSource plexMediaSource) + { + await using TvContext context = _dbContextFactory.CreateDbContext(); + context.PlexMediaSources.Update(plexMediaSource); + await context.SaveChangesAsync(); + } + + public async Task Update(PlexLibrary plexMediaSourceLibrary) + { + await using TvContext context = _dbContextFactory.CreateDbContext(); + context.PlexLibraries.Update(plexMediaSourceLibrary); + await context.SaveChangesAsync(); + } public async Task Delete(int id) { - MediaSource mediaSource = await _dbContext.MediaSources.FindAsync(id); - _dbContext.MediaSources.Remove(mediaSource); - await _dbContext.SaveChangesAsync(); + await using TvContext context = _dbContextFactory.CreateDbContext(); + MediaSource mediaSource = await context.MediaSources.FindAsync(id); + context.MediaSources.Remove(mediaSource); + await context.SaveChangesAsync(); } + + public Task DisablePlexLibrarySync(IEnumerable libraryIds) => + _dbConnection.ExecuteAsync( + "UPDATE PlexMediaSourceLibraries SET ShouldSyncItems = 0 WHERE Id in @ids", + new { ids = libraryIds }); + + public Task EnablePlexLibrarySync(IEnumerable libraryIds) => + _dbConnection.ExecuteAsync( + "UPDATE PlexMediaSourceLibraries SET ShouldSyncItems = 1 WHERE Id in @ids", + new { ids = libraryIds }); } } diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs index 29b7a3a6..eef1d68c 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs @@ -1,7 +1,9 @@ using System; 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; @@ -13,54 +15,102 @@ namespace ErsatzTV.Infrastructure.Data.Repositories { public class MovieRepository : IMovieRepository { + private readonly IDbConnection _dbConnection; private readonly TvContext _dbContext; + private readonly IDbContextFactory _dbContextFactory; - public MovieRepository(TvContext dbContext) => _dbContext = dbContext; + public MovieRepository( + TvContext dbContext, + IDbContextFactory dbContextFactory, + IDbConnection dbConnection) + { + _dbContext = dbContext; + _dbContextFactory = dbContextFactory; + _dbConnection = dbConnection; + } - public Task> GetMovie(int movieId) => - _dbContext.MovieMediaItems - .Include(m => m.Metadata) + public async Task> GetMovie(int movieId) + { + await using TvContext dbContext = _dbContextFactory.CreateDbContext(); + return await dbContext.Movies + .Include(m => m.MovieMetadata) + .ThenInclude(m => m.Artwork) + .OrderBy(m => m.Id) .SingleOrDefaultAsync(m => m.Id == movieId) .Map(Optional); + } - public async Task> GetOrAdd(int mediaSourceId, string path) + public async Task> GetOrAdd(LibraryPath libraryPath, string path) { - Option maybeExisting = await _dbContext.MovieMediaItems - .Include(i => i.Metadata) - .SingleOrDefaultAsync(i => i.Path == path); + Option maybeExisting = await _dbContext.Movies + .Include(i => i.MovieMetadata) + .ThenInclude(mm => mm.Artwork) + .Include(i => i.LibraryPath) + .Include(i => i.MediaVersions) + .ThenInclude(mv => mv.MediaFiles) + .OrderBy(i => i.MediaVersions.First().MediaFiles.First().Path) + .SingleOrDefaultAsync(i => i.MediaVersions.First().MediaFiles.First().Path == path); return await maybeExisting.Match( - mediaItem => Right(mediaItem).AsTask(), - async () => await AddMovie(mediaSourceId, path)); + mediaItem => Right(mediaItem).AsTask(), + async () => await AddMovie(libraryPath.Id, path)); } - public async Task Update(MovieMediaItem movie) + public async Task> GetOrAdd(PlexLibrary library, PlexMovie item) { - _dbContext.MovieMediaItems.Update(movie); + Option maybeExisting = await _dbContext.PlexMovieMediaItems + .Include(i => i.MovieMetadata) + .Include(i => i.Part) + .OrderBy(i => i.Key) + .SingleOrDefaultAsync(i => i.Key == item.Key); + + return await maybeExisting.Match( + plexMovie => Right(plexMovie).AsTask(), + async () => await AddPlexMovie(library, item)); + } + + public async Task Update(Movie movie) + { + _dbContext.Movies.Update(movie); return await _dbContext.SaveChangesAsync() > 0; } public Task GetMovieCount() => - _dbContext.MovieMediaItems - .AsNoTracking() - .CountAsync(); + _dbConnection.QuerySingleAsync(@"SELECT COUNT(DISTINCT MovieId) FROM MovieMetadata"); - public Task> GetPagedMovies(int pageNumber, int pageSize) => - _dbContext.MovieMediaItems - .Include(s => s.Metadata) - .OrderBy(s => s.Metadata.SortTitle) - .Skip((pageNumber - 1) * pageSize) - .Take(pageSize) - .AsNoTracking() + public Task> GetPagedMovies(int pageNumber, int pageSize) => + _dbContext.MovieMetadata.FromSqlRaw( + @"SELECT * FROM MovieMetadata WHERE Id IN + (SELECT Id FROM MovieMetadata GROUP BY MovieId, MetadataKind HAVING MetadataKind = MAX(MetadataKind)) + ORDER BY SortTitle + LIMIT {0} OFFSET {1}", + pageSize, + (pageNumber - 1) * pageSize) + .Include(mm => mm.Artwork) + .OrderBy(mm => mm.SortTitle) .ToListAsync(); - private async Task> AddMovie(int mediaSourceId, string path) + private async Task> AddMovie(int libraryPathId, string path) { try { - var movie = new MovieMediaItem { MediaSourceId = mediaSourceId, Path = path }; - await _dbContext.MovieMediaItems.AddAsync(movie); + var movie = new Movie + { + LibraryPathId = libraryPathId, + MediaVersions = new List + { + new() + { + MediaFiles = new List + { + new() { Path = path } + } + } + } + }; + await _dbContext.Movies.AddAsync(movie); await _dbContext.SaveChangesAsync(); + await _dbContext.Entry(movie).Reference(m => m.LibraryPath).LoadAsync(); return movie; } catch (Exception ex) @@ -68,5 +118,24 @@ namespace ErsatzTV.Infrastructure.Data.Repositories return BaseError.New(ex.Message); } } + + private async Task> AddPlexMovie( + PlexLibrary library, + PlexMovie item) + { + try + { + item.LibraryPathId = library.Paths.Head().Id; + + await _dbContext.PlexMovieMediaItems.AddAsync(item); + await _dbContext.SaveChangesAsync(); + await _dbContext.Entry(item).Reference(i => i.LibraryPath).LoadAsync(); + return item; + } + catch (Exception ex) + { + return BaseError.New(ex.Message); + } + } } } diff --git a/ErsatzTV.Infrastructure/Data/Repositories/PlayoutRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/PlayoutRepository.cs index 42e3657d..28e94076 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/PlayoutRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/PlayoutRepository.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; -using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using static LanguageExt.Prelude; @@ -25,63 +24,57 @@ namespace ErsatzTV.Infrastructure.Data.Repositories } public Task> Get(int id) => - _dbContext.Playouts.SingleOrDefaultAsync(p => p.Id == id).Map(Optional); + _dbContext.Playouts + .OrderBy(p => p.Id) + .SingleOrDefaultAsync(p => p.Id == id) + .Map(Optional); public async Task> GetFull(int id) => await _dbContext.Playouts .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.MediaCollection) + .ThenInclude(psi => psi.Collection) .Include(p => p.ProgramSchedule) .ThenInclude(ps => ps.Items) - .ThenInclude(psi => psi.TelevisionShow) + .ThenInclude(psi => psi.MediaItem) .OrderBy(p => p.Id) // https://github.com/dotnet/efcore/issues/22579#issuecomment-694772289 .SingleOrDefaultAsync(p => p.Id == id); - public async Task> GetPlayoutItem(int channelId, DateTimeOffset now) - { - var p1 = new SqliteParameter("channelId", channelId); - var p2 = new SqliteParameter("now", now); - return await _dbContext.PlayoutItems - .FromSqlRaw( - "select i.* from playoutitems i inner join playouts p on i.playoutid = p.id where p.channelid = @channelId and i.start <= @now and i.finish > @now", - p1, - p2) + public async Task> GetPlayoutItem(int channelId, DateTimeOffset now) => + await _dbContext.PlayoutItems + .Where(pi => pi.Playout.ChannelId == channelId) + .Where(pi => pi.Start <= now.UtcDateTime && pi.Finish > now.UtcDateTime) + .Include(i => i.MediaItem) + .ThenInclude(mi => (mi as Episode).MediaVersions) + .ThenInclude(mv => mv.MediaFiles) .Include(i => i.MediaItem) + .ThenInclude(mi => (mi as Movie).MediaVersions) + .ThenInclude(mv => mv.MediaFiles) .AsNoTracking() .SingleOrDefaultAsync(); - } public Task> GetPlayoutItems(int playoutId) => _dbContext.PlayoutItems .Include(i => i.MediaItem) - .ThenInclude(m => (m as MovieMediaItem).Metadata) + .ThenInclude(mi => (mi as Movie).MovieMetadata) + .ThenInclude(mm => mm.Artwork) + .Include(i => i.MediaItem) + .ThenInclude(mi => (mi as Movie).MediaVersions) .Include(i => i.MediaItem) - .ThenInclude(m => (m as TelevisionEpisodeMediaItem).Metadata) + .ThenInclude(mi => (mi as Episode).EpisodeMetadata) + .ThenInclude(em => em.Artwork) + .Include(i => i.MediaItem) + .ThenInclude(mi => (mi as Episode).MediaVersions) + .Include(i => i.MediaItem) + .ThenInclude(mi => (mi as Episode).Season) + .ThenInclude(s => s.SeasonMetadata) .Filter(i => i.PlayoutId == playoutId) .ToListAsync(); - public Task> GetPlayoutIdsForMediaItems(Seq mediaItems) - { - var ids = string.Join(", ", mediaItems.Map(mi => mi.Id)); - return _dbContext.Playouts.FromSqlRaw( - @"SELECT DISTINCT p.* FROM Playouts p -INNER JOIN ProgramScheduleItems psi on psi.ProgramScheduleId = p.ProgramScheduleId -INNER JOIN SimpleMediaCollections smc on smc.Id = psi.MediaCollectionId -INNER JOIN MediaItemSimpleMediaCollection mismc on mismc.SimpleMediaCollectionsId = smc.Id -WHERE mismc.ItemsId in ({0}) -UNION -SELECT DISTINCT p.* FROM Playouts p -INNER JOIN ProgramScheduleItems psi on psi.ProgramScheduleId = p.ProgramScheduleId -INNER JOIN TelevisionMediaCollections tmc on tmc.Id = psi.MediaCollectionId -INNER JOIN MediaItems mi on mi.Metadata_Title = tmc.ShowTitle and (tmc.SeasonNumber is null or tmc.SeasonNumber = mi.Metadata_SeasonNumber) -WHERE mi.Id in ({0})", - ids).Select(p => p.Id).ToListAsync(); - } - public Task> GetAll() => _dbContext.Playouts .Include(p => p.Channel) diff --git a/ErsatzTV.Infrastructure/Data/Repositories/ProgramScheduleRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/ProgramScheduleRepository.cs index 315abeaa..a4899c77 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/ProgramScheduleRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/ProgramScheduleRepository.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; @@ -22,12 +23,16 @@ namespace ErsatzTV.Infrastructure.Data.Repositories } public Task> Get(int id) => - _dbContext.ProgramSchedules.SingleOrDefaultAsync(s => s.Id == id).Map(Optional); + _dbContext.ProgramSchedules + .OrderBy(s => s.Id) + .SingleOrDefaultAsync(s => s.Id == id) + .Map(Optional); public async Task> GetWithPlayouts(int id) => await _dbContext.ProgramSchedules .Include(ps => ps.Items) .Include(ps => ps.Playouts) + .OrderBy(ps => ps.Id) .SingleOrDefaultAsync(ps => ps.Id == id); public Task> GetAll() => @@ -37,7 +42,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories { _dbContext.ProgramSchedules.Update(programSchedule); await _dbContext.SaveChangesAsync(); - await _dbContext.Entry(programSchedule).Collection(s => s.Items).Query().Include(i => i.MediaCollection) + await _dbContext.Entry(programSchedule).Collection(s => s.Items).Query().Include(i => i.Collection) .LoadAsync(); await _dbContext.Entry(programSchedule).Collection(s => s.Playouts).LoadAsync(); } @@ -57,12 +62,20 @@ namespace ErsatzTV.Infrastructure.Data.Repositories { await _dbContext.Entry(programSchedule).Collection(s => s.Items).LoadAsync(); await _dbContext.Entry(programSchedule).Collection(s => s.Items).Query() - .Include(i => i.MediaCollection) - .Include(i => i.TelevisionShow) - .ThenInclude(s => s.Metadata) - .Include(i => i.TelevisionSeason) - .ThenInclude(s => s.TelevisionShow) - .ThenInclude(s => s.Metadata) + .Include(i => i.Collection) + .Include(i => i.MediaItem) + .ThenInclude(i => (i as Movie).MovieMetadata) + .ThenInclude(mm => mm.Artwork) + .Include(i => i.MediaItem) + .ThenInclude(i => (i as Season).SeasonMetadata) + .ThenInclude(sm => sm.Artwork) + .Include(i => i.MediaItem) + .ThenInclude(i => (i as Season).Show) + .ThenInclude(s => s.ShowMetadata) + .ThenInclude(sm => sm.Artwork) + .Include(i => i.MediaItem) + .ThenInclude(i => (i as Show).ShowMetadata) + .ThenInclude(sm => sm.Artwork) .LoadAsync(); return programSchedule.Items; }).Sequence(); diff --git a/ErsatzTV.Infrastructure/Data/Repositories/ResolutionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/ResolutionRepository.cs index 0bdcf2fc..0cbe4b81 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/ResolutionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/ResolutionRepository.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; @@ -15,7 +16,10 @@ namespace ErsatzTV.Infrastructure.Data.Repositories public ResolutionRepository(TvContext dbContext) => _dbContext = dbContext; public Task> Get(int id) => - _dbContext.Resolutions.SingleOrDefaultAsync(r => r.Id == id).Map(Optional); + _dbContext.Resolutions + .OrderBy(r => r.Id) + .SingleOrDefaultAsync(r => r.Id == id) + .Map(Optional); public Task> GetAll() => _dbContext.Resolutions.ToListAsync(); diff --git a/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs index ce2894ec..9dd94ed8 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs @@ -24,170 +24,172 @@ namespace ErsatzTV.Infrastructure.Data.Repositories _dbConnection = dbConnection; } - public async Task Update(TelevisionShow show) + public async Task Update(Show show) { - _dbContext.TelevisionShows.Update(show); + _dbContext.Shows.Update(show); return await _dbContext.SaveChangesAsync() > 0; } - public async Task Update(TelevisionSeason season) + public async Task Update(Season season) { - _dbContext.TelevisionSeasons.Update(season); + _dbContext.Seasons.Update(season); return await _dbContext.SaveChangesAsync() > 0; } - public async Task Update(TelevisionEpisodeMediaItem episode) + public async Task Update(Episode episode) { - _dbContext.TelevisionEpisodeMediaItems.Update(episode); + _dbContext.Episodes.Update(episode); return await _dbContext.SaveChangesAsync() > 0; } - public Task> GetAllShows() => - _dbContext.TelevisionShows + public Task> GetAllShows() => + _dbContext.Shows .AsNoTracking() - .Include(s => s.Metadata) + .Include(s => s.ShowMetadata) + .ThenInclude(sm => sm.Artwork) .ToListAsync(); - public Task> GetShow(int televisionShowId) => - _dbContext.TelevisionShows + public Task> GetShow(int showId) => + _dbContext.Shows .AsNoTracking() - .Filter(s => s.Id == televisionShowId) - .Include(s => s.Metadata) + .Filter(s => s.Id == showId) + .Include(s => s.ShowMetadata) + .ThenInclude(sm => sm.Artwork) + .OrderBy(s => s.Id) .SingleOrDefaultAsync() .Map(Optional); public Task GetShowCount() => - _dbContext.TelevisionShows + _dbContext.ShowMetadata .AsNoTracking() + .GroupBy(sm => new { sm.Title, sm.Year }) .CountAsync(); - public Task> GetPagedShows(int pageNumber, int pageSize) => - _dbContext.TelevisionShows - .AsNoTracking() - .Include(s => s.Metadata) - .OrderBy(s => s.Metadata == null ? string.Empty : s.Metadata.SortTitle) - .Skip((pageNumber - 1) * pageSize) - .Take(pageSize) + public Task> GetPagedShows(int pageNumber, int pageSize) => + _dbContext.ShowMetadata.FromSqlRaw( + @"SELECT * FROM ShowMetadata WHERE Id IN + (SELECT MIN(Id) FROM ShowMetadata GROUP BY Title, Year, MetadataKind HAVING MetadataKind = MAX(MetadataKind)) + ORDER BY SortTitle + LIMIT {0} OFFSET {1}", + pageSize, + (pageNumber - 1) * pageSize) + .Include(mm => mm.Artwork) + .OrderBy(mm => mm.SortTitle) .ToListAsync(); - public Task> GetAllSeasons() => - _dbContext.TelevisionSeasons + public Task> GetAllSeasons() => + _dbContext.Seasons .AsNoTracking() - .Include(s => s.TelevisionShow) - .ThenInclude(s => s.Metadata) + .Include(s => s.SeasonMetadata) + .ThenInclude(sm => sm.Artwork) + .Include(s => s.Show) + .ThenInclude(s => s.ShowMetadata) + .ThenInclude(sm => sm.Artwork) .ToListAsync(); - public Task> GetSeason(int televisionSeasonId) => - _dbContext.TelevisionSeasons + public Task> GetSeason(int seasonId) => + _dbContext.Seasons .AsNoTracking() - .Include(s => s.TelevisionShow) - .ThenInclude(s => s.Metadata) - .SingleOrDefaultAsync(s => s.Id == televisionSeasonId) + .Include(s => s.SeasonMetadata) + .ThenInclude(sm => sm.Artwork) + .Include(s => s.Show) + .ThenInclude(s => s.ShowMetadata) + .OrderBy(s => s.Id) + .SingleOrDefaultAsync(s => s.Id == seasonId) .Map(Optional); - public Task GetSeasonCount(int televisionShowId) => - _dbContext.TelevisionSeasons + public Task GetSeasonCount(int showId) => + _dbContext.Seasons .AsNoTracking() - .Where(s => s.TelevisionShowId == televisionShowId) - .CountAsync(); + .CountAsync(s => s.ShowId == showId); - public Task> GetPagedSeasons(int televisionShowId, int pageNumber, int pageSize) => - _dbContext.TelevisionSeasons + public async Task> GetPagedSeasons(int televisionShowId, int pageNumber, int pageSize) + { + List showIds = await _dbConnection.QueryAsync( + @"SELECT m1.ShowId + FROM ShowMetadata m1 + LEFT OUTER JOIN ShowMetadata m2 ON m2.ShowId = @ShowId + WHERE m1.Title = m2.Title AND m1.Year = m2.Year", + new { ShowId = televisionShowId }) + .Map(results => results.ToList()); + + return await _dbContext.Seasons .AsNoTracking() - .Where(s => s.TelevisionShowId == televisionShowId) - .Include(s => s.TelevisionShow) - .ThenInclude(s => s.Metadata) - .OrderBy(s => s.Number) + .Where(s => showIds.Contains(s.ShowId)) + .Include(s => s.SeasonMetadata) + .ThenInclude(sm => sm.Artwork) + .Include(s => s.Show) + .ThenInclude(s => s.ShowMetadata) + .OrderBy(s => s.SeasonNumber) .Skip((pageNumber - 1) * pageSize) .Take(pageSize) .ToListAsync(); + } - public Task> GetEpisode(int televisionEpisodeId) => - _dbContext.TelevisionEpisodeMediaItems + public Task> GetEpisode(int episodeId) => + _dbContext.Episodes .AsNoTracking() - .Include(s => s.Season) - .Include(s => s.Metadata) - .SingleOrDefaultAsync(s => s.Id == televisionEpisodeId) + .Include(e => e.Season) + .Include(e => e.EpisodeMetadata) + .ThenInclude(em => em.Artwork) + .OrderBy(s => s.Id) + .SingleOrDefaultAsync(s => s.Id == episodeId) .Map(Optional); - public Task GetEpisodeCount(int televisionSeasonId) => - _dbContext.TelevisionEpisodeMediaItems + public Task GetEpisodeCount(int seasonId) => + _dbContext.Episodes .AsNoTracking() - .Where(e => e.SeasonId == televisionSeasonId) - .CountAsync(); + .CountAsync(e => e.SeasonId == seasonId); - public Task> GetPagedEpisodes( - int televisionSeasonId, - int pageNumber, - int pageSize) => - _dbContext.TelevisionEpisodeMediaItems + public Task> GetPagedEpisodes(int seasonId, int pageNumber, int pageSize) => + _dbContext.EpisodeMetadata .AsNoTracking() - .Include(e => e.Metadata) - .Include(e => e.Season) - .ThenInclude(s => s.TelevisionShow) - .ThenInclude(s => s.Metadata) - .Where(e => e.SeasonId == televisionSeasonId) - .OrderBy(s => s.Metadata.Episode) + .Filter(em => em.Episode.SeasonId == seasonId) + .Include(em => em.Artwork) + .Include(em => em.Episode) + .ThenInclude(e => e.Season) + .ThenInclude(s => s.Show) + .ThenInclude(s => s.ShowMetadata) + .OrderBy(em => em.Episode.EpisodeNumber) .Skip((pageNumber - 1) * pageSize) .Take(pageSize) .ToListAsync(); - public async Task> GetShowByPath(int mediaSourceId, string path) + public async Task> GetShowByMetadata(int libraryPathId, ShowMetadata metadata) { - Option maybeShowId = await _dbContext.LocalTelevisionShowSources - .SingleOrDefaultAsync(s => s.MediaSourceId == mediaSourceId && s.Path == path) - .Map(Optional) - .MapT(s => s.TelevisionShowId); - - return await maybeShowId.Match>>( - async id => await _dbContext.TelevisionShows - .Include(s => s.Metadata) - .Include(s => s.Sources) - .SingleOrDefaultAsync(s => s.Id == id), - () => Task.FromResult(Option.None)); - } - - public async Task> GetShowByMetadata(TelevisionShowMetadata metadata) - { - Option maybeShow = await _dbContext.TelevisionShows - .Include(s => s.Metadata) - .Where(s => s.Metadata.Title == metadata.Title && s.Metadata.Year == metadata.Year) + Option maybeId = await _dbContext.ShowMetadata + .Where(s => s.Title == metadata.Title && s.Year == metadata.Year) + .Where(s => s.Show.LibraryPathId == libraryPathId) .SingleOrDefaultAsync() - .Map(Optional); + .Map(Optional) + .MapT(sm => sm.ShowId); - await maybeShow.IfSomeAsync( - async show => + return await maybeId.Match( + id => { - await _dbContext.Entry(show).Reference(s => s.Metadata).LoadAsync(); - await _dbContext.Entry(show).Collection(s => s.Sources).LoadAsync(); - }); - - return maybeShow; + return _dbContext.Shows + .Include(s => s.ShowMetadata) + .ThenInclude(sm => sm.Artwork) + .OrderBy(s => s.Id) + .SingleOrDefaultAsync(s => s.Id == id) + .Map(Optional); + }, + () => Option.None.AsTask()); } - public async Task> AddShow( - int localMediaSourceId, - string showFolder, - TelevisionShowMetadata metadata) + public async Task> AddShow(int libraryPathId, string showFolder, ShowMetadata metadata) { try { - var show = new TelevisionShow + metadata.DateAdded = DateTime.UtcNow; + var show = new Show { - Sources = new List(), - Metadata = metadata, - Seasons = new List() + LibraryPathId = libraryPathId, + ShowMetadata = new List { metadata }, + Seasons = new List() }; - show.Sources.Add( - new LocalTelevisionShowSource - { - MediaSourceId = localMediaSourceId, - Path = showFolder, - TelevisionShow = show - }); - - await _dbContext.TelevisionShows.AddAsync(show); + await _dbContext.Shows.AddAsync(show); await _dbContext.SaveChangesAsync(); return show; @@ -198,122 +200,86 @@ namespace ErsatzTV.Infrastructure.Data.Repositories } } - public async Task> GetOrAddSeason( - TelevisionShow show, - string path, - int seasonNumber) + public async Task> GetOrAddSeason(Show show, int libraryPathId, int seasonNumber) { - Option maybeExisting = await _dbContext.TelevisionSeasons - .SingleOrDefaultAsync(i => i.Path == path); + Option maybeExisting = await _dbContext.Seasons + .Include(s => s.SeasonMetadata) + .ThenInclude(sm => sm.Artwork) + .SingleOrDefaultAsync(s => s.ShowId == show.Id && s.SeasonNumber == seasonNumber); return await maybeExisting.Match( - season => Right(season).AsTask(), - () => AddSeason(show, path, seasonNumber)); + season => Right(season).AsTask(), + () => AddSeason(show, libraryPathId, seasonNumber)); } - public async Task> GetOrAddEpisode( - TelevisionSeason season, - int mediaSourceId, + public async Task> GetOrAddEpisode( + Season season, + LibraryPath libraryPath, string path) { - Option maybeExisting = await _dbContext.TelevisionEpisodeMediaItems - .Include(i => i.Metadata) - .Include(i => i.Statistics) - .SingleOrDefaultAsync(i => i.Path == path); + Option maybeExisting = await _dbContext.Episodes + .Include(i => i.EpisodeMetadata) + .ThenInclude(em => em.Artwork) + .Include(i => i.MediaVersions) + .ThenInclude(mv => mv.MediaFiles) + .OrderBy(i => i.MediaVersions.First().MediaFiles.First().Path) + .SingleOrDefaultAsync(i => i.MediaVersions.First().MediaFiles.First().Path == path); return await maybeExisting.Match( - episode => Right(episode).AsTask(), - () => AddEpisode(season, mediaSourceId, path)); + episode => Right(episode).AsTask(), + () => AddEpisode(season, libraryPath.Id, path)); } - public Task DeleteMissingSources(int localMediaSourceId, List allFolders) => - _dbContext.LocalTelevisionShowSources - .Where(s => s.MediaSourceId == localMediaSourceId && !allFolders.Contains(s.Path)) - .ToListAsync() - .Bind( - list => - { - _dbContext.LocalTelevisionShowSources.RemoveRange(list); - return _dbContext.SaveChangesAsync(); - }) - .ToUnit(); - public Task DeleteEmptyShows() => - _dbContext.TelevisionShows - .Where(s => s.Sources.Count == 0) + _dbContext.Shows + .Where(s => s.Seasons.Count == 0) .ToListAsync() .Bind( list => { - _dbContext.TelevisionShows.RemoveRange(list); + _dbContext.Shows.RemoveRange(list); return _dbContext.SaveChangesAsync(); }) .ToUnit(); - public async Task> GetShowItems(int televisionShowId) + public async Task> GetShowItems(int showId) { - var parameters = new { ShowId = televisionShowId }; - return await _dbConnection - .QueryAsync( - @"select tmi.Id, tmi.SeasonId, mi.MediaSourceId, 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 TelevisionEpisodes tmi -inner join MediaItems mi on tmi.Id = mi.Id -inner join TelevisionEpisodeMetadata tem on tem.TelevisionEpisodeId = tmi.Id -inner join TelevisionSeasons tsn on tsn.Id = tmi.SeasonId -inner join TelevisionShows 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()); + 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 }); + + return await _dbContext.Episodes + .Include(e => e.EpisodeMetadata) + .Include(e => e.MediaVersions) + .Include(e => e.Season) + .Filter(e => ids.Contains(e.Id)) + .ToListAsync(); } - public async Task> GetSeasonItems(int televisionSeasonId) - { - var parameters = new { SeasonId = televisionSeasonId }; - return await _dbConnection - .QueryAsync( - @"select tmi.Id, tmi.SeasonId, mi.MediaSourceId, 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 TelevisionEpisodes tmi -inner join MediaItems mi on tmi.Id = mi.Id -inner join TelevisionEpisodeMetadata tem on tem.TelevisionEpisodeId = tmi.Id -inner join TelevisionSeasons 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()); - } + public Task> GetSeasonItems(int seasonId) => + _dbContext.Episodes + .Include(e => e.EpisodeMetadata) + .Include(e => e.MediaVersions) + .Include(e => e.Season) + .Filter(e => e.SeasonId == seasonId) + .ToListAsync(); - private async Task> AddSeason( - TelevisionShow show, - string path, - int seasonNumber) + private async Task> AddSeason(Show show, int libraryPathId, int seasonNumber) { try { - var season = new TelevisionSeason + var season = new Season { - TelevisionShowId = show.Id, Path = path, Number = seasonNumber, - Episodes = new List() + LibraryPathId = libraryPathId, + ShowId = show.Id, + SeasonNumber = seasonNumber, + Episodes = new List(), + SeasonMetadata = new List() }; - await _dbContext.TelevisionSeasons.AddAsync(season); + await _dbContext.Seasons.AddAsync(season); await _dbContext.SaveChangesAsync(); return season; } @@ -323,20 +289,27 @@ where tsn.Id = @SeasonId", } } - private async Task> AddEpisode( - TelevisionSeason season, - int mediaSourceId, - string path) + private async Task> AddEpisode(Season season, int libraryPathId, string path) { try { - var episode = new TelevisionEpisodeMediaItem + var episode = new Episode { - MediaSourceId = mediaSourceId, + LibraryPathId = libraryPathId, SeasonId = season.Id, - Path = path + EpisodeMetadata = new List(), + MediaVersions = new List + { + new() + { + MediaFiles = new List + { + new() { Path = path } + } + } + } }; - await _dbContext.TelevisionEpisodeMediaItems.AddAsync(episode); + await _dbContext.Episodes.AddAsync(episode); await _dbContext.SaveChangesAsync(); return episode; } diff --git a/ErsatzTV.Infrastructure/Data/SqliteTypeHandlers.cs b/ErsatzTV.Infrastructure/Data/SqliteTypeHandlers.cs index c9ac2fb9..3fe2ece0 100644 --- a/ErsatzTV.Infrastructure/Data/SqliteTypeHandlers.cs +++ b/ErsatzTV.Infrastructure/Data/SqliteTypeHandlers.cs @@ -14,19 +14,18 @@ namespace ErsatzTV.Infrastructure.Data public class DateTimeOffsetHandler : SqliteTypeHandler { public override DateTimeOffset Parse(object value) - => DateTimeOffset.Parse((string)value); + => DateTimeOffset.Parse((string) value); } public class GuidHandler : SqliteTypeHandler { public override Guid Parse(object value) - => Guid.Parse((string)value); + => Guid.Parse((string) value); } public class TimeSpanHandler : SqliteTypeHandler { public override TimeSpan Parse(object value) - => TimeSpan.Parse((string)value); + => TimeSpan.Parse((string) value); } - } diff --git a/ErsatzTV.Infrastructure/Data/TvContext.cs b/ErsatzTV.Infrastructure/Data/TvContext.cs index d6a0854f..f809f0f8 100644 --- a/ErsatzTV.Infrastructure/Data/TvContext.cs +++ b/ErsatzTV.Infrastructure/Data/TvContext.cs @@ -17,21 +17,28 @@ namespace ErsatzTV.Infrastructure.Data public DbSet MediaSources { get; set; } public DbSet LocalMediaSources { get; set; } public DbSet PlexMediaSources { get; set; } + public DbSet Libraries { get; set; } + public DbSet LocalLibraries { get; set; } + public DbSet LibraryPaths { get; set; } + public DbSet PlexLibraries { get; set; } public DbSet MediaItems { get; set; } - public DbSet MovieMediaItems { get; set; } - public DbSet TelevisionEpisodeMediaItems { get; set; } - public DbSet MediaCollections { get; set; } - public DbSet SimpleMediaCollections { get; set; } + public DbSet MediaVersions { get; set; } + public DbSet MediaFiles { get; set; } + public DbSet Movies { get; set; } + public DbSet MovieMetadata { get; set; } + public DbSet Shows { get; set; } + public DbSet ShowMetadata { get; set; } + public DbSet Seasons { get; set; } + public DbSet Episodes { get; set; } + public DbSet EpisodeMetadata { get; set; } + public DbSet PlexMovieMediaItems { get; set; } + public DbSet Collections { get; set; } public DbSet ProgramSchedules { get; set; } public DbSet Playouts { get; set; } public DbSet PlayoutItems { get; set; } public DbSet PlayoutProgramScheduleItemAnchors { get; set; } public DbSet FFmpegProfiles { get; set; } public DbSet Resolutions { get; set; } - public DbSet TelevisionShows { get; set; } - public DbSet LocalTelevisionShowSources { get; set; } - public DbSet TelevisionShowMetadata { get; set; } - public DbSet TelevisionSeasons { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseLoggerFactory(_loggerFactory); diff --git a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj.DotSettings b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj.DotSettings new file mode 100644 index 00000000..8447872c --- /dev/null +++ b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj.DotSettings @@ -0,0 +1,6 @@ + + True + True + True + True + True \ No newline at end of file diff --git a/ErsatzTV.Infrastructure/Images/ImageCache.cs b/ErsatzTV.Infrastructure/Images/ImageCache.cs index 0c467781..1896c92e 100644 --- a/ErsatzTV.Infrastructure/Images/ImageCache.cs +++ b/ErsatzTV.Infrastructure/Images/ImageCache.cs @@ -1,9 +1,12 @@ using System; using System.IO; using System.Security.Cryptography; +using System.Text; using System.Threading.Tasks; using ErsatzTV.Core; +using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Images; +using ErsatzTV.Core.Interfaces.Metadata; using LanguageExt; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Jpeg; @@ -14,15 +17,18 @@ namespace ErsatzTV.Infrastructure.Images public class ImageCache : IImageCache { private static readonly SHA1CryptoServiceProvider Crypto; + private readonly ILocalFileSystem _localFileSystem; static ImageCache() => Crypto = new SHA1CryptoServiceProvider(); - public async Task> ResizeAndSaveImage(byte[] imageBuffer, int? height, int? width) + public ImageCache(ILocalFileSystem localFileSystem) => _localFileSystem = localFileSystem; + + public async Task> ResizeImage(byte[] imageBuffer, int height) { await using var inStream = new MemoryStream(imageBuffer); using var image = await Image.LoadAsync(inStream); - Size size = height.HasValue ? new Size { Height = height.Value } : new Size { Width = width.Value }; + var size = new Size { Height = height }; image.Mutate( i => i.Resize( @@ -35,24 +41,31 @@ namespace ErsatzTV.Infrastructure.Images await using var outStream = new MemoryStream(); await image.SaveAsync(outStream, new JpegEncoder { Quality = 90 }); - return await SaveImage(outStream.ToArray()); + return outStream.ToArray(); } - public async Task> SaveImage(byte[] imageBuffer) + public async Task> SaveArtworkToCache(byte[] imageBuffer, ArtworkKind artworkKind) { try { byte[] hash = Crypto.ComputeHash(imageBuffer); string hex = BitConverter.ToString(hash).Replace("-", string.Empty); + string subfolder = hex.Substring(0, 2); + string baseFolder = artworkKind switch + { + ArtworkKind.Poster => Path.Combine(FileSystemLayout.PosterCacheFolder, subfolder), + ArtworkKind.Thumbnail => Path.Combine(FileSystemLayout.ThumbnailCacheFolder, subfolder), + ArtworkKind.Logo => Path.Combine(FileSystemLayout.LogoCacheFolder, subfolder), + _ => FileSystemLayout.LegacyImageCacheFolder + }; + string target = Path.Combine(baseFolder, hex); - string fileName = Path.Combine(FileSystemLayout.ImageCacheFolder, hex); - - if (!Directory.Exists(FileSystemLayout.ImageCacheFolder)) + if (!Directory.Exists(baseFolder)) { - Directory.CreateDirectory(FileSystemLayout.ImageCacheFolder); + Directory.CreateDirectory(baseFolder); } - await File.WriteAllBytesAsync(fileName, imageBuffer); + await File.WriteAllBytesAsync(target, imageBuffer); return hex; } catch (Exception ex) @@ -60,5 +73,24 @@ namespace ErsatzTV.Infrastructure.Images return BaseError.New(ex.Message); } } + + public string CopyArtworkToCache(string path, ArtworkKind artworkKind) + { + var filenameKey = $"{path}:{_localFileSystem.GetLastWriteTime(path).ToFileTimeUtc()}"; + byte[] hash = Crypto.ComputeHash(Encoding.UTF8.GetBytes(filenameKey)); + string hex = BitConverter.ToString(hash).Replace("-", string.Empty); + string subfolder = hex.Substring(0, 2); + string baseFolder = artworkKind switch + { + ArtworkKind.Poster => Path.Combine(FileSystemLayout.PosterCacheFolder, subfolder), + ArtworkKind.Thumbnail => Path.Combine(FileSystemLayout.ThumbnailCacheFolder, subfolder), + ArtworkKind.Logo => Path.Combine(FileSystemLayout.LogoCacheFolder, subfolder), + _ => FileSystemLayout.LegacyImageCacheFolder + }; + string target = Path.Combine(baseFolder, hex); + _localFileSystem.CopyFile(path, target); + + return hex; + } } } diff --git a/ErsatzTV.Infrastructure/Locking/EntityLocker.cs b/ErsatzTV.Infrastructure/Locking/EntityLocker.cs index 26122230..a0e528bb 100644 --- a/ErsatzTV.Infrastructure/Locking/EntityLocker.cs +++ b/ErsatzTV.Infrastructure/Locking/EntityLocker.cs @@ -10,31 +10,31 @@ namespace ErsatzTV.Infrastructure.Locking public EntityLocker() => _lockedMediaSources = new ConcurrentDictionary(); - public event EventHandler OnMediaSourceChanged; + public event EventHandler OnLibraryChanged; - public bool LockMediaSource(int mediaSourceId) + public bool LockLibrary(int mediaSourceId) { if (!_lockedMediaSources.ContainsKey(mediaSourceId) && _lockedMediaSources.TryAdd(mediaSourceId, 0)) { - OnMediaSourceChanged?.Invoke(this, EventArgs.Empty); + OnLibraryChanged?.Invoke(this, EventArgs.Empty); return true; } return false; } - public bool UnlockMediaSource(int mediaSourceId) + public bool UnlockLibrary(int mediaSourceId) { if (_lockedMediaSources.TryRemove(mediaSourceId, out byte _)) { - OnMediaSourceChanged?.Invoke(this, EventArgs.Empty); + OnLibraryChanged?.Invoke(this, EventArgs.Empty); return true; } return false; } - public bool IsMediaSourceLocked(int mediaSourceId) => + public bool IsLibraryLocked(int mediaSourceId) => _lockedMediaSources.ContainsKey(mediaSourceId); } } diff --git a/ErsatzTV.Infrastructure/Migrations/20210225145537_LibraryRework.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210225145537_LibraryRework.Designer.cs new file mode 100644 index 00000000..3fa1c3c7 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210225145537_LibraryRework.Designer.cs @@ -0,0 +1,1402 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210225145537_LibraryRework")] + partial class LibraryRework + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channels"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElements"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaCollections"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContentRating") + .HasColumnType("TEXT"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("MovieId1") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Premiered") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId") + .IsUnique(); + + b.HasIndex("MovieId1"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleItemAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedules"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("ProgramScheduleId"); + + b.HasIndex("TelevisionSeasonId"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("ProgramScheduleItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolutions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Aired") + .HasColumnType("TEXT"); + + b.Property("Episode") + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Season") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionEpisodeId") + .IsUnique(); + + b.ToTable("TelevisionEpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionSeasons"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TelevisionShows"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId") + .IsUnique(); + + b.ToTable("TelevisionShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionShowSource"); + + b.HasDiscriminator("Discriminator").HasValue("TelevisionShowSource"); + }); + + modelBuilder.Entity("MovieMediaItemSimpleMediaCollection", b => + { + b.Property("MoviesId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("MoviesId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("SimpleMediaCollectionMovies"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodesId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionEpisodesId"); + + b.HasIndex("TelevisionEpisodesId"); + + b.ToTable("SimpleMediaCollectionEpisodes"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionSeasonsId"); + + b.HasIndex("TelevisionSeasonsId"); + + b.ToTable("SimpleMediaCollectionSeasons"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionShowsId"); + + b.HasIndex("TelevisionShowsId"); + + b.ToTable("SimpleMediaCollectionShows"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollections"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("MetadataId") + .HasColumnType("INTEGER"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("TelevisionEpisodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.TelevisionShowSource"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasIndex("MediaSourceId"); + + b.HasDiscriminator().HasValue("LocalTelevisionShowSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovieMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MovieMediaItem"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovies"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItems"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", null) + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.MovieMetadata", "MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", "Movie") + .WithMany() + .HasForeignKey("MovieId1"); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playouts"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleItemAnchors"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("EnumeratorState"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "TelevisionSeason") + .WithMany() + .HasForeignKey("TelevisionSeasonId"); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany() + .HasForeignKey("TelevisionShowId"); + + b.Navigation("MediaCollection"); + + b.Navigation("ProgramSchedule"); + + b.Navigation("TelevisionSeason"); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "TelevisionEpisode") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", "TelevisionEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Seasons") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionShowMetadata", "TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Sources") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("MovieMediaItemSimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", null) + .WithMany() + .HasForeignKey("MoviesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", null) + .WithMany() + .HasForeignKey("TelevisionEpisodesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", null) + .WithMany() + .HasForeignKey("TelevisionSeasonsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", null) + .WithMany() + .HasForeignKey("TelevisionShowsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.MovieMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.LocalMediaSource", "MediaSource") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovieMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovieMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Navigation("Metadata"); + + b.Navigation("Seasons"); + + b.Navigation("Sources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210225145537_LibraryRework.cs b/ErsatzTV.Infrastructure/Migrations/20210225145537_LibraryRework.cs new file mode 100644 index 00000000..1ed94b86 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210225145537_LibraryRework.cs @@ -0,0 +1,574 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class LibraryRework : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + // create local media source to attach all paths to + migrationBuilder.Sql("INSERT INTO MediaSources (SourceType) Values (99)"); + migrationBuilder.Sql("INSERT INTO LocalMediaSources (Id, MediaType) Values (last_insert_rowid(), 99)"); + + migrationBuilder.CreateTable( + "Library", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column("TEXT", nullable: true), + MediaKind = table.Column("INTEGER", nullable: false), + LastScan = table.Column("TEXT", nullable: true), + MediaSourceId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Library", x => x.Id); + table.ForeignKey( + "FK_Library_MediaSources_MediaSourceId", + x => x.MediaSourceId, + "MediaSources", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + + migrationBuilder.CreateTable( + "LocalLibrary", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true) + }, + constraints: table => + { + table.PrimaryKey("PK_LocalLibrary", x => x.Id); + table.ForeignKey( + "FK_LocalLibrary_Library_Id", + x => x.Id, + "Library", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + // create local movies library + migrationBuilder.Sql( + @"INSERT INTO Library (Name, MediaKind, MediaSourceId) + SELECT 'Movies', 1, Id FROM LocalMediaSources WHERE MediaType = 99"); + migrationBuilder.Sql("INSERT INTO LocalLibrary (Id) Values (last_insert_rowid())"); + + // create local shows library + migrationBuilder.Sql( + @"INSERT INTO Library (Name, MediaKind, MediaSourceId) + SELECT 'Shows', 2, Id FROM LocalMediaSources WHERE MediaType = 99"); + migrationBuilder.Sql("INSERT INTO LocalLibrary (Id) Values (last_insert_rowid())"); + + migrationBuilder.CreateTable( + "LibraryPath", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Path = table.Column("TEXT", nullable: true), + LibraryId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_LibraryPath", x => x.Id); + table.ForeignKey( + "FK_LibraryPath_Library_LibraryId", + x => x.LibraryId, + "Library", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + // migrate movie source/folders to library paths + migrationBuilder.Sql( + @"INSERT INTO LibraryPath (Path, LibraryId) + SELECT lms.Folder, l.Id + FROM LocalMediaSources lms + LEFT OUTER JOIN Library l ON l.MediaKind = 1 + WHERE lms.MediaType = 2"); + + // migrate show source/folders to library paths + migrationBuilder.Sql( + @"INSERT INTO LibraryPath (Path, LibraryId) + SELECT lms.Folder, l.Id + FROM LocalMediaSources lms + LEFT OUTER JOIN Library l ON l.MediaKind = 2 + WHERE lms.MediaType = 1"); + + // migrate media item links + migrationBuilder.AddColumn( + "LibraryPathId", + "MediaItems", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.Sql( + @"UPDATE MediaItems SET LibraryPathId = + (SELECT l.Id FROM LibraryPath l INNER JOIN LocalMediaSources lms ON lms.Folder = l.Path WHERE lms.Id = MediaItems.MediaSourceId)"); + + migrationBuilder.DropColumn( + "MediaSourceId", + "MediaItems"); + + migrationBuilder.DropIndex( + "IX_MediaItems_MediaSourceId", + "MediaItems"); + + migrationBuilder.DropForeignKey( + "FK_MediaItems_MediaSources_MediaSourceId", + "MediaItems"); + + migrationBuilder.CreateIndex( + "IX_MediaItems_LibraryPathId", + "MediaItems", + "LibraryPathId"); + + migrationBuilder.DropForeignKey( + "FK_LocalMediaSources_MediaSources_Id", + "LocalMediaSources"); + + migrationBuilder.DropForeignKey( + "FK_PlexMediaSources_MediaSources_Id", + "PlexMediaSources"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionShowSource_LocalMediaSources_MediaSourceId", + "TelevisionShowSource"); + + migrationBuilder.DropTable( + "PlexMediaSourceConnections"); + + migrationBuilder.DropTable( + "PlexMediaSourceLibraries"); + + migrationBuilder.DropIndex( + "IX_MediaSources_Name", + "MediaSources"); + + migrationBuilder.DropPrimaryKey( + "PK_PlexMediaSources", + "PlexMediaSources"); + + migrationBuilder.DropPrimaryKey( + "PK_LocalMediaSources", + "LocalMediaSources"); + + migrationBuilder.DropColumn( + "LastScan", + "MediaSources"); + + migrationBuilder.DropColumn( + "Name", + "MediaSources"); + + migrationBuilder.DropColumn( + "SourceType", + "MediaSources"); + + migrationBuilder.DropColumn( + "Folder", + "LocalMediaSources"); + + migrationBuilder.DropColumn( + "MediaType", + "LocalMediaSources"); + + migrationBuilder.RenameTable( + "PlexMediaSources", + newName: "PlexMediaSource"); + + migrationBuilder.RenameTable( + "LocalMediaSources", + newName: "LocalMediaSource"); + + migrationBuilder.AddColumn( + "MovieId1", + "MovieMetadata", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "ServerName", + "PlexMediaSource", + "TEXT", + nullable: true); + + migrationBuilder.AddPrimaryKey( + "PK_PlexMediaSource", + "PlexMediaSource", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_LocalMediaSource", + "LocalMediaSource", + "Id"); + + migrationBuilder.CreateTable( + "PlexConnection", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + IsActive = table.Column("INTEGER", nullable: false), + Uri = table.Column("TEXT", nullable: true), + PlexMediaSourceId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PlexConnection", x => x.Id); + table.ForeignKey( + "FK_PlexConnection_PlexMediaSource_PlexMediaSourceId", + x => x.PlexMediaSourceId, + "PlexMediaSource", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "PlexMediaItemPart", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + PlexId = table.Column("INTEGER", nullable: false), + Key = table.Column("TEXT", nullable: true), + Duration = table.Column("INTEGER", nullable: false), + File = table.Column("TEXT", nullable: true), + Size = table.Column("INTEGER", nullable: false) + }, + constraints: table => { table.PrimaryKey("PK_PlexMediaItemPart", x => x.Id); }); + + migrationBuilder.CreateTable( + "PlexLibrary", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Key = table.Column("TEXT", nullable: true), + ShouldSyncItems = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PlexLibrary", x => x.Id); + table.ForeignKey( + "FK_PlexLibrary_Library_Id", + x => x.Id, + "Library", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "PlexMovies", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Key = table.Column("TEXT", nullable: true), + PartId = table.Column("INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PlexMovies", x => x.Id); + table.ForeignKey( + "FK_PlexMovies_Movies_Id", + x => x.Id, + "Movies", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_PlexMovies_PlexMediaItemPart_PartId", + x => x.PartId, + "PlexMediaItemPart", + "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + "IX_MovieMetadata_MovieId1", + "MovieMetadata", + "MovieId1"); + + migrationBuilder.CreateIndex( + "IX_Library_MediaSourceId", + "Library", + "MediaSourceId"); + + migrationBuilder.CreateIndex( + "IX_LibraryPath_LibraryId", + "LibraryPath", + "LibraryId"); + + migrationBuilder.CreateIndex( + "IX_PlexConnection_PlexMediaSourceId", + "PlexConnection", + "PlexMediaSourceId"); + + migrationBuilder.CreateIndex( + "IX_PlexMovies_PartId", + "PlexMovies", + "PartId"); + + migrationBuilder.AddForeignKey( + "FK_LocalMediaSource_MediaSources_Id", + "LocalMediaSource", + "Id", + "MediaSources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_MediaItems_LibraryPath_LibraryPathId", + "MediaItems", + "LibraryPathId", + "LibraryPath", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_MovieMetadata_Movies_MovieId1", + "MovieMetadata", + "MovieId1", + "Movies", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_PlexMediaSource_MediaSources_Id", + "PlexMediaSource", + "Id", + "MediaSources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionShowSource_LocalMediaSource_MediaSourceId", + "TelevisionShowSource", + "MediaSourceId", + "LocalMediaSource", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_LocalMediaSource_MediaSources_Id", + "LocalMediaSource"); + + migrationBuilder.DropForeignKey( + "FK_MediaItems_LibraryPath_LibraryPathId", + "MediaItems"); + + migrationBuilder.DropForeignKey( + "FK_MovieMetadata_Movies_MovieId1", + "MovieMetadata"); + + migrationBuilder.DropForeignKey( + "FK_PlexMediaSource_MediaSources_Id", + "PlexMediaSource"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionShowSource_LocalMediaSource_MediaSourceId", + "TelevisionShowSource"); + + migrationBuilder.DropTable( + "LibraryPath"); + + migrationBuilder.DropTable( + "LocalLibrary"); + + migrationBuilder.DropTable( + "PlexConnection"); + + migrationBuilder.DropTable( + "PlexLibrary"); + + migrationBuilder.DropTable( + "PlexMovies"); + + migrationBuilder.DropTable( + "Library"); + + migrationBuilder.DropTable( + "PlexMediaItemPart"); + + migrationBuilder.DropIndex( + "IX_MovieMetadata_MovieId1", + "MovieMetadata"); + + migrationBuilder.DropPrimaryKey( + "PK_PlexMediaSource", + "PlexMediaSource"); + + migrationBuilder.DropPrimaryKey( + "PK_LocalMediaSource", + "LocalMediaSource"); + + migrationBuilder.DropColumn( + "MovieId1", + "MovieMetadata"); + + migrationBuilder.DropColumn( + "ServerName", + "PlexMediaSource"); + + migrationBuilder.RenameTable( + "PlexMediaSource", + newName: "PlexMediaSources"); + + migrationBuilder.RenameTable( + "LocalMediaSource", + newName: "LocalMediaSources"); + + migrationBuilder.RenameColumn( + "LibraryPathId", + "MediaItems", + "MediaSourceId"); + + migrationBuilder.RenameIndex( + "IX_MediaItems_LibraryPathId", + table: "MediaItems", + newName: "IX_MediaItems_MediaSourceId"); + + migrationBuilder.AddColumn( + "LastScan", + "MediaSources", + "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + "Name", + "MediaSources", + "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + "SourceType", + "MediaSources", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + "Folder", + "LocalMediaSources", + "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + "MediaType", + "LocalMediaSources", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddPrimaryKey( + "PK_PlexMediaSources", + "PlexMediaSources", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_LocalMediaSources", + "LocalMediaSources", + "Id"); + + migrationBuilder.CreateTable( + "PlexMediaSourceConnections", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + IsActive = table.Column("INTEGER", nullable: false), + PlexMediaSourceId = table.Column("INTEGER", nullable: true), + Uri = table.Column("TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PlexMediaSourceConnections", x => x.Id); + table.ForeignKey( + "FK_PlexMediaSourceConnections_PlexMediaSources_PlexMediaSourceId", + x => x.PlexMediaSourceId, + "PlexMediaSources", + "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + "PlexMediaSourceLibraries", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Key = table.Column("TEXT", nullable: true), + MediaType = table.Column("INTEGER", nullable: false), + Name = table.Column("TEXT", nullable: true), + PlexMediaSourceId = table.Column("INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PlexMediaSourceLibraries", x => x.Id); + table.ForeignKey( + "FK_PlexMediaSourceLibraries_PlexMediaSources_PlexMediaSourceId", + x => x.PlexMediaSourceId, + "PlexMediaSources", + "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + "IX_MediaSources_Name", + "MediaSources", + "Name", + unique: true); + + migrationBuilder.CreateIndex( + "IX_PlexMediaSourceConnections_PlexMediaSourceId", + "PlexMediaSourceConnections", + "PlexMediaSourceId"); + + migrationBuilder.CreateIndex( + "IX_PlexMediaSourceLibraries_PlexMediaSourceId", + "PlexMediaSourceLibraries", + "PlexMediaSourceId"); + + migrationBuilder.AddForeignKey( + "FK_LocalMediaSources_MediaSources_Id", + "LocalMediaSources", + "Id", + "MediaSources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_MediaItems_MediaSources_MediaSourceId", + "MediaItems", + "MediaSourceId", + "MediaSources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlexMediaSources_MediaSources_Id", + "PlexMediaSources", + "Id", + "MediaSources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionShowSource_LocalMediaSources_MediaSourceId", + "TelevisionShowSource", + "MediaSourceId", + "LocalMediaSources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210225153055_RemoveOldMediaSources.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210225153055_RemoveOldMediaSources.Designer.cs new file mode 100644 index 00000000..05a26a15 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210225153055_RemoveOldMediaSources.Designer.cs @@ -0,0 +1,1402 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210225153055_RemoveOldMediaSources")] + partial class RemoveOldMediaSources + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channels"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElements"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaCollections"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContentRating") + .HasColumnType("TEXT"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("MovieId1") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Premiered") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId") + .IsUnique(); + + b.HasIndex("MovieId1"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleItemAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedules"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("ProgramScheduleId"); + + b.HasIndex("TelevisionSeasonId"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("ProgramScheduleItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolutions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Aired") + .HasColumnType("TEXT"); + + b.Property("Episode") + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Season") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionEpisodeId") + .IsUnique(); + + b.ToTable("TelevisionEpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionSeasons"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TelevisionShows"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId") + .IsUnique(); + + b.ToTable("TelevisionShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionShowSource"); + + b.HasDiscriminator("Discriminator").HasValue("TelevisionShowSource"); + }); + + modelBuilder.Entity("MovieMediaItemSimpleMediaCollection", b => + { + b.Property("MoviesId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("MoviesId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("SimpleMediaCollectionMovies"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodesId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionEpisodesId"); + + b.HasIndex("TelevisionEpisodesId"); + + b.ToTable("SimpleMediaCollectionEpisodes"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionSeasonsId"); + + b.HasIndex("TelevisionSeasonsId"); + + b.ToTable("SimpleMediaCollectionSeasons"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionShowsId"); + + b.HasIndex("TelevisionShowsId"); + + b.ToTable("SimpleMediaCollectionShows"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollections"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("MetadataId") + .HasColumnType("INTEGER"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("TelevisionEpisodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.TelevisionShowSource"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasIndex("MediaSourceId"); + + b.HasDiscriminator().HasValue("LocalTelevisionShowSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovieMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MovieMediaItem"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovies"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItems"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", null) + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.MovieMetadata", "MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", "Movie") + .WithMany() + .HasForeignKey("MovieId1"); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playouts"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleItemAnchors"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("EnumeratorState"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "TelevisionSeason") + .WithMany() + .HasForeignKey("TelevisionSeasonId"); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany() + .HasForeignKey("TelevisionShowId"); + + b.Navigation("MediaCollection"); + + b.Navigation("ProgramSchedule"); + + b.Navigation("TelevisionSeason"); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "TelevisionEpisode") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", "TelevisionEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Seasons") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionShowMetadata", "TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Sources") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("MovieMediaItemSimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", null) + .WithMany() + .HasForeignKey("MoviesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", null) + .WithMany() + .HasForeignKey("TelevisionEpisodesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", null) + .WithMany() + .HasForeignKey("TelevisionSeasonsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", null) + .WithMany() + .HasForeignKey("TelevisionShowsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.MovieMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.LocalMediaSource", "MediaSource") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovieMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovieMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Navigation("Metadata"); + + b.Navigation("Seasons"); + + b.Navigation("Sources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210225153055_RemoveOldMediaSources.cs b/ErsatzTV.Infrastructure/Migrations/20210225153055_RemoveOldMediaSources.cs new file mode 100644 index 00000000..221d006a --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210225153055_RemoveOldMediaSources.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class RemoveOldMediaSources : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) => + // remove old source/folders + migrationBuilder.Sql(@"DELETE FROM MediaSources WHERE Id NOT IN (SELECT MediaSourceId FROM Library)"); + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210225203458_SingularTableNames.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210225203458_SingularTableNames.Designer.cs new file mode 100644 index 00000000..d04c2422 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210225203458_SingularTableNames.Designer.cs @@ -0,0 +1,1393 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210225203458_SingularTableNames")] + partial class SingularTableNames + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContentRating") + .HasColumnType("TEXT"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Premiered") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId") + .IsUnique(); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("ProgramScheduleId"); + + b.HasIndex("TelevisionSeasonId"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Aired") + .HasColumnType("TEXT"); + + b.Property("Episode") + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Season") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionEpisodeId") + .IsUnique(); + + b.ToTable("TelevisionEpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionSeason"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId") + .IsUnique(); + + b.ToTable("TelevisionShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionShowSource"); + + b.HasDiscriminator("Discriminator").HasValue("TelevisionShowSource"); + }); + + modelBuilder.Entity("MovieMediaItemSimpleMediaCollection", b => + { + b.Property("MoviesId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("MoviesId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("SimpleMediaCollectionMovie"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodesId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionEpisodesId"); + + b.HasIndex("TelevisionEpisodesId"); + + b.ToTable("SimpleMediaCollectionEpisode"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionSeasonsId"); + + b.HasIndex("TelevisionSeasonsId"); + + b.ToTable("SimpleMediaCollectionSeason"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionShowsId"); + + b.HasIndex("TelevisionShowsId"); + + b.ToTable("SimpleMediaCollectionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("MetadataId") + .HasColumnType("INTEGER"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.TelevisionShowSource"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasIndex("MediaSourceId"); + + b.HasDiscriminator().HasValue("LocalTelevisionShowSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovieMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MovieMediaItem"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", "Movie") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.MovieMetadata", "MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("EnumeratorState"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "TelevisionSeason") + .WithMany() + .HasForeignKey("TelevisionSeasonId"); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany() + .HasForeignKey("TelevisionShowId"); + + b.Navigation("MediaCollection"); + + b.Navigation("ProgramSchedule"); + + b.Navigation("TelevisionSeason"); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "TelevisionEpisode") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", "TelevisionEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Seasons") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionShowMetadata", "TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Sources") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("MovieMediaItemSimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", null) + .WithMany() + .HasForeignKey("MoviesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", null) + .WithMany() + .HasForeignKey("TelevisionEpisodesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", null) + .WithMany() + .HasForeignKey("TelevisionSeasonsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", null) + .WithMany() + .HasForeignKey("TelevisionShowsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.MovieMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.LocalMediaSource", "MediaSource") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovieMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovieMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Navigation("Metadata"); + + b.Navigation("Seasons"); + + b.Navigation("Sources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210225203458_SingularTableNames.cs b/ErsatzTV.Infrastructure/Migrations/20210225203458_SingularTableNames.cs new file mode 100644 index 00000000..029507ba --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210225203458_SingularTableNames.cs @@ -0,0 +1,1930 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class SingularTableNames : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_Channels_FFmpegProfiles_FFmpegProfileId", + "Channels"); + + migrationBuilder.DropForeignKey( + "FK_FFmpegProfiles_Resolutions_ResolutionId", + "FFmpegProfiles"); + + migrationBuilder.DropForeignKey( + "FK_Library_MediaSources_MediaSourceId", + "Library"); + + migrationBuilder.DropForeignKey( + "FK_LocalMediaSource_MediaSources_Id", + "LocalMediaSource"); + + migrationBuilder.DropForeignKey( + "FK_MediaItems_LibraryPath_LibraryPathId", + "MediaItems"); + + migrationBuilder.DropForeignKey( + "FK_MovieMetadata_Movies_MovieId", + "MovieMetadata"); + + migrationBuilder.DropForeignKey( + "FK_MovieMetadata_Movies_MovieId1", + "MovieMetadata"); + + migrationBuilder.DropForeignKey( + "FK_Movies_MediaItems_Id", + "Movies"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutItems_MediaItems_MediaItemId", + "PlayoutItems"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutItems_Playouts_PlayoutId", + "PlayoutItems"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutProgramScheduleItemAnchors_Playouts_PlayoutId", + "PlayoutProgramScheduleItemAnchors"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutProgramScheduleItemAnchors_ProgramSchedules_ProgramScheduleId", + "PlayoutProgramScheduleItemAnchors"); + + migrationBuilder.DropForeignKey( + "FK_Playouts_Channels_ChannelId", + "Playouts"); + + migrationBuilder.DropForeignKey( + "FK_Playouts_ProgramScheduleItems_Anchor_NextScheduleItemId", + "Playouts"); + + migrationBuilder.DropForeignKey( + "FK_Playouts_ProgramSchedules_ProgramScheduleId", + "Playouts"); + + migrationBuilder.DropForeignKey( + "FK_PlexMediaSource_MediaSources_Id", + "PlexMediaSource"); + + migrationBuilder.DropForeignKey( + "FK_PlexMovies_Movies_Id", + "PlexMovies"); + + migrationBuilder.DropForeignKey( + "FK_PlexMovies_PlexMediaItemPart_PartId", + "PlexMovies"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleDurationItems_ProgramScheduleItems_Id", + "ProgramScheduleDurationItems"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleFloodItems_ProgramScheduleItems_Id", + "ProgramScheduleFloodItems"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItems_MediaCollections_MediaCollectionId", + "ProgramScheduleItems"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItems_ProgramSchedules_ProgramScheduleId", + "ProgramScheduleItems"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItems_TelevisionSeasons_TelevisionSeasonId", + "ProgramScheduleItems"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItems_TelevisionShows_TelevisionShowId", + "ProgramScheduleItems"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleMultipleItems_ProgramScheduleItems_Id", + "ProgramScheduleMultipleItems"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleOneItems_ProgramScheduleItems_Id", + "ProgramScheduleOneItems"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionEpisodes_SimpleMediaCollections_SimpleMediaCollectionsId", + "SimpleMediaCollectionEpisodes"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionEpisodes_TelevisionEpisodes_TelevisionEpisodesId", + "SimpleMediaCollectionEpisodes"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionMovies_Movies_MoviesId", + "SimpleMediaCollectionMovies"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionMovies_SimpleMediaCollections_SimpleMediaCollectionsId", + "SimpleMediaCollectionMovies"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollections_MediaCollections_Id", + "SimpleMediaCollections"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionSeasons_SimpleMediaCollections_SimpleMediaCollectionsId", + "SimpleMediaCollectionSeasons"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionSeasons_TelevisionSeasons_TelevisionSeasonsId", + "SimpleMediaCollectionSeasons"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionShows_SimpleMediaCollections_SimpleMediaCollectionsId", + "SimpleMediaCollectionShows"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionShows_TelevisionShows_TelevisionShowsId", + "SimpleMediaCollectionShows"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionEpisodeMetadata_TelevisionEpisodes_TelevisionEpisodeId", + "TelevisionEpisodeMetadata"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionEpisodes_MediaItems_Id", + "TelevisionEpisodes"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionEpisodes_TelevisionSeasons_SeasonId", + "TelevisionEpisodes"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionSeasons_TelevisionShows_TelevisionShowId", + "TelevisionSeasons"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionShowMetadata_TelevisionShows_TelevisionShowId", + "TelevisionShowMetadata"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionShowSource_TelevisionShows_TelevisionShowId", + "TelevisionShowSource"); + + migrationBuilder.DropIndex( + "IX_MovieMetadata_MovieId1", + "MovieMetadata"); + + migrationBuilder.DropPrimaryKey( + "PK_TelevisionShows", + "TelevisionShows"); + + migrationBuilder.DropPrimaryKey( + "PK_TelevisionSeasons", + "TelevisionSeasons"); + + migrationBuilder.DropPrimaryKey( + "PK_TelevisionEpisodes", + "TelevisionEpisodes"); + + migrationBuilder.DropPrimaryKey( + "PK_SimpleMediaCollectionShows", + "SimpleMediaCollectionShows"); + + migrationBuilder.DropPrimaryKey( + "PK_SimpleMediaCollectionSeasons", + "SimpleMediaCollectionSeasons"); + + migrationBuilder.DropPrimaryKey( + "PK_SimpleMediaCollections", + "SimpleMediaCollections"); + + migrationBuilder.DropPrimaryKey( + "PK_SimpleMediaCollectionMovies", + "SimpleMediaCollectionMovies"); + + migrationBuilder.DropPrimaryKey( + "PK_SimpleMediaCollectionEpisodes", + "SimpleMediaCollectionEpisodes"); + + migrationBuilder.DropPrimaryKey( + "PK_Resolutions", + "Resolutions"); + + migrationBuilder.DropPrimaryKey( + "PK_ProgramSchedules", + "ProgramSchedules"); + + migrationBuilder.DropPrimaryKey( + "PK_ProgramScheduleOneItems", + "ProgramScheduleOneItems"); + + migrationBuilder.DropPrimaryKey( + "PK_ProgramScheduleMultipleItems", + "ProgramScheduleMultipleItems"); + + migrationBuilder.DropPrimaryKey( + "PK_ProgramScheduleItems", + "ProgramScheduleItems"); + + migrationBuilder.DropPrimaryKey( + "PK_ProgramScheduleFloodItems", + "ProgramScheduleFloodItems"); + + migrationBuilder.DropPrimaryKey( + "PK_ProgramScheduleDurationItems", + "ProgramScheduleDurationItems"); + + migrationBuilder.DropPrimaryKey( + "PK_PlexMovies", + "PlexMovies"); + + migrationBuilder.DropPrimaryKey( + "PK_Playouts", + "Playouts"); + + migrationBuilder.DropPrimaryKey( + "PK_PlayoutProgramScheduleItemAnchors", + "PlayoutProgramScheduleItemAnchors"); + + migrationBuilder.DropPrimaryKey( + "PK_PlayoutItems", + "PlayoutItems"); + + migrationBuilder.DropPrimaryKey( + "PK_Movies", + "Movies"); + + migrationBuilder.DropPrimaryKey( + "PK_MediaSources", + "MediaSources"); + + migrationBuilder.DropPrimaryKey( + "PK_MediaItems", + "MediaItems"); + + migrationBuilder.DropPrimaryKey( + "PK_MediaCollections", + "MediaCollections"); + + migrationBuilder.DropPrimaryKey( + "PK_FFmpegProfiles", + "FFmpegProfiles"); + + migrationBuilder.DropPrimaryKey( + "PK_ConfigElements", + "ConfigElements"); + + migrationBuilder.DropPrimaryKey( + "PK_Channels", + "Channels"); + + migrationBuilder.DropColumn( + "MovieId1", + "MovieMetadata"); + + migrationBuilder.RenameTable( + "TelevisionShows", + newName: "TelevisionShow"); + + migrationBuilder.RenameTable( + "TelevisionSeasons", + newName: "TelevisionSeason"); + + migrationBuilder.RenameTable( + "TelevisionEpisodes", + newName: "TelevisionEpisode"); + + migrationBuilder.RenameTable( + "SimpleMediaCollectionShows", + newName: "SimpleMediaCollectionShow"); + + migrationBuilder.RenameTable( + "SimpleMediaCollectionSeasons", + newName: "SimpleMediaCollectionSeason"); + + migrationBuilder.RenameTable( + "SimpleMediaCollections", + newName: "SimpleMediaCollection"); + + migrationBuilder.RenameTable( + "SimpleMediaCollectionMovies", + newName: "SimpleMediaCollectionMovie"); + + migrationBuilder.RenameTable( + "SimpleMediaCollectionEpisodes", + newName: "SimpleMediaCollectionEpisode"); + + migrationBuilder.RenameTable( + "Resolutions", + newName: "Resolution"); + + migrationBuilder.RenameTable( + "ProgramSchedules", + newName: "ProgramSchedule"); + + migrationBuilder.RenameTable( + "ProgramScheduleOneItems", + newName: "ProgramScheduleOneItem"); + + migrationBuilder.RenameTable( + "ProgramScheduleMultipleItems", + newName: "ProgramScheduleMultipleItem"); + + migrationBuilder.RenameTable( + "ProgramScheduleItems", + newName: "ProgramScheduleItem"); + + migrationBuilder.RenameTable( + "ProgramScheduleFloodItems", + newName: "ProgramScheduleFloodItem"); + + migrationBuilder.RenameTable( + "ProgramScheduleDurationItems", + newName: "ProgramScheduleDurationItem"); + + migrationBuilder.RenameTable( + "PlexMovies", + newName: "PlexMovie"); + + migrationBuilder.RenameTable( + "Playouts", + newName: "Playout"); + + migrationBuilder.RenameTable( + "PlayoutProgramScheduleItemAnchors", + newName: "PlayoutProgramScheduleAnchor"); + + migrationBuilder.RenameTable( + "PlayoutItems", + newName: "PlayoutItem"); + + migrationBuilder.RenameTable( + "Movies", + newName: "Movie"); + + migrationBuilder.RenameTable( + "MediaSources", + newName: "MediaSource"); + + migrationBuilder.RenameTable( + "MediaItems", + newName: "MediaItem"); + + migrationBuilder.RenameTable( + "MediaCollections", + newName: "MediaCollection"); + + migrationBuilder.RenameTable( + "FFmpegProfiles", + newName: "FFmpegProfile"); + + migrationBuilder.RenameTable( + "ConfigElements", + newName: "ConfigElement"); + + migrationBuilder.RenameTable( + "Channels", + newName: "Channel"); + + migrationBuilder.RenameIndex( + "IX_TelevisionSeasons_TelevisionShowId", + table: "TelevisionSeason", + newName: "IX_TelevisionSeason_TelevisionShowId"); + + migrationBuilder.RenameIndex( + "IX_TelevisionEpisodes_SeasonId", + table: "TelevisionEpisode", + newName: "IX_TelevisionEpisode_SeasonId"); + + migrationBuilder.RenameIndex( + "IX_SimpleMediaCollectionShows_TelevisionShowsId", + table: "SimpleMediaCollectionShow", + newName: "IX_SimpleMediaCollectionShow_TelevisionShowsId"); + + migrationBuilder.RenameIndex( + "IX_SimpleMediaCollectionSeasons_TelevisionSeasonsId", + table: "SimpleMediaCollectionSeason", + newName: "IX_SimpleMediaCollectionSeason_TelevisionSeasonsId"); + + migrationBuilder.RenameIndex( + "IX_SimpleMediaCollectionMovies_SimpleMediaCollectionsId", + table: "SimpleMediaCollectionMovie", + newName: "IX_SimpleMediaCollectionMovie_SimpleMediaCollectionsId"); + + migrationBuilder.RenameIndex( + "IX_SimpleMediaCollectionEpisodes_TelevisionEpisodesId", + table: "SimpleMediaCollectionEpisode", + newName: "IX_SimpleMediaCollectionEpisode_TelevisionEpisodesId"); + + migrationBuilder.RenameIndex( + "IX_ProgramSchedules_Name", + table: "ProgramSchedule", + newName: "IX_ProgramSchedule_Name"); + + migrationBuilder.RenameIndex( + "IX_ProgramScheduleItems_TelevisionShowId", + table: "ProgramScheduleItem", + newName: "IX_ProgramScheduleItem_TelevisionShowId"); + + migrationBuilder.RenameIndex( + "IX_ProgramScheduleItems_TelevisionSeasonId", + table: "ProgramScheduleItem", + newName: "IX_ProgramScheduleItem_TelevisionSeasonId"); + + migrationBuilder.RenameIndex( + "IX_ProgramScheduleItems_ProgramScheduleId", + table: "ProgramScheduleItem", + newName: "IX_ProgramScheduleItem_ProgramScheduleId"); + + migrationBuilder.RenameIndex( + "IX_ProgramScheduleItems_MediaCollectionId", + table: "ProgramScheduleItem", + newName: "IX_ProgramScheduleItem_MediaCollectionId"); + + migrationBuilder.RenameIndex( + "IX_PlexMovies_PartId", + table: "PlexMovie", + newName: "IX_PlexMovie_PartId"); + + migrationBuilder.RenameIndex( + "IX_Playouts_ProgramScheduleId", + table: "Playout", + newName: "IX_Playout_ProgramScheduleId"); + + migrationBuilder.RenameIndex( + "IX_Playouts_ChannelId", + table: "Playout", + newName: "IX_Playout_ChannelId"); + + migrationBuilder.RenameIndex( + "IX_Playouts_Anchor_NextScheduleItemId", + table: "Playout", + newName: "IX_Playout_Anchor_NextScheduleItemId"); + + migrationBuilder.RenameIndex( + "IX_PlayoutProgramScheduleItemAnchors_ProgramScheduleId", + table: "PlayoutProgramScheduleAnchor", + newName: "IX_PlayoutProgramScheduleAnchor_ProgramScheduleId"); + + migrationBuilder.RenameIndex( + "IX_PlayoutProgramScheduleItemAnchors_PlayoutId", + table: "PlayoutProgramScheduleAnchor", + newName: "IX_PlayoutProgramScheduleAnchor_PlayoutId"); + + migrationBuilder.RenameIndex( + "IX_PlayoutItems_PlayoutId", + table: "PlayoutItem", + newName: "IX_PlayoutItem_PlayoutId"); + + migrationBuilder.RenameIndex( + "IX_PlayoutItems_MediaItemId", + table: "PlayoutItem", + newName: "IX_PlayoutItem_MediaItemId"); + + migrationBuilder.RenameIndex( + "IX_MediaItems_LibraryPathId", + table: "MediaItem", + newName: "IX_MediaItem_LibraryPathId"); + + migrationBuilder.RenameIndex( + "IX_MediaCollections_Name", + table: "MediaCollection", + newName: "IX_MediaCollection_Name"); + + migrationBuilder.RenameIndex( + "IX_FFmpegProfiles_ResolutionId", + table: "FFmpegProfile", + newName: "IX_FFmpegProfile_ResolutionId"); + + migrationBuilder.RenameIndex( + "IX_ConfigElements_Key", + table: "ConfigElement", + newName: "IX_ConfigElement_Key"); + + migrationBuilder.RenameIndex( + "IX_Channels_Number", + table: "Channel", + newName: "IX_Channel_Number"); + + migrationBuilder.RenameIndex( + "IX_Channels_FFmpegProfileId", + table: "Channel", + newName: "IX_Channel_FFmpegProfileId"); + + migrationBuilder.AddPrimaryKey( + "PK_TelevisionShow", + "TelevisionShow", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_TelevisionSeason", + "TelevisionSeason", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_TelevisionEpisode", + "TelevisionEpisode", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_SimpleMediaCollectionShow", + "SimpleMediaCollectionShow", + new[] { "SimpleMediaCollectionsId", "TelevisionShowsId" }); + + migrationBuilder.AddPrimaryKey( + "PK_SimpleMediaCollectionSeason", + "SimpleMediaCollectionSeason", + new[] { "SimpleMediaCollectionsId", "TelevisionSeasonsId" }); + + migrationBuilder.AddPrimaryKey( + "PK_SimpleMediaCollection", + "SimpleMediaCollection", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_SimpleMediaCollectionMovie", + "SimpleMediaCollectionMovie", + new[] { "MoviesId", "SimpleMediaCollectionsId" }); + + migrationBuilder.AddPrimaryKey( + "PK_SimpleMediaCollectionEpisode", + "SimpleMediaCollectionEpisode", + new[] { "SimpleMediaCollectionsId", "TelevisionEpisodesId" }); + + migrationBuilder.AddPrimaryKey( + "PK_Resolution", + "Resolution", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ProgramSchedule", + "ProgramSchedule", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ProgramScheduleOneItem", + "ProgramScheduleOneItem", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ProgramScheduleMultipleItem", + "ProgramScheduleMultipleItem", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ProgramScheduleItem", + "ProgramScheduleItem", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ProgramScheduleFloodItem", + "ProgramScheduleFloodItem", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ProgramScheduleDurationItem", + "ProgramScheduleDurationItem", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_PlexMovie", + "PlexMovie", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_Playout", + "Playout", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_PlayoutProgramScheduleAnchor", + "PlayoutProgramScheduleAnchor", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_PlayoutItem", + "PlayoutItem", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_Movie", + "Movie", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_MediaSource", + "MediaSource", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_MediaItem", + "MediaItem", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_MediaCollection", + "MediaCollection", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_FFmpegProfile", + "FFmpegProfile", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ConfigElement", + "ConfigElement", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_Channel", + "Channel", + "Id"); + + migrationBuilder.AddForeignKey( + "FK_Channel_FFmpegProfile_FFmpegProfileId", + "Channel", + "FFmpegProfileId", + "FFmpegProfile", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_FFmpegProfile_Resolution_ResolutionId", + "FFmpegProfile", + "ResolutionId", + "Resolution", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_Library_MediaSource_MediaSourceId", + "Library", + "MediaSourceId", + "MediaSource", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_LocalMediaSource_MediaSource_Id", + "LocalMediaSource", + "Id", + "MediaSource", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_MediaItem_LibraryPath_LibraryPathId", + "MediaItem", + "LibraryPathId", + "LibraryPath", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_Movie_MediaItem_Id", + "Movie", + "Id", + "MediaItem", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_MovieMetadata_Movie_MovieId", + "MovieMetadata", + "MovieId", + "Movie", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_Playout_Channel_ChannelId", + "Playout", + "ChannelId", + "Channel", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_Playout_ProgramSchedule_ProgramScheduleId", + "Playout", + "ProgramScheduleId", + "ProgramSchedule", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_Playout_ProgramScheduleItem_Anchor_NextScheduleItemId", + "Playout", + "Anchor_NextScheduleItemId", + "ProgramScheduleItem", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlayoutItem_MediaItem_MediaItemId", + "PlayoutItem", + "MediaItemId", + "MediaItem", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlayoutItem_Playout_PlayoutId", + "PlayoutItem", + "PlayoutId", + "Playout", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlayoutProgramScheduleAnchor_Playout_PlayoutId", + "PlayoutProgramScheduleAnchor", + "PlayoutId", + "Playout", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlayoutProgramScheduleAnchor_ProgramSchedule_ProgramScheduleId", + "PlayoutProgramScheduleAnchor", + "ProgramScheduleId", + "ProgramSchedule", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlexMediaSource_MediaSource_Id", + "PlexMediaSource", + "Id", + "MediaSource", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlexMovie_Movie_Id", + "PlexMovie", + "Id", + "Movie", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlexMovie_PlexMediaItemPart_PartId", + "PlexMovie", + "PartId", + "PlexMediaItemPart", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleDurationItem_ProgramScheduleItem_Id", + "ProgramScheduleDurationItem", + "Id", + "ProgramScheduleItem", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleFloodItem_ProgramScheduleItem_Id", + "ProgramScheduleFloodItem", + "Id", + "ProgramScheduleItem", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_MediaCollection_MediaCollectionId", + "ProgramScheduleItem", + "MediaCollectionId", + "MediaCollection", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_ProgramSchedule_ProgramScheduleId", + "ProgramScheduleItem", + "ProgramScheduleId", + "ProgramSchedule", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_TelevisionSeason_TelevisionSeasonId", + "ProgramScheduleItem", + "TelevisionSeasonId", + "TelevisionSeason", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_TelevisionShow_TelevisionShowId", + "ProgramScheduleItem", + "TelevisionShowId", + "TelevisionShow", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleMultipleItem_ProgramScheduleItem_Id", + "ProgramScheduleMultipleItem", + "Id", + "ProgramScheduleItem", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleOneItem_ProgramScheduleItem_Id", + "ProgramScheduleOneItem", + "Id", + "ProgramScheduleItem", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollection_MediaCollection_Id", + "SimpleMediaCollection", + "Id", + "MediaCollection", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionEpisode_SimpleMediaCollection_SimpleMediaCollectionsId", + "SimpleMediaCollectionEpisode", + "SimpleMediaCollectionsId", + "SimpleMediaCollection", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionEpisode_TelevisionEpisode_TelevisionEpisodesId", + "SimpleMediaCollectionEpisode", + "TelevisionEpisodesId", + "TelevisionEpisode", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionMovie_Movie_MoviesId", + "SimpleMediaCollectionMovie", + "MoviesId", + "Movie", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionMovie_SimpleMediaCollection_SimpleMediaCollectionsId", + "SimpleMediaCollectionMovie", + "SimpleMediaCollectionsId", + "SimpleMediaCollection", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionSeason_SimpleMediaCollection_SimpleMediaCollectionsId", + "SimpleMediaCollectionSeason", + "SimpleMediaCollectionsId", + "SimpleMediaCollection", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionSeason_TelevisionSeason_TelevisionSeasonsId", + "SimpleMediaCollectionSeason", + "TelevisionSeasonsId", + "TelevisionSeason", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionShow_SimpleMediaCollection_SimpleMediaCollectionsId", + "SimpleMediaCollectionShow", + "SimpleMediaCollectionsId", + "SimpleMediaCollection", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionShow_TelevisionShow_TelevisionShowsId", + "SimpleMediaCollectionShow", + "TelevisionShowsId", + "TelevisionShow", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionEpisode_MediaItem_Id", + "TelevisionEpisode", + "Id", + "MediaItem", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionEpisode_TelevisionSeason_SeasonId", + "TelevisionEpisode", + "SeasonId", + "TelevisionSeason", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionEpisodeMetadata_TelevisionEpisode_TelevisionEpisodeId", + "TelevisionEpisodeMetadata", + "TelevisionEpisodeId", + "TelevisionEpisode", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionSeason_TelevisionShow_TelevisionShowId", + "TelevisionSeason", + "TelevisionShowId", + "TelevisionShow", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionShowMetadata_TelevisionShow_TelevisionShowId", + "TelevisionShowMetadata", + "TelevisionShowId", + "TelevisionShow", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionShowSource_TelevisionShow_TelevisionShowId", + "TelevisionShowSource", + "TelevisionShowId", + "TelevisionShow", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_Channel_FFmpegProfile_FFmpegProfileId", + "Channel"); + + migrationBuilder.DropForeignKey( + "FK_FFmpegProfile_Resolution_ResolutionId", + "FFmpegProfile"); + + migrationBuilder.DropForeignKey( + "FK_Library_MediaSource_MediaSourceId", + "Library"); + + migrationBuilder.DropForeignKey( + "FK_LocalMediaSource_MediaSource_Id", + "LocalMediaSource"); + + migrationBuilder.DropForeignKey( + "FK_MediaItem_LibraryPath_LibraryPathId", + "MediaItem"); + + migrationBuilder.DropForeignKey( + "FK_Movie_MediaItem_Id", + "Movie"); + + migrationBuilder.DropForeignKey( + "FK_MovieMetadata_Movie_MovieId", + "MovieMetadata"); + + migrationBuilder.DropForeignKey( + "FK_Playout_Channel_ChannelId", + "Playout"); + + migrationBuilder.DropForeignKey( + "FK_Playout_ProgramSchedule_ProgramScheduleId", + "Playout"); + + migrationBuilder.DropForeignKey( + "FK_Playout_ProgramScheduleItem_Anchor_NextScheduleItemId", + "Playout"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutItem_MediaItem_MediaItemId", + "PlayoutItem"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutItem_Playout_PlayoutId", + "PlayoutItem"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutProgramScheduleAnchor_Playout_PlayoutId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutProgramScheduleAnchor_ProgramSchedule_ProgramScheduleId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropForeignKey( + "FK_PlexMediaSource_MediaSource_Id", + "PlexMediaSource"); + + migrationBuilder.DropForeignKey( + "FK_PlexMovie_Movie_Id", + "PlexMovie"); + + migrationBuilder.DropForeignKey( + "FK_PlexMovie_PlexMediaItemPart_PartId", + "PlexMovie"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleDurationItem_ProgramScheduleItem_Id", + "ProgramScheduleDurationItem"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleFloodItem_ProgramScheduleItem_Id", + "ProgramScheduleFloodItem"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_MediaCollection_MediaCollectionId", + "ProgramScheduleItem"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_ProgramSchedule_ProgramScheduleId", + "ProgramScheduleItem"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_TelevisionSeason_TelevisionSeasonId", + "ProgramScheduleItem"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_TelevisionShow_TelevisionShowId", + "ProgramScheduleItem"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleMultipleItem_ProgramScheduleItem_Id", + "ProgramScheduleMultipleItem"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleOneItem_ProgramScheduleItem_Id", + "ProgramScheduleOneItem"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollection_MediaCollection_Id", + "SimpleMediaCollection"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionEpisode_SimpleMediaCollection_SimpleMediaCollectionsId", + "SimpleMediaCollectionEpisode"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionEpisode_TelevisionEpisode_TelevisionEpisodesId", + "SimpleMediaCollectionEpisode"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionMovie_Movie_MoviesId", + "SimpleMediaCollectionMovie"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionMovie_SimpleMediaCollection_SimpleMediaCollectionsId", + "SimpleMediaCollectionMovie"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionSeason_SimpleMediaCollection_SimpleMediaCollectionsId", + "SimpleMediaCollectionSeason"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionSeason_TelevisionSeason_TelevisionSeasonsId", + "SimpleMediaCollectionSeason"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionShow_SimpleMediaCollection_SimpleMediaCollectionsId", + "SimpleMediaCollectionShow"); + + migrationBuilder.DropForeignKey( + "FK_SimpleMediaCollectionShow_TelevisionShow_TelevisionShowsId", + "SimpleMediaCollectionShow"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionEpisode_MediaItem_Id", + "TelevisionEpisode"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionEpisode_TelevisionSeason_SeasonId", + "TelevisionEpisode"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionEpisodeMetadata_TelevisionEpisode_TelevisionEpisodeId", + "TelevisionEpisodeMetadata"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionSeason_TelevisionShow_TelevisionShowId", + "TelevisionSeason"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionShowMetadata_TelevisionShow_TelevisionShowId", + "TelevisionShowMetadata"); + + migrationBuilder.DropForeignKey( + "FK_TelevisionShowSource_TelevisionShow_TelevisionShowId", + "TelevisionShowSource"); + + migrationBuilder.DropPrimaryKey( + "PK_TelevisionShow", + "TelevisionShow"); + + migrationBuilder.DropPrimaryKey( + "PK_TelevisionSeason", + "TelevisionSeason"); + + migrationBuilder.DropPrimaryKey( + "PK_TelevisionEpisode", + "TelevisionEpisode"); + + migrationBuilder.DropPrimaryKey( + "PK_SimpleMediaCollectionShow", + "SimpleMediaCollectionShow"); + + migrationBuilder.DropPrimaryKey( + "PK_SimpleMediaCollectionSeason", + "SimpleMediaCollectionSeason"); + + migrationBuilder.DropPrimaryKey( + "PK_SimpleMediaCollectionMovie", + "SimpleMediaCollectionMovie"); + + migrationBuilder.DropPrimaryKey( + "PK_SimpleMediaCollectionEpisode", + "SimpleMediaCollectionEpisode"); + + migrationBuilder.DropPrimaryKey( + "PK_SimpleMediaCollection", + "SimpleMediaCollection"); + + migrationBuilder.DropPrimaryKey( + "PK_Resolution", + "Resolution"); + + migrationBuilder.DropPrimaryKey( + "PK_ProgramScheduleOneItem", + "ProgramScheduleOneItem"); + + migrationBuilder.DropPrimaryKey( + "PK_ProgramScheduleMultipleItem", + "ProgramScheduleMultipleItem"); + + migrationBuilder.DropPrimaryKey( + "PK_ProgramScheduleItem", + "ProgramScheduleItem"); + + migrationBuilder.DropPrimaryKey( + "PK_ProgramScheduleFloodItem", + "ProgramScheduleFloodItem"); + + migrationBuilder.DropPrimaryKey( + "PK_ProgramScheduleDurationItem", + "ProgramScheduleDurationItem"); + + migrationBuilder.DropPrimaryKey( + "PK_ProgramSchedule", + "ProgramSchedule"); + + migrationBuilder.DropPrimaryKey( + "PK_PlexMovie", + "PlexMovie"); + + migrationBuilder.DropPrimaryKey( + "PK_PlayoutProgramScheduleAnchor", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropPrimaryKey( + "PK_PlayoutItem", + "PlayoutItem"); + + migrationBuilder.DropPrimaryKey( + "PK_Playout", + "Playout"); + + migrationBuilder.DropPrimaryKey( + "PK_Movie", + "Movie"); + + migrationBuilder.DropPrimaryKey( + "PK_MediaSource", + "MediaSource"); + + migrationBuilder.DropPrimaryKey( + "PK_MediaItem", + "MediaItem"); + + migrationBuilder.DropPrimaryKey( + "PK_MediaCollection", + "MediaCollection"); + + migrationBuilder.DropPrimaryKey( + "PK_FFmpegProfile", + "FFmpegProfile"); + + migrationBuilder.DropPrimaryKey( + "PK_ConfigElement", + "ConfigElement"); + + migrationBuilder.DropPrimaryKey( + "PK_Channel", + "Channel"); + + migrationBuilder.RenameTable( + "TelevisionShow", + newName: "TelevisionShows"); + + migrationBuilder.RenameTable( + "TelevisionSeason", + newName: "TelevisionSeasons"); + + migrationBuilder.RenameTable( + "TelevisionEpisode", + newName: "TelevisionEpisodes"); + + migrationBuilder.RenameTable( + "SimpleMediaCollectionShow", + newName: "SimpleMediaCollectionShows"); + + migrationBuilder.RenameTable( + "SimpleMediaCollectionSeason", + newName: "SimpleMediaCollectionSeasons"); + + migrationBuilder.RenameTable( + "SimpleMediaCollectionMovie", + newName: "SimpleMediaCollectionMovies"); + + migrationBuilder.RenameTable( + "SimpleMediaCollectionEpisode", + newName: "SimpleMediaCollectionEpisodes"); + + migrationBuilder.RenameTable( + "SimpleMediaCollection", + newName: "SimpleMediaCollections"); + + migrationBuilder.RenameTable( + "Resolution", + newName: "Resolutions"); + + migrationBuilder.RenameTable( + "ProgramScheduleOneItem", + newName: "ProgramScheduleOneItems"); + + migrationBuilder.RenameTable( + "ProgramScheduleMultipleItem", + newName: "ProgramScheduleMultipleItems"); + + migrationBuilder.RenameTable( + "ProgramScheduleItem", + newName: "ProgramScheduleItems"); + + migrationBuilder.RenameTable( + "ProgramScheduleFloodItem", + newName: "ProgramScheduleFloodItems"); + + migrationBuilder.RenameTable( + "ProgramScheduleDurationItem", + newName: "ProgramScheduleDurationItems"); + + migrationBuilder.RenameTable( + "ProgramSchedule", + newName: "ProgramSchedules"); + + migrationBuilder.RenameTable( + "PlexMovie", + newName: "PlexMovies"); + + migrationBuilder.RenameTable( + "PlayoutProgramScheduleAnchor", + newName: "PlayoutProgramScheduleItemAnchors"); + + migrationBuilder.RenameTable( + "PlayoutItem", + newName: "PlayoutItems"); + + migrationBuilder.RenameTable( + "Playout", + newName: "Playouts"); + + migrationBuilder.RenameTable( + "Movie", + newName: "Movies"); + + migrationBuilder.RenameTable( + "MediaSource", + newName: "MediaSources"); + + migrationBuilder.RenameTable( + "MediaItem", + newName: "MediaItems"); + + migrationBuilder.RenameTable( + "MediaCollection", + newName: "MediaCollections"); + + migrationBuilder.RenameTable( + "FFmpegProfile", + newName: "FFmpegProfiles"); + + migrationBuilder.RenameTable( + "ConfigElement", + newName: "ConfigElements"); + + migrationBuilder.RenameTable( + "Channel", + newName: "Channels"); + + migrationBuilder.RenameIndex( + "IX_TelevisionSeason_TelevisionShowId", + table: "TelevisionSeasons", + newName: "IX_TelevisionSeasons_TelevisionShowId"); + + migrationBuilder.RenameIndex( + "IX_TelevisionEpisode_SeasonId", + table: "TelevisionEpisodes", + newName: "IX_TelevisionEpisodes_SeasonId"); + + migrationBuilder.RenameIndex( + "IX_SimpleMediaCollectionShow_TelevisionShowsId", + table: "SimpleMediaCollectionShows", + newName: "IX_SimpleMediaCollectionShows_TelevisionShowsId"); + + migrationBuilder.RenameIndex( + "IX_SimpleMediaCollectionSeason_TelevisionSeasonsId", + table: "SimpleMediaCollectionSeasons", + newName: "IX_SimpleMediaCollectionSeasons_TelevisionSeasonsId"); + + migrationBuilder.RenameIndex( + "IX_SimpleMediaCollectionMovie_SimpleMediaCollectionsId", + table: "SimpleMediaCollectionMovies", + newName: "IX_SimpleMediaCollectionMovies_SimpleMediaCollectionsId"); + + migrationBuilder.RenameIndex( + "IX_SimpleMediaCollectionEpisode_TelevisionEpisodesId", + table: "SimpleMediaCollectionEpisodes", + newName: "IX_SimpleMediaCollectionEpisodes_TelevisionEpisodesId"); + + migrationBuilder.RenameIndex( + "IX_ProgramScheduleItem_TelevisionShowId", + table: "ProgramScheduleItems", + newName: "IX_ProgramScheduleItems_TelevisionShowId"); + + migrationBuilder.RenameIndex( + "IX_ProgramScheduleItem_TelevisionSeasonId", + table: "ProgramScheduleItems", + newName: "IX_ProgramScheduleItems_TelevisionSeasonId"); + + migrationBuilder.RenameIndex( + "IX_ProgramScheduleItem_ProgramScheduleId", + table: "ProgramScheduleItems", + newName: "IX_ProgramScheduleItems_ProgramScheduleId"); + + migrationBuilder.RenameIndex( + "IX_ProgramScheduleItem_MediaCollectionId", + table: "ProgramScheduleItems", + newName: "IX_ProgramScheduleItems_MediaCollectionId"); + + migrationBuilder.RenameIndex( + "IX_ProgramSchedule_Name", + table: "ProgramSchedules", + newName: "IX_ProgramSchedules_Name"); + + migrationBuilder.RenameIndex( + "IX_PlexMovie_PartId", + table: "PlexMovies", + newName: "IX_PlexMovies_PartId"); + + migrationBuilder.RenameIndex( + "IX_PlayoutProgramScheduleAnchor_ProgramScheduleId", + table: "PlayoutProgramScheduleItemAnchors", + newName: "IX_PlayoutProgramScheduleItemAnchors_ProgramScheduleId"); + + migrationBuilder.RenameIndex( + "IX_PlayoutProgramScheduleAnchor_PlayoutId", + table: "PlayoutProgramScheduleItemAnchors", + newName: "IX_PlayoutProgramScheduleItemAnchors_PlayoutId"); + + migrationBuilder.RenameIndex( + "IX_PlayoutItem_PlayoutId", + table: "PlayoutItems", + newName: "IX_PlayoutItems_PlayoutId"); + + migrationBuilder.RenameIndex( + "IX_PlayoutItem_MediaItemId", + table: "PlayoutItems", + newName: "IX_PlayoutItems_MediaItemId"); + + migrationBuilder.RenameIndex( + "IX_Playout_ProgramScheduleId", + table: "Playouts", + newName: "IX_Playouts_ProgramScheduleId"); + + migrationBuilder.RenameIndex( + "IX_Playout_ChannelId", + table: "Playouts", + newName: "IX_Playouts_ChannelId"); + + migrationBuilder.RenameIndex( + "IX_Playout_Anchor_NextScheduleItemId", + table: "Playouts", + newName: "IX_Playouts_Anchor_NextScheduleItemId"); + + migrationBuilder.RenameIndex( + "IX_MediaItem_LibraryPathId", + table: "MediaItems", + newName: "IX_MediaItems_LibraryPathId"); + + migrationBuilder.RenameIndex( + "IX_MediaCollection_Name", + table: "MediaCollections", + newName: "IX_MediaCollections_Name"); + + migrationBuilder.RenameIndex( + "IX_FFmpegProfile_ResolutionId", + table: "FFmpegProfiles", + newName: "IX_FFmpegProfiles_ResolutionId"); + + migrationBuilder.RenameIndex( + "IX_ConfigElement_Key", + table: "ConfigElements", + newName: "IX_ConfigElements_Key"); + + migrationBuilder.RenameIndex( + "IX_Channel_Number", + table: "Channels", + newName: "IX_Channels_Number"); + + migrationBuilder.RenameIndex( + "IX_Channel_FFmpegProfileId", + table: "Channels", + newName: "IX_Channels_FFmpegProfileId"); + + migrationBuilder.AddColumn( + "MovieId1", + "MovieMetadata", + "INTEGER", + nullable: true); + + migrationBuilder.AddPrimaryKey( + "PK_TelevisionShows", + "TelevisionShows", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_TelevisionSeasons", + "TelevisionSeasons", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_TelevisionEpisodes", + "TelevisionEpisodes", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_SimpleMediaCollectionShows", + "SimpleMediaCollectionShows", + new[] { "SimpleMediaCollectionsId", "TelevisionShowsId" }); + + migrationBuilder.AddPrimaryKey( + "PK_SimpleMediaCollectionSeasons", + "SimpleMediaCollectionSeasons", + new[] { "SimpleMediaCollectionsId", "TelevisionSeasonsId" }); + + migrationBuilder.AddPrimaryKey( + "PK_SimpleMediaCollectionMovies", + "SimpleMediaCollectionMovies", + new[] { "MoviesId", "SimpleMediaCollectionsId" }); + + migrationBuilder.AddPrimaryKey( + "PK_SimpleMediaCollectionEpisodes", + "SimpleMediaCollectionEpisodes", + new[] { "SimpleMediaCollectionsId", "TelevisionEpisodesId" }); + + migrationBuilder.AddPrimaryKey( + "PK_SimpleMediaCollections", + "SimpleMediaCollections", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_Resolutions", + "Resolutions", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ProgramScheduleOneItems", + "ProgramScheduleOneItems", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ProgramScheduleMultipleItems", + "ProgramScheduleMultipleItems", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ProgramScheduleItems", + "ProgramScheduleItems", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ProgramScheduleFloodItems", + "ProgramScheduleFloodItems", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ProgramScheduleDurationItems", + "ProgramScheduleDurationItems", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ProgramSchedules", + "ProgramSchedules", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_PlexMovies", + "PlexMovies", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_PlayoutProgramScheduleItemAnchors", + "PlayoutProgramScheduleItemAnchors", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_PlayoutItems", + "PlayoutItems", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_Playouts", + "Playouts", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_Movies", + "Movies", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_MediaSources", + "MediaSources", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_MediaItems", + "MediaItems", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_MediaCollections", + "MediaCollections", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_FFmpegProfiles", + "FFmpegProfiles", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_ConfigElements", + "ConfigElements", + "Id"); + + migrationBuilder.AddPrimaryKey( + "PK_Channels", + "Channels", + "Id"); + + migrationBuilder.CreateIndex( + "IX_MovieMetadata_MovieId1", + "MovieMetadata", + "MovieId1"); + + migrationBuilder.AddForeignKey( + "FK_Channels_FFmpegProfiles_FFmpegProfileId", + "Channels", + "FFmpegProfileId", + "FFmpegProfiles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_FFmpegProfiles_Resolutions_ResolutionId", + "FFmpegProfiles", + "ResolutionId", + "Resolutions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_Library_MediaSources_MediaSourceId", + "Library", + "MediaSourceId", + "MediaSources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_LocalMediaSource_MediaSources_Id", + "LocalMediaSource", + "Id", + "MediaSources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_MediaItems_LibraryPath_LibraryPathId", + "MediaItems", + "LibraryPathId", + "LibraryPath", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_MovieMetadata_Movies_MovieId", + "MovieMetadata", + "MovieId", + "Movies", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_MovieMetadata_Movies_MovieId1", + "MovieMetadata", + "MovieId1", + "Movies", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_Movies_MediaItems_Id", + "Movies", + "Id", + "MediaItems", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlayoutItems_MediaItems_MediaItemId", + "PlayoutItems", + "MediaItemId", + "MediaItems", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlayoutItems_Playouts_PlayoutId", + "PlayoutItems", + "PlayoutId", + "Playouts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlayoutProgramScheduleItemAnchors_Playouts_PlayoutId", + "PlayoutProgramScheduleItemAnchors", + "PlayoutId", + "Playouts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlayoutProgramScheduleItemAnchors_ProgramSchedules_ProgramScheduleId", + "PlayoutProgramScheduleItemAnchors", + "ProgramScheduleId", + "ProgramSchedules", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_Playouts_Channels_ChannelId", + "Playouts", + "ChannelId", + "Channels", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_Playouts_ProgramScheduleItems_Anchor_NextScheduleItemId", + "Playouts", + "Anchor_NextScheduleItemId", + "ProgramScheduleItems", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_Playouts_ProgramSchedules_ProgramScheduleId", + "Playouts", + "ProgramScheduleId", + "ProgramSchedules", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlexMediaSource_MediaSources_Id", + "PlexMediaSource", + "Id", + "MediaSources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlexMovies_Movies_Id", + "PlexMovies", + "Id", + "Movies", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlexMovies_PlexMediaItemPart_PartId", + "PlexMovies", + "PartId", + "PlexMediaItemPart", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleDurationItems_ProgramScheduleItems_Id", + "ProgramScheduleDurationItems", + "Id", + "ProgramScheduleItems", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleFloodItems_ProgramScheduleItems_Id", + "ProgramScheduleFloodItems", + "Id", + "ProgramScheduleItems", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItems_MediaCollections_MediaCollectionId", + "ProgramScheduleItems", + "MediaCollectionId", + "MediaCollections", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItems_ProgramSchedules_ProgramScheduleId", + "ProgramScheduleItems", + "ProgramScheduleId", + "ProgramSchedules", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItems_TelevisionSeasons_TelevisionSeasonId", + "ProgramScheduleItems", + "TelevisionSeasonId", + "TelevisionSeasons", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItems_TelevisionShows_TelevisionShowId", + "ProgramScheduleItems", + "TelevisionShowId", + "TelevisionShows", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleMultipleItems_ProgramScheduleItems_Id", + "ProgramScheduleMultipleItems", + "Id", + "ProgramScheduleItems", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleOneItems_ProgramScheduleItems_Id", + "ProgramScheduleOneItems", + "Id", + "ProgramScheduleItems", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionEpisodes_SimpleMediaCollections_SimpleMediaCollectionsId", + "SimpleMediaCollectionEpisodes", + "SimpleMediaCollectionsId", + "SimpleMediaCollections", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionEpisodes_TelevisionEpisodes_TelevisionEpisodesId", + "SimpleMediaCollectionEpisodes", + "TelevisionEpisodesId", + "TelevisionEpisodes", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionMovies_Movies_MoviesId", + "SimpleMediaCollectionMovies", + "MoviesId", + "Movies", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionMovies_SimpleMediaCollections_SimpleMediaCollectionsId", + "SimpleMediaCollectionMovies", + "SimpleMediaCollectionsId", + "SimpleMediaCollections", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollections_MediaCollections_Id", + "SimpleMediaCollections", + "Id", + "MediaCollections", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionSeasons_SimpleMediaCollections_SimpleMediaCollectionsId", + "SimpleMediaCollectionSeasons", + "SimpleMediaCollectionsId", + "SimpleMediaCollections", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionSeasons_TelevisionSeasons_TelevisionSeasonsId", + "SimpleMediaCollectionSeasons", + "TelevisionSeasonsId", + "TelevisionSeasons", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionShows_SimpleMediaCollections_SimpleMediaCollectionsId", + "SimpleMediaCollectionShows", + "SimpleMediaCollectionsId", + "SimpleMediaCollections", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_SimpleMediaCollectionShows_TelevisionShows_TelevisionShowsId", + "SimpleMediaCollectionShows", + "TelevisionShowsId", + "TelevisionShows", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionEpisodeMetadata_TelevisionEpisodes_TelevisionEpisodeId", + "TelevisionEpisodeMetadata", + "TelevisionEpisodeId", + "TelevisionEpisodes", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionEpisodes_MediaItems_Id", + "TelevisionEpisodes", + "Id", + "MediaItems", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionEpisodes_TelevisionSeasons_SeasonId", + "TelevisionEpisodes", + "SeasonId", + "TelevisionSeasons", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionSeasons_TelevisionShows_TelevisionShowId", + "TelevisionSeasons", + "TelevisionShowId", + "TelevisionShows", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionShowMetadata_TelevisionShows_TelevisionShowId", + "TelevisionShowMetadata", + "TelevisionShowId", + "TelevisionShows", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_TelevisionShowSource_TelevisionShows_TelevisionShowId", + "TelevisionShowSource", + "TelevisionShowId", + "TelevisionShows", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226025109_TelevisionMediaItems.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210226025109_TelevisionMediaItems.Designer.cs new file mode 100644 index 00000000..d8248814 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226025109_TelevisionMediaItems.Designer.cs @@ -0,0 +1,1544 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210226025109_TelevisionMediaItems")] + partial class TelevisionMediaItems + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContentRating") + .HasColumnType("TEXT"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Premiered") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId") + .IsUnique(); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("ProgramScheduleId"); + + b.HasIndex("TelevisionSeasonId"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Aired") + .HasColumnType("TEXT"); + + b.Property("Episode") + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Season") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionEpisodeId") + .IsUnique(); + + b.ToTable("TelevisionEpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionSeason"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId") + .IsUnique(); + + b.ToTable("TelevisionShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionShowSource"); + + b.HasDiscriminator("Discriminator").HasValue("TelevisionShowSource"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.Property("MoviesId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("MoviesId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("SimpleMediaCollectionMovie"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodesId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionEpisodesId"); + + b.HasIndex("TelevisionEpisodesId"); + + b.ToTable("SimpleMediaCollectionEpisode"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionSeasonsId"); + + b.HasIndex("TelevisionSeasonsId"); + + b.ToTable("SimpleMediaCollectionSeason"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionShowsId"); + + b.HasIndex("TelevisionShowsId"); + + b.ToTable("SimpleMediaCollectionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("MetadataId") + .HasColumnType("INTEGER"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.TelevisionShowSource"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasIndex("MediaSourceId"); + + b.HasDiscriminator().HasValue("LocalTelevisionShowSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.MovieMetadata", "MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("EnumeratorState"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "TelevisionSeason") + .WithMany() + .HasForeignKey("TelevisionSeasonId"); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany() + .HasForeignKey("TelevisionShowId"); + + b.Navigation("MediaCollection"); + + b.Navigation("ProgramSchedule"); + + b.Navigation("TelevisionSeason"); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "TelevisionEpisode") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", "TelevisionEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Seasons") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionShowMetadata", "TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Sources") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany() + .HasForeignKey("MoviesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", null) + .WithMany() + .HasForeignKey("TelevisionEpisodesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", null) + .WithMany() + .HasForeignKey("TelevisionSeasonsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", null) + .WithMany() + .HasForeignKey("TelevisionShowsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.LocalMediaSource", "MediaSource") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Navigation("Metadata"); + + b.Navigation("Seasons"); + + b.Navigation("Sources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226025109_TelevisionMediaItems.cs b/ErsatzTV.Infrastructure/Migrations/20210226025109_TelevisionMediaItems.cs new file mode 100644 index 00000000..9baa5fe4 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226025109_TelevisionMediaItems.cs @@ -0,0 +1,257 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class TelevisionMediaItems : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + "TelevisionEpisodeId", + "MediaItem", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + "TelevisionSeasonId", + "MediaItem", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + "TelevisionShowId", + "MediaItem", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + "Collection", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column("TEXT", nullable: true) + }, + constraints: table => { table.PrimaryKey("PK_Collection", x => x.Id); }); + + migrationBuilder.CreateTable( + "Show", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true) + }, + constraints: table => + { + table.PrimaryKey("PK_Show", x => x.Id); + table.ForeignKey( + "FK_Show_MediaItem_Id", + x => x.Id, + "MediaItem", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "CollectionItem", + table => new + { + CollectionId = table.Column("INTEGER", nullable: false), + MediaItemId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CollectionItem", x => new { x.CollectionId, x.MediaItemId }); + table.ForeignKey( + "FK_CollectionItem_Collection_CollectionId", + x => x.CollectionId, + "Collection", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_CollectionItem_MediaItem_MediaItemId", + x => x.MediaItemId, + "MediaItem", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "Season", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ShowId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Season", x => x.Id); + table.ForeignKey( + "FK_Season_MediaItem_Id", + x => x.Id, + "MediaItem", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_Season_Show_ShowId", + x => x.ShowId, + "Show", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "Episode", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + SeasonId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Episode", x => x.Id); + table.ForeignKey( + "FK_Episode_MediaItem_Id", + x => x.Id, + "MediaItem", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_Episode_Season_SeasonId", + x => x.SeasonId, + "Season", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + "IX_CollectionItem_MediaItemId", + "CollectionItem", + "MediaItemId"); + + migrationBuilder.CreateIndex( + "IX_Episode_SeasonId", + "Episode", + "SeasonId"); + + migrationBuilder.CreateIndex( + "IX_Season_ShowId", + "Season", + "ShowId"); + + // create show media items + migrationBuilder.Sql( + @"INSERT INTO MediaItem (TelevisionShowId, LibraryPathId) + SELECT distinct ts.Id, mi.LibraryPathId + FROM TelevisionShow ts + INNER JOIN TelevisionSeason tsn ON tsn.TelevisionShowId = ts.Id + INNER JOIN TelevisionEpisode te ON te.SeasonId = tsn.Id + INNER JOIN MediaItem mi on mi.Id = te.Id"); + + // create shows + migrationBuilder.Sql(@"INSERT INTO Show (Id) SELECT Id FROM MediaItem WHERE TelevisionShowId > 0"); + + // create season media items + migrationBuilder.Sql( + @"INSERT INTO MediaItem (TelevisionSeasonId, LibraryPathId) + SELECT distinct tsn.Id, mi.LibraryPathId + FROM TelevisionSeason tsn + INNER JOIN TelevisionEpisode te ON te.SeasonId = tsn.Id + INNER JOIN MediaItem mi on mi.Id = te.Id"); + + // create seasons + migrationBuilder.Sql( + @"INSERT INTO Season (Id, ShowId) + SELECT mi.Id, mi2.Id + FROM MediaItem mi + INNER JOIN TelevisionSeason tsn ON tsn.Id = mi.TelevisionSeasonId + INNER JOIN MediaItem mi2 ON mi2.TelevisionShowId = tsn.TelevisionShowId AND mi.LibraryPathId = mi2.LibraryPathId"); + + // mark episode media items + migrationBuilder.Sql( + @"UPDATE MediaItem SET TelevisionEpisodeId = Id + WHERE Id IN (SELECT Id FROM TelevisionEpisode)"); + + // create episodes + migrationBuilder.Sql( + @"INSERT INTO Episode (Id, SeasonId) + SELECT mi.Id, mi2.Id + FROM MediaItem mi + INNER JOIN TelevisionEpisode te on mi.Id = te.Id + INNER JOIN MediaItem mi2 ON mi2.TelevisionSeasonId = te.SeasonId AND mi.LibraryPathId = mi2.LibraryPathId"); + + // collections + migrationBuilder.Sql(@"INSERT INTO Collection (Name) SELECT Name FROM MediaCollection"); + + // collection movies + migrationBuilder.Sql( + @"INSERT INTO CollectionItem (CollectionId, MediaItemId) + SELECT c.Id, smcm.MoviesId + FROM Collection c + INNER JOIN MediaCollection mc on mc.Name = c.Name + INNER JOIN SimpleMediaCollectionMovie smcm ON smcm.SimpleMediaCollectionsId = mc.Id"); + + // collection shows + migrationBuilder.Sql( + @"INSERT INTO CollectionItem (CollectionId, MediaItemId) + SELECT c.Id, mi.Id + FROM Collection c + INNER JOIN MediaCollection mc on mc.Name = c.Name + INNER JOIN SimpleMediaCollectionShow smcs ON smcs.SimpleMediaCollectionsId = mc.Id + INNER JOIN MediaItem mi ON mi.TelevisionShowId = smcs.TelevisionShowsId"); + + // collection seasons + migrationBuilder.Sql( + @"INSERT INTO CollectionItem (CollectionId, MediaItemId) + SELECT c.Id, mi.Id + FROM Collection c + INNER JOIN MediaCollection mc on mc.Name = c.Name + INNER JOIN SimpleMediaCollectionSeason smcs ON smcs.SimpleMediaCollectionsId = mc.Id + INNER JOIN MediaItem mi ON mi.TelevisionSeasonId = smcs.TelevisionSeasonsId"); + + // collection episodes + migrationBuilder.Sql( + @"INSERT INTO CollectionItem (CollectionId, MediaItemId) + SELECT c.Id, mi.Id + FROM Collection c + INNER JOIN MediaCollection mc on mc.Name = c.Name + INNER JOIN SimpleMediaCollectionEpisode smce ON smce.SimpleMediaCollectionsId = mc.Id + INNER JOIN MediaItem mi ON mi.TelevisionEpisodeId = smce.TelevisionEpisodesId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + "CollectionItem"); + + migrationBuilder.DropTable( + "Episode"); + + migrationBuilder.DropTable( + "Collection"); + + migrationBuilder.DropTable( + "Season"); + + migrationBuilder.DropTable( + "Show"); + + migrationBuilder.DropColumn( + "TelevisionEpisodeId", + "MediaItem"); + + migrationBuilder.DropColumn( + "TelevisionSeasonId", + "MediaItem"); + + migrationBuilder.DropColumn( + "TelevisionShowId", + "MediaItem"); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226110438_ProgramSchedule_CollectionAndMediaItem.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210226110438_ProgramSchedule_CollectionAndMediaItem.Designer.cs new file mode 100644 index 00000000..63b7c255 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226110438_ProgramSchedule_CollectionAndMediaItem.Designer.cs @@ -0,0 +1,1588 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210226110438_ProgramSchedule_CollectionAndMediaItem")] + partial class ProgramSchedule_CollectionAndMediaItem + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContentRating") + .HasColumnType("TEXT"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Premiered") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId") + .IsUnique(); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("NewCollectionId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("NewCollectionId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.HasIndex("TelevisionSeasonId"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Aired") + .HasColumnType("TEXT"); + + b.Property("Episode") + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Season") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionEpisodeId") + .IsUnique(); + + b.ToTable("TelevisionEpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionSeason"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId") + .IsUnique(); + + b.ToTable("TelevisionShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionShowSource"); + + b.HasDiscriminator("Discriminator").HasValue("TelevisionShowSource"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.Property("MoviesId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("MoviesId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("SimpleMediaCollectionMovie"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodesId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionEpisodesId"); + + b.HasIndex("TelevisionEpisodesId"); + + b.ToTable("SimpleMediaCollectionEpisode"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionSeasonsId"); + + b.HasIndex("TelevisionSeasonsId"); + + b.ToTable("SimpleMediaCollectionSeason"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionShowsId"); + + b.HasIndex("TelevisionShowsId"); + + b.ToTable("SimpleMediaCollectionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("MetadataId") + .HasColumnType("INTEGER"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.TelevisionShowSource"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasIndex("MediaSourceId"); + + b.HasDiscriminator().HasValue("LocalTelevisionShowSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.MovieMetadata", "MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("NewCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "TelevisionSeason") + .WithMany() + .HasForeignKey("TelevisionSeasonId"); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany() + .HasForeignKey("TelevisionShowId"); + + b.Navigation("Collection"); + + b.Navigation("MediaCollection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + + b.Navigation("TelevisionSeason"); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "TelevisionEpisode") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", "TelevisionEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Seasons") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionShowMetadata", "TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Sources") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany() + .HasForeignKey("MoviesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", null) + .WithMany() + .HasForeignKey("TelevisionEpisodesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", null) + .WithMany() + .HasForeignKey("TelevisionSeasonsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", null) + .WithMany() + .HasForeignKey("TelevisionShowsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.LocalMediaSource", "MediaSource") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Navigation("Metadata"); + + b.Navigation("Seasons"); + + b.Navigation("Sources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226110438_ProgramSchedule_CollectionAndMediaItem.cs b/ErsatzTV.Infrastructure/Migrations/20210226110438_ProgramSchedule_CollectionAndMediaItem.cs new file mode 100644 index 00000000..41729c51 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226110438_ProgramSchedule_CollectionAndMediaItem.cs @@ -0,0 +1,137 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class ProgramSchedule_CollectionAndMediaItem : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + "CollectionId", + "ProgramScheduleItem", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "MediaItemId", + "ProgramScheduleItem", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "MediaItemId", + "PlayoutProgramScheduleAnchor", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "NewCollectionId", + "PlayoutProgramScheduleAnchor", + "INTEGER", + nullable: true); + + migrationBuilder.CreateIndex( + "IX_ProgramScheduleItem_CollectionId", + "ProgramScheduleItem", + "CollectionId"); + + migrationBuilder.CreateIndex( + "IX_ProgramScheduleItem_MediaItemId", + "ProgramScheduleItem", + "MediaItemId"); + + migrationBuilder.CreateIndex( + "IX_PlayoutProgramScheduleAnchor_MediaItemId", + "PlayoutProgramScheduleAnchor", + "MediaItemId"); + + migrationBuilder.CreateIndex( + "IX_PlayoutProgramScheduleAnchor_NewCollectionId", + "PlayoutProgramScheduleAnchor", + "NewCollectionId"); + + migrationBuilder.AddForeignKey( + "FK_PlayoutProgramScheduleAnchor_Collection_NewCollectionId", + "PlayoutProgramScheduleAnchor", + "NewCollectionId", + "Collection", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_PlayoutProgramScheduleAnchor_MediaItem_MediaItemId", + "PlayoutProgramScheduleAnchor", + "MediaItemId", + "MediaItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_Collection_CollectionId", + "ProgramScheduleItem", + "CollectionId", + "Collection", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_MediaItem_MediaItemId", + "ProgramScheduleItem", + "MediaItemId", + "MediaItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_PlayoutProgramScheduleAnchor_Collection_NewCollectionId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutProgramScheduleAnchor_MediaItem_MediaItemId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_Collection_CollectionId", + "ProgramScheduleItem"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_MediaItem_MediaItemId", + "ProgramScheduleItem"); + + migrationBuilder.DropIndex( + "IX_ProgramScheduleItem_CollectionId", + "ProgramScheduleItem"); + + migrationBuilder.DropIndex( + "IX_ProgramScheduleItem_MediaItemId", + "ProgramScheduleItem"); + + migrationBuilder.DropIndex( + "IX_PlayoutProgramScheduleAnchor_MediaItemId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropIndex( + "IX_PlayoutProgramScheduleAnchor_NewCollectionId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropColumn( + "CollectionId", + "ProgramScheduleItem"); + + migrationBuilder.DropColumn( + "MediaItemId", + "ProgramScheduleItem"); + + migrationBuilder.DropColumn( + "MediaItemId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropColumn( + "NewCollectionId", + "PlayoutProgramScheduleAnchor"); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226110531_ProgramSchedule_UpdateCollectionAndMediaItem.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210226110531_ProgramSchedule_UpdateCollectionAndMediaItem.Designer.cs new file mode 100644 index 00000000..f6fd5871 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226110531_ProgramSchedule_UpdateCollectionAndMediaItem.Designer.cs @@ -0,0 +1,1588 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210226110531_ProgramSchedule_UpdateCollectionAndMediaItem")] + partial class ProgramSchedule_UpdateCollectionAndMediaItem + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContentRating") + .HasColumnType("TEXT"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Premiered") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId") + .IsUnique(); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("NewCollectionId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("NewCollectionId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.HasIndex("TelevisionSeasonId"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Aired") + .HasColumnType("TEXT"); + + b.Property("Episode") + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Season") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionEpisodeId") + .IsUnique(); + + b.ToTable("TelevisionEpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionSeason"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId") + .IsUnique(); + + b.ToTable("TelevisionShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionShowSource"); + + b.HasDiscriminator("Discriminator").HasValue("TelevisionShowSource"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.Property("MoviesId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("MoviesId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("SimpleMediaCollectionMovie"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodesId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionEpisodesId"); + + b.HasIndex("TelevisionEpisodesId"); + + b.ToTable("SimpleMediaCollectionEpisode"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionSeasonsId"); + + b.HasIndex("TelevisionSeasonsId"); + + b.ToTable("SimpleMediaCollectionSeason"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionShowsId"); + + b.HasIndex("TelevisionShowsId"); + + b.ToTable("SimpleMediaCollectionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("MetadataId") + .HasColumnType("INTEGER"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.TelevisionShowSource"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasIndex("MediaSourceId"); + + b.HasDiscriminator().HasValue("LocalTelevisionShowSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.MovieMetadata", "MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("NewCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "TelevisionSeason") + .WithMany() + .HasForeignKey("TelevisionSeasonId"); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany() + .HasForeignKey("TelevisionShowId"); + + b.Navigation("Collection"); + + b.Navigation("MediaCollection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + + b.Navigation("TelevisionSeason"); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "TelevisionEpisode") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", "TelevisionEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Seasons") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionShowMetadata", "TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Sources") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany() + .HasForeignKey("MoviesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", null) + .WithMany() + .HasForeignKey("TelevisionEpisodesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", null) + .WithMany() + .HasForeignKey("TelevisionSeasonsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", null) + .WithMany() + .HasForeignKey("TelevisionShowsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.LocalMediaSource", "MediaSource") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Navigation("Metadata"); + + b.Navigation("Seasons"); + + b.Navigation("Sources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226110531_ProgramSchedule_UpdateCollectionAndMediaItem.cs b/ErsatzTV.Infrastructure/Migrations/20210226110531_ProgramSchedule_UpdateCollectionAndMediaItem.cs new file mode 100644 index 00000000..db187270 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226110531_ProgramSchedule_UpdateCollectionAndMediaItem.cs @@ -0,0 +1,50 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class ProgramSchedule_UpdateCollectionAndMediaItem : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + // update schedule items that reference collections + migrationBuilder.Sql( + @"UPDATE ProgramScheduleItem SET CollectionId = + (SELECT c.Id FROM Collection c INNER JOIN MediaCollection mc ON mc.Name = c.Name WHERE mc.Id = MediaCollectionId) + WHERE MediaCollectionId > 0"); + + // update schedule items that reference shows + migrationBuilder.Sql( + @"UPDATE ProgramScheduleItem SET MediaItemId = + (SELECT mi.Id FROM MediaItem mi WHERE mi.TelevisionShowId = ProgramScheduleItem.TelevisionShowId) + WHERE TelevisionShowId > 0"); + + // update schedule items that reference seasons + migrationBuilder.Sql( + @"UPDATE ProgramScheduleItem SET MediaItemId = + (SELECT mi.Id FROM MediaItem mi WHERE mi.TelevisionSeasonId = ProgramScheduleItem.TelevisionSeasonId) + WHERE TelevisionSeasonId > 0"); + + // update anchors that reference collections + migrationBuilder.Sql( + @"UPDATE PlayoutProgramScheduleAnchor SET NewCollectionId = + (SELECT c.Id FROM Collection c INNER JOIN MediaCollection mc on c.Name = mc.Name WHERE mc.Id = CollectionId) + WHERE CollectionType = 0"); + + // update anchors that reference shows + migrationBuilder.Sql( + @"UPDATE PlayoutProgramScheduleAnchor SET MediaItemId = + (SELECT mi.Id from MediaItem mi WHERE mi.TelevisionShowId = CollectionId) + WHERE CollectionType = 1"); + + // update anchors that reference seasons + migrationBuilder.Sql( + @"UPDATE PlayoutProgramScheduleAnchor SET MediaItemId = + (SELECT mi.Id FROM MediaItem mi WHERE mi.TelevisionSeasonId = CollectionId) + WHERE CollectionType = 2"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226114450_Add_VersionsAndMetadata.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210226114450_Add_VersionsAndMetadata.Designer.cs new file mode 100644 index 00000000..3d2329bb --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226114450_Add_VersionsAndMetadata.Designer.cs @@ -0,0 +1,1874 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210226114450_Add_VersionsAndMetadata")] + partial class Add_VersionsAndMetadata + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContentRating") + .HasColumnType("TEXT"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Premiered") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId") + .IsUnique(); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.NewMovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("NewMovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("NewCollectionId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("NewCollectionId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.HasIndex("TelevisionSeasonId"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Aired") + .HasColumnType("TEXT"); + + b.Property("Episode") + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Season") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionEpisodeId") + .IsUnique(); + + b.ToTable("TelevisionEpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionSeason"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId") + .IsUnique(); + + b.ToTable("TelevisionShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionShowSource"); + + b.HasDiscriminator("Discriminator").HasValue("TelevisionShowSource"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.Property("MoviesId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("MoviesId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("SimpleMediaCollectionMovie"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodesId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionEpisodesId"); + + b.HasIndex("TelevisionEpisodesId"); + + b.ToTable("SimpleMediaCollectionEpisode"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionSeasonsId"); + + b.HasIndex("TelevisionSeasonsId"); + + b.ToTable("SimpleMediaCollectionSeason"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionShowsId"); + + b.HasIndex("TelevisionShowsId"); + + b.ToTable("SimpleMediaCollectionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("MetadataId") + .HasColumnType("INTEGER"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.TelevisionShowSource"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasIndex("MediaSourceId"); + + b.HasDiscriminator().HasValue("LocalTelevisionShowSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.MovieMetadata", "MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.NewMovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("NewCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "TelevisionSeason") + .WithMany() + .HasForeignKey("TelevisionSeasonId"); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany() + .HasForeignKey("TelevisionShowId"); + + b.Navigation("Collection"); + + b.Navigation("MediaCollection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + + b.Navigation("TelevisionSeason"); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "TelevisionEpisode") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", "TelevisionEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Seasons") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionShowMetadata", "TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Sources") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany() + .HasForeignKey("MoviesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", null) + .WithMany() + .HasForeignKey("TelevisionEpisodesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", null) + .WithMany() + .HasForeignKey("TelevisionSeasonsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", null) + .WithMany() + .HasForeignKey("TelevisionShowsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.LocalMediaSource", "MediaSource") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Navigation("Metadata"); + + b.Navigation("Seasons"); + + b.Navigation("Sources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("Metadata"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226114450_Add_VersionsAndMetadata.cs b/ErsatzTV.Infrastructure/Migrations/20210226114450_Add_VersionsAndMetadata.cs new file mode 100644 index 00000000..35e060a3 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226114450_Add_VersionsAndMetadata.cs @@ -0,0 +1,223 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Add_VersionsAndMetadata : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + "SeasonNumber", + "Season", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + "EpisodeNumber", + "Episode", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + "EpisodeMetadata", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Outline = table.Column("TEXT", nullable: true), + Plot = table.Column("TEXT", nullable: true), + Tagline = table.Column("TEXT", nullable: true), + EpisodeId = table.Column("INTEGER", nullable: false), + MetadataKind = table.Column("INTEGER", nullable: false), + Title = table.Column("TEXT", nullable: true), + OriginalTitle = table.Column("TEXT", nullable: true), + SortTitle = table.Column("TEXT", nullable: true), + ReleaseDate = table.Column("TEXT", nullable: true), + DateAdded = table.Column("TEXT", nullable: false), + DateUpdated = table.Column("TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EpisodeMetadata", x => x.Id); + table.ForeignKey( + "FK_EpisodeMetadata_Episode_EpisodeId", + x => x.EpisodeId, + "Episode", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "MediaVersion", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column("TEXT", nullable: true), + Duration = table.Column("TEXT", nullable: false), + SampleAspectRatio = table.Column("TEXT", nullable: true), + DisplayAspectRatio = table.Column("TEXT", nullable: true), + VideoCodec = table.Column("TEXT", nullable: true), + AudioCodec = table.Column("TEXT", nullable: true), + IsInterlaced = table.Column("INTEGER", nullable: false), + Width = table.Column("INTEGER", nullable: false), + Height = table.Column("INTEGER", nullable: false), + EpisodeId = table.Column("INTEGER", nullable: true), + MovieId = table.Column("INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MediaVersion", x => x.Id); + table.ForeignKey( + "FK_MediaVersion_Episode_EpisodeId", + x => x.EpisodeId, + "Episode", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_MediaVersion_Movie_MovieId", + x => x.MovieId, + "Movie", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "NewMovieMetadata", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Outline = table.Column("TEXT", nullable: true), + Plot = table.Column("TEXT", nullable: true), + Tagline = table.Column("TEXT", nullable: true), + MovieId = table.Column("INTEGER", nullable: false), + MetadataKind = table.Column("INTEGER", nullable: false), + Title = table.Column("TEXT", nullable: true), + OriginalTitle = table.Column("TEXT", nullable: true), + SortTitle = table.Column("TEXT", nullable: true), + ReleaseDate = table.Column("TEXT", nullable: true), + DateAdded = table.Column("TEXT", nullable: false), + DateUpdated = table.Column("TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_NewMovieMetadata", x => x.Id); + table.ForeignKey( + "FK_NewMovieMetadata_Movie_MovieId", + x => x.MovieId, + "Movie", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "ShowMetadata", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Outline = table.Column("TEXT", nullable: true), + Plot = table.Column("TEXT", nullable: true), + Tagline = table.Column("TEXT", nullable: true), + ShowId = table.Column("INTEGER", nullable: false), + MetadataKind = table.Column("INTEGER", nullable: false), + Title = table.Column("TEXT", nullable: true), + OriginalTitle = table.Column("TEXT", nullable: true), + SortTitle = table.Column("TEXT", nullable: true), + ReleaseDate = table.Column("TEXT", nullable: true), + DateAdded = table.Column("TEXT", nullable: false), + DateUpdated = table.Column("TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ShowMetadata", x => x.Id); + table.ForeignKey( + "FK_ShowMetadata_Show_ShowId", + x => x.ShowId, + "Show", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "MediaFile", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Path = table.Column("TEXT", nullable: true), + MediaVersionId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MediaFile", x => x.Id); + table.ForeignKey( + "FK_MediaFile_MediaVersion_MediaVersionId", + x => x.MediaVersionId, + "MediaVersion", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + "IX_EpisodeMetadata_EpisodeId", + "EpisodeMetadata", + "EpisodeId"); + + migrationBuilder.CreateIndex( + "IX_MediaFile_MediaVersionId", + "MediaFile", + "MediaVersionId"); + + migrationBuilder.CreateIndex( + "IX_MediaVersion_EpisodeId", + "MediaVersion", + "EpisodeId"); + + migrationBuilder.CreateIndex( + "IX_MediaVersion_MovieId", + "MediaVersion", + "MovieId"); + + migrationBuilder.CreateIndex( + "IX_NewMovieMetadata_MovieId", + "NewMovieMetadata", + "MovieId"); + + migrationBuilder.CreateIndex( + "IX_ShowMetadata_ShowId", + "ShowMetadata", + "ShowId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + "EpisodeMetadata"); + + migrationBuilder.DropTable( + "MediaFile"); + + migrationBuilder.DropTable( + "NewMovieMetadata"); + + migrationBuilder.DropTable( + "ShowMetadata"); + + migrationBuilder.DropTable( + "MediaVersion"); + + migrationBuilder.DropColumn( + "SeasonNumber", + "Season"); + + migrationBuilder.DropColumn( + "EpisodeNumber", + "Episode"); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226114922_Update_SeasonAndEpisodeNumbers.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210226114922_Update_SeasonAndEpisodeNumbers.Designer.cs new file mode 100644 index 00000000..7fe8d756 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226114922_Update_SeasonAndEpisodeNumbers.Designer.cs @@ -0,0 +1,1874 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210226114922_Update_SeasonAndEpisodeNumbers")] + partial class Update_SeasonAndEpisodeNumbers + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContentRating") + .HasColumnType("TEXT"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Premiered") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId") + .IsUnique(); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.NewMovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("NewMovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("NewCollectionId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("NewCollectionId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.HasIndex("TelevisionSeasonId"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Aired") + .HasColumnType("TEXT"); + + b.Property("Episode") + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Season") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionEpisodeId") + .IsUnique(); + + b.ToTable("TelevisionEpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionSeason"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId") + .IsUnique(); + + b.ToTable("TelevisionShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionShowSource"); + + b.HasDiscriminator("Discriminator").HasValue("TelevisionShowSource"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.Property("MoviesId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("MoviesId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("SimpleMediaCollectionMovie"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodesId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionEpisodesId"); + + b.HasIndex("TelevisionEpisodesId"); + + b.ToTable("SimpleMediaCollectionEpisode"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionSeasonsId"); + + b.HasIndex("TelevisionSeasonsId"); + + b.ToTable("SimpleMediaCollectionSeason"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionShowsId"); + + b.HasIndex("TelevisionShowsId"); + + b.ToTable("SimpleMediaCollectionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("MetadataId") + .HasColumnType("INTEGER"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.TelevisionShowSource"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasIndex("MediaSourceId"); + + b.HasDiscriminator().HasValue("LocalTelevisionShowSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.MovieMetadata", "MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.NewMovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("NewCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "TelevisionSeason") + .WithMany() + .HasForeignKey("TelevisionSeasonId"); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany() + .HasForeignKey("TelevisionShowId"); + + b.Navigation("Collection"); + + b.Navigation("MediaCollection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + + b.Navigation("TelevisionSeason"); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "TelevisionEpisode") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", "TelevisionEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Seasons") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionShowMetadata", "TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Sources") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany() + .HasForeignKey("MoviesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", null) + .WithMany() + .HasForeignKey("TelevisionEpisodesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", null) + .WithMany() + .HasForeignKey("TelevisionSeasonsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", null) + .WithMany() + .HasForeignKey("TelevisionShowsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.LocalMediaSource", "MediaSource") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Navigation("Metadata"); + + b.Navigation("Seasons"); + + b.Navigation("Sources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("Metadata"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226114922_Update_SeasonAndEpisodeNumbers.cs b/ErsatzTV.Infrastructure/Migrations/20210226114922_Update_SeasonAndEpisodeNumbers.cs new file mode 100644 index 00000000..35ecb3a0 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226114922_Update_SeasonAndEpisodeNumbers.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Update_SeasonAndEpisodeNumbers : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql( + @"UPDATE Season SET SeasonNumber = + (SELECT Number FROM TelevisionSeason ts INNER JOIN MediaItem mi on mi.Id = Season.Id AND mi.TelevisionSeasonId = ts.Id) + WHERE SeasonNumber = 0"); + + migrationBuilder.Sql( + @"UPDATE Episode SET EpisodeNumber = + (SELECT Episode FROM TelevisionEpisodeMetadata tem INNER JOIN MediaItem mi ON mi.Id = Episode.Id AND mi.TelevisionEpisodeId = tem.TelevisionEpisodeId) + WHERE EpisodeNumber = 0"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226132335_Update_MovieMetadata.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210226132335_Update_MovieMetadata.Designer.cs new file mode 100644 index 00000000..f9e2a591 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226132335_Update_MovieMetadata.Designer.cs @@ -0,0 +1,1874 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210226132335_Update_MovieMetadata")] + partial class Update_MovieMetadata + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContentRating") + .HasColumnType("TEXT"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Premiered") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId") + .IsUnique(); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.NewMovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("NewMovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("NewCollectionId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("NewCollectionId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.HasIndex("TelevisionSeasonId"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Aired") + .HasColumnType("TEXT"); + + b.Property("Episode") + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Season") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionEpisodeId") + .IsUnique(); + + b.ToTable("TelevisionEpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionSeason"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId") + .IsUnique(); + + b.ToTable("TelevisionShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionShowSource"); + + b.HasDiscriminator("Discriminator").HasValue("TelevisionShowSource"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.Property("MoviesId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("MoviesId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("SimpleMediaCollectionMovie"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodesId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionEpisodesId"); + + b.HasIndex("TelevisionEpisodesId"); + + b.ToTable("SimpleMediaCollectionEpisode"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionSeasonsId"); + + b.HasIndex("TelevisionSeasonsId"); + + b.ToTable("SimpleMediaCollectionSeason"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionShowsId"); + + b.HasIndex("TelevisionShowsId"); + + b.ToTable("SimpleMediaCollectionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("MetadataId") + .HasColumnType("INTEGER"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.TelevisionShowSource"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasIndex("MediaSourceId"); + + b.HasDiscriminator().HasValue("LocalTelevisionShowSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.MovieMetadata", "MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.NewMovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("NewCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "TelevisionSeason") + .WithMany() + .HasForeignKey("TelevisionSeasonId"); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany() + .HasForeignKey("TelevisionShowId"); + + b.Navigation("Collection"); + + b.Navigation("MediaCollection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + + b.Navigation("TelevisionSeason"); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "TelevisionEpisode") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", "TelevisionEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Seasons") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionShowMetadata", "TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Sources") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany() + .HasForeignKey("MoviesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", null) + .WithMany() + .HasForeignKey("TelevisionEpisodesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", null) + .WithMany() + .HasForeignKey("TelevisionSeasonsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", null) + .WithMany() + .HasForeignKey("TelevisionShowsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.LocalMediaSource", "MediaSource") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Navigation("Metadata"); + + b.Navigation("Seasons"); + + b.Navigation("Sources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("Metadata"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226132335_Update_MovieMetadata.cs b/ErsatzTV.Infrastructure/Migrations/20210226132335_Update_MovieMetadata.cs new file mode 100644 index 00000000..be6eda80 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226132335_Update_MovieMetadata.cs @@ -0,0 +1,23 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Update_MovieMetadata : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + var now = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.FFFFFFF"); + + migrationBuilder.Sql( + @$"INSERT INTO NewMovieMetadata (Outline, Plot, Tagline, MovieId, MetadataKind, Title, OriginalTitle, SortTitle, ReleaseDate, DateAdded, DateUpdated) +SELECT mm.Outline, mm.Plot, mm.Tagline, m.Id, mm.Source, mm.Title, null, mm.SortTitle, mm.Premiered, '{now}', mm.LastWriteTime +FROM MovieMetadata mm +INNER JOIN Movie m ON mm.MovieId = m.Id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226150135_Update_TelevisionMetadata.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210226150135_Update_TelevisionMetadata.Designer.cs new file mode 100644 index 00000000..ac7ad1d1 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226150135_Update_TelevisionMetadata.Designer.cs @@ -0,0 +1,1874 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210226150135_Update_TelevisionMetadata")] + partial class Update_TelevisionMetadata + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContentRating") + .HasColumnType("TEXT"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Premiered") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId") + .IsUnique(); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.NewMovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("NewMovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("NewCollectionId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("NewCollectionId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.HasIndex("TelevisionSeasonId"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Aired") + .HasColumnType("TEXT"); + + b.Property("Episode") + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Season") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionEpisodeId") + .IsUnique(); + + b.ToTable("TelevisionEpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionSeason"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId") + .IsUnique(); + + b.ToTable("TelevisionShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionShowSource"); + + b.HasDiscriminator("Discriminator").HasValue("TelevisionShowSource"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.Property("MoviesId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("MoviesId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("SimpleMediaCollectionMovie"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodesId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionEpisodesId"); + + b.HasIndex("TelevisionEpisodesId"); + + b.ToTable("SimpleMediaCollectionEpisode"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionSeasonsId"); + + b.HasIndex("TelevisionSeasonsId"); + + b.ToTable("SimpleMediaCollectionSeason"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionShowsId"); + + b.HasIndex("TelevisionShowsId"); + + b.ToTable("SimpleMediaCollectionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("MetadataId") + .HasColumnType("INTEGER"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.TelevisionShowSource"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasIndex("MediaSourceId"); + + b.HasDiscriminator().HasValue("LocalTelevisionShowSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.MovieMetadata", "MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.NewMovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("NewCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "TelevisionSeason") + .WithMany() + .HasForeignKey("TelevisionSeasonId"); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany() + .HasForeignKey("TelevisionShowId"); + + b.Navigation("Collection"); + + b.Navigation("MediaCollection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + + b.Navigation("TelevisionSeason"); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "TelevisionEpisode") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", "TelevisionEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Seasons") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionShowMetadata", "TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Sources") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany() + .HasForeignKey("MoviesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", null) + .WithMany() + .HasForeignKey("TelevisionEpisodesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", null) + .WithMany() + .HasForeignKey("TelevisionSeasonsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", null) + .WithMany() + .HasForeignKey("TelevisionShowsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.LocalMediaSource", "MediaSource") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Navigation("Metadata"); + + b.Navigation("Seasons"); + + b.Navigation("Sources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("Metadata"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226150135_Update_TelevisionMetadata.cs b/ErsatzTV.Infrastructure/Migrations/20210226150135_Update_TelevisionMetadata.cs new file mode 100644 index 00000000..9e5d4166 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226150135_Update_TelevisionMetadata.cs @@ -0,0 +1,29 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Update_TelevisionMetadata : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + var now = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.FFFFFFF"); + + migrationBuilder.Sql( + $@"INSERT INTO ShowMetadata (Outline, Plot, Tagline, ShowId, MetadataKind, Title, OriginalTitle, SortTitle, ReleaseDate, DateAdded, DateUpdated) +SELECT null, tsm.Plot, null, m.Id, tsm.Source, tsm.Title, null, tsm.SortTitle, tsm.Year || '-01-01 00:00:00', '{now}', '0001-01-01 00:00:00' +FROM TelevisionShowMetadata tsm +INNER JOIN MediaItem m ON tsm.TelevisionShowId = m.TelevisionShowId"); + + migrationBuilder.Sql( + $@"INSERT INTO EpisodeMetadata (Outline, Plot, Tagline, EpisodeId, MetadataKind, Title, OriginalTitle, SortTitle, ReleaseDate, DateAdded, DateUpdated) +SELECT null, tem.Plot, null, m.Id, tem.Source, tem.Title, null, tem.SortTitle, tem.Aired, '{now}', IFNULL(tem.LastWriteTime, '0001-01-01 00:00:00') +FROM TelevisionEpisodeMetadata tem +INNER JOIN MediaItem m ON tem.TelevisionEpisodeId = m.TelevisionEpisodeId;"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226154155_Remove_OldMovieMetadata.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210226154155_Remove_OldMovieMetadata.Designer.cs new file mode 100644 index 00000000..950279f7 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226154155_Remove_OldMovieMetadata.Designer.cs @@ -0,0 +1,1811 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210226154155_Remove_OldMovieMetadata")] + partial class Remove_OldMovieMetadata + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.NewMovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("NewMovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("NewCollectionId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("NewCollectionId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.HasIndex("TelevisionSeasonId"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Aired") + .HasColumnType("TEXT"); + + b.Property("Episode") + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Season") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionEpisodeId") + .IsUnique(); + + b.ToTable("TelevisionEpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionSeason"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId") + .IsUnique(); + + b.ToTable("TelevisionShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TelevisionShowId"); + + b.ToTable("TelevisionShowSource"); + + b.HasDiscriminator("Discriminator").HasValue("TelevisionShowSource"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.Property("MoviesId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("MoviesId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("SimpleMediaCollectionMovie"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionEpisodesId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionEpisodesId"); + + b.HasIndex("TelevisionEpisodesId"); + + b.ToTable("SimpleMediaCollectionEpisode"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionSeasonsId"); + + b.HasIndex("TelevisionSeasonsId"); + + b.ToTable("SimpleMediaCollectionSeason"); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowsId") + .HasColumnType("INTEGER"); + + b.HasKey("SimpleMediaCollectionsId", "TelevisionShowsId"); + + b.HasIndex("TelevisionShowsId"); + + b.ToTable("SimpleMediaCollectionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.TelevisionShowSource"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasIndex("MediaSourceId"); + + b.HasDiscriminator().HasValue("LocalTelevisionShowSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.NewMovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("NewCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "TelevisionSeason") + .WithMany() + .HasForeignKey("TelevisionSeasonId"); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany() + .HasForeignKey("TelevisionShowId"); + + b.Navigation("Collection"); + + b.Navigation("MediaCollection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + + b.Navigation("TelevisionSeason"); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "TelevisionEpisode") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", "TelevisionEpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionEpisode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Seasons") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithOne("Metadata") + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionShowMetadata", "TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") + .WithMany("Sources") + .HasForeignKey("TelevisionShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TelevisionShow"); + }); + + modelBuilder.Entity("MovieSimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany() + .HasForeignKey("MoviesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", null) + .WithMany() + .HasForeignKey("TelevisionEpisodesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionSeason", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", null) + .WithMany() + .HasForeignKey("TelevisionSeasonsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SimpleMediaCollectionTelevisionShow", b => + { + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", null) + .WithMany() + .HasForeignKey("TelevisionShowsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalTelevisionShowSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.LocalMediaSource", "MediaSource") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionShow", b => + { + b.Navigation("Metadata"); + + b.Navigation("Seasons"); + + b.Navigation("Sources"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226154155_Remove_OldMovieMetadata.cs b/ErsatzTV.Infrastructure/Migrations/20210226154155_Remove_OldMovieMetadata.cs new file mode 100644 index 00000000..21ca199e --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226154155_Remove_OldMovieMetadata.cs @@ -0,0 +1,63 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Remove_OldMovieMetadata : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + "MovieMetadata"); + + migrationBuilder.DropColumn( + "MetadataId", + "Movie"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + "MetadataId", + "Movie", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + "MovieMetadata", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ContentRating = table.Column("TEXT", nullable: true), + LastWriteTime = table.Column("TEXT", nullable: true), + MovieId = table.Column("INTEGER", nullable: false), + Outline = table.Column("TEXT", nullable: true), + Plot = table.Column("TEXT", nullable: true), + Premiered = table.Column("TEXT", nullable: true), + SortTitle = table.Column("TEXT", nullable: true), + Source = table.Column("INTEGER", nullable: false), + Tagline = table.Column("TEXT", nullable: true), + Title = table.Column("TEXT", nullable: true), + Year = table.Column("INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MovieMetadata", x => x.Id); + table.ForeignKey( + "FK_MovieMetadata_Movie_MovieId", + x => x.MovieId, + "Movie", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + "IX_MovieMetadata_MovieId", + "MovieMetadata", + "MovieId", + unique: true); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226183532_Remove_OldTelevisionEntities.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210226183532_Remove_OldTelevisionEntities.Designer.cs new file mode 100644 index 00000000..a0a5ed48 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226183532_Remove_OldTelevisionEntities.Designer.cs @@ -0,0 +1,1361 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210226183532_Remove_OldTelevisionEntities")] + partial class Remove_OldTelevisionEntities + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("NewMovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("NewCollectionId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("NewCollectionId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("NewCollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226183532_Remove_OldTelevisionEntities.cs b/ErsatzTV.Infrastructure/Migrations/20210226183532_Remove_OldTelevisionEntities.cs new file mode 100644 index 00000000..44e1102b --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226183532_Remove_OldTelevisionEntities.cs @@ -0,0 +1,489 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Remove_OldTelevisionEntities : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql( + @"UPDATE ProgramScheduleItem SET + MediaCollectionId = null, + TelevisionSeasonId = null, + TelevisionShowId = null"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_MediaCollection_MediaCollectionId", + "ProgramScheduleItem"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_TelevisionSeason_TelevisionSeasonId", + "ProgramScheduleItem"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_TelevisionShow_TelevisionShowId", + "ProgramScheduleItem"); + + migrationBuilder.DropTable( + "SimpleMediaCollectionEpisode"); + + migrationBuilder.DropTable( + "SimpleMediaCollectionMovie"); + + migrationBuilder.DropTable( + "SimpleMediaCollectionSeason"); + + migrationBuilder.DropTable( + "SimpleMediaCollectionShow"); + + migrationBuilder.DropTable( + "TelevisionEpisodeMetadata"); + + migrationBuilder.DropTable( + "TelevisionShowMetadata"); + + migrationBuilder.DropTable( + "TelevisionShowSource"); + + migrationBuilder.DropTable( + "SimpleMediaCollection"); + + migrationBuilder.DropTable( + "TelevisionEpisode"); + + migrationBuilder.DropTable( + "MediaCollection"); + + migrationBuilder.DropTable( + "TelevisionSeason"); + + migrationBuilder.DropTable( + "TelevisionShow"); + + migrationBuilder.DropIndex( + "IX_ProgramScheduleItem_MediaCollectionId", + "ProgramScheduleItem"); + + migrationBuilder.DropIndex( + "IX_ProgramScheduleItem_TelevisionSeasonId", + "ProgramScheduleItem"); + + migrationBuilder.DropIndex( + "IX_ProgramScheduleItem_TelevisionShowId", + "ProgramScheduleItem"); + + migrationBuilder.DropColumn( + "MediaCollectionId", + "ProgramScheduleItem"); + + migrationBuilder.DropColumn( + "TelevisionSeasonId", + "ProgramScheduleItem"); + + migrationBuilder.DropColumn( + "TelevisionShowId", + "ProgramScheduleItem"); + + migrationBuilder.DropColumn( + "CollectionId", + "PlayoutProgramScheduleAnchor"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + "MediaCollectionId", + "ProgramScheduleItem", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "TelevisionSeasonId", + "ProgramScheduleItem", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "TelevisionShowId", + "ProgramScheduleItem", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "CollectionId", + "PlayoutProgramScheduleAnchor", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + "MediaCollection", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column("TEXT", nullable: true) + }, + constraints: table => { table.PrimaryKey("PK_MediaCollection", x => x.Id); }); + + migrationBuilder.CreateTable( + "TelevisionShow", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Poster = table.Column("TEXT", nullable: true), + PosterLastWriteTime = table.Column("TEXT", nullable: true) + }, + constraints: table => { table.PrimaryKey("PK_TelevisionShow", x => x.Id); }); + + migrationBuilder.CreateTable( + "SimpleMediaCollection", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true) + }, + constraints: table => + { + table.PrimaryKey("PK_SimpleMediaCollection", x => x.Id); + table.ForeignKey( + "FK_SimpleMediaCollection_MediaCollection_Id", + x => x.Id, + "MediaCollection", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "TelevisionSeason", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Number = table.Column("INTEGER", nullable: false), + Path = table.Column("TEXT", nullable: true), + Poster = table.Column("TEXT", nullable: true), + PosterLastWriteTime = table.Column("TEXT", nullable: true), + TelevisionShowId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TelevisionSeason", x => x.Id); + table.ForeignKey( + "FK_TelevisionSeason_TelevisionShow_TelevisionShowId", + x => x.TelevisionShowId, + "TelevisionShow", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "TelevisionShowMetadata", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + LastWriteTime = table.Column("TEXT", nullable: true), + Plot = table.Column("TEXT", nullable: true), + SortTitle = table.Column("TEXT", nullable: true), + Source = table.Column("INTEGER", nullable: false), + TelevisionShowId = table.Column("INTEGER", nullable: false), + Title = table.Column("TEXT", nullable: true), + Year = table.Column("INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TelevisionShowMetadata", x => x.Id); + table.ForeignKey( + "FK_TelevisionShowMetadata_TelevisionShow_TelevisionShowId", + x => x.TelevisionShowId, + "TelevisionShow", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "TelevisionShowSource", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Discriminator = table.Column("TEXT", nullable: false), + TelevisionShowId = table.Column("INTEGER", nullable: false), + MediaSourceId = table.Column("INTEGER", nullable: true), + Path = table.Column("TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TelevisionShowSource", x => x.Id); + table.ForeignKey( + "FK_TelevisionShowSource_LocalMediaSource_MediaSourceId", + x => x.MediaSourceId, + "LocalMediaSource", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_TelevisionShowSource_TelevisionShow_TelevisionShowId", + x => x.TelevisionShowId, + "TelevisionShow", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "SimpleMediaCollectionMovie", + table => new + { + MoviesId = table.Column("INTEGER", nullable: false), + SimpleMediaCollectionsId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey( + "PK_SimpleMediaCollectionMovie", + x => new { x.MoviesId, x.SimpleMediaCollectionsId }); + table.ForeignKey( + "FK_SimpleMediaCollectionMovie_Movie_MoviesId", + x => x.MoviesId, + "Movie", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_SimpleMediaCollectionMovie_SimpleMediaCollection_SimpleMediaCollectionsId", + x => x.SimpleMediaCollectionsId, + "SimpleMediaCollection", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "SimpleMediaCollectionShow", + table => new + { + SimpleMediaCollectionsId = table.Column("INTEGER", nullable: false), + TelevisionShowsId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey( + "PK_SimpleMediaCollectionShow", + x => new { x.SimpleMediaCollectionsId, x.TelevisionShowsId }); + table.ForeignKey( + "FK_SimpleMediaCollectionShow_SimpleMediaCollection_SimpleMediaCollectionsId", + x => x.SimpleMediaCollectionsId, + "SimpleMediaCollection", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_SimpleMediaCollectionShow_TelevisionShow_TelevisionShowsId", + x => x.TelevisionShowsId, + "TelevisionShow", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "SimpleMediaCollectionSeason", + table => new + { + SimpleMediaCollectionsId = table.Column("INTEGER", nullable: false), + TelevisionSeasonsId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey( + "PK_SimpleMediaCollectionSeason", + x => new { x.SimpleMediaCollectionsId, x.TelevisionSeasonsId }); + table.ForeignKey( + "FK_SimpleMediaCollectionSeason_SimpleMediaCollection_SimpleMediaCollectionsId", + x => x.SimpleMediaCollectionsId, + "SimpleMediaCollection", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_SimpleMediaCollectionSeason_TelevisionSeason_TelevisionSeasonsId", + x => x.TelevisionSeasonsId, + "TelevisionSeason", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "TelevisionEpisode", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + SeasonId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TelevisionEpisode", x => x.Id); + table.ForeignKey( + "FK_TelevisionEpisode_MediaItem_Id", + x => x.Id, + "MediaItem", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_TelevisionEpisode_TelevisionSeason_SeasonId", + x => x.SeasonId, + "TelevisionSeason", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "SimpleMediaCollectionEpisode", + table => new + { + SimpleMediaCollectionsId = table.Column("INTEGER", nullable: false), + TelevisionEpisodesId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey( + "PK_SimpleMediaCollectionEpisode", + x => new { x.SimpleMediaCollectionsId, x.TelevisionEpisodesId }); + table.ForeignKey( + "FK_SimpleMediaCollectionEpisode_SimpleMediaCollection_SimpleMediaCollectionsId", + x => x.SimpleMediaCollectionsId, + "SimpleMediaCollection", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_SimpleMediaCollectionEpisode_TelevisionEpisode_TelevisionEpisodesId", + x => x.TelevisionEpisodesId, + "TelevisionEpisode", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "TelevisionEpisodeMetadata", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Aired = table.Column("TEXT", nullable: true), + Episode = table.Column("INTEGER", nullable: false), + LastWriteTime = table.Column("TEXT", nullable: true), + Plot = table.Column("TEXT", nullable: true), + Season = table.Column("INTEGER", nullable: false), + SortTitle = table.Column("TEXT", nullable: true), + Source = table.Column("INTEGER", nullable: false), + TelevisionEpisodeId = table.Column("INTEGER", nullable: false), + Title = table.Column("TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TelevisionEpisodeMetadata", x => x.Id); + table.ForeignKey( + "FK_TelevisionEpisodeMetadata_TelevisionEpisode_TelevisionEpisodeId", + x => x.TelevisionEpisodeId, + "TelevisionEpisode", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + "IX_ProgramScheduleItem_MediaCollectionId", + "ProgramScheduleItem", + "MediaCollectionId"); + + migrationBuilder.CreateIndex( + "IX_ProgramScheduleItem_TelevisionSeasonId", + "ProgramScheduleItem", + "TelevisionSeasonId"); + + migrationBuilder.CreateIndex( + "IX_ProgramScheduleItem_TelevisionShowId", + "ProgramScheduleItem", + "TelevisionShowId"); + + migrationBuilder.CreateIndex( + "IX_MediaCollection_Name", + "MediaCollection", + "Name", + unique: true); + + migrationBuilder.CreateIndex( + "IX_SimpleMediaCollectionEpisode_TelevisionEpisodesId", + "SimpleMediaCollectionEpisode", + "TelevisionEpisodesId"); + + migrationBuilder.CreateIndex( + "IX_SimpleMediaCollectionMovie_SimpleMediaCollectionsId", + "SimpleMediaCollectionMovie", + "SimpleMediaCollectionsId"); + + migrationBuilder.CreateIndex( + "IX_SimpleMediaCollectionSeason_TelevisionSeasonsId", + "SimpleMediaCollectionSeason", + "TelevisionSeasonsId"); + + migrationBuilder.CreateIndex( + "IX_SimpleMediaCollectionShow_TelevisionShowsId", + "SimpleMediaCollectionShow", + "TelevisionShowsId"); + + migrationBuilder.CreateIndex( + "IX_TelevisionEpisode_SeasonId", + "TelevisionEpisode", + "SeasonId"); + + migrationBuilder.CreateIndex( + "IX_TelevisionEpisodeMetadata_TelevisionEpisodeId", + "TelevisionEpisodeMetadata", + "TelevisionEpisodeId", + unique: true); + + migrationBuilder.CreateIndex( + "IX_TelevisionSeason_TelevisionShowId", + "TelevisionSeason", + "TelevisionShowId"); + + migrationBuilder.CreateIndex( + "IX_TelevisionShowMetadata_TelevisionShowId", + "TelevisionShowMetadata", + "TelevisionShowId", + unique: true); + + migrationBuilder.CreateIndex( + "IX_TelevisionShowSource_MediaSourceId", + "TelevisionShowSource", + "MediaSourceId"); + + migrationBuilder.CreateIndex( + "IX_TelevisionShowSource_TelevisionShowId", + "TelevisionShowSource", + "TelevisionShowId"); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_MediaCollection_MediaCollectionId", + "ProgramScheduleItem", + "MediaCollectionId", + "MediaCollection", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_TelevisionSeason_TelevisionSeasonId", + "ProgramScheduleItem", + "TelevisionSeasonId", + "TelevisionSeason", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_TelevisionShow_TelevisionShowId", + "ProgramScheduleItem", + "TelevisionShowId", + "TelevisionShow", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226201105_CleanUp_MovieMetadata.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210226201105_CleanUp_MovieMetadata.Designer.cs new file mode 100644 index 00000000..d3e76ac0 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226201105_CleanUp_MovieMetadata.Designer.cs @@ -0,0 +1,1361 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210226201105_CleanUp_MovieMetadata")] + partial class CleanUp_MovieMetadata + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210226201105_CleanUp_MovieMetadata.cs b/ErsatzTV.Infrastructure/Migrations/20210226201105_CleanUp_MovieMetadata.cs new file mode 100644 index 00000000..d7b9d3df --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210226201105_CleanUp_MovieMetadata.cs @@ -0,0 +1,117 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class CleanUp_MovieMetadata : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_NewMovieMetadata_Movie_MovieId", + "NewMovieMetadata"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutProgramScheduleAnchor_Collection_NewCollectionId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropPrimaryKey( + "PK_NewMovieMetadata", + "NewMovieMetadata"); + + migrationBuilder.RenameTable( + "NewMovieMetadata", + newName: "MovieMetadata"); + + migrationBuilder.RenameColumn( + "NewCollectionId", + "PlayoutProgramScheduleAnchor", + "CollectionId"); + + migrationBuilder.RenameIndex( + "IX_PlayoutProgramScheduleAnchor_NewCollectionId", + table: "PlayoutProgramScheduleAnchor", + newName: "IX_PlayoutProgramScheduleAnchor_CollectionId"); + + migrationBuilder.RenameIndex( + "IX_NewMovieMetadata_MovieId", + table: "MovieMetadata", + newName: "IX_MovieMetadata_MovieId"); + + migrationBuilder.AddPrimaryKey( + "PK_MovieMetadata", + "MovieMetadata", + "Id"); + + migrationBuilder.AddForeignKey( + "FK_MovieMetadata_Movie_MovieId", + "MovieMetadata", + "MovieId", + "Movie", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlayoutProgramScheduleAnchor_Collection_CollectionId", + "PlayoutProgramScheduleAnchor", + "CollectionId", + "Collection", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_MovieMetadata_Movie_MovieId", + "MovieMetadata"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutProgramScheduleAnchor_Collection_CollectionId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropPrimaryKey( + "PK_MovieMetadata", + "MovieMetadata"); + + migrationBuilder.RenameTable( + "MovieMetadata", + newName: "NewMovieMetadata"); + + migrationBuilder.RenameColumn( + "CollectionId", + "PlayoutProgramScheduleAnchor", + "NewCollectionId"); + + migrationBuilder.RenameIndex( + "IX_PlayoutProgramScheduleAnchor_CollectionId", + table: "PlayoutProgramScheduleAnchor", + newName: "IX_PlayoutProgramScheduleAnchor_NewCollectionId"); + + migrationBuilder.RenameIndex( + "IX_MovieMetadata_MovieId", + table: "NewMovieMetadata", + newName: "IX_NewMovieMetadata_MovieId"); + + migrationBuilder.AddPrimaryKey( + "PK_NewMovieMetadata", + "NewMovieMetadata", + "Id"); + + migrationBuilder.AddForeignKey( + "FK_NewMovieMetadata_Movie_MovieId", + "NewMovieMetadata", + "MovieId", + "Movie", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlayoutProgramScheduleAnchor_Collection_NewCollectionId", + "PlayoutProgramScheduleAnchor", + "NewCollectionId", + "Collection", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210227001213_Add_MediaItemPathIndex.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210227001213_Add_MediaItemPathIndex.Designer.cs new file mode 100644 index 00000000..1f9df705 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210227001213_Add_MediaItemPathIndex.Designer.cs @@ -0,0 +1,1364 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210227001213_Add_MediaItemPathIndex")] + partial class Add_MediaItemPathIndex + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("PosterLastWriteTime") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210227001213_Add_MediaItemPathIndex.cs b/ErsatzTV.Infrastructure/Migrations/20210227001213_Add_MediaItemPathIndex.cs new file mode 100644 index 00000000..a6482e6c --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210227001213_Add_MediaItemPathIndex.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Add_MediaItemPathIndex : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + // delete orphan movies, if any exist + migrationBuilder.Sql( + @"DELETE FROM MediaItem WHERE Id IN + (SELECT mi.Id FROM Library l + INNER JOIN LibraryPath lp ON l.Id = lp.LibraryId + INNER JOIN MediaItem mi ON lp.Id = mi.LibraryPathId + LEFT OUTER JOIN Movie m ON mi.Id = m.Id + WHERE l.MediaKind = 1 AND m.Id IS NULL)"); + + // delete orphan episodes, if any exist + migrationBuilder.Sql( + @"DELETE FROM MediaItem WHERE Id IN + (SELECT mi.Id FROM Library l + INNER JOIN LibraryPath lp ON l.Id = lp.LibraryId + INNER JOIN MediaItem mi ON lp.Id = mi.LibraryPathId + LEFT OUTER JOIN Show s ON mi.Id = s.Id + LEFT OUTER JOIN Season ssn ON mi.Id = ssn.Id + LEFT OUTER JOIN Episode e ON mi.Id = e.Id + WHERE l.MediaKind = 2 AND s.Id IS NULL AND ssn.Id IS NULL AND e.Id IS NULL)"); + + migrationBuilder.CreateIndex( + "IX_MediaItem_Path", + "MediaItem", + "Path", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) => + migrationBuilder.DropIndex( + "IX_MediaItem_Path", + "MediaItem"); + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210227020133_Add_Artwork.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210227020133_Add_Artwork.Designer.cs new file mode 100644 index 00000000..cd16b1a0 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210227020133_Add_Artwork.Designer.cs @@ -0,0 +1,1429 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210227020133_Add_Artwork")] + partial class Add_Artwork + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210227020133_Add_Artwork.cs b/ErsatzTV.Infrastructure/Migrations/20210227020133_Add_Artwork.cs new file mode 100644 index 00000000..55a458d1 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210227020133_Add_Artwork.cs @@ -0,0 +1,89 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Add_Artwork : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + "Poster", + "MediaItem"); + + migrationBuilder.DropColumn( + "PosterLastWriteTime", + "MediaItem"); + + migrationBuilder.CreateTable( + "Artwork", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Path = table.Column("TEXT", nullable: true), + ArtworkKind = table.Column("INTEGER", nullable: false), + DateAdded = table.Column("TEXT", nullable: false), + DateUpdated = table.Column("TEXT", nullable: false), + EpisodeMetadataId = table.Column("INTEGER", nullable: true), + MovieMetadataId = table.Column("INTEGER", nullable: true), + ShowMetadataId = table.Column("INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Artwork", x => x.Id); + table.ForeignKey( + "FK_Artwork_EpisodeMetadata_EpisodeMetadataId", + x => x.EpisodeMetadataId, + "EpisodeMetadata", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_Artwork_MovieMetadata_MovieMetadataId", + x => x.MovieMetadataId, + "MovieMetadata", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_Artwork_ShowMetadata_ShowMetadataId", + x => x.ShowMetadataId, + "ShowMetadata", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + "IX_Artwork_EpisodeMetadataId", + "Artwork", + "EpisodeMetadataId"); + + migrationBuilder.CreateIndex( + "IX_Artwork_MovieMetadataId", + "Artwork", + "MovieMetadataId"); + + migrationBuilder.CreateIndex( + "IX_Artwork_ShowMetadataId", + "Artwork", + "ShowMetadataId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + "Artwork"); + + migrationBuilder.AddColumn( + "Poster", + "MediaItem", + "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + "PosterLastWriteTime", + "MediaItem", + "TEXT", + nullable: true); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210227105739_Add_SeasonMetadata.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210227105739_Add_SeasonMetadata.Designer.cs new file mode 100644 index 00000000..1bb232c9 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210227105739_Add_SeasonMetadata.Designer.cs @@ -0,0 +1,1496 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210227105739_Add_SeasonMetadata")] + partial class Add_SeasonMetadata + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId"); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210227105739_Add_SeasonMetadata.cs b/ErsatzTV.Infrastructure/Migrations/20210227105739_Add_SeasonMetadata.cs new file mode 100644 index 00000000..da4aeb41 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210227105739_Add_SeasonMetadata.cs @@ -0,0 +1,80 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Add_SeasonMetadata : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + "SeasonMetadataId", + "Artwork", + "INTEGER", + nullable: true); + + migrationBuilder.CreateTable( + "SeasonMetadata", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Outline = table.Column("TEXT", nullable: true), + SeasonId = table.Column("INTEGER", nullable: false), + MetadataKind = table.Column("INTEGER", nullable: false), + Title = table.Column("TEXT", nullable: true), + OriginalTitle = table.Column("TEXT", nullable: true), + SortTitle = table.Column("TEXT", nullable: true), + ReleaseDate = table.Column("TEXT", nullable: true), + DateAdded = table.Column("TEXT", nullable: false), + DateUpdated = table.Column("TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SeasonMetadata", x => x.Id); + table.ForeignKey( + "FK_SeasonMetadata_Season_SeasonId", + x => x.SeasonId, + "Season", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + "IX_Artwork_SeasonMetadataId", + "Artwork", + "SeasonMetadataId"); + + migrationBuilder.CreateIndex( + "IX_SeasonMetadata_SeasonId", + "SeasonMetadata", + "SeasonId"); + + migrationBuilder.AddForeignKey( + "FK_Artwork_SeasonMetadata_SeasonMetadataId", + "Artwork", + "SeasonMetadataId", + "SeasonMetadata", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_Artwork_SeasonMetadata_SeasonMetadataId", + "Artwork"); + + migrationBuilder.DropTable( + "SeasonMetadata"); + + migrationBuilder.DropIndex( + "IX_Artwork_SeasonMetadataId", + "Artwork"); + + migrationBuilder.DropColumn( + "SeasonMetadataId", + "Artwork"); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210227134707_Add_MetadataYear.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210227134707_Add_MetadataYear.Designer.cs new file mode 100644 index 00000000..f8cea8d3 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210227134707_Add_MetadataYear.Designer.cs @@ -0,0 +1,1508 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210227134707_Add_MetadataYear")] + partial class Add_MetadataYear + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId"); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210227134707_Add_MetadataYear.cs b/ErsatzTV.Infrastructure/Migrations/20210227134707_Add_MetadataYear.cs new file mode 100644 index 00000000..d6797693 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210227134707_Add_MetadataYear.cs @@ -0,0 +1,53 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Add_MetadataYear : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + "Year", + "ShowMetadata", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "Year", + "SeasonMetadata", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "Year", + "MovieMetadata", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "Year", + "EpisodeMetadata", + "INTEGER", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + "Year", + "ShowMetadata"); + + migrationBuilder.DropColumn( + "Year", + "SeasonMetadata"); + + migrationBuilder.DropColumn( + "Year", + "MovieMetadata"); + + migrationBuilder.DropColumn( + "Year", + "EpisodeMetadata"); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210227134812_Reset_MetadataDateUpdated.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210227134812_Reset_MetadataDateUpdated.Designer.cs new file mode 100644 index 00000000..1199a886 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210227134812_Reset_MetadataDateUpdated.Designer.cs @@ -0,0 +1,1508 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210227134812_Reset_MetadataDateUpdated")] + partial class Reset_MetadataDateUpdated + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId"); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210227134812_Reset_MetadataDateUpdated.cs b/ErsatzTV.Infrastructure/Migrations/20210227134812_Reset_MetadataDateUpdated.cs new file mode 100644 index 00000000..09c801a6 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210227134812_Reset_MetadataDateUpdated.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Reset_MetadataDateUpdated : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(@"UPDATE MovieMetadata SET DateUpdated = '0001-01-01 00:00:00'"); + migrationBuilder.Sql(@"UPDATE ShowMetadata SET Year = substr(ReleaseDate, 1, 4)"); + migrationBuilder.Sql(@"UPDATE SeasonMetadata SET DateUpdated = '0001-01-01 00:00:00'"); + migrationBuilder.Sql(@"UPDATE EpisodeMetadata SET DateUpdated = '0001-01-01 00:00:00'"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228003644_Add_SeasonMetadataCascade.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210228003644_Add_SeasonMetadataCascade.Designer.cs new file mode 100644 index 00000000..40d7e28f --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228003644_Add_SeasonMetadataCascade.Designer.cs @@ -0,0 +1,1509 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210228003644_Add_SeasonMetadataCascade")] + partial class Add_SeasonMetadataCascade + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("IsInterlaced") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228003644_Add_SeasonMetadataCascade.cs b/ErsatzTV.Infrastructure/Migrations/20210228003644_Add_SeasonMetadataCascade.cs new file mode 100644 index 00000000..daf88d61 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228003644_Add_SeasonMetadataCascade.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Add_SeasonMetadataCascade : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_Artwork_SeasonMetadata_SeasonMetadataId", + "Artwork"); + + migrationBuilder.AddForeignKey( + "FK_Artwork_SeasonMetadata_SeasonMetadataId", + "Artwork", + "SeasonMetadataId", + "SeasonMetadata", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_Artwork_SeasonMetadata_SeasonMetadataId", + "Artwork"); + + migrationBuilder.AddForeignKey( + "FK_Artwork_SeasonMetadata_SeasonMetadataId", + "Artwork", + "SeasonMetadataId", + "SeasonMetadata", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228021437_Add_MediaVersionVideoScanKind.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210228021437_Add_MediaVersionVideoScanKind.Designer.cs new file mode 100644 index 00000000..e27d743c --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228021437_Add_MediaVersionVideoScanKind.Designer.cs @@ -0,0 +1,1509 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210228021437_Add_MediaVersionVideoScanKind")] + partial class Add_MediaVersionVideoScanKind + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("VideoScanKind") + .HasColumnType("INTEGER"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228021437_Add_MediaVersionVideoScanKind.cs b/ErsatzTV.Infrastructure/Migrations/20210228021437_Add_MediaVersionVideoScanKind.cs new file mode 100644 index 00000000..ed5ccb2a --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228021437_Add_MediaVersionVideoScanKind.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Add_MediaVersionVideoScanKind : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) => + migrationBuilder.RenameColumn( + "IsInterlaced", + "MediaVersion", + "VideoScanKind"); + + protected override void Down(MigrationBuilder migrationBuilder) => + migrationBuilder.RenameColumn( + "VideoScanKind", + "MediaVersion", + "IsInterlaced"); + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228021822_Update_MediaVersion.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210228021822_Update_MediaVersion.Designer.cs new file mode 100644 index 00000000..199ad0ff --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228021822_Update_MediaVersion.Designer.cs @@ -0,0 +1,1509 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210228021822_Update_MediaVersion")] + partial class Update_MediaVersion + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("TelevisionEpisodeId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionSeasonId") + .HasColumnType("INTEGER"); + + b.Property("TelevisionShowId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("VideoScanKind") + .HasColumnType("INTEGER"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaItemStatistics", "Statistics", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("LastWriteTime") + .HasColumnType("TEXT"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("VideoCodec") + .HasColumnType("TEXT"); + + b1.Property("VideoScanType") + .HasColumnType("INTEGER"); + + b1.Property("Width") + .HasColumnType("INTEGER"); + + b1.HasKey("MediaItemId"); + + b1.ToTable("MediaItem"); + + b1.WithOwner() + .HasForeignKey("MediaItemId"); + }); + + b.Navigation("LibraryPath"); + + b.Navigation("Statistics"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228021822_Update_MediaVersion.cs b/ErsatzTV.Infrastructure/Migrations/20210228021822_Update_MediaVersion.cs new file mode 100644 index 00000000..3bd108f2 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228021822_Update_MediaVersion.cs @@ -0,0 +1,42 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Update_MediaVersion : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + // movie versions + migrationBuilder.Sql( + @"INSERT INTO MediaVersion (Name, Duration, SampleAspectRatio, DisplayAspectRatio, VideoCodec, AudioCodec, VideoScanKind, Width, Height, EpisodeId, MovieId) + SELECT 'Main', mi.Statistics_Duration, mi.Statistics_SampleAspectRatio, mi.Statistics_DisplayAspectRatio, mi.Statistics_VideoCodec, mi.Statistics_AudioCodec, mi.Statistics_VideoScanType, mi.Statistics_Width, mi.Statistics_Height, null, m.Id + FROM MediaItem mi + INNER JOIN Movie m on m.Id = mi.Id"); + + // episode versions + migrationBuilder.Sql( + @"INSERT INTO MediaVersion (Name, Duration, SampleAspectRatio, DisplayAspectRatio, VideoCodec, AudioCodec, VideoScanKind, Width, Height, EpisodeId, MovieId) + SELECT 'Main', mi.Statistics_Duration, mi.Statistics_SampleAspectRatio, mi.Statistics_DisplayAspectRatio, mi.Statistics_VideoCodec, mi.Statistics_AudioCodec, mi.Statistics_VideoScanType, mi.Statistics_Width, mi.Statistics_Height, e.Id, null + FROM MediaItem mi + INNER JOIN Episode e on e.Id = mi.Id"); + + // movie files + migrationBuilder.Sql( + @"INSERT INTO MediaFile (Path, MediaVersionId) + SELECT mi.Path, mv.Id + FROM MediaItem mi + INNER JOIN MediaVersion mv ON mv.MovieId = mi.Id"); + + // episode files + migrationBuilder.Sql( + @"INSERT INTO MediaFile (Path, MediaVersionId) + SELECT mi.Path, mv.Id + FROM MediaItem mi + INNER JOIN MediaVersion mv ON mv.EpisodeId = mi.Id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228114241_CleanUp_MediaItemStatisticsAndPath.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210228114241_CleanUp_MediaItemStatisticsAndPath.Designer.cs new file mode 100644 index 00000000..1c8d7c23 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228114241_CleanUp_MediaItemStatisticsAndPath.Designer.cs @@ -0,0 +1,1455 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210228114241_CleanUp_MediaItemStatisticsAndPath")] + partial class CleanUp_MediaItemStatisticsAndPath + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("VideoScanKind") + .HasColumnType("INTEGER"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228114241_CleanUp_MediaItemStatisticsAndPath.cs b/ErsatzTV.Infrastructure/Migrations/20210228114241_CleanUp_MediaItemStatisticsAndPath.cs new file mode 100644 index 00000000..0ea1ae8f --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228114241_CleanUp_MediaItemStatisticsAndPath.cs @@ -0,0 +1,189 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class CleanUp_MediaItemStatisticsAndPath : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + "IX_MediaItem_Path", + "MediaItem"); + + migrationBuilder.DropColumn( + "LastWriteTime", + "MediaItem"); + + migrationBuilder.DropColumn( + "Path", + "MediaItem"); + + migrationBuilder.DropColumn( + "Statistics_AudioCodec", + "MediaItem"); + + migrationBuilder.DropColumn( + "Statistics_DisplayAspectRatio", + "MediaItem"); + + migrationBuilder.DropColumn( + "Statistics_Duration", + "MediaItem"); + + migrationBuilder.DropColumn( + "Statistics_Height", + "MediaItem"); + + migrationBuilder.DropColumn( + "Statistics_LastWriteTime", + "MediaItem"); + + migrationBuilder.DropColumn( + "Statistics_SampleAspectRatio", + "MediaItem"); + + migrationBuilder.DropColumn( + "Statistics_VideoCodec", + "MediaItem"); + + migrationBuilder.DropColumn( + "Statistics_VideoScanType", + "MediaItem"); + + migrationBuilder.DropColumn( + "Statistics_Width", + "MediaItem"); + + migrationBuilder.DropColumn( + "TelevisionEpisodeId", + "MediaItem"); + + migrationBuilder.DropColumn( + "TelevisionSeasonId", + "MediaItem"); + + migrationBuilder.DropColumn( + "TelevisionShowId", + "MediaItem"); + + migrationBuilder.AddColumn( + "DateAdded", + "MediaVersion", + "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + "DateUpdated", + "MediaVersion", + "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + "DateAdded", + "MediaVersion"); + + migrationBuilder.DropColumn( + "DateUpdated", + "MediaVersion"); + + migrationBuilder.AddColumn( + "LastWriteTime", + "MediaItem", + "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + "Path", + "MediaItem", + "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + "Statistics_AudioCodec", + "MediaItem", + "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + "Statistics_DisplayAspectRatio", + "MediaItem", + "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + "Statistics_Duration", + "MediaItem", + "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + "Statistics_Height", + "MediaItem", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "Statistics_LastWriteTime", + "MediaItem", + "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + "Statistics_SampleAspectRatio", + "MediaItem", + "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + "Statistics_VideoCodec", + "MediaItem", + "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + "Statistics_VideoScanType", + "MediaItem", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "Statistics_Width", + "MediaItem", + "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + "TelevisionEpisodeId", + "MediaItem", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + "TelevisionSeasonId", + "MediaItem", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + "TelevisionShowId", + "MediaItem", + "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateIndex( + "IX_MediaItem_Path", + "MediaItem", + "Path", + unique: true); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228114929_Update_MediaVersionDateAdded.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210228114929_Update_MediaVersionDateAdded.Designer.cs new file mode 100644 index 00000000..9e5b7e75 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228114929_Update_MediaVersionDateAdded.Designer.cs @@ -0,0 +1,1455 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210228114929_Update_MediaVersionDateAdded")] + partial class Update_MediaVersionDateAdded + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("VideoScanKind") + .HasColumnType("INTEGER"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId"); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId"); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228114929_Update_MediaVersionDateAdded.cs b/ErsatzTV.Infrastructure/Migrations/20210228114929_Update_MediaVersionDateAdded.cs new file mode 100644 index 00000000..2cf576ab --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228114929_Update_MediaVersionDateAdded.cs @@ -0,0 +1,18 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Update_MediaVersionDateAdded : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + var now = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.FFFFFFF"); + migrationBuilder.Sql($@"UPDATE MediaVersion SET DateAdded = '{now}'"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228134438_Add_PlayoutDeleteCascades.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210228134438_Add_PlayoutDeleteCascades.Designer.cs new file mode 100644 index 00000000..0320bcc2 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228134438_Add_PlayoutDeleteCascades.Designer.cs @@ -0,0 +1,1462 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210228134438_Add_PlayoutDeleteCascades")] + partial class Add_PlayoutDeleteCascades + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("VideoScanKind") + .HasColumnType("INTEGER"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228134438_Add_PlayoutDeleteCascades.cs b/ErsatzTV.Infrastructure/Migrations/20210228134438_Add_PlayoutDeleteCascades.cs new file mode 100644 index 00000000..206050d0 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228134438_Add_PlayoutDeleteCascades.cs @@ -0,0 +1,119 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Add_PlayoutDeleteCascades : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_PlayoutProgramScheduleAnchor_Collection_CollectionId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutProgramScheduleAnchor_MediaItem_MediaItemId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_Collection_CollectionId", + "ProgramScheduleItem"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_MediaItem_MediaItemId", + "ProgramScheduleItem"); + + migrationBuilder.CreateIndex( + "IX_MediaFile_Path", + "MediaFile", + "Path", + unique: true); + + migrationBuilder.AddForeignKey( + "FK_PlayoutProgramScheduleAnchor_Collection_CollectionId", + "PlayoutProgramScheduleAnchor", + "CollectionId", + "Collection", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_PlayoutProgramScheduleAnchor_MediaItem_MediaItemId", + "PlayoutProgramScheduleAnchor", + "MediaItemId", + "MediaItem", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_Collection_CollectionId", + "ProgramScheduleItem", + "CollectionId", + "Collection", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_MediaItem_MediaItemId", + "ProgramScheduleItem", + "MediaItemId", + "MediaItem", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_PlayoutProgramScheduleAnchor_Collection_CollectionId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropForeignKey( + "FK_PlayoutProgramScheduleAnchor_MediaItem_MediaItemId", + "PlayoutProgramScheduleAnchor"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_Collection_CollectionId", + "ProgramScheduleItem"); + + migrationBuilder.DropForeignKey( + "FK_ProgramScheduleItem_MediaItem_MediaItemId", + "ProgramScheduleItem"); + + migrationBuilder.DropIndex( + "IX_MediaFile_Path", + "MediaFile"); + + migrationBuilder.AddForeignKey( + "FK_PlayoutProgramScheduleAnchor_Collection_CollectionId", + "PlayoutProgramScheduleAnchor", + "CollectionId", + "Collection", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_PlayoutProgramScheduleAnchor_MediaItem_MediaItemId", + "PlayoutProgramScheduleAnchor", + "MediaItemId", + "MediaItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_Collection_CollectionId", + "ProgramScheduleItem", + "CollectionId", + "Collection", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + "FK_ProgramScheduleItem_MediaItem_MediaItemId", + "ProgramScheduleItem", + "MediaItemId", + "MediaItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228155457_Add_ChannelArtwork.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210228155457_Add_ChannelArtwork.Designer.cs new file mode 100644 index 00000000..a4a1fa4f --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228155457_Add_ChannelArtwork.Designer.cs @@ -0,0 +1,1474 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210228155457_Add_ChannelArtwork")] + partial class Add_ChannelArtwork + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("VideoScanKind") + .HasColumnType("INTEGER"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", null) + .WithMany("Artwork") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Artwork"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228155457_Add_ChannelArtwork.cs b/ErsatzTV.Infrastructure/Migrations/20210228155457_Add_ChannelArtwork.cs new file mode 100644 index 00000000..2031b2cd --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228155457_Add_ChannelArtwork.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Add_ChannelArtwork : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + "ChannelId", + "Artwork", + "INTEGER", + nullable: true); + + migrationBuilder.CreateIndex( + "IX_Artwork_ChannelId", + "Artwork", + "ChannelId"); + + migrationBuilder.AddForeignKey( + "FK_Artwork_Channel_ChannelId", + "Artwork", + "ChannelId", + "Channel", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_Artwork_Channel_ChannelId", + "Artwork"); + + migrationBuilder.DropIndex( + "IX_Artwork_ChannelId", + "Artwork"); + + migrationBuilder.DropColumn( + "ChannelId", + "Artwork"); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228155627_Update_ChannelArtwork.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210228155627_Update_ChannelArtwork.Designer.cs new file mode 100644 index 00000000..56b945c5 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228155627_Update_ChannelArtwork.Designer.cs @@ -0,0 +1,1474 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210228155627_Update_ChannelArtwork")] + partial class Update_ChannelArtwork + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Logo") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("VideoScanKind") + .HasColumnType("INTEGER"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", null) + .WithMany("Artwork") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Artwork"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228155627_Update_ChannelArtwork.cs b/ErsatzTV.Infrastructure/Migrations/20210228155627_Update_ChannelArtwork.cs new file mode 100644 index 00000000..387483ac --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228155627_Update_ChannelArtwork.cs @@ -0,0 +1,23 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Update_ChannelArtwork : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + var now = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.FFFFFFF"); + + migrationBuilder.Sql( + $@"INSERT INTO Artwork (ArtworkKind, ChannelId, DateAdded, DateUpdated, Path) + SELECT 2, c.Id, '{now}', '{now}', c.Logo + FROM Channel c + WHERE c.Logo is not null"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228163013_Remove_ChannelLogo.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210228163013_Remove_ChannelLogo.Designer.cs new file mode 100644 index 00000000..35981de7 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228163013_Remove_ChannelLogo.Designer.cs @@ -0,0 +1,1471 @@ +// +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + [Migration("20210228163013_Remove_ChannelLogo")] + partial class Remove_ChannelLogo + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") + .HasColumnType("INTEGER"); + + b.Property("MovieMetadataId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); + + b.Property("ShowMetadataId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("EpisodeMetadataId"); + + b.HasIndex("MovieMetadataId"); + + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FFmpegProfileId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("StreamingMode") + .HasColumnType("INTEGER"); + + b.Property("UniqueId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FFmpegProfileId"); + + b.HasIndex("Number") + .IsUnique(); + + b.ToTable("Channel"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("ConfigElement"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioBitrate") + .HasColumnType("INTEGER"); + + b.Property("AudioBufferSize") + .HasColumnType("INTEGER"); + + b.Property("AudioChannels") + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("AudioSampleRate") + .HasColumnType("INTEGER"); + + b.Property("AudioVolume") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizeAudio") + .HasColumnType("INTEGER"); + + b.Property("NormalizeAudioCodec") + .HasColumnType("INTEGER"); + + b.Property("NormalizeResolution") + .HasColumnType("INTEGER"); + + b.Property("NormalizeVideoCodec") + .HasColumnType("INTEGER"); + + b.Property("ResolutionId") + .HasColumnType("INTEGER"); + + b.Property("ThreadCount") + .HasColumnType("INTEGER"); + + b.Property("Transcode") + .HasColumnType("INTEGER"); + + b.Property("VideoBitrate") + .HasColumnType("INTEGER"); + + b.Property("VideoBufferSize") + .HasColumnType("INTEGER"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResolutionId"); + + b.ToTable("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaVersionId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("VideoScanKind") + .HasColumnType("INTEGER"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.ToTable("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("ProgramSchedulePlayoutType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Finish") + .HasColumnType("TEXT"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.ToTable("PlayoutItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("PlayoutId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleAnchor"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexConnection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("PlexMediaItemPart"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionPlaybackOrder") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("CollectionType") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("ProgramScheduleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ShowId"); + + b.ToTable("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.ToTable("LocalLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Library"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ShouldSyncItems") + .HasColumnType("INTEGER"); + + b.ToTable("PlexLibrary"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonId"); + + b.ToTable("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); + + b.ToTable("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.ToTable("LocalMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("OfflineTail") + .HasColumnType("INTEGER"); + + b.Property("PlayoutDuration") + .HasColumnType("TEXT"); + + b.ToTable("ProgramScheduleDurationItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleFloodItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.ToTable("ProgramScheduleMultipleItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); + + b.ToTable("ProgramScheduleOneItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") + .HasColumnType("INTEGER"); + + b.HasIndex("PartId"); + + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", null) + .WithMany("Artwork") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") + .WithMany() + .HasForeignKey("FFmpegProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FFmpegProfile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + { + b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") + .WithMany() + .HasForeignKey("ResolutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Resolution"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaVersion"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") + .WithMany("Playouts") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Playouts") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => + { + b1.Property("PlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("NextScheduleItemId") + .HasColumnType("INTEGER"); + + b1.Property("NextStart") + .HasColumnType("TEXT"); + + b1.HasKey("PlayoutId"); + + b1.HasIndex("NextScheduleItemId"); + + b1.ToTable("Playout"); + + b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") + .WithMany() + .HasForeignKey("NextScheduleItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b1.WithOwner() + .HasForeignKey("PlayoutId"); + + b1.Navigation("NextScheduleItem"); + }); + + b.Navigation("Anchor"); + + b.Navigation("Channel"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("Items") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") + .WithMany("ProgramScheduleAnchors") + .HasForeignKey("PlayoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany() + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + { + b1.Property("PlayoutProgramScheduleAnchorId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorId"); + + b1.ToTable("PlayoutProgramScheduleAnchor"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorId"); + }); + + b.Navigation("Collection"); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaItem"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlexMediaSource"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => + { + b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + { + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Artwork"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + { + b.Navigation("Items"); + + b.Navigation("ProgramScheduleAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => + { + b.Navigation("Items"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); + + b.Navigation("MediaVersions"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); + + b.Navigation("MovieMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210228163013_Remove_ChannelLogo.cs b/ErsatzTV.Infrastructure/Migrations/20210228163013_Remove_ChannelLogo.cs new file mode 100644 index 00000000..b8eb2313 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228163013_Remove_ChannelLogo.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Remove_ChannelLogo : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) => + migrationBuilder.DropColumn( + "Logo", + "Channel"); + + protected override void Down(MigrationBuilder migrationBuilder) => + migrationBuilder.AddColumn( + "Logo", + "Channel", + "TEXT", + nullable: true); + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs b/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs index c0b9613a..3bdb826a 100644 --- a/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs +++ b/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs @@ -1,14 +1,14 @@ // - using System; using ErsatzTV.Infrastructure.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace ErsatzTV.Infrastructure.Migrations { [DbContext(typeof(TvContext))] - internal class TvContextModelSnapshot : ModelSnapshot + partial class TvContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { @@ -16,60 +16,55 @@ namespace ErsatzTV.Infrastructure.Migrations modelBuilder .HasAnnotation("ProductVersion", "5.0.3"); - modelBuilder.Entity( - "ErsatzTV.Core.AggregateModels.GenericIntegerId", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.ToView("No table or view exists for GenericIntegerId"); - }); + b.Property("ArtworkKind") + .HasColumnType("INTEGER"); - modelBuilder.Entity( - "ErsatzTV.Core.AggregateModels.MediaCollectionSummary", - b => - { - b.Property("Id") + b.Property("ChannelId") .HasColumnType("INTEGER"); - b.Property("IsSimple") + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeMetadataId") .HasColumnType("INTEGER"); - b.Property("ItemCount") + b.Property("MovieMetadataId") .HasColumnType("INTEGER"); - b.Property("Name") + b.Property("Path") .HasColumnType("TEXT"); - b.ToView("No table or view exists for MediaCollectionSummary"); - }); + b.Property("SeasonMetadataId") + .HasColumnType("INTEGER"); - modelBuilder.Entity( - "ErsatzTV.Core.AggregateModels.MediaItemSummary", - b => - { - b.Property("MediaItemId") + b.Property("ShowMetadataId") .HasColumnType("INTEGER"); - b.Property("Poster") - .HasColumnType("TEXT"); + b.HasKey("Id"); - b.Property("SortTitle") - .HasColumnType("TEXT"); + b.HasIndex("ChannelId"); - b.Property("Subtitle") - .HasColumnType("TEXT"); + b.HasIndex("EpisodeMetadataId"); - b.Property("Title") - .HasColumnType("TEXT"); + b.HasIndex("MovieMetadataId"); - b.ToView("No table or view exists for MediaItemSummary"); + b.HasIndex("SeasonMetadataId"); + + b.HasIndex("ShowMetadataId"); + + b.ToTable("Artwork"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Channel", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -78,9 +73,6 @@ namespace ErsatzTV.Infrastructure.Migrations b.Property("FFmpegProfileId") .HasColumnType("INTEGER"); - b.Property("Logo") - .HasColumnType("TEXT"); - b.Property("Name") .HasColumnType("TEXT"); @@ -100,12 +92,39 @@ namespace ErsatzTV.Infrastructure.Migrations b.HasIndex("Number") .IsUnique(); - b.ToTable("Channels"); + b.ToTable("Channel"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ConfigElement", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.Property("CollectionId") + .HasColumnType("INTEGER"); + + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "MediaItemId"); + + b.HasIndex("MediaItemId"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -122,12 +141,59 @@ namespace ErsatzTV.Infrastructure.Migrations b.HasIndex("Key") .IsUnique(); - b.ToTable("ConfigElements"); + b.ToTable("ConfigElement"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.FFmpegProfile", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.ToTable("EpisodeMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -188,115 +254,189 @@ namespace ErsatzTV.Infrastructure.Migrations b.HasIndex("ResolutionId"); - b.ToTable("FFmpegProfiles"); + b.ToTable("FFmpegProfile"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MediaCollection", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); + b.Property("LastScan") + .HasColumnType("TEXT"); + + b.Property("MediaKind") + .HasColumnType("INTEGER"); + + b.Property("MediaSourceId") + .HasColumnType("INTEGER"); + b.Property("Name") .HasColumnType("TEXT"); b.HasKey("Id"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("MediaSourceId"); - b.ToTable("MediaCollections"); + b.ToTable("Library"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MediaItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("LastWriteTime") + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Path") .HasColumnType("TEXT"); - b.Property("MediaSourceId") + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("LibraryPath"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaVersionId") .HasColumnType("INTEGER"); b.Property("Path") .HasColumnType("TEXT"); - b.Property("Poster") - .HasColumnType("TEXT"); + b.HasKey("Id"); - b.Property("PosterLastWriteTime") - .HasColumnType("TEXT"); + b.HasIndex("MediaVersionId"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LibraryPathId") + .HasColumnType("INTEGER"); b.HasKey("Id"); - b.HasIndex("MediaSourceId"); + b.HasIndex("LibraryPathId"); + + b.ToTable("MediaItem"); + }); - b.ToTable("MediaItems"); + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MediaSource"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MediaSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("LastScan") + b.Property("AudioCodec") + .HasColumnType("TEXT"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateUpdated") + .HasColumnType("TEXT"); + + b.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b.Property("Duration") .HasColumnType("TEXT"); + b.Property("EpisodeId") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + b.Property("Name") .HasColumnType("TEXT"); - b.Property("SourceType") + b.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b.Property("VideoCodec") + .HasColumnType("TEXT"); + + b.Property("VideoScanKind") + .HasColumnType("INTEGER"); + + b.Property("Width") .HasColumnType("INTEGER"); b.HasKey("Id"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); - b.ToTable("MediaSources"); + b.ToTable("MediaVersion"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MovieMetadata", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("ContentRating") + b.Property("DateAdded") .HasColumnType("TEXT"); - b.Property("LastWriteTime") + b.Property("DateUpdated") .HasColumnType("TEXT"); + b.Property("MetadataKind") + .HasColumnType("INTEGER"); + b.Property("MovieId") .HasColumnType("INTEGER"); + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + b.Property("Outline") .HasColumnType("TEXT"); b.Property("Plot") .HasColumnType("TEXT"); - b.Property("Premiered") + b.Property("ReleaseDate") .HasColumnType("TEXT"); b.Property("SortTitle") .HasColumnType("TEXT"); - b.Property("Source") - .HasColumnType("INTEGER"); - b.Property("Tagline") .HasColumnType("TEXT"); @@ -308,15 +448,12 @@ namespace ErsatzTV.Infrastructure.Migrations b.HasKey("Id"); - b.HasIndex("MovieId") - .IsUnique(); + b.HasIndex("MovieId"); b.ToTable("MovieMetadata"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Playout", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -337,18 +474,16 @@ namespace ErsatzTV.Infrastructure.Migrations b.HasIndex("ProgramScheduleId"); - b.ToTable("Playouts"); + b.ToTable("Playout"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlayoutItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("Finish") + b.Property("Finish") .HasColumnType("TEXT"); b.Property("MediaItemId") @@ -357,7 +492,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Property("PlayoutId") .HasColumnType("INTEGER"); - b.Property("Start") + b.Property("Start") .HasColumnType("TEXT"); b.HasKey("Id"); @@ -366,23 +501,24 @@ namespace ErsatzTV.Infrastructure.Migrations b.HasIndex("PlayoutId"); - b.ToTable("PlayoutItems"); + b.ToTable("PlayoutItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("CollectionId") + b.Property("CollectionId") .HasColumnType("INTEGER"); b.Property("CollectionType") .HasColumnType("INTEGER"); + b.Property("MediaItemId") + .HasColumnType("INTEGER"); + b.Property("PlayoutId") .HasColumnType("INTEGER"); @@ -391,16 +527,18 @@ namespace ErsatzTV.Infrastructure.Migrations b.HasKey("Id"); + b.HasIndex("CollectionId"); + + b.HasIndex("MediaItemId"); + b.HasIndex("PlayoutId"); b.HasIndex("ProgramScheduleId"); - b.ToTable("PlayoutProgramScheduleItemAnchors"); + b.ToTable("PlayoutProgramScheduleAnchor"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMediaSourceConnection", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -409,7 +547,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Property("IsActive") .HasColumnType("INTEGER"); - b.Property("PlexMediaSourceId") + b.Property("PlexMediaSourceId") .HasColumnType("INTEGER"); b.Property("Uri") @@ -419,39 +557,36 @@ namespace ErsatzTV.Infrastructure.Migrations b.HasIndex("PlexMediaSourceId"); - b.ToTable("PlexMediaSourceConnections"); + b.ToTable("PlexConnection"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMediaSourceLibrary", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("File") + .HasColumnType("TEXT"); + b.Property("Key") .HasColumnType("TEXT"); - b.Property("MediaType") + b.Property("PlexId") .HasColumnType("INTEGER"); - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("PlexMediaSourceId") + b.Property("Size") .HasColumnType("INTEGER"); b.HasKey("Id"); - b.HasIndex("PlexMediaSourceId"); - - b.ToTable("PlexMediaSourceLibraries"); + b.ToTable("PlexMediaItemPart"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramSchedule", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -468,24 +603,25 @@ namespace ErsatzTV.Infrastructure.Migrations b.HasIndex("Name") .IsUnique(); - b.ToTable("ProgramSchedules"); + b.ToTable("ProgramSchedule"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); + b.Property("CollectionId") + .HasColumnType("INTEGER"); + b.Property("CollectionType") .HasColumnType("INTEGER"); b.Property("Index") .HasColumnType("INTEGER"); - b.Property("MediaCollectionId") + b.Property("MediaItemId") .HasColumnType("INTEGER"); b.Property("ProgramScheduleId") @@ -494,28 +630,18 @@ namespace ErsatzTV.Infrastructure.Migrations b.Property("StartTime") .HasColumnType("TEXT"); - b.Property("TelevisionSeasonId") - .HasColumnType("INTEGER"); - - b.Property("TelevisionShowId") - .HasColumnType("INTEGER"); - b.HasKey("Id"); - b.HasIndex("MediaCollectionId"); - - b.HasIndex("ProgramScheduleId"); + b.HasIndex("CollectionId"); - b.HasIndex("TelevisionSeasonId"); + b.HasIndex("MediaItemId"); - b.HasIndex("TelevisionShowId"); + b.HasIndex("ProgramScheduleId"); - b.ToTable("ProgramScheduleItems"); + b.ToTable("ProgramScheduleItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Resolution", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -532,123 +658,87 @@ namespace ErsatzTV.Infrastructure.Migrations b.HasKey("Id"); - b.ToTable("Resolutions"); + b.ToTable("Resolution"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("Aired") - .HasColumnType("TEXT"); - - b.Property("Episode") - .HasColumnType("INTEGER"); - - b.Property("LastWriteTime") + b.Property("DateAdded") .HasColumnType("TEXT"); - b.Property("Plot") + b.Property("DateUpdated") .HasColumnType("TEXT"); - b.Property("Season") + b.Property("MetadataKind") .HasColumnType("INTEGER"); - b.Property("SortTitle") + b.Property("OriginalTitle") .HasColumnType("TEXT"); - b.Property("Source") - .HasColumnType("INTEGER"); - - b.Property("TelevisionEpisodeId") - .HasColumnType("INTEGER"); - - b.Property("Title") + b.Property("Outline") .HasColumnType("TEXT"); - b.HasKey("Id"); - - b.HasIndex("TelevisionEpisodeId") - .IsUnique(); - - b.ToTable("TelevisionEpisodeMetadata"); - }); - - modelBuilder.Entity( - "ErsatzTV.Core.Domain.TelevisionSeason", - b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + b.Property("ReleaseDate") + .HasColumnType("TEXT"); - b.Property("Number") + b.Property("SeasonId") .HasColumnType("INTEGER"); - b.Property("Path") - .HasColumnType("TEXT"); - - b.Property("Poster") + b.Property("SortTitle") .HasColumnType("TEXT"); - b.Property("PosterLastWriteTime") + b.Property("Title") .HasColumnType("TEXT"); - b.Property("TelevisionShowId") + b.Property("Year") .HasColumnType("INTEGER"); b.HasKey("Id"); - b.HasIndex("TelevisionShowId"); + b.HasIndex("SeasonId"); - b.ToTable("TelevisionSeasons"); + b.ToTable("SeasonMetadata"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.TelevisionShow", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("Poster") + b.Property("DateAdded") .HasColumnType("TEXT"); - b.Property("PosterLastWriteTime") + b.Property("DateUpdated") .HasColumnType("TEXT"); - b.HasKey("Id"); - - b.ToTable("TelevisionShows"); - }); - - modelBuilder.Entity( - "ErsatzTV.Core.Domain.TelevisionShowMetadata", - b => - { - b.Property("Id") - .ValueGeneratedOnAdd() + b.Property("MetadataKind") .HasColumnType("INTEGER"); - b.Property("LastWriteTime") + b.Property("OriginalTitle") + .HasColumnType("TEXT"); + + b.Property("Outline") .HasColumnType("TEXT"); b.Property("Plot") .HasColumnType("TEXT"); - b.Property("SortTitle") + b.Property("ReleaseDate") .HasColumnType("TEXT"); - b.Property("Source") + b.Property("ShowId") .HasColumnType("INTEGER"); - b.Property("TelevisionShowId") - .HasColumnType("INTEGER"); + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasColumnType("TEXT"); b.Property("Title") .HasColumnType("TEXT"); @@ -658,157 +748,83 @@ namespace ErsatzTV.Infrastructure.Migrations b.HasKey("Id"); - b.HasIndex("TelevisionShowId") - .IsUnique(); + b.HasIndex("ShowId"); - b.ToTable("TelevisionShowMetadata"); + b.ToTable("ShowMetadata"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.TelevisionShowSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Discriminator") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("TelevisionShowId") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); + b.HasBaseType("ErsatzTV.Core.Domain.Library"); - b.HasIndex("TelevisionShowId"); - - b.ToTable("TelevisionShowSource"); - - b.HasDiscriminator("Discriminator").HasValue("TelevisionShowSource"); + b.ToTable("LocalLibrary"); }); - modelBuilder.Entity( - "MovieMediaItemSimpleMediaCollection", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => { - b.Property("MoviesId") - .HasColumnType("INTEGER"); - - b.Property("SimpleMediaCollectionsId") - .HasColumnType("INTEGER"); - - b.HasKey("MoviesId", "SimpleMediaCollectionsId"); - - b.HasIndex("SimpleMediaCollectionsId"); - - b.ToTable("SimpleMediaCollectionMovies"); - }); + b.HasBaseType("ErsatzTV.Core.Domain.Library"); - modelBuilder.Entity( - "SimpleMediaCollectionTelevisionEpisodeMediaItem", - b => - { - b.Property("SimpleMediaCollectionsId") - .HasColumnType("INTEGER"); + b.Property("Key") + .HasColumnType("TEXT"); - b.Property("TelevisionEpisodesId") + b.Property("ShouldSyncItems") .HasColumnType("INTEGER"); - b.HasKey("SimpleMediaCollectionsId", "TelevisionEpisodesId"); - - b.HasIndex("TelevisionEpisodesId"); - - b.ToTable("SimpleMediaCollectionEpisodes"); + b.ToTable("PlexLibrary"); }); - modelBuilder.Entity( - "SimpleMediaCollectionTelevisionSeason", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => { - b.Property("SimpleMediaCollectionsId") - .HasColumnType("INTEGER"); - - b.Property("TelevisionSeasonsId") - .HasColumnType("INTEGER"); - - b.HasKey("SimpleMediaCollectionsId", "TelevisionSeasonsId"); - - b.HasIndex("TelevisionSeasonsId"); - - b.ToTable("SimpleMediaCollectionSeasons"); - }); + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); - modelBuilder.Entity( - "SimpleMediaCollectionTelevisionShow", - b => - { - b.Property("SimpleMediaCollectionsId") + b.Property("EpisodeNumber") .HasColumnType("INTEGER"); - b.Property("TelevisionShowsId") + b.Property("SeasonId") .HasColumnType("INTEGER"); - b.HasKey("SimpleMediaCollectionsId", "TelevisionShowsId"); - - b.HasIndex("TelevisionShowsId"); + b.HasIndex("SeasonId"); - b.ToTable("SimpleMediaCollectionShows"); + b.ToTable("Episode"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.SimpleMediaCollection", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => { - b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); - b.ToTable("SimpleMediaCollections"); + b.ToTable("Movie"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MovieMediaItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); - b.Property("MetadataId") + b.Property("SeasonNumber") .HasColumnType("INTEGER"); - b.ToTable("Movies"); + b.Property("ShowId") + .HasColumnType("INTEGER"); + + b.HasIndex("ShowId"); + + b.ToTable("Season"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); - b.Property("SeasonId") - .HasColumnType("INTEGER"); - - b.HasIndex("SeasonId"); - - b.ToTable("TelevisionEpisodes"); + b.ToTable("Show"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.LocalMediaSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); - b.Property("Folder") - .HasColumnType("TEXT"); - - b.Property("MediaType") - .HasColumnType("INTEGER"); - - b.ToTable("LocalMediaSources"); + b.ToTable("LocalMediaSource"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMediaSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); @@ -818,12 +834,13 @@ namespace ErsatzTV.Infrastructure.Migrations b.Property("ProductVersion") .HasColumnType("TEXT"); - b.ToTable("PlexMediaSources"); + b.Property("ServerName") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSource"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemDuration", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => { b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); @@ -833,59 +850,77 @@ namespace ErsatzTV.Infrastructure.Migrations b.Property("PlayoutDuration") .HasColumnType("TEXT"); - b.ToTable("ProgramScheduleDurationItems"); + b.ToTable("ProgramScheduleDurationItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemFlood", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => { b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); - b.ToTable("ProgramScheduleFloodItems"); + b.ToTable("ProgramScheduleFloodItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => { b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); b.Property("Count") .HasColumnType("INTEGER"); - b.ToTable("ProgramScheduleMultipleItems"); + b.ToTable("ProgramScheduleMultipleItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemOne", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => { b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); - b.ToTable("ProgramScheduleOneItems"); + b.ToTable("ProgramScheduleOneItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.LocalTelevisionShowSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => { - b.HasBaseType("ErsatzTV.Core.Domain.TelevisionShowSource"); + b.HasBaseType("ErsatzTV.Core.Domain.Movie"); - b.Property("MediaSourceId") + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PartId") .HasColumnType("INTEGER"); - b.Property("Path") - .HasColumnType("TEXT"); + b.HasIndex("PartId"); - b.HasIndex("MediaSourceId"); + b.ToTable("PlexMovie"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + { + b.HasOne("ErsatzTV.Core.Domain.Channel", null) + .WithMany("Artwork") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.EpisodeMetadata", null) + .WithMany("Artwork") + .HasForeignKey("EpisodeMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MovieMetadata", null) + .WithMany("Artwork") + .HasForeignKey("MovieMetadataId") + .OnDelete(DeleteBehavior.Cascade); - b.HasDiscriminator().HasValue("LocalTelevisionShowSource"); + b.HasOne("ErsatzTV.Core.Domain.SeasonMetadata", null) + .WithMany("Artwork") + .HasForeignKey("SeasonMetadataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.ShowMetadata", null) + .WithMany("Artwork") + .HasForeignKey("ShowMetadataId") + .OnDelete(DeleteBehavior.Cascade); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Channel", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => { b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") .WithMany() @@ -896,9 +931,37 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("FFmpegProfile"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.FFmpegProfile", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany("CollectionItems") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany("CollectionItems") + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("MediaItem"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => { b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") .WithMany() @@ -909,80 +972,75 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Resolution"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MediaItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => { - b.HasOne("ErsatzTV.Core.Domain.MediaSource", "Source") - .WithMany() + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") + .WithMany("Libraries") .HasForeignKey("MediaSourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.OwnsOne( - "ErsatzTV.Core.Domain.MediaItemStatistics", - "Statistics", - b1 => - { - b1.Property("MediaItemId") - .HasColumnType("INTEGER"); - - b1.Property("AudioCodec") - .HasColumnType("TEXT"); - - b1.Property("DisplayAspectRatio") - .HasColumnType("TEXT"); - - b1.Property("Duration") - .HasColumnType("TEXT"); - - b1.Property("Height") - .HasColumnType("INTEGER"); - - b1.Property("LastWriteTime") - .HasColumnType("TEXT"); - - b1.Property("SampleAspectRatio") - .HasColumnType("TEXT"); + b.Navigation("MediaSource"); + }); - b1.Property("VideoCodec") - .HasColumnType("TEXT"); + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.HasOne("ErsatzTV.Core.Domain.Library", "Library") + .WithMany("Paths") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); - b1.Property("VideoScanType") - .HasColumnType("INTEGER"); + b.Navigation("Library"); + }); - b1.Property("Width") - .HasColumnType("INTEGER"); + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") + .WithMany("MediaFiles") + .HasForeignKey("MediaVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); - b1.HasKey("MediaItemId"); + b.Navigation("MediaVersion"); + }); - b1.ToTable("MediaItems"); + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") + .WithMany("MediaItems") + .HasForeignKey("LibraryPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); - b1.WithOwner() - .HasForeignKey("MediaItemId"); - }); + b.Navigation("LibraryPath"); + }); - b.Navigation("Source"); + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.HasOne("ErsatzTV.Core.Domain.Episode", null) + .WithMany("MediaVersions") + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); - b.Navigation("Statistics"); + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithMany("MediaVersions") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MovieMetadata", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => { - b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", "Movie") - .WithOne("Metadata") - .HasForeignKey("ErsatzTV.Core.Domain.MovieMetadata", "MovieId") + b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") + .WithMany("MovieMetadata") + .HasForeignKey("MovieId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.Navigation("Movie"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Playout", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => { b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") .WithMany("Playouts") @@ -996,10 +1054,7 @@ namespace ErsatzTV.Infrastructure.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.OwnsOne( - "ErsatzTV.Core.Domain.PlayoutAnchor", - "Anchor", - b1 => + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => { b1.Property("PlayoutId") .HasColumnType("INTEGER"); @@ -1007,14 +1062,14 @@ namespace ErsatzTV.Infrastructure.Migrations b1.Property("NextScheduleItemId") .HasColumnType("INTEGER"); - b1.Property("NextStart") + b1.Property("NextStart") .HasColumnType("TEXT"); b1.HasKey("PlayoutId"); b1.HasIndex("NextScheduleItemId"); - b1.ToTable("Playouts"); + b1.ToTable("Playout"); b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem") .WithMany() @@ -1035,9 +1090,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("ProgramSchedule"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlayoutItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => { b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") .WithMany() @@ -1056,10 +1109,18 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Playout"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => { + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") + .WithMany() + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade); + b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout") .WithMany("ProgramScheduleAnchors") .HasForeignKey("PlayoutId") @@ -1072,10 +1133,7 @@ namespace ErsatzTV.Infrastructure.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.OwnsOne( - "ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", - "EnumeratorState", - b1 => + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => { b1.Property("PlayoutProgramScheduleAnchorId") .HasColumnType("INTEGER"); @@ -1088,44 +1146,45 @@ namespace ErsatzTV.Infrastructure.Migrations b1.HasKey("PlayoutProgramScheduleAnchorId"); - b1.ToTable("PlayoutProgramScheduleItemAnchors"); + b1.ToTable("PlayoutProgramScheduleAnchor"); b1.WithOwner() .HasForeignKey("PlayoutProgramScheduleAnchorId"); }); + b.Navigation("Collection"); + b.Navigation("EnumeratorState"); + b.Navigation("MediaItem"); + b.Navigation("Playout"); b.Navigation("ProgramSchedule"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMediaSourceConnection", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => { - b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", null) + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") .WithMany("Connections") - .HasForeignKey("PlexMediaSourceId"); - }); + .HasForeignKey("PlexMediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMediaSourceLibrary", - b => - { - b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", null) - .WithMany("Libraries") - .HasForeignKey("PlexMediaSourceId"); + b.Navigation("PlexMediaSource"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => { - b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") .WithMany() - .HasForeignKey("MediaCollectionId"); + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") + .WithMany() + .HasForeignKey("MediaItemId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") .WithMany("Items") @@ -1133,187 +1192,106 @@ namespace ErsatzTV.Infrastructure.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "TelevisionSeason") - .WithMany() - .HasForeignKey("TelevisionSeasonId"); - - b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") - .WithMany() - .HasForeignKey("TelevisionShowId"); + b.Navigation("Collection"); - b.Navigation("MediaCollection"); + b.Navigation("MediaItem"); b.Navigation("ProgramSchedule"); - - b.Navigation("TelevisionSeason"); - - b.Navigation("TelevisionShow"); - }); - - modelBuilder.Entity( - "ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", - b => - { - b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "TelevisionEpisode") - .WithOne("Metadata") - .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMetadata", "TelevisionEpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("TelevisionEpisode"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.TelevisionSeason", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => { - b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") - .WithMany("Seasons") - .HasForeignKey("TelevisionShowId") + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("TelevisionShow"); + b.Navigation("Season"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.TelevisionShowMetadata", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => { - b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") - .WithOne("Metadata") - .HasForeignKey("ErsatzTV.Core.Domain.TelevisionShowMetadata", "TelevisionShowId") + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("ShowMetadata") + .HasForeignKey("ShowId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("TelevisionShow"); + b.Navigation("Show"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.TelevisionShowSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => { - b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", "TelevisionShow") - .WithMany("Sources") - .HasForeignKey("TelevisionShowId") + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - - b.Navigation("TelevisionShow"); }); - modelBuilder.Entity( - "MovieMediaItemSimpleMediaCollection", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => { - b.HasOne("ErsatzTV.Core.Domain.MovieMediaItem", null) - .WithMany() - .HasForeignKey("MoviesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) - .WithMany() - .HasForeignKey("SimpleMediaCollectionsId") + b.HasOne("ErsatzTV.Core.Domain.Library", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity( - "SimpleMediaCollectionTelevisionEpisodeMediaItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => { - b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) - .WithMany() - .HasForeignKey("SimpleMediaCollectionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", null) - .WithMany() - .HasForeignKey("TelevisionEpisodesId") + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Episode", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - }); - modelBuilder.Entity( - "SimpleMediaCollectionTelevisionSeason", - b => - { - b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) - .WithMany() - .HasForeignKey("SimpleMediaCollectionsId") + b.HasOne("ErsatzTV.Core.Domain.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", null) - .WithMany() - .HasForeignKey("TelevisionSeasonsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + b.Navigation("Season"); }); - modelBuilder.Entity( - "SimpleMediaCollectionTelevisionShow", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => { - b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) - .WithMany() - .HasForeignKey("SimpleMediaCollectionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("ErsatzTV.Core.Domain.TelevisionShow", null) - .WithMany() - .HasForeignKey("TelevisionShowsId") + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.Movie", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.SimpleMediaCollection", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => { - b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) .WithOne() - .HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id") + .HasForeignKey("ErsatzTV.Core.Domain.Season", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MovieMediaItem", - b => - { - b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) - .WithOne() - .HasForeignKey("ErsatzTV.Core.Domain.MovieMediaItem", "Id") + b.HasOne("ErsatzTV.Core.Domain.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + + b.Navigation("Show"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => { b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) .WithOne() - .HasForeignKey("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("ErsatzTV.Core.Domain.TelevisionSeason", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") + .HasForeignKey("ErsatzTV.Core.Domain.Show", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - - b.Navigation("Season"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.LocalMediaSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => { b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) .WithOne() @@ -1322,9 +1300,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMediaSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => { b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) .WithOne() @@ -1333,9 +1309,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemDuration", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => { b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) .WithOne() @@ -1344,9 +1318,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemFlood", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => { b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) .WithOne() @@ -1355,9 +1327,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => { b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) .WithOne() @@ -1366,9 +1336,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemOne", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => { b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) .WithOne() @@ -1377,63 +1345,123 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.LocalTelevisionShowSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => { - b.HasOne("ErsatzTV.Core.Domain.LocalMediaSource", "MediaSource") - .WithMany() - .HasForeignKey("MediaSourceId") + b.HasOne("ErsatzTV.Core.Domain.Movie", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("MediaSource"); + b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part") + .WithMany() + .HasForeignKey("PartId"); + + b.Navigation("Part"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + { + b.Navigation("Artwork"); + + b.Navigation("Playouts"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + { + b.Navigation("Artwork"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + { + b.Navigation("Paths"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + { + b.Navigation("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + { + b.Navigation("CollectionItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Navigation("Libraries"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => { b.Navigation("Playouts"); }); + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + { + b.Navigation("MediaFiles"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + { + b.Navigation("Artwork"); + }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Playout", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => { b.Navigation("Items"); b.Navigation("ProgramScheduleAnchors"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramSchedule", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => { b.Navigation("Items"); b.Navigation("Playouts"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionSeason", b => { b.Navigation("Episodes"); }); + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + { + b.Navigation("Artwork"); + }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.TelevisionShow", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => { - b.Navigation("Metadata"); + b.Navigation("Artwork"); + }); - b.Navigation("Seasons"); + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + { + b.Navigation("EpisodeMetadata"); - b.Navigation("Sources"); + b.Navigation("MediaVersions"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMediaItem", b => { b.Navigation("Metadata"); }); + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + { + b.Navigation("MediaVersions"); - modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionEpisodeMediaItem", b => { b.Navigation("Metadata"); }); + b.Navigation("MovieMetadata"); + }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMediaSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => { - b.Navigation("Connections"); + b.Navigation("Episodes"); - b.Navigation("Libraries"); + b.Navigation("SeasonMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + { + b.Navigation("Seasons"); + + b.Navigation("ShowMetadata"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.Navigation("Connections"); }); #pragma warning restore 612, 618 } diff --git a/ErsatzTV.Infrastructure/Plex/IPlexServerApi.cs b/ErsatzTV.Infrastructure/Plex/IPlexServerApi.cs index edd2d107..075c8fb5 100644 --- a/ErsatzTV.Infrastructure/Plex/IPlexServerApi.cs +++ b/ErsatzTV.Infrastructure/Plex/IPlexServerApi.cs @@ -8,8 +8,15 @@ namespace ErsatzTV.Infrastructure.Plex public interface IPlexServerApi { [Get("/library/sections")] - public Task> GetLibraries( + public Task>> GetLibraries( [Query] [AliasAs("X-Plex-Token")] string token); + + [Get("/library/sections/{key}/all")] + public Task>> + GetLibrarySectionContents( + string key, + [Query] [AliasAs("X-Plex-Token")] + string token); } } diff --git a/ErsatzTV.Infrastructure/Plex/Models/PlexMediaContainerResponse.cs b/ErsatzTV.Infrastructure/Plex/Models/PlexMediaContainerResponse.cs index 37af14d6..c39eeca8 100644 --- a/ErsatzTV.Infrastructure/Plex/Models/PlexMediaContainerResponse.cs +++ b/ErsatzTV.Infrastructure/Plex/Models/PlexMediaContainerResponse.cs @@ -4,11 +4,16 @@ namespace ErsatzTV.Infrastructure.Plex.Models { public class PlexMediaContainerResponse { - public PlexMediaContainerContent MediaContainer { get; set; } + public T MediaContainer { get; set; } } - public class PlexMediaContainerContent + public class PlexMediaContainerDirectoryContent { public List Directory { get; set; } } + + public class PlexMediaContainerMetadataContent + { + public List Metadata { get; set; } + } } diff --git a/ErsatzTV.Infrastructure/Plex/Models/PlexMediaResponse.cs b/ErsatzTV.Infrastructure/Plex/Models/PlexMediaResponse.cs new file mode 100644 index 00000000..909482c1 --- /dev/null +++ b/ErsatzTV.Infrastructure/Plex/Models/PlexMediaResponse.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace ErsatzTV.Infrastructure.Plex.Models +{ + public class PlexMediaResponse + { + public int Id { get; set; } + public int Duration { get; set; } + public int Bitrate { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public double AspectRatio { get; set; } + public int AudioChannels { get; set; } + public string AudioCodec { get; set; } + public string VideoCodec { get; set; } + public string Container { get; set; } + public string VideoFrameRate { get; set; } + public List Part { get; set; } + } +} diff --git a/ErsatzTV.Infrastructure/Plex/Models/PlexMetadataResponse.cs b/ErsatzTV.Infrastructure/Plex/Models/PlexMetadataResponse.cs new file mode 100644 index 00000000..293505e3 --- /dev/null +++ b/ErsatzTV.Infrastructure/Plex/Models/PlexMetadataResponse.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace ErsatzTV.Infrastructure.Plex.Models +{ + public class PlexMetadataResponse + { + public string Key { get; set; } + public string Title { get; set; } + public string Summary { get; set; } + public int Year { get; set; } + public string Tagline { get; set; } + public string Thumb { get; set; } + public string Art { get; set; } + public string OriginallyAvailableAt { get; set; } + public int AddedAt { get; set; } + public int UpdatedAt { get; set; } + public List Media { get; set; } + } +} diff --git a/ErsatzTV.Infrastructure/Plex/Models/PlexPartResponse.cs b/ErsatzTV.Infrastructure/Plex/Models/PlexPartResponse.cs new file mode 100644 index 00000000..758de563 --- /dev/null +++ b/ErsatzTV.Infrastructure/Plex/Models/PlexPartResponse.cs @@ -0,0 +1,11 @@ +namespace ErsatzTV.Infrastructure.Plex.Models +{ + public class PlexPartResponse + { + public int Id { get; set; } + public string Key { get; set; } + public int Duration { get; set; } + public string File { get; set; } + public int Size { get; set; } + } +} diff --git a/ErsatzTV.Infrastructure/Plex/Models/PlexResourceConnection.cs b/ErsatzTV.Infrastructure/Plex/Models/PlexResourceConnection.cs index 4d494093..6f1a8e1c 100644 --- a/ErsatzTV.Infrastructure/Plex/Models/PlexResourceConnection.cs +++ b/ErsatzTV.Infrastructure/Plex/Models/PlexResourceConnection.cs @@ -4,8 +4,8 @@ { public string Protocol { get; set; } public string Address { get; set; } - public string Port { get; set; } + public int Port { get; set; } public string Uri { get; set; } - public string Local { get; set; } + public bool Local { get; set; } } } diff --git a/ErsatzTV.Infrastructure/Plex/PlexSecretStore.cs b/ErsatzTV.Infrastructure/Plex/PlexSecretStore.cs index 31844a29..e811f5c1 100644 --- a/ErsatzTV.Infrastructure/Plex/PlexSecretStore.cs +++ b/ErsatzTV.Infrastructure/Plex/PlexSecretStore.cs @@ -64,7 +64,14 @@ namespace ErsatzTV.Infrastructure.Plex private static Task ReadSecrets() => File.ReadAllTextAsync(FileSystemLayout.PlexSecretsPath) .Map(JsonConvert.DeserializeObject) - .Map(s => Optional(s).IfNone(new PlexSecrets())); + .Map(s => Optional(s).IfNone(new PlexSecrets())) + .Map( + s => + { + s.ServerAuthTokens ??= new Dictionary(); + s.UserAuthTokens ??= new Dictionary(); + return s; + }); private static Task SaveSecrets(PlexSecrets plexSecrets) => Some(JsonConvert.SerializeObject(plexSecrets)).Match( diff --git a/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs b/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs index 90dc3200..1be8594c 100644 --- a/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs +++ b/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs @@ -15,8 +15,8 @@ namespace ErsatzTV.Infrastructure.Plex { public class PlexServerApiClient : IPlexServerApiClient { - public async Task>> GetLibraries( - PlexMediaSourceConnection connection, + public async Task>> GetLibraries( + PlexConnection connection, PlexServerAuthToken token) { try @@ -36,23 +36,134 @@ namespace ErsatzTV.Infrastructure.Plex } } - private static Option Project(PlexLibraryResponse response) => + public async Task>> GetLibraryContents( + PlexLibrary library, + PlexConnection connection, + PlexServerAuthToken token) + { + try + { + IPlexServerApi service = RestService.For(connection.Uri); + return await service.GetLibrarySectionContents(library.Key, token.AuthToken) + .Map(r => r.MediaContainer.Metadata.Filter(m => m.Media.Count > 0 && m.Media[0].Part.Count > 0)) + .Map(list => list.Map(ProjectToMovie).ToList()); + } + catch (Exception ex) + { + return BaseError.New(ex.Message); + } + } + + private static Option Project(PlexLibraryResponse response) => response.Type switch { - "show" => new PlexMediaSourceLibrary + "show" => new PlexLibrary { Key = response.Key, Name = response.Title, - MediaType = MediaType.TvShow + MediaKind = LibraryMediaKind.Shows, + ShouldSyncItems = false }, - "movie" => new PlexMediaSourceLibrary + "movie" => new PlexLibrary { Key = response.Key, Name = response.Title, - MediaType = MediaType.Movie + MediaKind = LibraryMediaKind.Movies, + ShouldSyncItems = false }, // TODO: "artist" for music libraries _ => None }; + + private static PlexPartEntry Project(PlexPartResponse response) => + new() + { + Id = response.Id, + Key = response.Key, + Duration = response.Duration, + File = response.File, + Size = response.Size + }; + + private static PlexMediaEntry Project(PlexMediaResponse response) => + new() + { + Id = response.Id, + Duration = response.Duration, + Bitrate = response.Bitrate, + Width = response.Width, + Height = response.Height, + AspectRatio = response.AspectRatio, + AudioChannels = response.AudioChannels, + AudioCodec = response.AudioCodec, + VideoCodec = response.VideoCodec, + Container = response.Container, + VideoFrameRate = response.VideoFrameRate, + Part = response.Part.Map(Project).ToList() + }; + + private static PlexMetadataEntry Project(PlexMetadataResponse response) => + new() + { + Key = response.Key, + Title = response.Title, + Summary = response.Summary, + Year = response.Year, + Tagline = response.Tagline, + Thumb = response.Thumb, + Art = response.Art, + AddedAt = response.AddedAt, + UpdatedAt = response.UpdatedAt, + Media = response.Media.Map(Project).ToList() + }; + + private static PlexMovie ProjectToMovie(PlexMetadataResponse response) + { + PlexMediaResponse media = response.Media.Head(); + PlexPartResponse part = media.Part.Head(); + DateTime lastWriteTime = DateTimeOffset.FromUnixTimeSeconds(response.UpdatedAt).DateTime; + + var metadata = new MovieMetadata + { + Title = response.Title, + Plot = response.Summary, + ReleaseDate = new DateTime(response.Year, 1, 1), // TODO: actual release date? + Year = response.Year, + Tagline = response.Tagline, + DateAdded = DateTime.UtcNow, // TODO: actual date added? + DateUpdated = lastWriteTime + }; + + // TODO: artwork + + var movie = new PlexMovie + { + Key = response.Key, + // DateUpdated = lastWriteTime, + MovieMetadata = new List { metadata }, + // TODO: versions + // Statistics = new MediaItemStatistics + // { + // Duration = TimeSpan.FromMilliseconds(media.Duration), + // Width = media.Width, + // Height = media.Height, + // // TODO: aspect ratio + // AudioCodec = media.AudioCodec, + // VideoCodec = media.VideoCodec, + // LastWriteTime = lastWriteTime + // }, + // TODO: multi-part movies? + Part = new PlexMediaItemPart + { + PlexId = part.Id, + Key = part.Key, + File = part.File, + Duration = part.Duration, + Size = part.Size + } + }; + + return movie; + } } } diff --git a/ErsatzTV.Infrastructure/Plex/PlexTvApiClient.cs b/ErsatzTV.Infrastructure/Plex/PlexTvApiClient.cs index 0ab5103b..7f5f8ec1 100644 --- a/ErsatzTV.Infrastructure/Plex/PlexTvApiClient.cs +++ b/ErsatzTV.Infrastructure/Plex/PlexTvApiClient.cs @@ -49,11 +49,11 @@ namespace ErsatzTV.Infrastructure.Plex var source = new PlexMediaSource { - Name = resource.Name, + ServerName = resource.Name, ProductVersion = resource.ProductVersion, ClientIdentifier = resource.ClientIdentifier, Connections = resource.Connections - .Map(c => new PlexMediaSourceConnection { Uri = c.Uri }).ToList() + .Map(c => new PlexConnection { Uri = c.Uri }).ToList() }; return source; diff --git a/ErsatzTV/Controllers/Api/MediaCollectionsController.cs b/ErsatzTV/Controllers/Api/MediaCollectionsController.cs index 5c2e6c1c..a45f5f95 100644 --- a/ErsatzTV/Controllers/Api/MediaCollectionsController.cs +++ b/ErsatzTV/Controllers/Api/MediaCollectionsController.cs @@ -25,24 +25,24 @@ namespace ErsatzTV.Controllers.Api [ProducesResponseType(400)] public Task Add( [Required] [FromBody] - CreateSimpleMediaCollection createCollection) => + CreateCollection createCollection) => _mediator.Send(createCollection).ToActionResult(); [HttpGet] [ProducesResponseType(typeof(IEnumerable), 200)] public Task GetAll() => - _mediator.Send(new GetAllMediaCollections()).ToActionResult(); + _mediator.Send(new GetAllCollections()).ToActionResult(); [HttpGet("{id}")] [ProducesResponseType(typeof(MediaCollectionViewModel), 200)] [ProducesResponseType(404)] public Task Get(int id) => - _mediator.Send(new GetSimpleMediaCollectionById(id)).ToActionResult(); + _mediator.Send(new GetCollectionById(id)).ToActionResult(); [HttpGet("{id}/items")] [ProducesResponseType(typeof(IEnumerable), 200)] [ProducesResponseType(404)] public Task GetItems(int id) => - _mediator.Send(new GetSimpleMediaCollectionItems(id)).ToActionResult(); + _mediator.Send(new GetCollectionItems(id)).ToActionResult(); } } diff --git a/ErsatzTV/Controllers/ArtworkController.cs b/ErsatzTV/Controllers/ArtworkController.cs new file mode 100644 index 00000000..66014ecd --- /dev/null +++ b/ErsatzTV/Controllers/ArtworkController.cs @@ -0,0 +1,40 @@ +using System.Threading.Tasks; +using ErsatzTV.Application.Images; +using ErsatzTV.Application.Images.Queries; +using ErsatzTV.Core; +using ErsatzTV.Core.Domain; +using LanguageExt; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace ErsatzTV.Controllers +{ + [ApiController] + [ApiExplorerSettings(IgnoreApi = true)] + public class PostersController : ControllerBase + { + private readonly IMediator _mediator; + + public PostersController(IMediator mediator) => _mediator = mediator; + + [HttpGet("/artwork/posters/{fileName}")] + public async Task GetPoster(string fileName) + { + Either imageContents = + await _mediator.Send(new GetImageContents(fileName, ArtworkKind.Poster, 440)); + return imageContents.Match( + Left: _ => new NotFoundResult(), + Right: r => new FileContentResult(r.Contents, r.MimeType)); + } + + [HttpGet("/artwork/thumbnails/{fileName}")] + public async Task GetThumbnail(string fileName) + { + Either imageContents = + await _mediator.Send(new GetImageContents(fileName, ArtworkKind.Thumbnail, 220)); + return imageContents.Match( + Left: _ => new NotFoundResult(), + Right: r => new FileContentResult(r.Contents, r.MimeType)); + } + } +} diff --git a/ErsatzTV/Controllers/IptvController.cs b/ErsatzTV/Controllers/IptvController.cs index cd2a0002..20c416a8 100644 --- a/ErsatzTV/Controllers/IptvController.cs +++ b/ErsatzTV/Controllers/IptvController.cs @@ -4,6 +4,7 @@ using ErsatzTV.Application.Images; using ErsatzTV.Application.Images.Queries; using ErsatzTV.Application.Streaming.Queries; using ErsatzTV.Core; +using ErsatzTV.Core.Domain; using ErsatzTV.Core.Iptv; using LanguageExt; using MediatR; @@ -60,10 +61,11 @@ namespace ErsatzTV.Controllers }, error => BadRequest(error.Value))); - [HttpGet("iptv/images/{fileName}")] + [HttpGet("iptv/logos/{fileName}")] public async Task GetImage(string fileName) { - Either imageContents = await _mediator.Send(new GetImageContents(fileName)); + Either imageContents = + await _mediator.Send(new GetImageContents(fileName, ArtworkKind.Logo)); return imageContents.Match( Left: _ => new NotFoundResult(), Right: r => new FileContentResult(r.Contents, r.MimeType)); diff --git a/ErsatzTV/Controllers/PostersController.cs b/ErsatzTV/Controllers/PostersController.cs deleted file mode 100644 index 9395903a..00000000 --- a/ErsatzTV/Controllers/PostersController.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Threading.Tasks; -using ErsatzTV.Application.Images; -using ErsatzTV.Application.Images.Queries; -using ErsatzTV.Core; -using LanguageExt; -using MediatR; -using Microsoft.AspNetCore.Mvc; - -namespace ErsatzTV.Controllers -{ - [ApiController] - [ApiExplorerSettings(IgnoreApi = true)] - public class PostersController : ControllerBase - { - private readonly IMediator _mediator; - - public PostersController(IMediator mediator) => _mediator = mediator; - - [HttpGet("/posters/{fileName}")] - public async Task GetImage(string fileName) - { - Either imageContents = await _mediator.Send(new GetImageContents(fileName)); - return imageContents.Match( - Left: _ => new NotFoundResult(), - Right: r => new FileContentResult(r.Contents, r.MimeType)); - } - } -} diff --git a/ErsatzTV/ErsatzTV.csproj b/ErsatzTV/ErsatzTV.csproj index 96955e2b..2ea24bc1 100644 --- a/ErsatzTV/ErsatzTV.csproj +++ b/ErsatzTV/ErsatzTV.csproj @@ -20,7 +20,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/ErsatzTV/Extensions/HostExtensions.cs b/ErsatzTV/Extensions/HostExtensions.cs index 4c252294..226a2353 100644 --- a/ErsatzTV/Extensions/HostExtensions.cs +++ b/ErsatzTV/Extensions/HostExtensions.cs @@ -1,4 +1,8 @@ using System; +using System.IO; +using System.Linq; +using ErsatzTV.Core; +using ErsatzTV.Core.Domain; using ErsatzTV.Infrastructure.Data; using LanguageExt; using Microsoft.EntityFrameworkCore; @@ -17,29 +21,74 @@ namespace ErsatzTV.Extensions return host; } + public static IHost CleanCacheFolder(this IHost host) + { + Unit _ = use(() => host.Services.CreateScope(), CleanCache); + return host; + } + private static Unit Seed(IServiceScope scope) => Try(() => scope.ServiceProvider) .Bind(services => Try(GetDbContext(services))) - .Bind(ctx => Try(Migrate(ctx))) + .Bind(ctx => Try(Migrate(ctx, scope.ServiceProvider))) .Bind(ctx => Try(InitializeDb(ctx))) - .IfFail(ex => LogException(ex, scope.ServiceProvider)); + .IfFail(ex => LogException(ex, "Error occured while seeding database", scope.ServiceProvider)); + + private static Unit CleanCache(IServiceScope scope) => + Try(() => scope.ServiceProvider) + .Bind(services => Try(GetDbContext(services))) + .Bind(ctx => Try(CleanCache(ctx, scope.ServiceProvider))) + .IfFail(ex => LogException(ex, "Error occured while cleaning cache", scope.ServiceProvider)); private static TvContext GetDbContext(IServiceProvider provider) => provider.GetRequiredService(); - private static TvContext Migrate(TvContext context) + private static TvContext Migrate(TvContext context, IServiceProvider provider) { + ILogger logger = provider.GetRequiredService>(); + logger.LogInformation("Applying database migrations"); context.Database.Migrate(); + logger.LogInformation("Done applying database migrations"); return context; } private static Unit InitializeDb(TvContext context) => DbInitializer.Initialize(context); - private static Unit LogException(Exception ex, IServiceProvider provider) + private static Unit CleanCache(TvContext context, IServiceProvider provider) + { + if (Directory.Exists(FileSystemLayout.LegacyImageCacheFolder)) + { + ILogger logger = provider.GetRequiredService>(); + logger.LogInformation("Migrating channel logos from legacy image cache folder"); + + var logos = context.Channels + .SelectMany(c => c.Artwork) + .Where(a => a.ArtworkKind == ArtworkKind.Logo) + .Map(a => a.Path) + .ToList(); + + foreach (string logo in logos) + { + string legacyPath = Path.Combine(FileSystemLayout.LegacyImageCacheFolder, logo); + if (File.Exists(legacyPath)) + { + string subfolder = logo.Substring(0, 2); + string newPath = Path.Combine(FileSystemLayout.LogoCacheFolder, subfolder, logo); + File.Copy(legacyPath, newPath, true); + } + } + + logger.LogInformation("Deleting legacy image cache folder"); + Directory.Delete(FileSystemLayout.LegacyImageCacheFolder, true); + } + + return Unit.Default; + } + + private static Unit LogException(Exception ex, string message, IServiceProvider provider) { - provider.GetRequiredService>() - .LogError(ex, "Error occured while seeding database"); + provider.GetRequiredService>().LogError(ex, message); return unit; } } diff --git a/ErsatzTV/Pages/ChannelEditor.razor b/ErsatzTV/Pages/ChannelEditor.razor index 57d81b14..cc9cee77 100644 --- a/ErsatzTV/Pages/ChannelEditor.razor +++ b/ErsatzTV/Pages/ChannelEditor.razor @@ -37,7 +37,7 @@