diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollection.cs new file mode 100644 index 00000000..a3d1a2d7 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollection.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using ErsatzTV.Core; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public record AddItemsToSimpleMediaCollection + (int MediaCollectionId, List ItemIds) : MediatR.IRequest>; +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollectionHandler.cs new file mode 100644 index 00000000..a94211d8 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollectionHandler.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +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 static LanguageExt.Prelude; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public class + AddItemsToSimpleMediaCollectionHandler : MediatR.IRequestHandler> + { + private readonly IMediaCollectionRepository _mediaCollectionRepository; + private readonly IMediaItemRepository _mediaItemRepository; + + public AddItemsToSimpleMediaCollectionHandler( + IMediaCollectionRepository mediaCollectionRepository, + IMediaItemRepository mediaItemRepository) + { + _mediaCollectionRepository = mediaCollectionRepository; + _mediaItemRepository = mediaItemRepository; + } + + public Task> Handle( + AddItemsToSimpleMediaCollection request, + CancellationToken cancellationToken) => + Validate(request) + .MapT(ApplyAddItemsRequest) + .Bind(v => v.ToEitherAsync()); + + private async Task ApplyAddItemsRequest(RequestParameters parameters) + { + foreach (MediaItem item in parameters.ItemsToAdd.Where( + item => parameters.Collection.Items.All(i => i.Id != item.Id))) + { + parameters.Collection.Items.Add(item); + } + + await _mediaCollectionRepository.Update(parameters.Collection); + + return Unit.Default; + } + + private async Task> + Validate(AddItemsToSimpleMediaCollection request) => + (await SimpleMediaCollectionMustExist(request), await ValidateItems(request)) + .Apply( + (simpleMediaCollectionToUpdate, itemsToAdd) => + new RequestParameters(simpleMediaCollectionToUpdate, itemsToAdd)); + + private Task> SimpleMediaCollectionMustExist( + AddItemsToSimpleMediaCollection updateSimpleMediaCollection) => + _mediaCollectionRepository.GetSimpleMediaCollection(updateSimpleMediaCollection.MediaCollectionId) + .Map(v => v.ToValidation("SimpleMediaCollection does not exist.")); + + private Task>> ValidateItems( + AddItemsToSimpleMediaCollection request) => + LoadAllMediaItems(request) + .Map(v => v.ToValidation("MediaItem does not exist")); + + private async Task>> LoadAllMediaItems(AddItemsToSimpleMediaCollection request) + { + var items = (await request.ItemIds.Map(async id => await _mediaItemRepository.Get(id)).Sequence()) + .ToList(); + if (items.Any(i => i.IsNone)) + { + return None; + } + + return items.Somes().ToList(); + } + + private record RequestParameters(SimpleMediaCollection Collection, List ItemsToAdd); + } +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateChannelHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateSimpleMediaCollectionHandler.cs similarity index 100% rename from ErsatzTV.Application/MediaCollections/Commands/UpdateChannelHandler.cs rename to ErsatzTV.Application/MediaCollections/Commands/UpdateSimpleMediaCollectionHandler.cs diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsById.cs b/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsById.cs new file mode 100644 index 00000000..470ff51a --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsById.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using ErsatzTV.Application.MediaItems; +using LanguageExt; +using MediatR; + +namespace ErsatzTV.Application.MediaCollections.Queries +{ + public record GetSimpleMediaCollectionWithItemsById + (int Id) : IRequest>>>; +} diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsByIdHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsByIdHandler.cs new file mode 100644 index 00000000..11f9f79b --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsByIdHandler.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Application.MediaItems; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using static LanguageExt.Prelude; +using static ErsatzTV.Application.MediaCollections.Mapper; +using static ErsatzTV.Application.MediaItems.Mapper; + +namespace ErsatzTV.Application.MediaCollections.Queries +{ + public class GetSimpleMediaCollectionWithItemsByIdHandler : IRequestHandler>>> + { + private readonly IMediaCollectionRepository _mediaCollectionRepository; + + public GetSimpleMediaCollectionWithItemsByIdHandler(IMediaCollectionRepository mediaCollectionRepository) => + _mediaCollectionRepository = mediaCollectionRepository; + + public async Task>>> Handle( + GetSimpleMediaCollectionWithItemsById request, + CancellationToken cancellationToken) + { + Option maybeCollection = + await _mediaCollectionRepository.GetSimpleMediaCollectionWithItems(request.Id); + + return maybeCollection.Match>>>( + c => Tuple(ProjectToViewModel(c), c.Items.Map(ProjectToSearchViewModel).ToList()), + None); + } + } +} diff --git a/ErsatzTV.Application/MediaItems/Mapper.cs b/ErsatzTV.Application/MediaItems/Mapper.cs index fdac1781..e72cd160 100644 --- a/ErsatzTV.Application/MediaItems/Mapper.cs +++ b/ErsatzTV.Application/MediaItems/Mapper.cs @@ -1,4 +1,5 @@ using ErsatzTV.Core.Domain; +using static LanguageExt.Prelude; namespace ErsatzTV.Application.MediaItems { @@ -9,5 +10,26 @@ namespace ErsatzTV.Application.MediaItems mediaItem.Id, mediaItem.MediaSourceId, mediaItem.Path); + + internal static MediaItemSearchResultViewModel ProjectToSearchViewModel(MediaItem mediaItem) => + new( + mediaItem.Id, + mediaItem.Source.Name, + mediaItem.Metadata.MediaType.ToString(), + GetDisplayTitle(mediaItem), + GetDisplayDuration(mediaItem)); + + + private static string GetDisplayTitle(this MediaItem mediaItem) => + mediaItem.Metadata.MediaType == MediaType.TvShow && + Optional(mediaItem.Metadata.SeasonNumber).IsSome && + Optional(mediaItem.Metadata.EpisodeNumber).IsSome + ? $"{mediaItem.Metadata.Title} s{mediaItem.Metadata.SeasonNumber:00}e{mediaItem.Metadata.EpisodeNumber:00}" + : mediaItem.Metadata.Title; + + private static string GetDisplayDuration(MediaItem mediaItem) => + string.Format( + mediaItem.Metadata.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}", + mediaItem.Metadata.Duration); } } diff --git a/ErsatzTV.Application/MediaItems/MediaItemSearchResultViewModel.cs b/ErsatzTV.Application/MediaItems/MediaItemSearchResultViewModel.cs new file mode 100644 index 00000000..6ff72c91 --- /dev/null +++ b/ErsatzTV.Application/MediaItems/MediaItemSearchResultViewModel.cs @@ -0,0 +1,9 @@ +namespace ErsatzTV.Application.MediaItems +{ + public record MediaItemSearchResultViewModel( + int Id, + string Source, + string MediaType, + string Title, + string Duration); +} diff --git a/ErsatzTV.Application/MediaItems/Queries/GetAllMediaItemsHandler.cs b/ErsatzTV.Application/MediaItems/Queries/GetAllMediaItemsHandler.cs index 2806e91d..e646db23 100644 --- a/ErsatzTV.Application/MediaItems/Queries/GetAllMediaItemsHandler.cs +++ b/ErsatzTV.Application/MediaItems/Queries/GetAllMediaItemsHandler.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; using MediatR; using static ErsatzTV.Application.MediaItems.Mapper; @@ -15,9 +16,9 @@ namespace ErsatzTV.Application.MediaItems.Queries public GetAllMediaItemsHandler(IMediaItemRepository mediaItemRepository) => _mediaItemRepository = mediaItemRepository; - public async Task> Handle( + public Task> Handle( GetAllMediaItems request, CancellationToken cancellationToken) => - (await _mediaItemRepository.GetAll()).Map(ProjectToViewModel).ToList(); + _mediaItemRepository.GetAll().Map(list => list.Map(ProjectToViewModel).ToList()); } } diff --git a/ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItems.cs b/ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItems.cs new file mode 100644 index 00000000..f4227717 --- /dev/null +++ b/ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItems.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; +using MediatR; + +namespace ErsatzTV.Application.MediaItems.Queries +{ + public record SearchAllMediaItems(string SearchString) : IRequest>; +} diff --git a/ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItemsHandler.cs b/ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItemsHandler.cs new file mode 100644 index 00000000..52005df9 --- /dev/null +++ b/ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItemsHandler.cs @@ -0,0 +1,23 @@ +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.MediaItems.Mapper; + +namespace ErsatzTV.Application.MediaItems.Queries +{ + public class SearchAllMediaItemsHandler : IRequestHandler> + { + private readonly IMediaItemRepository _mediaItemRepository; + + public SearchAllMediaItemsHandler(IMediaItemRepository mediaItemRepository) => + _mediaItemRepository = mediaItemRepository; + + public Task> + Handle(SearchAllMediaItems request, CancellationToken cancellationToken) => + _mediaItemRepository.Search(request.SearchString).Map(list => list.Map(ProjectToSearchViewModel).ToList()); + } +} diff --git a/ErsatzTV.Application/Playouts/Mapper.cs b/ErsatzTV.Application/Playouts/Mapper.cs index 7aba77ac..93793982 100644 --- a/ErsatzTV.Application/Playouts/Mapper.cs +++ b/ErsatzTV.Application/Playouts/Mapper.cs @@ -28,7 +28,7 @@ namespace ErsatzTV.Application.Playouts ? $"{mediaItem.Metadata.Title} s{mediaItem.Metadata.SeasonNumber:00}e{mediaItem.Metadata.EpisodeNumber:00}" : mediaItem.Metadata.Title; - public static string GetDisplayDuration(MediaItem mediaItem) => + private static string GetDisplayDuration(MediaItem mediaItem) => string.Format( mediaItem.Metadata.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}", mediaItem.Metadata.Duration); diff --git a/ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs b/ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs index ecf65ee2..be4e22d7 100644 --- a/ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs +++ b/ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs @@ -22,6 +22,9 @@ namespace ErsatzTV.Core.Tests.Fakes public Task> GetSimpleMediaCollection(int id) => throw new NotSupportedException(); + public Task> GetSimpleMediaCollectionWithItems(int id) => + throw new NotSupportedException(); + public Task> GetTelevisionMediaCollection(int id) => throw new NotSupportedException(); diff --git a/ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs index 4ce9801c..afc86643 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs @@ -11,6 +11,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories public Task Add(SimpleMediaCollection collection); public Task> Get(int id); public Task> GetSimpleMediaCollection(int id); + public Task> GetSimpleMediaCollectionWithItems(int id); public Task> GetTelevisionMediaCollection(int id); public Task> GetSimpleMediaCollections(); public Task> GetAll(); diff --git a/ErsatzTV.Core/Interfaces/Repositories/IMediaItemRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IMediaItemRepository.cs index 93df278c..9c6961c4 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IMediaItemRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IMediaItemRepository.cs @@ -10,6 +10,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories public Task Add(MediaItem mediaItem); public Task> Get(int id); public Task> GetAll(); + public Task> Search(string searchString); public Task> GetAll(MediaType mediaType); public Task> GetAllByMediaSourceId(int mediaSourceId); public Task Update(MediaItem mediaItem); diff --git a/ErsatzTV.Core/Iptv/ChannelGuide.cs b/ErsatzTV.Core/Iptv/ChannelGuide.cs index 8465ff14..f4f71b88 100644 --- a/ErsatzTV.Core/Iptv/ChannelGuide.cs +++ b/ErsatzTV.Core/Iptv/ChannelGuide.cs @@ -26,15 +26,15 @@ namespace ErsatzTV.Core.Iptv using var ms = new MemoryStream(); using var xml = XmlWriter.Create(ms); xml.WriteStartDocument(); - + xml.WriteStartElement("tv"); xml.WriteAttributeString("generator-info-name", "ersatztv"); - + foreach (Channel channel in _channels) { xml.WriteStartElement("channel"); xml.WriteAttributeString("id", channel.Number.ToString()); - + xml.WriteStartElement("display-name"); xml.WriteAttributeString("lang", "en"); xml.WriteString(channel.Name); @@ -72,7 +72,7 @@ namespace ErsatzTV.Core.Iptv xml.WriteAttributeString("lang", "en"); xml.WriteString(metadata.Title); xml.WriteEndElement(); // title - + xml.WriteStartElement("previously-shown"); xml.WriteEndElement(); // previously-shown diff --git a/ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs b/ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs index cb611e4f..745b49a7 100644 --- a/ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs +++ b/ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs @@ -15,10 +15,12 @@ namespace ErsatzTV.Core.Metadata { public class LocalStatisticsProvider : ILocalStatisticsProvider { - private readonly IMediaItemRepository _mediaItemRepository; private readonly ILogger _logger; + private readonly IMediaItemRepository _mediaItemRepository; - public LocalStatisticsProvider(IMediaItemRepository mediaItemRepository, ILogger logger) + public LocalStatisticsProvider( + IMediaItemRepository mediaItemRepository, + ILogger logger) { _mediaItemRepository = mediaItemRepository; _logger = logger; diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs index 7d2052a1..e767108a 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs @@ -29,6 +29,13 @@ namespace ErsatzTV.Infrastructure.Data.Repositories public Task> GetSimpleMediaCollection(int id) => Get(id).Map(c => c.OfType().HeadOrNone()); + public Task> GetSimpleMediaCollectionWithItems(int id) => + _dbContext.SimpleMediaCollections + .Include(s => s.Items) + .ThenInclude(i => i.Source) + .SingleOrDefaultAsync(c => c.Id == id) + .Map(Optional); + public Task> GetTelevisionMediaCollection(int id) => Get(id).Map(c => c.OfType().HeadOrNone()); diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs index 92c85f6b..670b67d8 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.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; @@ -26,6 +27,19 @@ namespace ErsatzTV.Infrastructure.Data.Repositories public Task> GetAll() => _dbContext.MediaItems.ToListAsync(); + public Task> Search(string searchString) + { + IQueryable data = from c in _dbContext.MediaItems.Include(c => c.Source) select c; + + if (!string.IsNullOrEmpty(searchString)) + { + data = data.Where(c => EF.Functions.Like(c.Metadata.Title, $"%{searchString}%")); + } + + return data.ToListAsync(); + } + + public Task> GetAll(MediaType mediaType) => _dbContext.MediaItems .Include(i => i.Source) diff --git a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj index f0159b15..6f4df44d 100644 --- a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj +++ b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj @@ -2,11 +2,16 @@ net5.0 + true - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/ErsatzTV.Infrastructure/Migrations/20210212105010_Initial.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210212105010_Initial.Designer.cs new file mode 100644 index 00000000..d58578ac --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210212105010_Initial.Designer.cs @@ -0,0 +1,876 @@ +// +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("20210212105010_Initial")] + partial class Initial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("ErsatzTV.Core.AggregateModels.GenericIntegerId", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.ToTable("GenericIntegerIds"); + }); + + modelBuilder.Entity("ErsatzTV.Core.AggregateModels.MediaCollectionSummary", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("IsSimple") + .HasColumnType("INTEGER"); + + b.Property("ItemCount") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.ToTable("MediaCollectionSummaries"); + }); + + 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.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("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("MediaItems"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SourceType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaSources"); + }); + + 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("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.HasKey("PlayoutId", "ProgramScheduleId", "MediaCollectionId"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleItemAnchors"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSourceConnection", 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("PlexMediaSourceConnections"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSourceLibrary", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("MediaType") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexMediaSourceLibraries"); + }); + + 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("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("ProgramScheduleId"); + + 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("MediaItemSimpleMediaCollection", b => + { + b.Property("ItemsId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("ItemsId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("MediaItemSimpleMediaCollection"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollections"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionMediaCollection", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowTitle") + .HasColumnType("TEXT"); + + b.HasIndex("ShowTitle", "SeasonNumber") + .IsUnique(); + + b.ToTable("TelevisionMediaCollections"); + }); + + 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"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSources"); + }); + + 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.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.MediaItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "Source") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("ErsatzTV.Core.Domain.MediaMetadata", "Metadata", b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("Aired") + .HasColumnType("TEXT"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("ContentRating") + .HasColumnType("TEXT"); + + b1.Property("Description") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("MediaType") + .HasColumnType("INTEGER"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b1.Property("Subtitle") + .HasColumnType("TEXT"); + + b1.Property("Title") + .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("Metadata"); + + b.Navigation("Source"); + }); + + 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.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + 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("PlayoutProgramScheduleAnchorPlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("PlayoutProgramScheduleAnchorProgramScheduleId") + .HasColumnType("INTEGER"); + + b1.Property("PlayoutProgramScheduleAnchorMediaCollectionId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey("PlayoutProgramScheduleAnchorPlayoutId", "PlayoutProgramScheduleAnchorProgramScheduleId", "PlayoutProgramScheduleAnchorMediaCollectionId"); + + b1.ToTable("PlayoutProgramScheduleItemAnchors"); + + b1.WithOwner() + .HasForeignKey("PlayoutProgramScheduleAnchorPlayoutId", "PlayoutProgramScheduleAnchorProgramScheduleId", "PlayoutProgramScheduleAnchorMediaCollectionId"); + }); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaCollection"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSourceConnection", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", null) + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSourceLibrary", b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", null) + .WithMany("Libraries") + .HasForeignKey("PlexMediaSourceId"); + }); + + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaCollection"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity("MediaItemSimpleMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithMany() + .HasForeignKey("ItemsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .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.TelevisionMediaCollection", b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionMediaCollection", "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.Channel", b => + { + b.Navigation("Playouts"); + }); + + 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.PlexMediaSource", b => + { + b.Navigation("Connections"); + + b.Navigation("Libraries"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/20210212105010_Initial.cs b/ErsatzTV.Infrastructure/Migrations/20210212105010_Initial.cs new file mode 100644 index 00000000..c1281deb --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/20210212105010_Initial.cs @@ -0,0 +1,718 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ErsatzTV.Infrastructure.Migrations +{ + public partial class Initial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + "ConfigElements", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Key = table.Column("TEXT", nullable: true), + Value = table.Column("TEXT", nullable: true) + }, + constraints: table => { table.PrimaryKey("PK_ConfigElements", x => x.Id); }); + + migrationBuilder.CreateTable( + "GenericIntegerIds", + table => new + { + Id = table.Column("INTEGER", nullable: false) + }, + constraints: table => { }); + + migrationBuilder.CreateTable( + "MediaCollections", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column("TEXT", nullable: true) + }, + constraints: table => { table.PrimaryKey("PK_MediaCollections", x => x.Id); }); + + migrationBuilder.CreateTable( + "MediaCollectionSummaries", + table => new + { + Id = table.Column("INTEGER", nullable: false), + Name = table.Column("TEXT", nullable: true), + ItemCount = table.Column("INTEGER", nullable: false), + IsSimple = table.Column("INTEGER", nullable: false) + }, + constraints: table => { }); + + migrationBuilder.CreateTable( + "MediaSources", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + SourceType = table.Column("INTEGER", nullable: false), + Name = table.Column("TEXT", nullable: true) + }, + constraints: table => { table.PrimaryKey("PK_MediaSources", x => x.Id); }); + + migrationBuilder.CreateTable( + "ProgramSchedules", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column("TEXT", nullable: true), + MediaCollectionPlaybackOrder = table.Column("INTEGER", nullable: false) + }, + constraints: table => { table.PrimaryKey("PK_ProgramSchedules", x => x.Id); }); + + migrationBuilder.CreateTable( + "Resolutions", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column("TEXT", nullable: true), + Height = table.Column("INTEGER", nullable: false), + Width = table.Column("INTEGER", nullable: false) + }, + constraints: table => { table.PrimaryKey("PK_Resolutions", x => x.Id); }); + + migrationBuilder.CreateTable( + "SimpleMediaCollections", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true) + }, + constraints: table => + { + table.PrimaryKey("PK_SimpleMediaCollections", x => x.Id); + table.ForeignKey( + "FK_SimpleMediaCollections_MediaCollections_Id", + x => x.Id, + "MediaCollections", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "TelevisionMediaCollections", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ShowTitle = table.Column("TEXT", nullable: true), + SeasonNumber = table.Column("INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TelevisionMediaCollections", x => x.Id); + table.ForeignKey( + "FK_TelevisionMediaCollections_MediaCollections_Id", + x => x.Id, + "MediaCollections", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "LocalMediaSources", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + MediaType = table.Column("INTEGER", nullable: false), + Folder = table.Column("TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_LocalMediaSources", x => x.Id); + table.ForeignKey( + "FK_LocalMediaSources_MediaSources_Id", + x => x.Id, + "MediaSources", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "MediaItems", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + MediaSourceId = table.Column("INTEGER", nullable: false), + Path = table.Column("TEXT", nullable: true), + Metadata_Duration = table.Column("TEXT", nullable: true), + Metadata_SampleAspectRatio = table.Column("TEXT", nullable: true), + Metadata_DisplayAspectRatio = table.Column("TEXT", nullable: true), + Metadata_VideoCodec = table.Column("TEXT", nullable: true), + Metadata_AudioCodec = table.Column("TEXT", nullable: true), + Metadata_MediaType = table.Column("INTEGER", nullable: true), + Metadata_Title = table.Column("TEXT", nullable: true), + Metadata_Subtitle = table.Column("TEXT", nullable: true), + Metadata_Description = table.Column("TEXT", nullable: true), + Metadata_SeasonNumber = table.Column("INTEGER", nullable: true), + Metadata_EpisodeNumber = table.Column("INTEGER", nullable: true), + Metadata_ContentRating = table.Column("TEXT", nullable: true), + Metadata_Aired = table.Column("TEXT", nullable: true), + Metadata_VideoScanType = table.Column("INTEGER", nullable: true), + Metadata_Width = table.Column("INTEGER", nullable: true), + Metadata_Height = table.Column("INTEGER", nullable: true), + LastWriteTime = table.Column("TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MediaItems", x => x.Id); + table.ForeignKey( + "FK_MediaItems_MediaSources_MediaSourceId", + x => x.MediaSourceId, + "MediaSources", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "PlexMediaSources", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ProductVersion = table.Column("TEXT", nullable: true), + ClientIdentifier = table.Column("TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PlexMediaSources", x => x.Id); + table.ForeignKey( + "FK_PlexMediaSources_MediaSources_Id", + x => x.Id, + "MediaSources", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "ProgramScheduleItems", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Index = table.Column("INTEGER", nullable: false), + StartTime = table.Column("TEXT", nullable: true), + MediaCollectionId = table.Column("INTEGER", nullable: false), + ProgramScheduleId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ProgramScheduleItems", x => x.Id); + table.ForeignKey( + "FK_ProgramScheduleItems_MediaCollections_MediaCollectionId", + x => x.MediaCollectionId, + "MediaCollections", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_ProgramScheduleItems_ProgramSchedules_ProgramScheduleId", + x => x.ProgramScheduleId, + "ProgramSchedules", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "FFmpegProfiles", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column("TEXT", nullable: true), + ThreadCount = table.Column("INTEGER", nullable: false), + Transcode = table.Column("INTEGER", nullable: false), + ResolutionId = table.Column("INTEGER", nullable: false), + NormalizeResolution = table.Column("INTEGER", nullable: false), + VideoCodec = table.Column("TEXT", nullable: true), + NormalizeVideoCodec = table.Column("INTEGER", nullable: false), + VideoBitrate = table.Column("INTEGER", nullable: false), + VideoBufferSize = table.Column("INTEGER", nullable: false), + AudioCodec = table.Column("TEXT", nullable: true), + NormalizeAudioCodec = table.Column("INTEGER", nullable: false), + AudioBitrate = table.Column("INTEGER", nullable: false), + AudioBufferSize = table.Column("INTEGER", nullable: false), + AudioVolume = table.Column("INTEGER", nullable: false), + AudioChannels = table.Column("INTEGER", nullable: false), + AudioSampleRate = table.Column("INTEGER", nullable: false), + NormalizeAudio = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_FFmpegProfiles", x => x.Id); + table.ForeignKey( + "FK_FFmpegProfiles_Resolutions_ResolutionId", + x => x.ResolutionId, + "Resolutions", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "MediaItemSimpleMediaCollection", + table => new + { + ItemsId = table.Column("INTEGER", nullable: false), + SimpleMediaCollectionsId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey( + "PK_MediaItemSimpleMediaCollection", + x => new { x.ItemsId, x.SimpleMediaCollectionsId }); + table.ForeignKey( + "FK_MediaItemSimpleMediaCollection_MediaItems_ItemsId", + x => x.ItemsId, + "MediaItems", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_MediaItemSimpleMediaCollection_SimpleMediaCollections_SimpleMediaCollectionsId", + x => x.SimpleMediaCollectionsId, + "SimpleMediaCollections", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "PlexMediaSourceConnections", + 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: 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), + Name = table.Column("TEXT", nullable: true), + MediaType = table.Column("INTEGER", nullable: false), + 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.CreateTable( + "ProgramScheduleDurationItems", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + PlayoutDuration = table.Column("TEXT", nullable: false), + OfflineTail = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ProgramScheduleDurationItems", x => x.Id); + table.ForeignKey( + "FK_ProgramScheduleDurationItems_ProgramScheduleItems_Id", + x => x.Id, + "ProgramScheduleItems", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "ProgramScheduleFloodItems", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true) + }, + constraints: table => + { + table.PrimaryKey("PK_ProgramScheduleFloodItems", x => x.Id); + table.ForeignKey( + "FK_ProgramScheduleFloodItems_ProgramScheduleItems_Id", + x => x.Id, + "ProgramScheduleItems", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "ProgramScheduleMultipleItems", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Count = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ProgramScheduleMultipleItems", x => x.Id); + table.ForeignKey( + "FK_ProgramScheduleMultipleItems_ProgramScheduleItems_Id", + x => x.Id, + "ProgramScheduleItems", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "ProgramScheduleOneItems", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true) + }, + constraints: table => + { + table.PrimaryKey("PK_ProgramScheduleOneItems", x => x.Id); + table.ForeignKey( + "FK_ProgramScheduleOneItems_ProgramScheduleItems_Id", + x => x.Id, + "ProgramScheduleItems", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "Channels", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UniqueId = table.Column("TEXT", nullable: false), + Number = table.Column("INTEGER", nullable: false), + Name = table.Column("TEXT", nullable: true), + Logo = table.Column("TEXT", nullable: true), + FFmpegProfileId = table.Column("INTEGER", nullable: false), + StreamingMode = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Channels", x => x.Id); + table.ForeignKey( + "FK_Channels_FFmpegProfiles_FFmpegProfileId", + x => x.FFmpegProfileId, + "FFmpegProfiles", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "Playouts", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ChannelId = table.Column("INTEGER", nullable: false), + ProgramScheduleId = table.Column("INTEGER", nullable: false), + ProgramSchedulePlayoutType = table.Column("INTEGER", nullable: false), + Anchor_NextScheduleItemId = table.Column("INTEGER", nullable: true), + Anchor_NextStart = table.Column("TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Playouts", x => x.Id); + table.ForeignKey( + "FK_Playouts_Channels_ChannelId", + x => x.ChannelId, + "Channels", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_Playouts_ProgramScheduleItems_Anchor_NextScheduleItemId", + x => x.Anchor_NextScheduleItemId, + "ProgramScheduleItems", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_Playouts_ProgramSchedules_ProgramScheduleId", + x => x.ProgramScheduleId, + "ProgramSchedules", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "PlayoutItems", + table => new + { + Id = table.Column("INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + MediaItemId = table.Column("INTEGER", nullable: false), + Start = table.Column("TEXT", nullable: false), + Finish = table.Column("TEXT", nullable: false), + PlayoutId = table.Column("INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PlayoutItems", x => x.Id); + table.ForeignKey( + "FK_PlayoutItems_MediaItems_MediaItemId", + x => x.MediaItemId, + "MediaItems", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_PlayoutItems_Playouts_PlayoutId", + x => x.PlayoutId, + "Playouts", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + "PlayoutProgramScheduleItemAnchors", + table => new + { + PlayoutId = table.Column("INTEGER", nullable: false), + ProgramScheduleId = table.Column("INTEGER", nullable: false), + MediaCollectionId = table.Column("INTEGER", nullable: false), + EnumeratorState_Seed = table.Column("INTEGER", nullable: true), + EnumeratorState_Index = table.Column("INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey( + "PK_PlayoutProgramScheduleItemAnchors", + x => new { x.PlayoutId, x.ProgramScheduleId, x.MediaCollectionId }); + table.ForeignKey( + "FK_PlayoutProgramScheduleItemAnchors_MediaCollections_MediaCollectionId", + x => x.MediaCollectionId, + "MediaCollections", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_PlayoutProgramScheduleItemAnchors_Playouts_PlayoutId", + x => x.PlayoutId, + "Playouts", + "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + "FK_PlayoutProgramScheduleItemAnchors_ProgramSchedules_ProgramScheduleId", + x => x.ProgramScheduleId, + "ProgramSchedules", + "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + "IX_Channels_FFmpegProfileId", + "Channels", + "FFmpegProfileId"); + + migrationBuilder.CreateIndex( + "IX_Channels_Number", + "Channels", + "Number", + unique: true); + + migrationBuilder.CreateIndex( + "IX_ConfigElements_Key", + "ConfigElements", + "Key", + unique: true); + + migrationBuilder.CreateIndex( + "IX_FFmpegProfiles_ResolutionId", + "FFmpegProfiles", + "ResolutionId"); + + migrationBuilder.CreateIndex( + "IX_MediaCollections_Name", + "MediaCollections", + "Name", + unique: true); + + migrationBuilder.CreateIndex( + "IX_MediaItems_MediaSourceId", + "MediaItems", + "MediaSourceId"); + + migrationBuilder.CreateIndex( + "IX_MediaItemSimpleMediaCollection_SimpleMediaCollectionsId", + "MediaItemSimpleMediaCollection", + "SimpleMediaCollectionsId"); + + migrationBuilder.CreateIndex( + "IX_MediaSources_Name", + "MediaSources", + "Name", + unique: true); + + migrationBuilder.CreateIndex( + "IX_PlayoutItems_MediaItemId", + "PlayoutItems", + "MediaItemId"); + + migrationBuilder.CreateIndex( + "IX_PlayoutItems_PlayoutId", + "PlayoutItems", + "PlayoutId"); + + migrationBuilder.CreateIndex( + "IX_PlayoutProgramScheduleItemAnchors_MediaCollectionId", + "PlayoutProgramScheduleItemAnchors", + "MediaCollectionId"); + + migrationBuilder.CreateIndex( + "IX_PlayoutProgramScheduleItemAnchors_ProgramScheduleId", + "PlayoutProgramScheduleItemAnchors", + "ProgramScheduleId"); + + migrationBuilder.CreateIndex( + "IX_Playouts_Anchor_NextScheduleItemId", + "Playouts", + "Anchor_NextScheduleItemId"); + + migrationBuilder.CreateIndex( + "IX_Playouts_ChannelId", + "Playouts", + "ChannelId"); + + migrationBuilder.CreateIndex( + "IX_Playouts_ProgramScheduleId", + "Playouts", + "ProgramScheduleId"); + + migrationBuilder.CreateIndex( + "IX_PlexMediaSourceConnections_PlexMediaSourceId", + "PlexMediaSourceConnections", + "PlexMediaSourceId"); + + migrationBuilder.CreateIndex( + "IX_PlexMediaSourceLibraries_PlexMediaSourceId", + "PlexMediaSourceLibraries", + "PlexMediaSourceId"); + + migrationBuilder.CreateIndex( + "IX_ProgramScheduleItems_MediaCollectionId", + "ProgramScheduleItems", + "MediaCollectionId"); + + migrationBuilder.CreateIndex( + "IX_ProgramScheduleItems_ProgramScheduleId", + "ProgramScheduleItems", + "ProgramScheduleId"); + + migrationBuilder.CreateIndex( + "IX_ProgramSchedules_Name", + "ProgramSchedules", + "Name", + unique: true); + + migrationBuilder.CreateIndex( + "IX_TelevisionMediaCollections_ShowTitle_SeasonNumber", + "TelevisionMediaCollections", + new[] { "ShowTitle", "SeasonNumber" }, + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + "ConfigElements"); + + migrationBuilder.DropTable( + "GenericIntegerIds"); + + migrationBuilder.DropTable( + "LocalMediaSources"); + + migrationBuilder.DropTable( + "MediaCollectionSummaries"); + + migrationBuilder.DropTable( + "MediaItemSimpleMediaCollection"); + + migrationBuilder.DropTable( + "PlayoutItems"); + + migrationBuilder.DropTable( + "PlayoutProgramScheduleItemAnchors"); + + migrationBuilder.DropTable( + "PlexMediaSourceConnections"); + + migrationBuilder.DropTable( + "PlexMediaSourceLibraries"); + + migrationBuilder.DropTable( + "ProgramScheduleDurationItems"); + + migrationBuilder.DropTable( + "ProgramScheduleFloodItems"); + + migrationBuilder.DropTable( + "ProgramScheduleMultipleItems"); + + migrationBuilder.DropTable( + "ProgramScheduleOneItems"); + + migrationBuilder.DropTable( + "TelevisionMediaCollections"); + + migrationBuilder.DropTable( + "SimpleMediaCollections"); + + migrationBuilder.DropTable( + "MediaItems"); + + migrationBuilder.DropTable( + "Playouts"); + + migrationBuilder.DropTable( + "PlexMediaSources"); + + migrationBuilder.DropTable( + "Channels"); + + migrationBuilder.DropTable( + "ProgramScheduleItems"); + + migrationBuilder.DropTable( + "MediaSources"); + + migrationBuilder.DropTable( + "FFmpegProfiles"); + + migrationBuilder.DropTable( + "MediaCollections"); + + migrationBuilder.DropTable( + "ProgramSchedules"); + + migrationBuilder.DropTable( + "Resolutions"); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs b/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs new file mode 100644 index 00000000..252da1d5 --- /dev/null +++ b/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs @@ -0,0 +1,978 @@ +// + +using System; +using ErsatzTV.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace ErsatzTV.Infrastructure.Migrations +{ + [DbContext(typeof(TvContext))] + internal class TvContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity( + "ErsatzTV.Core.AggregateModels.GenericIntegerId", + b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.ToTable("GenericIntegerIds"); + }); + + modelBuilder.Entity( + "ErsatzTV.Core.AggregateModels.MediaCollectionSummary", + b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("IsSimple") + .HasColumnType("INTEGER"); + + b.Property("ItemCount") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.ToTable("MediaCollectionSummaries"); + }); + + 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.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("MediaSourceId") + .HasColumnType("INTEGER"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaSourceId"); + + b.ToTable("MediaItems"); + }); + + modelBuilder.Entity( + "ErsatzTV.Core.Domain.MediaSource", + b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SourceType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("MediaSources"); + }); + + 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("PlayoutId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.HasKey("PlayoutId", "ProgramScheduleId", "MediaCollectionId"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("ProgramScheduleId"); + + b.ToTable("PlayoutProgramScheduleItemAnchors"); + }); + + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlexMediaSourceConnection", + 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("PlexMediaSourceConnections"); + }); + + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlexMediaSourceLibrary", + b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("MediaType") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("PlexMediaSourceId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("PlexMediaSourceId"); + + b.ToTable("PlexMediaSourceLibraries"); + }); + + 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("Index") + .HasColumnType("INTEGER"); + + b.Property("MediaCollectionId") + .HasColumnType("INTEGER"); + + b.Property("ProgramScheduleId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MediaCollectionId"); + + b.HasIndex("ProgramScheduleId"); + + 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( + "MediaItemSimpleMediaCollection", + b => + { + b.Property("ItemsId") + .HasColumnType("INTEGER"); + + b.Property("SimpleMediaCollectionsId") + .HasColumnType("INTEGER"); + + b.HasKey("ItemsId", "SimpleMediaCollectionsId"); + + b.HasIndex("SimpleMediaCollectionsId"); + + b.ToTable("MediaItemSimpleMediaCollection"); + }); + + modelBuilder.Entity( + "ErsatzTV.Core.Domain.SimpleMediaCollection", + b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.ToTable("SimpleMediaCollections"); + }); + + modelBuilder.Entity( + "ErsatzTV.Core.Domain.TelevisionMediaCollection", + b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("ShowTitle") + .HasColumnType("TEXT"); + + b.HasIndex("ShowTitle", "SeasonNumber") + .IsUnique(); + + b.ToTable("TelevisionMediaCollections"); + }); + + 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"); + }); + + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlexMediaSource", + b => + { + b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); + + b.Property("ClientIdentifier") + .HasColumnType("TEXT"); + + b.Property("ProductVersion") + .HasColumnType("TEXT"); + + b.ToTable("PlexMediaSources"); + }); + + 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.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.MediaItem", + b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaSource", "Source") + .WithMany() + .HasForeignKey("MediaSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne( + "ErsatzTV.Core.Domain.MediaMetadata", + "Metadata", + b1 => + { + b1.Property("MediaItemId") + .HasColumnType("INTEGER"); + + b1.Property("Aired") + .HasColumnType("TEXT"); + + b1.Property("AudioCodec") + .HasColumnType("TEXT"); + + b1.Property("ContentRating") + .HasColumnType("TEXT"); + + b1.Property("Description") + .HasColumnType("TEXT"); + + b1.Property("DisplayAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("Duration") + .HasColumnType("TEXT"); + + b1.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b1.Property("Height") + .HasColumnType("INTEGER"); + + b1.Property("MediaType") + .HasColumnType("INTEGER"); + + b1.Property("SampleAspectRatio") + .HasColumnType("TEXT"); + + b1.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b1.Property("Subtitle") + .HasColumnType("TEXT"); + + b1.Property("Title") + .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("Metadata"); + + b.Navigation("Source"); + }); + + 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.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + 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("PlayoutProgramScheduleAnchorPlayoutId") + .HasColumnType("INTEGER"); + + b1.Property("PlayoutProgramScheduleAnchorProgramScheduleId") + .HasColumnType("INTEGER"); + + b1.Property("PlayoutProgramScheduleAnchorMediaCollectionId") + .HasColumnType("INTEGER"); + + b1.Property("Index") + .HasColumnType("INTEGER"); + + b1.Property("Seed") + .HasColumnType("INTEGER"); + + b1.HasKey( + "PlayoutProgramScheduleAnchorPlayoutId", + "PlayoutProgramScheduleAnchorProgramScheduleId", + "PlayoutProgramScheduleAnchorMediaCollectionId"); + + b1.ToTable("PlayoutProgramScheduleItemAnchors"); + + b1.WithOwner() + .HasForeignKey( + "PlayoutProgramScheduleAnchorPlayoutId", + "PlayoutProgramScheduleAnchorProgramScheduleId", + "PlayoutProgramScheduleAnchorMediaCollectionId"); + }); + + b.Navigation("EnumeratorState"); + + b.Navigation("MediaCollection"); + + b.Navigation("Playout"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlexMediaSourceConnection", + b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", null) + .WithMany("Connections") + .HasForeignKey("PlexMediaSourceId"); + }); + + modelBuilder.Entity( + "ErsatzTV.Core.Domain.PlexMediaSourceLibrary", + b => + { + b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", null) + .WithMany("Libraries") + .HasForeignKey("PlexMediaSourceId"); + }); + + modelBuilder.Entity( + "ErsatzTV.Core.Domain.ProgramScheduleItem", + b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection") + .WithMany() + .HasForeignKey("MediaCollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule") + .WithMany("Items") + .HasForeignKey("ProgramScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MediaCollection"); + + b.Navigation("ProgramSchedule"); + }); + + modelBuilder.Entity( + "MediaItemSimpleMediaCollection", + b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) + .WithMany() + .HasForeignKey("ItemsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null) + .WithMany() + .HasForeignKey("SimpleMediaCollectionsId") + .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.TelevisionMediaCollection", + b => + { + b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null) + .WithOne() + .HasForeignKey("ErsatzTV.Core.Domain.TelevisionMediaCollection", "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.Channel", b => { b.Navigation("Playouts"); }); + + 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.PlexMediaSource", + b => + { + b.Navigation("Connections"); + + b.Navigation("Libraries"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ErsatzTV/ErsatzTV.csproj b/ErsatzTV/ErsatzTV.csproj index 2f9bde8c..890d128a 100644 --- a/ErsatzTV/ErsatzTV.csproj +++ b/ErsatzTV/ErsatzTV.csproj @@ -16,6 +16,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/ErsatzTV/Extensions/HostExtensions.cs b/ErsatzTV/Extensions/HostExtensions.cs index 9dbcb6fc..4c252294 100644 --- a/ErsatzTV/Extensions/HostExtensions.cs +++ b/ErsatzTV/Extensions/HostExtensions.cs @@ -1,6 +1,7 @@ using System; using ErsatzTV.Infrastructure.Data; using LanguageExt; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -28,7 +29,7 @@ namespace ErsatzTV.Extensions private static TvContext Migrate(TvContext context) { - context.Database.EnsureCreated(); + context.Database.Migrate(); return context; } diff --git a/ErsatzTV/Models/UI/MediaItemExtensions.cs b/ErsatzTV/Models/UI/MediaItemExtensions.cs deleted file mode 100644 index 46f41f35..00000000 --- a/ErsatzTV/Models/UI/MediaItemExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ErsatzTV.Core.Domain; -using static LanguageExt.Prelude; - -namespace ErsatzTV.Models.UI -{ - public static class MediaItemExtensions - { - public static string GetDisplayDuration(this MediaItem mediaItem) => - string.Format( - mediaItem.Metadata.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}", - mediaItem.Metadata.Duration); - - public static string GetDisplayTitle(this MediaItem mediaItem) => - mediaItem.Metadata.MediaType == MediaType.TvShow && - Optional(mediaItem.Metadata.SeasonNumber).IsSome && - Optional(mediaItem.Metadata.EpisodeNumber).IsSome - ? $"{mediaItem.Metadata.Title} s{mediaItem.Metadata.SeasonNumber:00}e{mediaItem.Metadata.EpisodeNumber:00}" - : mediaItem.Metadata.Title; - - public static string GetDisplayMediaType(this MediaItem mediaItem) => - mediaItem.Metadata.MediaType == MediaType.TvShow - ? "TV Show" - : mediaItem.Metadata.MediaType.ToString(); - } -} diff --git a/ErsatzTV/Pages/MediaCollectionItemsEditor.razor b/ErsatzTV/Pages/MediaCollectionItemsEditor.razor index 929dd0fa..fbe2df0c 100644 --- a/ErsatzTV/Pages/MediaCollectionItemsEditor.razor +++ b/ErsatzTV/Pages/MediaCollectionItemsEditor.razor @@ -1,8 +1,16 @@ @page "/media/collections/{Id:int}/items" -@inject IDbContextFactory DbFactory +@using ErsatzTV.Application.MediaCollections +@using ErsatzTV.Application.MediaCollections.Commands +@using ErsatzTV.Application.MediaCollections.Queries +@using ErsatzTV.Application.MediaItems +@using ErsatzTV.Application.MediaItems.Queries +@using Unit = LanguageExt.Unit @inject NavigationManager NavigationManager +@inject IMediator Mediator +@inject ILogger Logger +@inject ISnackbar Snackbar - + @_mediaCollection.Name Media Items @@ -13,20 +21,20 @@ Duration - @context.Source.Name - @context.GetDisplayMediaType() - @context.GetDisplayTitle() - @context.Metadata.Duration.ToString(@"hh\:mm\:ss\.fff") + @context.Source + @context.MediaType + @context.Title + @context.Duration - @if (_mediaCollection.Items.Count > 0) + @if (_collectionItems.Any()) { } - + All Media Items @@ -41,10 +49,10 @@ Duration - @context.Source.Name - @context.GetDisplayMediaType() - @context.GetDisplayTitle() - @context.GetDisplayDuration() + @context.Source + @context.MediaType + @context.Title + @context.Duration @@ -59,32 +67,27 @@ [Parameter] public int Id { get; set; } - private SimpleMediaCollection _mediaCollection; + private MediaCollectionViewModel _mediaCollection; + private IEnumerable _collectionItems; protected override async Task OnParametersSetAsync() => await LoadMediaCollectionAsync(); private List _mediaItemIds; - private IEnumerable _pagedData; - private MudTable _table; + private IEnumerable _pagedData; + private MudTable _table; private int _totalItems; private string _searchString; - private async Task> ServerReload(TableState state) + private async Task> ServerReload(TableState state) { - await using TvContext context = DbFactory.CreateDbContext(); - IQueryable data = from c in context.MediaItems.Include(c => c.Source) select c; - - if (!string.IsNullOrEmpty(_searchString)) - { - data = data.Where(c => EF.Functions.Like(c.Metadata.Title, $"%{_searchString}%")); - } + List data = await Mediator.Send(new SearchAllMediaItems(_searchString)); _mediaItemIds = data.Map(c => c.Id).ToList(); - _totalItems = data.Count(); + _totalItems = data.Count; _pagedData = data.OrderBy(c => c.Id).Skip(state.Page * state.PageSize).Take(state.PageSize).ToArray(); - return new TableData { TotalItems = _totalItems, Items = _pagedData }; + return new TableData { TotalItems = _totalItems, Items = _pagedData }; } private async Task OnSearch(string text) @@ -95,32 +98,24 @@ private async Task AddResultsAsync() { - await using TvContext context = DbFactory.CreateDbContext(); - - SimpleMediaCollection mediaCollection = await context.SimpleMediaCollections.FindAsync(_mediaCollection.Id); - await context.Entry(mediaCollection).Collection(cg => cg.Items).LoadAsync(); - IEnumerable existingMediaItems = _mediaCollection.Items.Select(c => c.Id); - IQueryable mediaItemsToAdd = from c in context.MediaItems - where _mediaItemIds.Contains(c.Id) && !existingMediaItems.Contains(c.Id) - select c; - foreach (MediaItem mediaItem in mediaItemsToAdd) - { - mediaCollection.Items.Add(mediaItem); - } - context.MediaCollections.Update(mediaCollection); - await context.SaveChangesAsync(); - - await LoadMediaCollectionAsync(); + Either result = await Mediator.Send(new AddItemsToSimpleMediaCollection(Id, _mediaItemIds)); + await result.Match( + async _ => await LoadMediaCollectionAsync(), + error => + { + Snackbar.Add(error.Value, Severity.Error); + Logger.LogError("Error adding items to media collection: {Error}", error.Value); + return Task.CompletedTask; + }); } private async Task LoadMediaCollectionAsync() { - await using TvContext context = DbFactory.CreateDbContext(); - _mediaCollection = await context.SimpleMediaCollections - .AsNoTracking() - .Include(cg => cg.Items) - .ThenInclude(c => c.Source) - .FirstAsync(cg => cg.Id == Id); + Option>> maybeResult = + await Mediator.Send(new GetSimpleMediaCollectionWithItemsById(Id)); + maybeResult.Match( + result => (_mediaCollection, _collectionItems) = result, + () => NavigationManager.NavigateTo("404")); } } \ No newline at end of file diff --git a/ErsatzTV/Pages/MediaItems.razor b/ErsatzTV/Pages/MediaItems.razor index 5dd78d15..63bf3e38 100644 --- a/ErsatzTV/Pages/MediaItems.razor +++ b/ErsatzTV/Pages/MediaItems.razor @@ -1,5 +1,4 @@ @page "/media/items" -@inject IDbContextFactory DbFactory diff --git a/ErsatzTV/Program.cs b/ErsatzTV/Program.cs index d9d32a89..51774609 100644 --- a/ErsatzTV/Program.cs +++ b/ErsatzTV/Program.cs @@ -54,4 +54,4 @@ namespace ErsatzTV .UseKestrel(options => options.AddServerHeader = false)) .UseSerilog(); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/MainLayout.razor b/ErsatzTV/Shared/MainLayout.razor index 221b2337..e348ed1a 100644 --- a/ErsatzTV/Shared/MainLayout.razor +++ b/ErsatzTV/Shared/MainLayout.razor @@ -12,7 +12,7 @@ M3U XMLTV API - + diff --git a/ErsatzTV/Startup.cs b/ErsatzTV/Startup.cs index 97a5b9ca..7ae4b9ce 100644 --- a/ErsatzTV/Startup.cs +++ b/ErsatzTV/Startup.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using System.Threading.Channels; using ErsatzTV.Application; @@ -100,23 +99,14 @@ namespace ErsatzTV // string xmltvPath = Path.Combine(appDataFolder, "xmltv.xml"); // Log.Logger.Information("XMLTV is at {XmltvPath}", xmltvPath); - services.AddDbContextFactory( - options => - { - options.UseSqlite( - $"Data Source={FileSystemLayout.DatabasePath}", - o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)); - - if (Debugger.IsAttached) - { - options.EnableSensitiveDataLogging(); - } - }); - services.AddDbContext( options => options.UseSqlite( $"Data Source={FileSystemLayout.DatabasePath}", - o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery))); + o => + { + o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); + o.MigrationsAssembly("ErsatzTV.Infrastructure"); + })); services.AddMediatR(typeof(GetAllChannels).Assembly); diff --git a/ErsatzTV/_Imports.razor b/ErsatzTV/_Imports.razor index a94e44b4..17af02e6 100644 --- a/ErsatzTV/_Imports.razor +++ b/ErsatzTV/_Imports.razor @@ -20,7 +20,5 @@ @using ErsatzTV.Core @using ErsatzTV.Core.Domain @using ErsatzTV.Infrastructure.Data -@using ErsatzTV.Models -@using ErsatzTV.Models.UI @using ErsatzTV.Shared @using ErsatzTV.ViewModels \ No newline at end of file