mirror of https://github.com/ErsatzTV/ErsatzTV.git
Browse Source
* replace media items tables with card grids * style cleanup * add basic paging * sort and page in the db * optimize sql for movies * support movie posters * resize movie posters and store in cache with channel logos * fix bug preventing folders with more than 50 chars as local media sources * support tv posterspull/1213/head
48 changed files with 2386 additions and 178 deletions
@ -0,0 +1,9 @@ |
|||||||
|
namespace ErsatzTV.Application |
||||||
|
{ |
||||||
|
public interface IMediaCard |
||||||
|
{ |
||||||
|
string Title { get; } |
||||||
|
string SortTitle { get; } |
||||||
|
string Subtitle { get; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,6 @@ |
|||||||
|
using System.Collections.Generic; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.MediaItems |
||||||
|
{ |
||||||
|
public record AggregateMediaItemResults(int Count, List<AggregateMediaItemViewModel> DataPage); |
||||||
|
} |
||||||
@ -1,4 +1,8 @@ |
|||||||
namespace ErsatzTV.Application.MediaItems |
namespace ErsatzTV.Application.MediaItems |
||||||
{ |
{ |
||||||
public record AggregateMediaItemViewModel(string Source, string Title, int Count, string Duration); |
public record AggregateMediaItemViewModel( |
||||||
|
string Title, |
||||||
|
string Subtitle, |
||||||
|
string SortTitle, |
||||||
|
string Poster); |
||||||
} |
} |
||||||
|
|||||||
@ -0,0 +1,9 @@ |
|||||||
|
namespace ErsatzTV.Application.MediaItems.Commands |
||||||
|
{ |
||||||
|
public record RefreshMediaItemPoster : RefreshMediaItem |
||||||
|
{ |
||||||
|
public RefreshMediaItemPoster(int mediaItemId) : base(mediaItemId) |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -1,9 +1,8 @@ |
|||||||
using System.Collections.Generic; |
using ErsatzTV.Core.Domain; |
||||||
using ErsatzTV.Core.Domain; |
|
||||||
using MediatR; |
using MediatR; |
||||||
|
|
||||||
namespace ErsatzTV.Application.MediaItems.Queries |
namespace ErsatzTV.Application.MediaItems.Queries |
||||||
{ |
{ |
||||||
public record GetAggregateMediaItems |
public record GetAggregateMediaItems |
||||||
(MediaType MediaType, string SearchString) : IRequest<List<AggregateMediaItemViewModel>>; |
(MediaType MediaType, int PageNumber, int PageSize) : IRequest<AggregateMediaItemResults>; |
||||||
} |
} |
||||||
|
|||||||
@ -1,46 +1,42 @@ |
|||||||
using System; |
using System.Collections.Generic; |
||||||
using System.Collections.Generic; |
|
||||||
using System.Linq; |
using System.Linq; |
||||||
using System.Threading; |
using System.Threading; |
||||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||||
using ErsatzTV.Core.Domain; |
using ErsatzTV.Core.AggregateModels; |
||||||
using ErsatzTV.Core.Interfaces.Repositories; |
using ErsatzTV.Core.Interfaces.Repositories; |
||||||
using MediatR; |
using MediatR; |
||||||
|
|
||||||
namespace ErsatzTV.Application.MediaItems.Queries |
namespace ErsatzTV.Application.MediaItems.Queries |
||||||
{ |
{ |
||||||
public class |
public class |
||||||
GetAggregateMediaItemsHandler : IRequestHandler<GetAggregateMediaItems, List<AggregateMediaItemViewModel>> |
GetAggregateMediaItemsHandler : IRequestHandler<GetAggregateMediaItems, AggregateMediaItemResults> |
||||||
{ |
{ |
||||||
private readonly IMediaItemRepository _mediaItemRepository; |
private readonly IMediaItemRepository _mediaItemRepository; |
||||||
|
|
||||||
public GetAggregateMediaItemsHandler(IMediaItemRepository mediaItemRepository) => |
public GetAggregateMediaItemsHandler(IMediaItemRepository mediaItemRepository) => |
||||||
_mediaItemRepository = mediaItemRepository; |
_mediaItemRepository = mediaItemRepository; |
||||||
|
|
||||||
public async Task<List<AggregateMediaItemViewModel>> Handle( |
public async Task<AggregateMediaItemResults> Handle( |
||||||
GetAggregateMediaItems request, |
GetAggregateMediaItems request, |
||||||
CancellationToken cancellationToken) |
CancellationToken cancellationToken) |
||||||
{ |
{ |
||||||
IEnumerable<MediaItem> allItems = await _mediaItemRepository.GetAll(request.MediaType); |
int count = await _mediaItemRepository.GetCountByType(request.MediaType); |
||||||
|
|
||||||
if (!string.IsNullOrEmpty(request.SearchString)) |
IEnumerable<MediaItemSummary> allItems = await _mediaItemRepository.GetPageByType( |
||||||
{ |
request.MediaType, |
||||||
allItems = allItems.Filter( |
request.PageNumber, |
||||||
i => i.Metadata?.Title.Contains(request.SearchString, StringComparison.OrdinalIgnoreCase) == |
request.PageSize); |
||||||
true); |
|
||||||
} |
|
||||||
|
|
||||||
return allItems.GroupBy(c => new { c.Source.Name, c.Metadata.Title }).Map( |
var results = allItems |
||||||
group => new AggregateMediaItemViewModel( |
.Map( |
||||||
group.Key.Name, |
s => new AggregateMediaItemViewModel( |
||||||
group.Key.Title, |
s.Title, |
||||||
group.Count(), |
s.Subtitle, |
||||||
group.Count() == 1 ? DisplayDuration(group.Head()) : string.Empty)) |
s.SortTitle, |
||||||
|
s.Poster)) |
||||||
.ToList(); |
.ToList(); |
||||||
} |
|
||||||
|
|
||||||
private static string DisplayDuration(MediaItem mediaItem) => string.Format( |
return new AggregateMediaItemResults(count, results); |
||||||
mediaItem.Metadata?.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}", |
} |
||||||
mediaItem.Metadata?.Duration); |
|
||||||
} |
} |
||||||
} |
} |
||||||
|
|||||||
@ -0,0 +1,4 @@ |
|||||||
|
namespace ErsatzTV.Core.AggregateModels |
||||||
|
{ |
||||||
|
public record MediaItemSummary(string Title, string SortTitle, string Subtitle, string Poster); |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
using System.Threading.Tasks; |
||||||
|
using LanguageExt; |
||||||
|
|
||||||
|
namespace ErsatzTV.Core.Interfaces.Images |
||||||
|
{ |
||||||
|
public interface IImageCache |
||||||
|
{ |
||||||
|
Task<Either<BaseError, string>> ResizeAndSaveImage(byte[] imageBuffer, int? height, int? width); |
||||||
|
Task<Either<BaseError, string>> SaveImage(byte[] imageBuffer); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
using System.Threading.Tasks; |
||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
|
||||||
|
namespace ErsatzTV.Core.Interfaces.Metadata |
||||||
|
{ |
||||||
|
public interface ILocalPosterProvider |
||||||
|
{ |
||||||
|
Task RefreshPoster(MediaItem mediaItem); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,90 @@ |
|||||||
|
using System.IO; |
||||||
|
using System.Linq; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
using ErsatzTV.Core.Interfaces.Images; |
||||||
|
using ErsatzTV.Core.Interfaces.Metadata; |
||||||
|
using ErsatzTV.Core.Interfaces.Repositories; |
||||||
|
using LanguageExt; |
||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using static LanguageExt.Prelude; |
||||||
|
|
||||||
|
namespace ErsatzTV.Core.Metadata |
||||||
|
{ |
||||||
|
public class LocalPosterProvider : ILocalPosterProvider |
||||||
|
{ |
||||||
|
private readonly IImageCache _imageCache; |
||||||
|
private readonly ILogger<LocalPosterProvider> _logger; |
||||||
|
private readonly IMediaItemRepository _mediaItemRepository; |
||||||
|
|
||||||
|
public LocalPosterProvider( |
||||||
|
IMediaItemRepository mediaItemRepository, |
||||||
|
IImageCache imageCache, |
||||||
|
ILogger<LocalPosterProvider> logger) |
||||||
|
{ |
||||||
|
_mediaItemRepository = mediaItemRepository; |
||||||
|
_imageCache = imageCache; |
||||||
|
_logger = logger; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task RefreshPoster(MediaItem mediaItem) |
||||||
|
{ |
||||||
|
Option<string> maybePosterPath = mediaItem.Metadata.MediaType switch |
||||||
|
{ |
||||||
|
MediaType.Movie => RefreshMoviePoster(mediaItem), |
||||||
|
MediaType.TvShow => RefreshTelevisionPoster(mediaItem), |
||||||
|
_ => None |
||||||
|
}; |
||||||
|
|
||||||
|
await maybePosterPath.Match( |
||||||
|
path => SavePosterToDisk(mediaItem, path), |
||||||
|
Task.CompletedTask); |
||||||
|
} |
||||||
|
|
||||||
|
private static Option<string> RefreshMoviePoster(MediaItem mediaItem) |
||||||
|
{ |
||||||
|
string folder = Path.GetDirectoryName(mediaItem.Path); |
||||||
|
if (folder != null) |
||||||
|
{ |
||||||
|
string[] possiblePaths = |
||||||
|
{ "poster.jpg", Path.GetFileNameWithoutExtension(mediaItem.Path) + "-poster.jpg" }; |
||||||
|
Option<string> maybePoster = |
||||||
|
possiblePaths.Map(p => Path.Combine(folder, p)).FirstOrDefault(File.Exists); |
||||||
|
return maybePoster; |
||||||
|
} |
||||||
|
|
||||||
|
return None; |
||||||
|
} |
||||||
|
|
||||||
|
private Option<string> RefreshTelevisionPoster(MediaItem mediaItem) |
||||||
|
{ |
||||||
|
string folder = Directory.GetParent(Path.GetDirectoryName(mediaItem.Path) ?? string.Empty)?.FullName; |
||||||
|
if (folder != null) |
||||||
|
{ |
||||||
|
string[] possiblePaths = { "poster.jpg" }; |
||||||
|
Option<string> maybePoster = |
||||||
|
possiblePaths.Map(p => Path.Combine(folder, p)).FirstOrDefault(File.Exists); |
||||||
|
return maybePoster; |
||||||
|
} |
||||||
|
|
||||||
|
return None; |
||||||
|
} |
||||||
|
|
||||||
|
private async Task SavePosterToDisk(MediaItem mediaItem, string posterPath) |
||||||
|
{ |
||||||
|
byte[] originalBytes = await File.ReadAllBytesAsync(posterPath); |
||||||
|
Either<BaseError, string> maybeHash = await _imageCache.ResizeAndSaveImage(originalBytes, 220, null); |
||||||
|
await maybeHash.Match( |
||||||
|
hash => |
||||||
|
{ |
||||||
|
mediaItem.Poster = hash; |
||||||
|
return _mediaItemRepository.Update(mediaItem); |
||||||
|
}, |
||||||
|
error => |
||||||
|
{ |
||||||
|
_logger.LogWarning("Unable to save poster to disk from {Path}: {Error}", posterPath, error.Value); |
||||||
|
return Task.CompletedTask; |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -1,12 +0,0 @@ |
|||||||
using ErsatzTV.Core.Domain; |
|
||||||
using Microsoft.EntityFrameworkCore; |
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
|
||||||
|
|
||||||
namespace ErsatzTV.Infrastructure.Data.Configurations |
|
||||||
{ |
|
||||||
public class LogEntryConfiguration : IEntityTypeConfiguration<LogEntry> |
|
||||||
{ |
|
||||||
public void Configure(EntityTypeBuilder<LogEntry> builder) => |
|
||||||
builder.ToTable("Logs"); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,12 @@ |
|||||||
|
using ErsatzTV.Core.AggregateModels; |
||||||
|
using Microsoft.EntityFrameworkCore; |
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Data.Configurations |
||||||
|
{ |
||||||
|
public class MediaItemSummaryConfiguration : IEntityTypeConfiguration<MediaItemSummary> |
||||||
|
{ |
||||||
|
public void Configure(EntityTypeBuilder<MediaItemSummary> builder) => |
||||||
|
builder.HasNoKey().ToView(null); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,64 @@ |
|||||||
|
using System; |
||||||
|
using System.IO; |
||||||
|
using System.Security.Cryptography; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using ErsatzTV.Core; |
||||||
|
using ErsatzTV.Core.Interfaces.Images; |
||||||
|
using LanguageExt; |
||||||
|
using SixLabors.ImageSharp; |
||||||
|
using SixLabors.ImageSharp.Formats.Jpeg; |
||||||
|
using SixLabors.ImageSharp.Processing; |
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Images |
||||||
|
{ |
||||||
|
public class ImageCache : IImageCache |
||||||
|
{ |
||||||
|
private static readonly SHA1CryptoServiceProvider Crypto; |
||||||
|
|
||||||
|
static ImageCache() => Crypto = new SHA1CryptoServiceProvider(); |
||||||
|
|
||||||
|
public async Task<Either<BaseError, string>> ResizeAndSaveImage(byte[] imageBuffer, int? height, int? width) |
||||||
|
{ |
||||||
|
await using var inStream = new MemoryStream(imageBuffer); |
||||||
|
using var image = await Image.LoadAsync(inStream); |
||||||
|
|
||||||
|
Size size = height.HasValue ? new Size { Height = height.Value } : new Size { Width = width.Value }; |
||||||
|
|
||||||
|
image.Mutate( |
||||||
|
i => i.Resize( |
||||||
|
new ResizeOptions |
||||||
|
{ |
||||||
|
Mode = ResizeMode.Max, |
||||||
|
Size = size |
||||||
|
})); |
||||||
|
|
||||||
|
await using var outStream = new MemoryStream(); |
||||||
|
await image.SaveAsync(outStream, new JpegEncoder { Quality = 90 }); |
||||||
|
|
||||||
|
return await SaveImage(outStream.ToArray()); |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<Either<BaseError, string>> SaveImage(byte[] imageBuffer) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
byte[] hash = Crypto.ComputeHash(imageBuffer); |
||||||
|
string hex = BitConverter.ToString(hash).Replace("-", string.Empty); |
||||||
|
|
||||||
|
string fileName = Path.Combine(FileSystemLayout.ImageCacheFolder, hex); |
||||||
|
|
||||||
|
if (!Directory.Exists(FileSystemLayout.ImageCacheFolder)) |
||||||
|
{ |
||||||
|
Directory.CreateDirectory(FileSystemLayout.ImageCacheFolder); |
||||||
|
} |
||||||
|
|
||||||
|
await File.WriteAllBytesAsync(fileName, imageBuffer); |
||||||
|
return hex; |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
return BaseError.New(ex.Message); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,879 @@ |
|||||||
|
// <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("20210213155419_MetadataSortTitle")] |
||||||
|
partial class MetadataSortTitle |
||||||
|
{ |
||||||
|
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>("SortTitle") |
||||||
|
.HasColumnType("TEXT"); |
||||||
|
|
||||||
|
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
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,30 @@ |
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Migrations |
||||||
|
{ |
||||||
|
public partial class MetadataSortTitle : Migration |
||||||
|
{ |
||||||
|
protected override void Up(MigrationBuilder migrationBuilder) |
||||||
|
{ |
||||||
|
migrationBuilder.AddColumn<string>( |
||||||
|
"Metadata_SortTitle", |
||||||
|
"MediaItems", |
||||||
|
"TEXT", |
||||||
|
nullable: true); |
||||||
|
|
||||||
|
migrationBuilder.Sql( |
||||||
|
@"UPDATE MediaItems
|
||||||
|
SET Metadata_SortTitle = Metadata_Title");
|
||||||
|
|
||||||
|
migrationBuilder.Sql( |
||||||
|
@"UPDATE MediaItems
|
||||||
|
SET Metadata_SortTitle = substr(Metadata_Title, 5) |
||||||
|
WHERE Metadata_Title LIKE 'the %'");
|
||||||
|
} |
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder) => |
||||||
|
migrationBuilder.DropColumn( |
||||||
|
"Metadata_SortTitle", |
||||||
|
"MediaItems"); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,893 @@ |
|||||||
|
// <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("20210213221040_MediaItemPoster")] |
||||||
|
partial class MediaItemPoster |
||||||
|
{ |
||||||
|
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"); |
||||||
|
}); |
||||||
|
|
||||||
|
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"); |
||||||
|
}); |
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.AggregateModels.MediaItemSummary", b => |
||||||
|
{ |
||||||
|
b.Property<string>("Poster") |
||||||
|
.HasColumnType("TEXT"); |
||||||
|
|
||||||
|
b.Property<string>("SortTitle") |
||||||
|
.HasColumnType("TEXT"); |
||||||
|
|
||||||
|
b.Property<string>("Subtitle") |
||||||
|
.HasColumnType("TEXT"); |
||||||
|
|
||||||
|
b.Property<string>("Title") |
||||||
|
.HasColumnType("TEXT"); |
||||||
|
}); |
||||||
|
|
||||||
|
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.Property<string>("Poster") |
||||||
|
.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>("SortTitle") |
||||||
|
.HasColumnType("TEXT"); |
||||||
|
|
||||||
|
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
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,48 @@ |
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Migrations |
||||||
|
{ |
||||||
|
public partial class MediaItemPoster : Migration |
||||||
|
{ |
||||||
|
protected override void Up(MigrationBuilder migrationBuilder) |
||||||
|
{ |
||||||
|
migrationBuilder.DropTable( |
||||||
|
"GenericIntegerIds"); |
||||||
|
|
||||||
|
migrationBuilder.DropTable( |
||||||
|
"MediaCollectionSummaries"); |
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>( |
||||||
|
"Poster", |
||||||
|
"MediaItems", |
||||||
|
"TEXT", |
||||||
|
nullable: true); |
||||||
|
} |
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder) |
||||||
|
{ |
||||||
|
migrationBuilder.DropColumn( |
||||||
|
"Poster", |
||||||
|
"MediaItems"); |
||||||
|
|
||||||
|
migrationBuilder.CreateTable( |
||||||
|
"GenericIntegerIds", |
||||||
|
table => new |
||||||
|
{ |
||||||
|
Id = table.Column<int>("INTEGER", nullable: false) |
||||||
|
}, |
||||||
|
constraints: table => { }); |
||||||
|
|
||||||
|
migrationBuilder.CreateTable( |
||||||
|
"MediaCollectionSummaries", |
||||||
|
table => new |
||||||
|
{ |
||||||
|
Id = table.Column<int>("INTEGER", nullable: false), |
||||||
|
IsSimple = table.Column<bool>("INTEGER", nullable: false), |
||||||
|
ItemCount = table.Column<int>("INTEGER", nullable: false), |
||||||
|
Name = table.Column<string>("TEXT", nullable: true) |
||||||
|
}, |
||||||
|
constraints: table => { }); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,28 @@ |
|||||||
|
using System.Threading.Tasks; |
||||||
|
using ErsatzTV.Application.Images; |
||||||
|
using ErsatzTV.Application.Images.Queries; |
||||||
|
using ErsatzTV.Core; |
||||||
|
using LanguageExt; |
||||||
|
using MediatR; |
||||||
|
using Microsoft.AspNetCore.Mvc; |
||||||
|
|
||||||
|
namespace ErsatzTV.Controllers |
||||||
|
{ |
||||||
|
[ApiController] |
||||||
|
[ApiExplorerSettings(IgnoreApi = true)] |
||||||
|
public class PostersController : ControllerBase |
||||||
|
{ |
||||||
|
private readonly IMediator _mediator; |
||||||
|
|
||||||
|
public PostersController(IMediator mediator) => _mediator = mediator; |
||||||
|
|
||||||
|
[HttpGet("/posters/{fileName}")] |
||||||
|
public async Task<IActionResult> GetImage(string fileName) |
||||||
|
{ |
||||||
|
Either<BaseError, ImageViewModel> imageContents = await _mediator.Send(new GetImageContents(fileName)); |
||||||
|
return imageContents.Match<IActionResult>( |
||||||
|
Left: _ => new NotFoundResult(), |
||||||
|
Right: r => new FileContentResult(r.Contents, r.MimeType)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -1,17 +0,0 @@ |
|||||||
@page "/media/items" |
|
||||||
|
|
||||||
<MudTabs Elevation="1"> |
|
||||||
<MudTabPanel Text="TV Shows"> |
|
||||||
<MediaItemTable MediaType="@MediaType.TvShow"/> |
|
||||||
</MudTabPanel> |
|
||||||
<MudTabPanel Text="Movies"> |
|
||||||
<MediaItemTable MediaType="@MediaType.Movie"/> |
|
||||||
</MudTabPanel> |
|
||||||
<MudTabPanel Text="Other"> |
|
||||||
<MediaItemTable MediaType="@MediaType.Other"/> |
|
||||||
</MudTabPanel> |
|
||||||
</MudTabs> |
|
||||||
|
|
||||||
@code { |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,8 @@ |
|||||||
|
@page "/media/movies/items" |
||||||
|
@inject IMediator Mediator |
||||||
|
|
||||||
|
<MediaItemsGrid MediaType="@MediaType.Movie"/> |
||||||
|
|
||||||
|
@code { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
@page "/media/other/items" |
||||||
|
@inject IMediator Mediator |
||||||
|
|
||||||
|
<MediaItemsGrid MediaType="@MediaType.Other"/> |
||||||
|
|
||||||
|
@code { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
@page "/media/tv/items" |
||||||
|
@inject IMediator Mediator |
||||||
|
|
||||||
|
<MediaItemsGrid MediaType="@MediaType.TvShow"/> |
||||||
|
|
||||||
|
@code { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
@using ErsatzTV.Application.MediaItems |
||||||
|
<div class="media-card-container mx-3 pb-3"> |
||||||
|
<MudPaper Class="media-card"> |
||||||
|
@if (!string.IsNullOrWhiteSpace(Data.Poster)) |
||||||
|
{ |
||||||
|
<img src="@($"/posters/{Data.Poster}")" style="max-height: 220px"/> |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
<MudText Align="Align.Center" Typo="Typo.h1" Class="media-card-poster-placeholder mud-text-disabled"> |
||||||
|
@Placeholder(Data.SortTitle) |
||||||
|
</MudText> |
||||||
|
} |
||||||
|
</MudPaper> |
||||||
|
<MudText Align="Align.Center" Class="media-card-title" UserAttributes="@(new Dictionary<string, object> { { "title", Data.Title } })"> |
||||||
|
@Data.Title |
||||||
|
</MudText> |
||||||
|
<MudText Typo="Typo.body2" Align="Align.Center" Class="mud-text-secondary"> |
||||||
|
@Data.Subtitle |
||||||
|
</MudText> |
||||||
|
</div> |
||||||
|
|
||||||
|
@code { |
||||||
|
|
||||||
|
[Parameter] |
||||||
|
public AggregateMediaItemViewModel Data { get; set; } |
||||||
|
|
||||||
|
private string Placeholder(string sortTitle) |
||||||
|
{ |
||||||
|
string first = sortTitle.Substring(0, 1).ToUpperInvariant(); |
||||||
|
return int.TryParse(first, out _) ? "#" : first; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,66 +0,0 @@ |
|||||||
@using ErsatzTV.Application.MediaItems |
|
||||||
@using ErsatzTV.Application.MediaItems.Queries |
|
||||||
@inject IMediator Mediator |
|
||||||
|
|
||||||
<MudTable @ref="_table" Hover="true" ServerData="@(new Func<TableState, Task<TableData<AggregateMediaItemViewModel>>>(ServerReload))"> |
|
||||||
<ToolBarContent> |
|
||||||
<MudText Typo="Typo.h6">Media Items</MudText> |
|
||||||
<MudToolBarSpacer/> |
|
||||||
<MudTextField T="string" ValueChanged="@OnSearch" Placeholder="Search" Adornment="Adornment.Start" |
|
||||||
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"> |
|
||||||
</MudTextField> |
|
||||||
</ToolBarContent> |
|
||||||
<HeaderContent> |
|
||||||
<MudTh>Source</MudTh> |
|
||||||
<MudTh>Title</MudTh> |
|
||||||
<MudTh>Count</MudTh> |
|
||||||
<MudTh>Duration</MudTh> |
|
||||||
</HeaderContent> |
|
||||||
<RowTemplate> |
|
||||||
<MudTd DataLabel="Source">@context.Source</MudTd> |
|
||||||
<MudTd DataLabel="Title">@context.Title</MudTd> |
|
||||||
<MudTd DataLabel="Count">@context.Count</MudTd> |
|
||||||
<MudTd DataLabel="Duration">@context.Duration</MudTd> |
|
||||||
</RowTemplate> |
|
||||||
<PagerContent> |
|
||||||
<MudTablePager/> |
|
||||||
</PagerContent> |
|
||||||
</MudTable> |
|
||||||
|
|
||||||
@code { |
|
||||||
|
|
||||||
[Parameter] |
|
||||||
public MediaType MediaType { get; set; } |
|
||||||
|
|
||||||
private IEnumerable<AggregateMediaItemViewModel> _pagedData; |
|
||||||
private MudTable<AggregateMediaItemViewModel> _table; |
|
||||||
|
|
||||||
private int _totalItems; |
|
||||||
private string _searchString; |
|
||||||
|
|
||||||
private async Task<TableData<AggregateMediaItemViewModel>> ServerReload(TableState state) |
|
||||||
{ |
|
||||||
List<AggregateMediaItemViewModel> aggregateData = |
|
||||||
await Mediator.Send(new GetAggregateMediaItems(MediaType, _searchString)); |
|
||||||
|
|
||||||
_totalItems = aggregateData.Count; |
|
||||||
|
|
||||||
_pagedData = aggregateData.Skip(state.Page * state.PageSize).Take(state.PageSize); |
|
||||||
return new TableData<AggregateMediaItemViewModel> { TotalItems = _totalItems, Items = _pagedData }; |
|
||||||
} |
|
||||||
|
|
||||||
private async Task OnSearch(string text) |
|
||||||
{ |
|
||||||
_searchString = text; |
|
||||||
await _table.ReloadServerData(); |
|
||||||
} |
|
||||||
|
|
||||||
private class MediaItemAggregate |
|
||||||
{ |
|
||||||
public string Source { get; set; } |
|
||||||
public string Title { get; set; } |
|
||||||
public int Count { get; set; } |
|
||||||
public string Duration { get; set; } |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,55 @@ |
|||||||
|
@using ErsatzTV.Application.MediaItems |
||||||
|
@using ErsatzTV.Application.MediaItems.Queries |
||||||
|
@inject IMediator Mediator |
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.Small" Class="mb-6" Style="max-width: 300px"> |
||||||
|
<MudPaper Style="align-items: center; display: flex; justify-content: center;"> |
||||||
|
<MudIconButton Icon="@Icons.Material.Outlined.ChevronLeft" |
||||||
|
OnClick="@(() => PrevPage())" |
||||||
|
Disabled="@(_pageNumber <= 1)"> |
||||||
|
</MudIconButton> |
||||||
|
<MudText Style="flex-grow: 1" |
||||||
|
Align="Align.Center"> |
||||||
|
@Math.Min((_pageNumber - 1) * PageSize + 1, _data.Count)-@Math.Min(_data.Count, _pageNumber * PageSize) of @_data.Count |
||||||
|
</MudText> |
||||||
|
<MudIconButton Icon="@Icons.Material.Outlined.ChevronRight" |
||||||
|
OnClick="@(() => NextPage())" Disabled="@(_pageNumber * PageSize >= _data.Count)"> |
||||||
|
</MudIconButton> |
||||||
|
</MudPaper> |
||||||
|
</MudContainer> |
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.False" Class="media-card-grid"> |
||||||
|
@foreach (AggregateMediaItemViewModel item in _data.DataPage) |
||||||
|
{ |
||||||
|
<MediaCard Data="@item"/> |
||||||
|
} |
||||||
|
</MudContainer> |
||||||
|
|
||||||
|
@code { |
||||||
|
|
||||||
|
[Parameter] |
||||||
|
public MediaType MediaType { get; set; } |
||||||
|
|
||||||
|
private int PageSize => 100; |
||||||
|
private int _pageNumber = 1; |
||||||
|
|
||||||
|
private AggregateMediaItemResults _data; |
||||||
|
|
||||||
|
protected override Task OnParametersSetAsync() => RefreshData(); |
||||||
|
|
||||||
|
private async Task RefreshData() => |
||||||
|
_data = await Mediator.Send(new GetAggregateMediaItems(MediaType, _pageNumber, PageSize)); |
||||||
|
|
||||||
|
private async Task PrevPage() |
||||||
|
{ |
||||||
|
_pageNumber -= 1; |
||||||
|
await RefreshData(); |
||||||
|
} |
||||||
|
|
||||||
|
private async Task NextPage() |
||||||
|
{ |
||||||
|
_pageNumber += 1; |
||||||
|
await RefreshData(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1 +1,24 @@ |
|||||||
|
.media-card-grid { |
||||||
|
display: flex; |
||||||
|
flex-direction: row; |
||||||
|
flex-wrap: wrap; |
||||||
|
} |
||||||
|
|
||||||
|
.media-card-container { width: 152px; } |
||||||
|
|
||||||
|
.media-card { |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
height: 220px; |
||||||
|
justify-content: center; |
||||||
|
width: 152px; |
||||||
|
} |
||||||
|
|
||||||
|
.media-card-title { |
||||||
|
overflow: hidden; |
||||||
|
text-overflow: ellipsis; |
||||||
|
white-space: nowrap; |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
.media-card-poster-placeholder { font-weight: bold; } |
||||||
Loading…
Reference in new issue