Browse Source

Database migrations (#13)

* remove last use of dbcontextfactory

* add initial migration
pull/14/head v0.0.5-prealpha
Jason Dove 4 years ago committed by GitHub
parent
commit
db054ece24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollection.cs
  2. 79
      ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollectionHandler.cs
  3. 0
      ErsatzTV.Application/MediaCollections/Commands/UpdateSimpleMediaCollectionHandler.cs
  4. 11
      ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsById.cs
  5. 37
      ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsByIdHandler.cs
  6. 22
      ErsatzTV.Application/MediaItems/Mapper.cs
  7. 9
      ErsatzTV.Application/MediaItems/MediaItemSearchResultViewModel.cs
  8. 5
      ErsatzTV.Application/MediaItems/Queries/GetAllMediaItemsHandler.cs
  9. 7
      ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItems.cs
  10. 23
      ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItemsHandler.cs
  11. 2
      ErsatzTV.Application/Playouts/Mapper.cs
  12. 3
      ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs
  13. 1
      ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs
  14. 1
      ErsatzTV.Core/Interfaces/Repositories/IMediaItemRepository.cs
  15. 8
      ErsatzTV.Core/Iptv/ChannelGuide.cs
  16. 6
      ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs
  17. 7
      ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs
  18. 14
      ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs
  19. 9
      ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj
  20. 876
      ErsatzTV.Infrastructure/Migrations/20210212105010_Initial.Designer.cs
  21. 718
      ErsatzTV.Infrastructure/Migrations/20210212105010_Initial.cs
  22. 978
      ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs
  23. 4
      ErsatzTV/ErsatzTV.csproj
  24. 3
      ErsatzTV/Extensions/HostExtensions.cs
  25. 25
      ErsatzTV/Models/UI/MediaItemExtensions.cs
  26. 89
      ErsatzTV/Pages/MediaCollectionItemsEditor.razor
  27. 1
      ErsatzTV/Pages/MediaItems.razor
  28. 2
      ErsatzTV/Program.cs
  29. 2
      ErsatzTV/Shared/MainLayout.razor
  30. 20
      ErsatzTV/Startup.cs
  31. 2
      ErsatzTV/_Imports.razor

9
ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollection.cs

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
using System.Collections.Generic;
using ErsatzTV.Core;
using LanguageExt;
namespace ErsatzTV.Application.MediaCollections.Commands
{
public record AddItemsToSimpleMediaCollection
(int MediaCollectionId, List<int> ItemIds) : MediatR.IRequest<Either<BaseError, Unit>>;
}

79
ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollectionHandler.cs

@ -0,0 +1,79 @@ @@ -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<AddItemsToSimpleMediaCollection,
Either<BaseError, Unit>>
{
private readonly IMediaCollectionRepository _mediaCollectionRepository;
private readonly IMediaItemRepository _mediaItemRepository;
public AddItemsToSimpleMediaCollectionHandler(
IMediaCollectionRepository mediaCollectionRepository,
IMediaItemRepository mediaItemRepository)
{
_mediaCollectionRepository = mediaCollectionRepository;
_mediaItemRepository = mediaItemRepository;
}
public Task<Either<BaseError, Unit>> Handle(
AddItemsToSimpleMediaCollection request,
CancellationToken cancellationToken) =>
Validate(request)
.MapT(ApplyAddItemsRequest)
.Bind(v => v.ToEitherAsync());
private async Task<Unit> 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<Validation<BaseError, RequestParameters>>
Validate(AddItemsToSimpleMediaCollection request) =>
(await SimpleMediaCollectionMustExist(request), await ValidateItems(request))
.Apply(
(simpleMediaCollectionToUpdate, itemsToAdd) =>
new RequestParameters(simpleMediaCollectionToUpdate, itemsToAdd));
private Task<Validation<BaseError, SimpleMediaCollection>> SimpleMediaCollectionMustExist(
AddItemsToSimpleMediaCollection updateSimpleMediaCollection) =>
_mediaCollectionRepository.GetSimpleMediaCollection(updateSimpleMediaCollection.MediaCollectionId)
.Map(v => v.ToValidation<BaseError>("SimpleMediaCollection does not exist."));
private Task<Validation<BaseError, List<MediaItem>>> ValidateItems(
AddItemsToSimpleMediaCollection request) =>
LoadAllMediaItems(request)
.Map(v => v.ToValidation<BaseError>("MediaItem does not exist"));
private async Task<Option<List<MediaItem>>> 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<MediaItem> ItemsToAdd);
}
}

0
ErsatzTV.Application/MediaCollections/Commands/UpdateChannelHandler.cs → ErsatzTV.Application/MediaCollections/Commands/UpdateSimpleMediaCollectionHandler.cs

11
ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsById.cs

@ -0,0 +1,11 @@ @@ -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<Option<Tuple<MediaCollectionViewModel, List<MediaItemSearchResultViewModel>>>>;
}

37
ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsByIdHandler.cs

@ -0,0 +1,37 @@ @@ -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<GetSimpleMediaCollectionWithItemsById,
Option<Tuple<MediaCollectionViewModel, List<MediaItemSearchResultViewModel>>>>
{
private readonly IMediaCollectionRepository _mediaCollectionRepository;
public GetSimpleMediaCollectionWithItemsByIdHandler(IMediaCollectionRepository mediaCollectionRepository) =>
_mediaCollectionRepository = mediaCollectionRepository;
public async Task<Option<Tuple<MediaCollectionViewModel, List<MediaItemSearchResultViewModel>>>> Handle(
GetSimpleMediaCollectionWithItemsById request,
CancellationToken cancellationToken)
{
Option<SimpleMediaCollection> maybeCollection =
await _mediaCollectionRepository.GetSimpleMediaCollectionWithItems(request.Id);
return maybeCollection.Match<Option<Tuple<MediaCollectionViewModel, List<MediaItemSearchResultViewModel>>>>(
c => Tuple(ProjectToViewModel(c), c.Items.Map(ProjectToSearchViewModel).ToList()),
None);
}
}
}

22
ErsatzTV.Application/MediaItems/Mapper.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Core.Domain;
using static LanguageExt.Prelude;
namespace ErsatzTV.Application.MediaItems
{
@ -9,5 +10,26 @@ 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);
}
}

9
ErsatzTV.Application/MediaItems/MediaItemSearchResultViewModel.cs

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
namespace ErsatzTV.Application.MediaItems
{
public record MediaItemSearchResultViewModel(
int Id,
string Source,
string MediaType,
string Title,
string Duration);
}

5
ErsatzTV.Application/MediaItems/Queries/GetAllMediaItemsHandler.cs

@ -3,6 +3,7 @@ using System.Linq; @@ -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 @@ -15,9 +16,9 @@ namespace ErsatzTV.Application.MediaItems.Queries
public GetAllMediaItemsHandler(IMediaItemRepository mediaItemRepository) =>
_mediaItemRepository = mediaItemRepository;
public async Task<List<MediaItemViewModel>> Handle(
public Task<List<MediaItemViewModel>> Handle(
GetAllMediaItems request,
CancellationToken cancellationToken) =>
(await _mediaItemRepository.GetAll()).Map(ProjectToViewModel).ToList();
_mediaItemRepository.GetAll().Map(list => list.Map(ProjectToViewModel).ToList());
}
}

7
ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItems.cs

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
using System.Collections.Generic;
using MediatR;
namespace ErsatzTV.Application.MediaItems.Queries
{
public record SearchAllMediaItems(string SearchString) : IRequest<List<MediaItemSearchResultViewModel>>;
}

23
ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItemsHandler.cs

@ -0,0 +1,23 @@ @@ -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<SearchAllMediaItems, List<MediaItemSearchResultViewModel>>
{
private readonly IMediaItemRepository _mediaItemRepository;
public SearchAllMediaItemsHandler(IMediaItemRepository mediaItemRepository) =>
_mediaItemRepository = mediaItemRepository;
public Task<List<MediaItemSearchResultViewModel>>
Handle(SearchAllMediaItems request, CancellationToken cancellationToken) =>
_mediaItemRepository.Search(request.SearchString).Map(list => list.Map(ProjectToSearchViewModel).ToList());
}
}

2
ErsatzTV.Application/Playouts/Mapper.cs

@ -28,7 +28,7 @@ namespace ErsatzTV.Application.Playouts @@ -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);

3
ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs

@ -22,6 +22,9 @@ namespace ErsatzTV.Core.Tests.Fakes @@ -22,6 +22,9 @@ namespace ErsatzTV.Core.Tests.Fakes
public Task<Option<SimpleMediaCollection>> GetSimpleMediaCollection(int id) =>
throw new NotSupportedException();
public Task<Option<SimpleMediaCollection>> GetSimpleMediaCollectionWithItems(int id) =>
throw new NotSupportedException();
public Task<Option<TelevisionMediaCollection>> GetTelevisionMediaCollection(int id) =>
throw new NotSupportedException();

1
ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs

@ -11,6 +11,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories @@ -11,6 +11,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories
public Task<SimpleMediaCollection> Add(SimpleMediaCollection collection);
public Task<Option<MediaCollection>> Get(int id);
public Task<Option<SimpleMediaCollection>> GetSimpleMediaCollection(int id);
public Task<Option<SimpleMediaCollection>> GetSimpleMediaCollectionWithItems(int id);
public Task<Option<TelevisionMediaCollection>> GetTelevisionMediaCollection(int id);
public Task<List<SimpleMediaCollection>> GetSimpleMediaCollections();
public Task<List<MediaCollection>> GetAll();

1
ErsatzTV.Core/Interfaces/Repositories/IMediaItemRepository.cs

@ -10,6 +10,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories @@ -10,6 +10,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories
public Task<int> Add(MediaItem mediaItem);
public Task<Option<MediaItem>> Get(int id);
public Task<List<MediaItem>> GetAll();
public Task<List<MediaItem>> Search(string searchString);
public Task<List<MediaItem>> GetAll(MediaType mediaType);
public Task<List<MediaItem>> GetAllByMediaSourceId(int mediaSourceId);
public Task Update(MediaItem mediaItem);

8
ErsatzTV.Core/Iptv/ChannelGuide.cs

@ -26,15 +26,15 @@ namespace ErsatzTV.Core.Iptv @@ -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 @@ -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

6
ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs

@ -15,10 +15,12 @@ namespace ErsatzTV.Core.Metadata @@ -15,10 +15,12 @@ namespace ErsatzTV.Core.Metadata
{
public class LocalStatisticsProvider : ILocalStatisticsProvider
{
private readonly IMediaItemRepository _mediaItemRepository;
private readonly ILogger<LocalStatisticsProvider> _logger;
private readonly IMediaItemRepository _mediaItemRepository;
public LocalStatisticsProvider(IMediaItemRepository mediaItemRepository, ILogger<LocalStatisticsProvider> logger)
public LocalStatisticsProvider(
IMediaItemRepository mediaItemRepository,
ILogger<LocalStatisticsProvider> logger)
{
_mediaItemRepository = mediaItemRepository;
_logger = logger;

7
ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs

@ -29,6 +29,13 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -29,6 +29,13 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
public Task<Option<SimpleMediaCollection>> GetSimpleMediaCollection(int id) =>
Get(id).Map(c => c.OfType<SimpleMediaCollection>().HeadOrNone());
public Task<Option<SimpleMediaCollection>> GetSimpleMediaCollectionWithItems(int id) =>
_dbContext.SimpleMediaCollections
.Include(s => s.Items)
.ThenInclude(i => i.Source)
.SingleOrDefaultAsync(c => c.Id == id)
.Map(Optional);
public Task<Option<TelevisionMediaCollection>> GetTelevisionMediaCollection(int id) =>
Get(id).Map(c => c.OfType<TelevisionMediaCollection>().HeadOrNone());

14
ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs

@ -1,4 +1,5 @@ @@ -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 @@ -26,6 +27,19 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
public Task<List<MediaItem>> GetAll() => _dbContext.MediaItems.ToListAsync();
public Task<List<MediaItem>> Search(string searchString)
{
IQueryable<MediaItem> 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<List<MediaItem>> GetAll(MediaType mediaType) =>
_dbContext.MediaItems
.Include(i => i.Source)

9
ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj

@ -2,11 +2,16 @@ @@ -2,11 +2,16 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.3" />
<PackageReference Include="Refit" Version="6.0.1" />
</ItemGroup>

876
ErsatzTV.Infrastructure/Migrations/20210212105010_Initial.Designer.cs generated

@ -0,0 +1,876 @@ @@ -0,0 +1,876 @@
// <auto-generated />
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<int>("Id")
.HasColumnType("INTEGER");
b.ToTable("GenericIntegerIds");
});
modelBuilder.Entity("ErsatzTV.Core.AggregateModels.MediaCollectionSummary", b =>
{
b.Property<int>("Id")
.HasColumnType("INTEGER");
b.Property<bool>("IsSimple")
.HasColumnType("INTEGER");
b.Property<int>("ItemCount")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.ToTable("MediaCollectionSummaries");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("FFmpegProfileId")
.HasColumnType("INTEGER");
b.Property<string>("Logo")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<int>("Number")
.HasColumnType("INTEGER");
b.Property<int>("StreamingMode")
.HasColumnType("INTEGER");
b.Property<Guid>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Key")
.IsUnique();
b.ToTable("ConfigElements");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AudioBitrate")
.HasColumnType("INTEGER");
b.Property<int>("AudioBufferSize")
.HasColumnType("INTEGER");
b.Property<int>("AudioChannels")
.HasColumnType("INTEGER");
b.Property<string>("AudioCodec")
.HasColumnType("TEXT");
b.Property<int>("AudioSampleRate")
.HasColumnType("INTEGER");
b.Property<int>("AudioVolume")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<bool>("NormalizeAudio")
.HasColumnType("INTEGER");
b.Property<bool>("NormalizeAudioCodec")
.HasColumnType("INTEGER");
b.Property<bool>("NormalizeResolution")
.HasColumnType("INTEGER");
b.Property<bool>("NormalizeVideoCodec")
.HasColumnType("INTEGER");
b.Property<int>("ResolutionId")
.HasColumnType("INTEGER");
b.Property<int>("ThreadCount")
.HasColumnType("INTEGER");
b.Property<bool>("Transcode")
.HasColumnType("INTEGER");
b.Property<int>("VideoBitrate")
.HasColumnType("INTEGER");
b.Property<int>("VideoBufferSize")
.HasColumnType("INTEGER");
b.Property<string>("VideoCodec")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ResolutionId");
b.ToTable("FFmpegProfiles");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("MediaCollections");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastWriteTime")
.HasColumnType("TEXT");
b.Property<int>("MediaSourceId")
.HasColumnType("INTEGER");
b.Property<string>("Path")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("MediaSourceId");
b.ToTable("MediaItems");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<int>("SourceType")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("MediaSources");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("ChannelId")
.HasColumnType("INTEGER");
b.Property<int>("ProgramScheduleId")
.HasColumnType("INTEGER");
b.Property<int>("ProgramSchedulePlayoutType")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ChannelId");
b.HasIndex("ProgramScheduleId");
b.ToTable("Playouts");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("Finish")
.HasColumnType("TEXT");
b.Property<int>("MediaItemId")
.HasColumnType("INTEGER");
b.Property<int>("PlayoutId")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("Start")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("MediaItemId");
b.HasIndex("PlayoutId");
b.ToTable("PlayoutItems");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b =>
{
b.Property<int>("PlayoutId")
.HasColumnType("INTEGER");
b.Property<int>("ProgramScheduleId")
.HasColumnType("INTEGER");
b.Property<int>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<bool>("IsActive")
.HasColumnType("INTEGER");
b.Property<int?>("PlexMediaSourceId")
.HasColumnType("INTEGER");
b.Property<string>("Uri")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("PlexMediaSourceId");
b.ToTable("PlexMediaSourceConnections");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSourceLibrary", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<int>("MediaType")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<int?>("PlexMediaSourceId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("PlexMediaSourceId");
b.ToTable("PlexMediaSourceLibraries");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("MediaCollectionPlaybackOrder")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ProgramSchedules");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Index")
.HasColumnType("INTEGER");
b.Property<int>("MediaCollectionId")
.HasColumnType("INTEGER");
b.Property<int>("ProgramScheduleId")
.HasColumnType("INTEGER");
b.Property<TimeSpan?>("StartTime")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("MediaCollectionId");
b.HasIndex("ProgramScheduleId");
b.ToTable("ProgramScheduleItems");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Height")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<int>("Width")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Resolutions");
});
modelBuilder.Entity("MediaItemSimpleMediaCollection", b =>
{
b.Property<int>("ItemsId")
.HasColumnType("INTEGER");
b.Property<int>("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<int?>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<string>("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<string>("Folder")
.HasColumnType("TEXT");
b.Property<int>("MediaType")
.HasColumnType("INTEGER");
b.ToTable("LocalMediaSources");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
b.Property<string>("ClientIdentifier")
.HasColumnType("TEXT");
b.Property<string>("ProductVersion")
.HasColumnType("TEXT");
b.ToTable("PlexMediaSources");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
b.Property<bool>("OfflineTail")
.HasColumnType("INTEGER");
b.Property<TimeSpan>("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<int>("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<int>("MediaItemId")
.HasColumnType("INTEGER");
b1.Property<DateTime?>("Aired")
.HasColumnType("TEXT");
b1.Property<string>("AudioCodec")
.HasColumnType("TEXT");
b1.Property<string>("ContentRating")
.HasColumnType("TEXT");
b1.Property<string>("Description")
.HasColumnType("TEXT");
b1.Property<string>("DisplayAspectRatio")
.HasColumnType("TEXT");
b1.Property<TimeSpan>("Duration")
.HasColumnType("TEXT");
b1.Property<int?>("EpisodeNumber")
.HasColumnType("INTEGER");
b1.Property<int>("Height")
.HasColumnType("INTEGER");
b1.Property<int>("MediaType")
.HasColumnType("INTEGER");
b1.Property<string>("SampleAspectRatio")
.HasColumnType("TEXT");
b1.Property<int?>("SeasonNumber")
.HasColumnType("INTEGER");
b1.Property<string>("Subtitle")
.HasColumnType("TEXT");
b1.Property<string>("Title")
.HasColumnType("TEXT");
b1.Property<string>("VideoCodec")
.HasColumnType("TEXT");
b1.Property<int>("VideoScanType")
.HasColumnType("INTEGER");
b1.Property<int>("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<int>("PlayoutId")
.HasColumnType("INTEGER");
b1.Property<int>("NextScheduleItemId")
.HasColumnType("INTEGER");
b1.Property<DateTimeOffset>("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<int>("PlayoutProgramScheduleAnchorPlayoutId")
.HasColumnType("INTEGER");
b1.Property<int>("PlayoutProgramScheduleAnchorProgramScheduleId")
.HasColumnType("INTEGER");
b1.Property<int>("PlayoutProgramScheduleAnchorMediaCollectionId")
.HasColumnType("INTEGER");
b1.Property<int>("Index")
.HasColumnType("INTEGER");
b1.Property<int>("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
}
}
}

718
ErsatzTV.Infrastructure/Migrations/20210212105010_Initial.cs

@ -0,0 +1,718 @@ @@ -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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Key = table.Column<string>("TEXT", nullable: true),
Value = table.Column<string>("TEXT", nullable: true)
},
constraints: table => { table.PrimaryKey("PK_ConfigElements", x => x.Id); });
migrationBuilder.CreateTable(
"GenericIntegerIds",
table => new
{
Id = table.Column<int>("INTEGER", nullable: false)
},
constraints: table => { });
migrationBuilder.CreateTable(
"MediaCollections",
table => new
{
Id = table.Column<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>("TEXT", nullable: true)
},
constraints: table => { table.PrimaryKey("PK_MediaCollections", x => x.Id); });
migrationBuilder.CreateTable(
"MediaCollectionSummaries",
table => new
{
Id = table.Column<int>("INTEGER", nullable: false),
Name = table.Column<string>("TEXT", nullable: true),
ItemCount = table.Column<int>("INTEGER", nullable: false),
IsSimple = table.Column<bool>("INTEGER", nullable: false)
},
constraints: table => { });
migrationBuilder.CreateTable(
"MediaSources",
table => new
{
Id = table.Column<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
SourceType = table.Column<int>("INTEGER", nullable: false),
Name = table.Column<string>("TEXT", nullable: true)
},
constraints: table => { table.PrimaryKey("PK_MediaSources", x => x.Id); });
migrationBuilder.CreateTable(
"ProgramSchedules",
table => new
{
Id = table.Column<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>("TEXT", nullable: true),
MediaCollectionPlaybackOrder = table.Column<int>("INTEGER", nullable: false)
},
constraints: table => { table.PrimaryKey("PK_ProgramSchedules", x => x.Id); });
migrationBuilder.CreateTable(
"Resolutions",
table => new
{
Id = table.Column<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>("TEXT", nullable: true),
Height = table.Column<int>("INTEGER", nullable: false),
Width = table.Column<int>("INTEGER", nullable: false)
},
constraints: table => { table.PrimaryKey("PK_Resolutions", x => x.Id); });
migrationBuilder.CreateTable(
"SimpleMediaCollections",
table => new
{
Id = table.Column<int>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
ShowTitle = table.Column<string>("TEXT", nullable: true),
SeasonNumber = table.Column<int>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
MediaType = table.Column<int>("INTEGER", nullable: false),
Folder = table.Column<string>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
MediaSourceId = table.Column<int>("INTEGER", nullable: false),
Path = table.Column<string>("TEXT", nullable: true),
Metadata_Duration = table.Column<TimeSpan>("TEXT", nullable: true),
Metadata_SampleAspectRatio = table.Column<string>("TEXT", nullable: true),
Metadata_DisplayAspectRatio = table.Column<string>("TEXT", nullable: true),
Metadata_VideoCodec = table.Column<string>("TEXT", nullable: true),
Metadata_AudioCodec = table.Column<string>("TEXT", nullable: true),
Metadata_MediaType = table.Column<int>("INTEGER", nullable: true),
Metadata_Title = table.Column<string>("TEXT", nullable: true),
Metadata_Subtitle = table.Column<string>("TEXT", nullable: true),
Metadata_Description = table.Column<string>("TEXT", nullable: true),
Metadata_SeasonNumber = table.Column<int>("INTEGER", nullable: true),
Metadata_EpisodeNumber = table.Column<int>("INTEGER", nullable: true),
Metadata_ContentRating = table.Column<string>("TEXT", nullable: true),
Metadata_Aired = table.Column<DateTime>("TEXT", nullable: true),
Metadata_VideoScanType = table.Column<int>("INTEGER", nullable: true),
Metadata_Width = table.Column<int>("INTEGER", nullable: true),
Metadata_Height = table.Column<int>("INTEGER", nullable: true),
LastWriteTime = table.Column<DateTime>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
ProductVersion = table.Column<string>("TEXT", nullable: true),
ClientIdentifier = table.Column<string>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Index = table.Column<int>("INTEGER", nullable: false),
StartTime = table.Column<TimeSpan>("TEXT", nullable: true),
MediaCollectionId = table.Column<int>("INTEGER", nullable: false),
ProgramScheduleId = table.Column<int>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>("TEXT", nullable: true),
ThreadCount = table.Column<int>("INTEGER", nullable: false),
Transcode = table.Column<bool>("INTEGER", nullable: false),
ResolutionId = table.Column<int>("INTEGER", nullable: false),
NormalizeResolution = table.Column<bool>("INTEGER", nullable: false),
VideoCodec = table.Column<string>("TEXT", nullable: true),
NormalizeVideoCodec = table.Column<bool>("INTEGER", nullable: false),
VideoBitrate = table.Column<int>("INTEGER", nullable: false),
VideoBufferSize = table.Column<int>("INTEGER", nullable: false),
AudioCodec = table.Column<string>("TEXT", nullable: true),
NormalizeAudioCodec = table.Column<bool>("INTEGER", nullable: false),
AudioBitrate = table.Column<int>("INTEGER", nullable: false),
AudioBufferSize = table.Column<int>("INTEGER", nullable: false),
AudioVolume = table.Column<int>("INTEGER", nullable: false),
AudioChannels = table.Column<int>("INTEGER", nullable: false),
AudioSampleRate = table.Column<int>("INTEGER", nullable: false),
NormalizeAudio = table.Column<bool>("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<int>("INTEGER", nullable: false),
SimpleMediaCollectionsId = table.Column<int>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
IsActive = table.Column<bool>("INTEGER", nullable: false),
Uri = table.Column<string>("TEXT", nullable: true),
PlexMediaSourceId = table.Column<int>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Key = table.Column<string>("TEXT", nullable: true),
Name = table.Column<string>("TEXT", nullable: true),
MediaType = table.Column<int>("INTEGER", nullable: false),
PlexMediaSourceId = table.Column<int>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
PlayoutDuration = table.Column<TimeSpan>("TEXT", nullable: false),
OfflineTail = table.Column<bool>("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<int>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Count = table.Column<int>("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<int>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
UniqueId = table.Column<Guid>("TEXT", nullable: false),
Number = table.Column<int>("INTEGER", nullable: false),
Name = table.Column<string>("TEXT", nullable: true),
Logo = table.Column<string>("TEXT", nullable: true),
FFmpegProfileId = table.Column<int>("INTEGER", nullable: false),
StreamingMode = table.Column<int>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
ChannelId = table.Column<int>("INTEGER", nullable: false),
ProgramScheduleId = table.Column<int>("INTEGER", nullable: false),
ProgramSchedulePlayoutType = table.Column<int>("INTEGER", nullable: false),
Anchor_NextScheduleItemId = table.Column<int>("INTEGER", nullable: true),
Anchor_NextStart = table.Column<DateTimeOffset>("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<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
MediaItemId = table.Column<int>("INTEGER", nullable: false),
Start = table.Column<DateTimeOffset>("TEXT", nullable: false),
Finish = table.Column<DateTimeOffset>("TEXT", nullable: false),
PlayoutId = table.Column<int>("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<int>("INTEGER", nullable: false),
ProgramScheduleId = table.Column<int>("INTEGER", nullable: false),
MediaCollectionId = table.Column<int>("INTEGER", nullable: false),
EnumeratorState_Seed = table.Column<int>("INTEGER", nullable: true),
EnumeratorState_Index = table.Column<int>("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");
}
}
}

978
ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs

@ -0,0 +1,978 @@ @@ -0,0 +1,978 @@
// <auto-generated />
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<int>("Id")
.HasColumnType("INTEGER");
b.ToTable("GenericIntegerIds");
});
modelBuilder.Entity(
"ErsatzTV.Core.AggregateModels.MediaCollectionSummary",
b =>
{
b.Property<int>("Id")
.HasColumnType("INTEGER");
b.Property<bool>("IsSimple")
.HasColumnType("INTEGER");
b.Property<int>("ItemCount")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.ToTable("MediaCollectionSummaries");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Channel",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("FFmpegProfileId")
.HasColumnType("INTEGER");
b.Property<string>("Logo")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<int>("Number")
.HasColumnType("INTEGER");
b.Property<int>("StreamingMode")
.HasColumnType("INTEGER");
b.Property<Guid>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Key")
.IsUnique();
b.ToTable("ConfigElements");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.FFmpegProfile",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AudioBitrate")
.HasColumnType("INTEGER");
b.Property<int>("AudioBufferSize")
.HasColumnType("INTEGER");
b.Property<int>("AudioChannels")
.HasColumnType("INTEGER");
b.Property<string>("AudioCodec")
.HasColumnType("TEXT");
b.Property<int>("AudioSampleRate")
.HasColumnType("INTEGER");
b.Property<int>("AudioVolume")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<bool>("NormalizeAudio")
.HasColumnType("INTEGER");
b.Property<bool>("NormalizeAudioCodec")
.HasColumnType("INTEGER");
b.Property<bool>("NormalizeResolution")
.HasColumnType("INTEGER");
b.Property<bool>("NormalizeVideoCodec")
.HasColumnType("INTEGER");
b.Property<int>("ResolutionId")
.HasColumnType("INTEGER");
b.Property<int>("ThreadCount")
.HasColumnType("INTEGER");
b.Property<bool>("Transcode")
.HasColumnType("INTEGER");
b.Property<int>("VideoBitrate")
.HasColumnType("INTEGER");
b.Property<int>("VideoBufferSize")
.HasColumnType("INTEGER");
b.Property<string>("VideoCodec")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ResolutionId");
b.ToTable("FFmpegProfiles");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.MediaCollection",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("MediaCollections");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.MediaItem",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastWriteTime")
.HasColumnType("TEXT");
b.Property<int>("MediaSourceId")
.HasColumnType("INTEGER");
b.Property<string>("Path")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("MediaSourceId");
b.ToTable("MediaItems");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.MediaSource",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<int>("SourceType")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("MediaSources");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Playout",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("ChannelId")
.HasColumnType("INTEGER");
b.Property<int>("ProgramScheduleId")
.HasColumnType("INTEGER");
b.Property<int>("ProgramSchedulePlayoutType")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ChannelId");
b.HasIndex("ProgramScheduleId");
b.ToTable("Playouts");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlayoutItem",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("Finish")
.HasColumnType("TEXT");
b.Property<int>("MediaItemId")
.HasColumnType("INTEGER");
b.Property<int>("PlayoutId")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("Start")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("MediaItemId");
b.HasIndex("PlayoutId");
b.ToTable("PlayoutItems");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor",
b =>
{
b.Property<int>("PlayoutId")
.HasColumnType("INTEGER");
b.Property<int>("ProgramScheduleId")
.HasColumnType("INTEGER");
b.Property<int>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<bool>("IsActive")
.HasColumnType("INTEGER");
b.Property<int?>("PlexMediaSourceId")
.HasColumnType("INTEGER");
b.Property<string>("Uri")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("PlexMediaSourceId");
b.ToTable("PlexMediaSourceConnections");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlexMediaSourceLibrary",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<int>("MediaType")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<int?>("PlexMediaSourceId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("PlexMediaSourceId");
b.ToTable("PlexMediaSourceLibraries");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramSchedule",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("MediaCollectionPlaybackOrder")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ProgramSchedules");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramScheduleItem",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Index")
.HasColumnType("INTEGER");
b.Property<int>("MediaCollectionId")
.HasColumnType("INTEGER");
b.Property<int>("ProgramScheduleId")
.HasColumnType("INTEGER");
b.Property<TimeSpan?>("StartTime")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("MediaCollectionId");
b.HasIndex("ProgramScheduleId");
b.ToTable("ProgramScheduleItems");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Resolution",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Height")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<int>("Width")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Resolutions");
});
modelBuilder.Entity(
"MediaItemSimpleMediaCollection",
b =>
{
b.Property<int>("ItemsId")
.HasColumnType("INTEGER");
b.Property<int>("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<int?>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<string>("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<string>("Folder")
.HasColumnType("TEXT");
b.Property<int>("MediaType")
.HasColumnType("INTEGER");
b.ToTable("LocalMediaSources");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlexMediaSource",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
b.Property<string>("ClientIdentifier")
.HasColumnType("TEXT");
b.Property<string>("ProductVersion")
.HasColumnType("TEXT");
b.ToTable("PlexMediaSources");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramScheduleItemDuration",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
b.Property<bool>("OfflineTail")
.HasColumnType("INTEGER");
b.Property<TimeSpan>("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<int>("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<int>("MediaItemId")
.HasColumnType("INTEGER");
b1.Property<DateTime?>("Aired")
.HasColumnType("TEXT");
b1.Property<string>("AudioCodec")
.HasColumnType("TEXT");
b1.Property<string>("ContentRating")
.HasColumnType("TEXT");
b1.Property<string>("Description")
.HasColumnType("TEXT");
b1.Property<string>("DisplayAspectRatio")
.HasColumnType("TEXT");
b1.Property<TimeSpan>("Duration")
.HasColumnType("TEXT");
b1.Property<int?>("EpisodeNumber")
.HasColumnType("INTEGER");
b1.Property<int>("Height")
.HasColumnType("INTEGER");
b1.Property<int>("MediaType")
.HasColumnType("INTEGER");
b1.Property<string>("SampleAspectRatio")
.HasColumnType("TEXT");
b1.Property<int?>("SeasonNumber")
.HasColumnType("INTEGER");
b1.Property<string>("Subtitle")
.HasColumnType("TEXT");
b1.Property<string>("Title")
.HasColumnType("TEXT");
b1.Property<string>("VideoCodec")
.HasColumnType("TEXT");
b1.Property<int>("VideoScanType")
.HasColumnType("INTEGER");
b1.Property<int>("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<int>("PlayoutId")
.HasColumnType("INTEGER");
b1.Property<int>("NextScheduleItemId")
.HasColumnType("INTEGER");
b1.Property<DateTimeOffset>("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<int>("PlayoutProgramScheduleAnchorPlayoutId")
.HasColumnType("INTEGER");
b1.Property<int>("PlayoutProgramScheduleAnchorProgramScheduleId")
.HasColumnType("INTEGER");
b1.Property<int>("PlayoutProgramScheduleAnchorMediaCollectionId")
.HasColumnType("INTEGER");
b1.Property<int>("Index")
.HasColumnType("INTEGER");
b1.Property<int>("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
}
}
}

4
ErsatzTV/ErsatzTV.csproj

@ -16,6 +16,10 @@ @@ -16,6 +16,10 @@
<PackageReference Include="LanguageExt.Core" Version="3.4.15" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MudBlazor" Version="5.0.0" />
<PackageReference Include="Refit.HttpClientFactory" Version="6.0.1" />
<PackageReference Include="Serilog" Version="2.10.0" />

3
ErsatzTV/Extensions/HostExtensions.cs

@ -1,6 +1,7 @@ @@ -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 @@ -28,7 +29,7 @@ namespace ErsatzTV.Extensions
private static TvContext Migrate(TvContext context)
{
context.Database.EnsureCreated();
context.Database.Migrate();
return context;
}

25
ErsatzTV/Models/UI/MediaItemExtensions.cs

@ -1,25 +0,0 @@ @@ -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();
}
}

89
ErsatzTV/Pages/MediaCollectionItemsEditor.razor

@ -1,8 +1,16 @@ @@ -1,8 +1,16 @@
@page "/media/collections/{Id:int}/items"
@inject IDbContextFactory<TvContext> 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<MediaCollectionItemsEditor> Logger
@inject ISnackbar Snackbar
<MudTable Hover="true" Items="_mediaCollection.Items">
<MudTable Hover="true" Items="_collectionItems">
<ToolBarContent>
<MudText Typo="Typo.h6">@_mediaCollection.Name Media Items</MudText>
</ToolBarContent>
@ -13,20 +21,20 @@ @@ -13,20 +21,20 @@
<MudTh>Duration</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Source">@context.Source.Name</MudTd>
<MudTd DataLabel="Type">@context.GetDisplayMediaType()</MudTd>
<MudTd DataLabel="Title">@context.GetDisplayTitle()</MudTd>
<MudTd DataLabel="Duration">@context.Metadata.Duration.ToString(@"hh\:mm\:ss\.fff")</MudTd>
<MudTd DataLabel="Source">@context.Source</MudTd>
<MudTd DataLabel="Type">@context.MediaType</MudTd>
<MudTd DataLabel="Title">@context.Title</MudTd>
<MudTd DataLabel="Duration">@context.Duration</MudTd>
</RowTemplate>
<PagerContent>
@if (_mediaCollection.Items.Count > 0)
@if (_collectionItems.Any())
{
<MudTablePager/>
}
</PagerContent>
</MudTable>
<MudTable @ref="_table" Hover="true" ServerData="@(new Func<TableState, Task<TableData<MediaItem>>>(ServerReload))" Class="mt-8">
<MudTable @ref="_table" Hover="true" ServerData="@(new Func<TableState, Task<TableData<MediaItemSearchResultViewModel>>>(ServerReload))" Class="mt-8">
<ToolBarContent>
<MudText Typo="Typo.h6">All Media Items</MudText>
<MudToolBarSpacer/>
@ -41,10 +49,10 @@ @@ -41,10 +49,10 @@
<MudTh>Duration</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Source">@context.Source.Name</MudTd>
<MudTd DataLabel="Type">@context.GetDisplayMediaType()</MudTd>
<MudTd DataLabel="Title">@context.GetDisplayTitle()</MudTd>
<MudTd DataLabel="Duration">@context.GetDisplayDuration()</MudTd>
<MudTd DataLabel="Source">@context.Source</MudTd>
<MudTd DataLabel="Type">@context.MediaType</MudTd>
<MudTd DataLabel="Title">@context.Title</MudTd>
<MudTd DataLabel="Duration">@context.Duration</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager/>
@ -59,32 +67,27 @@ @@ -59,32 +67,27 @@
[Parameter]
public int Id { get; set; }
private SimpleMediaCollection _mediaCollection;
private MediaCollectionViewModel _mediaCollection;
private IEnumerable<MediaItemSearchResultViewModel> _collectionItems;
protected override async Task OnParametersSetAsync() => await LoadMediaCollectionAsync();
private List<int> _mediaItemIds;
private IEnumerable<MediaItem> _pagedData;
private MudTable<MediaItem> _table;
private IEnumerable<MediaItemSearchResultViewModel> _pagedData;
private MudTable<MediaItemSearchResultViewModel> _table;
private int _totalItems;
private string _searchString;
private async Task<TableData<MediaItem>> ServerReload(TableState state)
private async Task<TableData<MediaItemSearchResultViewModel>> ServerReload(TableState state)
{
await using TvContext context = DbFactory.CreateDbContext();
IQueryable<MediaItem> 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<MediaItemSearchResultViewModel> 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<MediaItem> { TotalItems = _totalItems, Items = _pagedData };
return new TableData<MediaItemSearchResultViewModel> { TotalItems = _totalItems, Items = _pagedData };
}
private async Task OnSearch(string text)
@ -95,32 +98,24 @@ @@ -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<int> existingMediaItems = _mediaCollection.Items.Select(c => c.Id);
IQueryable<MediaItem> 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<BaseError, Unit> 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<Tuple<MediaCollectionViewModel, List<MediaItemSearchResultViewModel>>> maybeResult =
await Mediator.Send(new GetSimpleMediaCollectionWithItemsById(Id));
maybeResult.Match(
result => (_mediaCollection, _collectionItems) = result,
() => NavigationManager.NavigateTo("404"));
}
}

1
ErsatzTV/Pages/MediaItems.razor

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
@page "/media/items"
@inject IDbContextFactory<TvContext> DbFactory
<MudTabs Elevation="1">
<MudTabPanel Text="TV Shows">

2
ErsatzTV/Program.cs

@ -54,4 +54,4 @@ namespace ErsatzTV @@ -54,4 +54,4 @@ namespace ErsatzTV
.UseKestrel(options => options.AddServerHeader = false))
.UseSerilog();
}
}
}

2
ErsatzTV/Shared/MainLayout.razor

@ -12,7 +12,7 @@ @@ -12,7 +12,7 @@
<MudLink Style="@($"color:{Colors.Shades.White}")" Color="Color.Inherit" Href="/iptv/channels.m3u" Target="_blank" Underline="Underline.None">M3U</MudLink>
<MudLink Style="@($"color:{Colors.Shades.White}")" Color="Color.Inherit" Href="/iptv/xmltv.xml" Target="_blank" Class="mx-4" Underline="Underline.None">XMLTV</MudLink>
<MudLink Style="@($"color:{Colors.Shades.White}")" Color="Color.Inherit" Href="/swagger" Target="_blank" Class="mr-4" Underline="Underline.None">API</MudLink>
<MudDivider Vertical="true" FlexItem="true" DividerType="DividerType.Middle" Class="mx-4 my-5" />
<MudDivider Vertical="true" FlexItem="true" DividerType="DividerType.Middle" Class="mx-4 my-5"/>
<MudTooltip Text="Discord">
<MudIconButton Icon="fab fa-discord" Color="Color.Inherit" Link="https://discord.gg/hHaJm3yGy6" Target="_blank"/>
</MudTooltip>

20
ErsatzTV/Startup.cs

@ -1,5 +1,4 @@ @@ -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 @@ -100,23 +99,14 @@ namespace ErsatzTV
// string xmltvPath = Path.Combine(appDataFolder, "xmltv.xml");
// Log.Logger.Information("XMLTV is at {XmltvPath}", xmltvPath);
services.AddDbContextFactory<TvContext>(
options =>
{
options.UseSqlite(
$"Data Source={FileSystemLayout.DatabasePath}",
o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
if (Debugger.IsAttached)
{
options.EnableSensitiveDataLogging();
}
});
services.AddDbContext<TvContext>(
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);

2
ErsatzTV/_Imports.razor

@ -20,7 +20,5 @@ @@ -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
Loading…
Cancel
Save