From 063579c60b01c69a7a9c4a1d17fe3b1c2bb9f612 Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Sun, 28 Feb 2021 15:39:55 -0600 Subject: [PATCH] add basic plex movie synchronization --- .../Libraries/LibraryViewModel.cs | 6 + .../Libraries/LocalLibraryViewModel.cs | 3 +- ErsatzTV.Application/Libraries/Mapper.cs | 11 +- .../Libraries/PlexLibraryViewModel.cs | 7 + ...etLocalLibraries.cs => GetAllLibraries.cs} | 2 +- .../Queries/GetAllLibrariesHandler.cs | 31 + .../Queries/GetLocalLibrariesHandler.cs | 22 - .../Commands/SynchronizePlexLibraryById.cs | 5 +- .../SynchronizePlexLibraryByIdHandler.cs | 12 +- .../Domain/MediaItem/PlexMediaFile.cs | 8 + .../Domain/MediaItem/PlexMediaItemPart.cs | 12 - ErsatzTV.Core/Domain/MediaItem/PlexMovie.cs | 1 - .../Repositories/ILibraryRepository.cs | 2 +- .../Repositories/IMediaSourceRepository.cs | 1 + ErsatzTV.Core/Iptv/ChannelGuide.cs | 2 +- .../MediaItem/PlexMediaFileConfiguration.cs | 11 + ...iguration.cs => PlexMovieConfiguration.cs} | 2 +- .../Data/Repositories/LibraryRepository.cs | 4 +- .../Repositories/MediaSourceRepository.cs | 12 + .../Data/Repositories/MovieRepository.cs | 11 +- ErsatzTV.Infrastructure/Data/TvContext.cs | 2 +- ...210228210815_Add_PlexMediaFile.Designer.cs | 1456 +++++++++++++++++ .../20210228210815_Add_PlexMediaFile.cs | 84 + .../Migrations/TvContextModelSnapshot.cs | 428 +++-- .../Plex/Models/PlexLibraryResponse.cs | 1 + .../Plex/PlexServerApiClient.cs | 52 +- ErsatzTV/Pages/Libraries.razor | 20 +- 27 files changed, 1960 insertions(+), 248 deletions(-) create mode 100644 ErsatzTV.Application/Libraries/LibraryViewModel.cs create mode 100644 ErsatzTV.Application/Libraries/PlexLibraryViewModel.cs rename ErsatzTV.Application/Libraries/Queries/{GetLocalLibraries.cs => GetAllLibraries.cs} (57%) create mode 100644 ErsatzTV.Application/Libraries/Queries/GetAllLibrariesHandler.cs delete mode 100644 ErsatzTV.Application/Libraries/Queries/GetLocalLibrariesHandler.cs create mode 100644 ErsatzTV.Core/Domain/MediaItem/PlexMediaFile.cs delete mode 100644 ErsatzTV.Core/Domain/MediaItem/PlexMediaItemPart.cs create mode 100644 ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMediaFileConfiguration.cs rename ErsatzTV.Infrastructure/Data/Configurations/MediaItem/{PlexMovieMediaItemConfiguration.cs => PlexMovieConfiguration.cs} (77%) create mode 100644 ErsatzTV.Infrastructure/Migrations/20210228210815_Add_PlexMediaFile.Designer.cs create mode 100644 ErsatzTV.Infrastructure/Migrations/20210228210815_Add_PlexMediaFile.cs diff --git a/ErsatzTV.Application/Libraries/LibraryViewModel.cs b/ErsatzTV.Application/Libraries/LibraryViewModel.cs new file mode 100644 index 000000000..8fbe74c0f --- /dev/null +++ b/ErsatzTV.Application/Libraries/LibraryViewModel.cs @@ -0,0 +1,6 @@ +using ErsatzTV.Core.Domain; + +namespace ErsatzTV.Application.Libraries +{ + public record LibraryViewModel(string LibraryKind, int Id, string Name, LibraryMediaKind MediaKind); +} diff --git a/ErsatzTV.Application/Libraries/LocalLibraryViewModel.cs b/ErsatzTV.Application/Libraries/LocalLibraryViewModel.cs index 3e37ecfb1..5c51fc60a 100644 --- a/ErsatzTV.Application/Libraries/LocalLibraryViewModel.cs +++ b/ErsatzTV.Application/Libraries/LocalLibraryViewModel.cs @@ -2,5 +2,6 @@ namespace ErsatzTV.Application.Libraries { - public record LocalLibraryViewModel(int Id, string Name, LibraryMediaKind MediaKind); + public record LocalLibraryViewModel(int Id, string Name, LibraryMediaKind MediaKind) + : LibraryViewModel("Local", Id, Name, MediaKind); } diff --git a/ErsatzTV.Application/Libraries/Mapper.cs b/ErsatzTV.Application/Libraries/Mapper.cs index 872a1a163..cee82d388 100644 --- a/ErsatzTV.Application/Libraries/Mapper.cs +++ b/ErsatzTV.Application/Libraries/Mapper.cs @@ -1,9 +1,18 @@ -using ErsatzTV.Core.Domain; +using System; +using ErsatzTV.Core.Domain; namespace ErsatzTV.Application.Libraries { internal static class Mapper { + public static LibraryViewModel ProjectToViewModel(Library library) => + library switch + { + LocalLibrary l => ProjectToViewModel(l), + PlexLibrary p => new PlexLibraryViewModel(p.Id, p.Name, p.MediaKind), + _ => throw new ArgumentOutOfRangeException(nameof(library)) + }; + public static LocalLibraryViewModel ProjectToViewModel(LocalLibrary library) => new(library.Id, library.Name, library.MediaKind); diff --git a/ErsatzTV.Application/Libraries/PlexLibraryViewModel.cs b/ErsatzTV.Application/Libraries/PlexLibraryViewModel.cs new file mode 100644 index 000000000..05d4be639 --- /dev/null +++ b/ErsatzTV.Application/Libraries/PlexLibraryViewModel.cs @@ -0,0 +1,7 @@ +using ErsatzTV.Core.Domain; + +namespace ErsatzTV.Application.Libraries +{ + public record PlexLibraryViewModel(int Id, string Name, LibraryMediaKind MediaKind) + : LibraryViewModel("Plex", Id, Name, MediaKind); +} diff --git a/ErsatzTV.Application/Libraries/Queries/GetLocalLibraries.cs b/ErsatzTV.Application/Libraries/Queries/GetAllLibraries.cs similarity index 57% rename from ErsatzTV.Application/Libraries/Queries/GetLocalLibraries.cs rename to ErsatzTV.Application/Libraries/Queries/GetAllLibraries.cs index 6abe74f5c..89ee551a0 100644 --- a/ErsatzTV.Application/Libraries/Queries/GetLocalLibraries.cs +++ b/ErsatzTV.Application/Libraries/Queries/GetAllLibraries.cs @@ -3,5 +3,5 @@ using MediatR; namespace ErsatzTV.Application.Libraries.Queries { - public record GetLocalLibraries : IRequest>; + public record GetAllLibraries : IRequest>; } diff --git a/ErsatzTV.Application/Libraries/Queries/GetAllLibrariesHandler.cs b/ErsatzTV.Application/Libraries/Queries/GetAllLibrariesHandler.cs new file mode 100644 index 000000000..7672ae208 --- /dev/null +++ b/ErsatzTV.Application/Libraries/Queries/GetAllLibrariesHandler.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using static ErsatzTV.Application.Libraries.Mapper; + +namespace ErsatzTV.Application.Libraries.Queries +{ + public class GetAllLibrariesHandler : IRequestHandler> + { + private readonly ILibraryRepository _libraryRepository; + + public GetAllLibrariesHandler(ILibraryRepository libraryRepository) => _libraryRepository = libraryRepository; + + public Task> Handle(GetAllLibraries request, CancellationToken cancellationToken) => + _libraryRepository.GetAll() + .Map(list => list.Filter(ShouldIncludeLibrary).Map(ProjectToViewModel).ToList()); + + private static bool ShouldIncludeLibrary(Library library) => + library switch + { + LocalLibrary => true, + PlexLibrary plex => plex.ShouldSyncItems, + _ => false + }; + } +} diff --git a/ErsatzTV.Application/Libraries/Queries/GetLocalLibrariesHandler.cs b/ErsatzTV.Application/Libraries/Queries/GetLocalLibrariesHandler.cs deleted file mode 100644 index 0ea7d2752..000000000 --- a/ErsatzTV.Application/Libraries/Queries/GetLocalLibrariesHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -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/Plex/Commands/SynchronizePlexLibraryById.cs b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryById.cs index 46e11ab55..80c612a4c 100644 --- a/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryById.cs +++ b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryById.cs @@ -6,19 +6,18 @@ 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 + (int PlexLibraryId) : ISynchronizePlexLibraryById { public bool ForceScan => false; } public record ForceSynchronizePlexLibraryById - (int PlexMediaSourceId, int PlexLibraryId) : ISynchronizePlexLibraryById + (int PlexLibraryId) : ISynchronizePlexLibraryById { public bool ForceScan => true; } diff --git a/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryByIdHandler.cs b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryByIdHandler.cs index 3d9d82a6e..e538408a7 100644 --- a/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryByIdHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryByIdHandler.cs @@ -82,7 +82,7 @@ namespace ErsatzTV.Application.Plex.Commands parameters.Library.Name); } - // _entityLocker.UnlockMediaSource(parameters.MediaSource.Id); + _entityLocker.UnlockLibrary(parameters.Library.Id); return Unit.Default; } @@ -103,8 +103,10 @@ namespace ErsatzTV.Application.Plex.Commands private Task> PlexMediaSourceMustExist( ISynchronizePlexLibraryById request) => - _mediaSourceRepository.GetPlex(request.PlexMediaSourceId) - .Map(v => v.ToValidation($"Plex media source {request.PlexMediaSourceId} does not exist.")); + _mediaSourceRepository.GetPlexByLibraryId(request.PlexLibraryId) + .Map( + v => v.ToValidation( + $"Plex media source for library {request.PlexLibraryId} does not exist.")); private Validation MediaSourceMustHaveActiveConnection( PlexMediaSource plexMediaSource) @@ -134,9 +136,7 @@ namespace ErsatzTV.Application.Plex.Commands PlexLibrary Library, bool ForceScan); - private record ConnectionParameters( - PlexMediaSource PlexMediaSource, - PlexConnection ActiveConnection) + private record ConnectionParameters(PlexMediaSource PlexMediaSource, PlexConnection ActiveConnection) { public PlexServerAuthToken PlexServerAuthToken { get; set; } } diff --git a/ErsatzTV.Core/Domain/MediaItem/PlexMediaFile.cs b/ErsatzTV.Core/Domain/MediaItem/PlexMediaFile.cs new file mode 100644 index 000000000..ee902d82e --- /dev/null +++ b/ErsatzTV.Core/Domain/MediaItem/PlexMediaFile.cs @@ -0,0 +1,8 @@ +namespace ErsatzTV.Core.Domain +{ + public class PlexMediaFile : MediaFile + { + public int PlexId { get; set; } + public string Key { get; set; } + } +} diff --git a/ErsatzTV.Core/Domain/MediaItem/PlexMediaItemPart.cs b/ErsatzTV.Core/Domain/MediaItem/PlexMediaItemPart.cs deleted file mode 100644 index 83b803bdb..000000000 --- a/ErsatzTV.Core/Domain/MediaItem/PlexMediaItemPart.cs +++ /dev/null @@ -1,12 +0,0 @@ -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 index 326ced9dc..37c100374 100644 --- a/ErsatzTV.Core/Domain/MediaItem/PlexMovie.cs +++ b/ErsatzTV.Core/Domain/MediaItem/PlexMovie.cs @@ -3,6 +3,5 @@ public class PlexMovie : Movie { public string Key { get; set; } - public PlexMediaItemPart Part { get; set; } } } diff --git a/ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs index 8ecca06fa..961e57e51 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs @@ -10,7 +10,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories Task Add(LibraryPath libraryPath); Task> Get(int libraryId); Task> GetLocal(int libraryId); - Task> GetAllLocal(); + Task> GetAll(); Task UpdateLastScan(Library library); Task> GetLocalPaths(int libraryId); Task> GetPath(int libraryPathId); diff --git a/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs index 3828cc084..f0b18299c 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs @@ -15,6 +15,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories Task> GetPlexLibrary(int plexLibraryId); Task> Get(int id); Task> GetPlex(int id); + Task> GetPlexByLibraryId(int plexLibraryId); Task CountMediaItems(int id); Task Update(LocalMediaSource localMediaSource); Task Update(PlexMediaSource plexMediaSource); diff --git a/ErsatzTV.Core/Iptv/ChannelGuide.cs b/ErsatzTV.Core/Iptv/ChannelGuide.cs index b155dd211..8743133b1 100644 --- a/ErsatzTV.Core/Iptv/ChannelGuide.cs +++ b/ErsatzTV.Core/Iptv/ChannelGuide.cs @@ -108,7 +108,7 @@ namespace ErsatzTV.Core.Iptv xml.WriteAttributeString("system", "xmltv_ns"); xml.WriteString($"{s - 1}.{e - 1}.0/1"); xml.WriteEndElement(); // episode-num - + xml.WriteStartElement("episode-num"); xml.WriteAttributeString("system", "onscreen"); xml.WriteString($"S{s:00}E{e:00}"); diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMediaFileConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMediaFileConfiguration.cs new file mode 100644 index 000000000..8f7ef0fcc --- /dev/null +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMediaFileConfiguration.cs @@ -0,0 +1,11 @@ +using ErsatzTV.Core.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ErsatzTV.Infrastructure.Data.Configurations +{ + public class PlexMediaFileConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) => builder.ToTable("PlexMediaFile"); + } +} diff --git a/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMovieMediaItemConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMovieConfiguration.cs similarity index 77% rename from ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMovieMediaItemConfiguration.cs rename to ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMovieConfiguration.cs index ea5269f16..454482111 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMovieMediaItemConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMovieConfiguration.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace ErsatzTV.Infrastructure.Data.Configurations { - public class PlexMovieMediaItemConfiguration : IEntityTypeConfiguration + public class PlexMovieConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) => builder.ToTable("PlexMovie"); } diff --git a/ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs index 340c88e97..0fb0a901d 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs @@ -49,10 +49,10 @@ namespace ErsatzTV.Infrastructure.Data.Repositories .Map(Optional); } - public Task> GetAllLocal() + public Task> GetAll() { using TvContext context = _dbContextFactory.CreateDbContext(); - return context.LocalLibraries.ToListAsync(); + return context.Libraries.ToListAsync(); } public Task UpdateLastScan(Library library) => _dbConnection.ExecuteAsync( diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs index 43c2029df..8f328f52b 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs @@ -72,6 +72,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories { using TvContext context = _dbContextFactory.CreateDbContext(); return context.PlexLibraries + .Include(l => l.Paths) .OrderBy(l => l.Id) // https://github.com/dotnet/efcore/issues/22579 .SingleOrDefaultAsync(l => l.Id == plexLibraryId) .Map(Optional); @@ -97,6 +98,17 @@ namespace ErsatzTV.Infrastructure.Data.Repositories .Map(Optional); } + public Task> GetPlexByLibraryId(int plexLibraryId) + { + using TvContext context = _dbContextFactory.CreateDbContext(); + return context.PlexMediaSources + .Include(p => p.Connections) + .Include(p => p.Libraries) + .Where(p => p.Libraries.Any(l => l.Id == plexLibraryId)) + .SingleOrDefaultAsync() + .Map(Optional); + } + public Task CountMediaItems(int id) { using TvContext context = _dbContextFactory.CreateDbContext(); diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs index eef1d68c4..6bae72bea 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs @@ -58,9 +58,10 @@ namespace ErsatzTV.Infrastructure.Data.Repositories public async Task> GetOrAdd(PlexLibrary library, PlexMovie item) { - Option maybeExisting = await _dbContext.PlexMovieMediaItems + Option maybeExisting = await _dbContext.PlexMovies .Include(i => i.MovieMetadata) - .Include(i => i.Part) + .Include(i => i.MediaVersions) + .ThenInclude(mv => mv.MediaFiles) .OrderBy(i => i.Key) .SingleOrDefaultAsync(i => i.Key == item.Key); @@ -119,15 +120,13 @@ namespace ErsatzTV.Infrastructure.Data.Repositories } } - private async Task> AddPlexMovie( - PlexLibrary library, - PlexMovie item) + private async Task> AddPlexMovie(PlexLibrary library, PlexMovie item) { try { item.LibraryPathId = library.Paths.Head().Id; - await _dbContext.PlexMovieMediaItems.AddAsync(item); + await _dbContext.PlexMovies.AddAsync(item); await _dbContext.SaveChangesAsync(); await _dbContext.Entry(item).Reference(i => i.LibraryPath).LoadAsync(); return item; diff --git a/ErsatzTV.Infrastructure/Data/TvContext.cs b/ErsatzTV.Infrastructure/Data/TvContext.cs index f809f0f85..e77cd5582 100644 --- a/ErsatzTV.Infrastructure/Data/TvContext.cs +++ b/ErsatzTV.Infrastructure/Data/TvContext.cs @@ -31,7 +31,7 @@ namespace ErsatzTV.Infrastructure.Data public DbSet Seasons { get; set; } public DbSet Episodes { get; set; } public DbSet EpisodeMetadata { get; set; } - public DbSet PlexMovieMediaItems { get; set; } + public DbSet PlexMovies { get; set; } public DbSet Collections { get; set; } public DbSet ProgramSchedules { get; set; } public DbSet Playouts { get; set; } diff --git a/ErsatzTV.Infrastructure/Migrations/20210228210815_Add_PlexMediaFile.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210228210815_Add_PlexMediaFile.Designer.cs new file mode 100644 index 000000000..db5e39c30 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228210815_Add_PlexMediaFile.Designer.cs @@ -0,0 +1,1456 @@ +// +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("20210228210815_Add_PlexMediaFile")] + partial class Add_PlexMediaFile + { + 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.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.PlexMediaFile", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaFile"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.ToTable("PlexMediaFile"); + }); + + 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.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.PlexMediaFile", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaFile", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaFile", "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(); + }); + + 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/20210228210815_Add_PlexMediaFile.cs b/ErsatzTV.Infrastructure/Migrations/20210228210815_Add_PlexMediaFile.cs new file mode 100644 index 000000000..733829a9d --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210228210815_Add_PlexMediaFile.cs @@ -0,0 +1,84 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Add_PlexMediaFile : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + "FK_PlexMovie_PlexMediaItemPart_PartId", + "PlexMovie"); + + migrationBuilder.DropTable( + "PlexMediaItemPart"); + + migrationBuilder.DropIndex( + "IX_PlexMovie_PartId", + "PlexMovie"); + + migrationBuilder.DropColumn( + "PartId", + "PlexMovie"); + + migrationBuilder.CreateTable( + "PlexMediaFile", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + PlexId = table.Column("INTEGER", nullable: false), + Key = table.Column("TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PlexMediaFile", x => x.Id); + table.ForeignKey( + "FK_PlexMediaFile_MediaFile_Id", + x => x.Id, + "MediaFile", + "Id", + onDelete: ReferentialAction.Cascade); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + "PlexMediaFile"); + + migrationBuilder.AddColumn( + "PartId", + "PlexMovie", + "INTEGER", + nullable: true); + + migrationBuilder.CreateTable( + "PlexMediaItemPart", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Duration = table.Column("INTEGER", nullable: false), + File = table.Column("TEXT", nullable: true), + Key = table.Column("TEXT", nullable: true), + PlexId = table.Column("INTEGER", nullable: false), + Size = table.Column("INTEGER", nullable: false) + }, + constraints: table => { table.PrimaryKey("PK_PlexMediaItemPart", x => x.Id); }); + + migrationBuilder.CreateIndex( + "IX_PlexMovie_PartId", + "PlexMovie", + "PartId"); + + migrationBuilder.AddForeignKey( + "FK_PlexMovie_PlexMediaItemPart_PartId", + "PlexMovie", + "PartId", + "PlexMediaItemPart", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs b/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs index 3bdb826ad..19106d621 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))] - partial class TvContextModelSnapshot : ModelSnapshot + internal class TvContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { @@ -16,7 +16,9 @@ namespace ErsatzTV.Infrastructure.Migrations modelBuilder .HasAnnotation("ProductVersion", "5.0.3"); - modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Artwork", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -64,7 +66,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Artwork"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Channel", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -95,7 +99,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Channel"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Collection", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -109,7 +115,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Collection"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.CollectionItem", + b => { b.Property("CollectionId") .HasColumnType("INTEGER"); @@ -124,7 +132,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("CollectionItem"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.ConfigElement", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -144,7 +154,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("ConfigElement"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.EpisodeMetadata", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -193,7 +205,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("EpisodeMetadata"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.FFmpegProfile", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -257,7 +271,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("FFmpegProfile"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Library", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -282,7 +298,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Library"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.LibraryPath", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -301,7 +319,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("LibraryPath"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.MediaFile", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -323,7 +343,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("MediaFile"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.MediaItem", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -339,7 +361,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("MediaItem"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.MediaSource", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -350,7 +374,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("MediaSource"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.MediaVersion", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -404,7 +430,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("MediaVersion"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.MovieMetadata", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -453,7 +481,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("MovieMetadata"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Playout", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -477,7 +507,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Playout"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlayoutItem", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -504,7 +536,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("PlayoutItem"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -538,7 +572,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("PlayoutProgramScheduleAnchor"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlexConnection", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -560,33 +596,9 @@ namespace ErsatzTV.Infrastructure.Migrations 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 => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.ProgramSchedule", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -606,7 +618,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("ProgramSchedule"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.ProgramScheduleItem", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -641,7 +655,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("ProgramScheduleItem"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Resolution", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -661,7 +677,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Resolution"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.SeasonMetadata", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -704,7 +722,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("SeasonMetadata"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.ShowMetadata", + b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -753,14 +773,18 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("ShowMetadata"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.LocalLibrary", + b => { b.HasBaseType("ErsatzTV.Core.Domain.Library"); b.ToTable("LocalLibrary"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlexLibrary", + b => { b.HasBaseType("ErsatzTV.Core.Domain.Library"); @@ -773,7 +797,24 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("PlexLibrary"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlexMediaFile", + b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaFile"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("PlexId") + .HasColumnType("INTEGER"); + + b.ToTable("PlexMediaFile"); + }); + + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Episode", + b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); @@ -788,14 +829,18 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Episode"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Movie", + b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); b.ToTable("Movie"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Season", + b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); @@ -810,21 +855,27 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Season"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Show", + b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); b.ToTable("Show"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.LocalMediaSource", + b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); b.ToTable("LocalMediaSource"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlexMediaSource", + b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); @@ -840,7 +891,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("PlexMediaSource"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.ProgramScheduleItemDuration", + b => { b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); @@ -853,14 +906,18 @@ namespace ErsatzTV.Infrastructure.Migrations 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("ProgramScheduleFloodItem"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", + b => { b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); @@ -870,29 +927,30 @@ namespace ErsatzTV.Infrastructure.Migrations 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("ProgramScheduleOneItem"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + 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 => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Artwork", + b => { b.HasOne("ErsatzTV.Core.Domain.Channel", null) .WithMany("Artwork") @@ -920,7 +978,9 @@ namespace ErsatzTV.Infrastructure.Migrations .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() @@ -931,7 +991,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("FFmpegProfile"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.CollectionItem", + b => { b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") .WithMany("CollectionItems") @@ -950,7 +1012,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("MediaItem"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.EpisodeMetadata", + b => { b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") .WithMany("EpisodeMetadata") @@ -961,7 +1025,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Episode"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.FFmpegProfile", + b => { b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") .WithMany() @@ -972,7 +1038,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Resolution"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Library", + b => { b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") .WithMany("Libraries") @@ -983,7 +1051,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("MediaSource"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.LibraryPath", + b => { b.HasOne("ErsatzTV.Core.Domain.Library", "Library") .WithMany("Paths") @@ -994,7 +1064,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Library"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.MediaFile", + b => { b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") .WithMany("MediaFiles") @@ -1005,7 +1077,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("MediaVersion"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.MediaItem", + b => { b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") .WithMany("MediaItems") @@ -1016,7 +1090,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("LibraryPath"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.MediaVersion", + b => { b.HasOne("ErsatzTV.Core.Domain.Episode", null) .WithMany("MediaVersions") @@ -1029,7 +1105,9 @@ namespace ErsatzTV.Infrastructure.Migrations .OnDelete(DeleteBehavior.Cascade); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.MovieMetadata", + b => { b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") .WithMany("MovieMetadata") @@ -1040,7 +1118,9 @@ namespace ErsatzTV.Infrastructure.Migrations 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") @@ -1054,7 +1134,10 @@ 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"); @@ -1090,7 +1173,9 @@ 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() @@ -1109,7 +1194,9 @@ 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() @@ -1133,7 +1220,10 @@ namespace ErsatzTV.Infrastructure.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => + b.OwnsOne( + "ErsatzTV.Core.Domain.CollectionEnumeratorState", + "EnumeratorState", + b1 => { b1.Property("PlayoutProgramScheduleAnchorId") .HasColumnType("INTEGER"); @@ -1163,7 +1253,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("ProgramSchedule"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlexConnection", + b => { b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") .WithMany("Connections") @@ -1174,7 +1266,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("PlexMediaSource"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.ProgramScheduleItem", + b => { b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") .WithMany() @@ -1199,7 +1293,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("ProgramSchedule"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.SeasonMetadata", + b => { b.HasOne("ErsatzTV.Core.Domain.Season", "Season") .WithMany("SeasonMetadata") @@ -1210,7 +1306,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Season"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.ShowMetadata", + b => { b.HasOne("ErsatzTV.Core.Domain.Show", "Show") .WithMany("ShowMetadata") @@ -1221,7 +1319,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Show"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.LocalLibrary", + b => { b.HasOne("ErsatzTV.Core.Domain.Library", null) .WithOne() @@ -1230,7 +1330,9 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlexLibrary", + b => { b.HasOne("ErsatzTV.Core.Domain.Library", null) .WithOne() @@ -1239,7 +1341,20 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlexMediaFile", + b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaFile", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.PlexMediaFile", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Episode", + b => { b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) .WithOne() @@ -1256,7 +1371,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Season"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Movie", + b => { b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) .WithOne() @@ -1265,7 +1382,9 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Season", + b => { b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) .WithOne() @@ -1282,7 +1401,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Show"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Show", + b => { b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) .WithOne() @@ -1291,7 +1412,9 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.LocalMediaSource", + b => { b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) .WithOne() @@ -1300,7 +1423,9 @@ 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() @@ -1309,7 +1434,9 @@ 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() @@ -1318,7 +1445,9 @@ 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() @@ -1327,7 +1456,9 @@ 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() @@ -1336,7 +1467,9 @@ 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() @@ -1345,124 +1478,101 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => + 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 => + 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.Collection", b => { b.Navigation("CollectionItems"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => - { - b.Navigation("Artwork"); - }); + 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.Library", b => { b.Navigation("Paths"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => - { - b.Navigation("MediaItems"); - }); + 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.MediaItem", b => { b.Navigation("CollectionItems"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => - { - b.Navigation("Libraries"); - }); + 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.MediaVersion", b => { b.Navigation("MediaFiles"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => - { - b.Navigation("Artwork"); - }); + 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.SeasonMetadata", b => - { - b.Navigation("Artwork"); - }); + 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.ShowMetadata", b => { b.Navigation("Artwork"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Episode", + b => { b.Navigation("EpisodeMetadata"); b.Navigation("MediaVersions"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Movie", + b => { b.Navigation("MediaVersions"); b.Navigation("MovieMetadata"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Season", + b => { b.Navigation("Episodes"); b.Navigation("SeasonMetadata"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => + modelBuilder.Entity( + "ErsatzTV.Core.Domain.Show", + b => { b.Navigation("Seasons"); b.Navigation("ShowMetadata"); }); - modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => - { - b.Navigation("Connections"); - }); + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => { b.Navigation("Connections"); }); #pragma warning restore 612, 618 } } diff --git a/ErsatzTV.Infrastructure/Plex/Models/PlexLibraryResponse.cs b/ErsatzTV.Infrastructure/Plex/Models/PlexLibraryResponse.cs index ada996f88..45602b36f 100644 --- a/ErsatzTV.Infrastructure/Plex/Models/PlexLibraryResponse.cs +++ b/ErsatzTV.Infrastructure/Plex/Models/PlexLibraryResponse.cs @@ -6,5 +6,6 @@ public string Title { get; set; } public string Type { get; set; } public int Hidden { get; set; } + public string Uuid { get; set; } } } diff --git a/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs b/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs index 1be8594ca..058063743 100644 --- a/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs +++ b/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs @@ -62,14 +62,16 @@ namespace ErsatzTV.Infrastructure.Plex Key = response.Key, Name = response.Title, MediaKind = LibraryMediaKind.Shows, - ShouldSyncItems = false + ShouldSyncItems = false, + Paths = new List { new() { Path = $"plex://{response.Uuid}" } } }, "movie" => new PlexLibrary { Key = response.Key, Name = response.Title, MediaKind = LibraryMediaKind.Movies, - ShouldSyncItems = false + ShouldSyncItems = false, + Paths = new List { new() { Path = $"plex://{response.Uuid}" } } }, // TODO: "artist" for music libraries _ => None @@ -127,40 +129,40 @@ namespace ErsatzTV.Infrastructure.Plex { Title = response.Title, Plot = response.Summary, - ReleaseDate = new DateTime(response.Year, 1, 1), // TODO: actual release date? + ReleaseDate = DateTime.Parse(response.OriginallyAvailableAt), Year = response.Year, Tagline = response.Tagline, - DateAdded = DateTime.UtcNow, // TODO: actual date added? + DateAdded = DateTimeOffset.FromUnixTimeSeconds(response.AddedAt).DateTime, DateUpdated = lastWriteTime }; // TODO: artwork + var version = new MediaVersion + { + Name = "Main", + Duration = TimeSpan.FromMilliseconds(media.Duration), + Width = media.Width, + Height = media.Height, + AudioCodec = media.AudioCodec, + VideoCodec = media.VideoCodec, + // TODO: aspect ratio + MediaFiles = new List + { + new PlexMediaFile + { + PlexId = part.Id, + Key = part.Key, + Path = part.File + } + } + }; + 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 - } + MediaVersions = new List { version } }; return movie; diff --git a/ErsatzTV/Pages/Libraries.razor b/ErsatzTV/Pages/Libraries.razor index 3efdd98fa..6cfd2e1aa 100644 --- a/ErsatzTV/Pages/Libraries.razor +++ b/ErsatzTV/Pages/Libraries.razor @@ -2,6 +2,7 @@ @using ErsatzTV.Application.Libraries @using ErsatzTV.Application.Libraries.Queries @using ErsatzTV.Application.MediaSources.Commands +@using ErsatzTV.Application.Plex.Commands @implements IDisposable @inject IMediator Mediator @inject IEntityLocker Locker @@ -25,7 +26,7 @@ - Local + @context.LibraryKind @context.Name @context.MediaKind @@ -61,7 +62,7 @@ @code { - private IList _libraries; + private IList _libraries; protected override void OnInitialized() => Locker.OnLibraryChanged += LockChanged; @@ -69,13 +70,22 @@ protected override async Task OnParametersSetAsync() => await LoadLibraries(); private async Task LoadLibraries() => - _libraries = await Mediator.Send(new GetLocalLibraries()); + _libraries = await Mediator.Send(new GetAllLibraries()); - private async Task ScanLibrary(LocalLibraryViewModel library) + private async Task ScanLibrary(LibraryViewModel library) { if (Locker.LockLibrary(library.Id)) { - await Channel.WriteAsync(new ForceScanLocalLibrary(library.Id)); + switch (library) + { + case LocalLibraryViewModel: + await Channel.WriteAsync(new ForceScanLocalLibrary(library.Id)); + break; + case PlexLibraryViewModel: + await Channel.WriteAsync(new ForceSynchronizePlexLibraryById(library.Id)); + break; + } + StateHasChanged(); } }