mirror of https://github.com/ErsatzTV/ErsatzTV.git
Browse Source
* spike new scanner * add existing items to new scanner * add collection refresh actions * add tv show metadata and posters * update metadata and posters when nfo/poster files are updated * add "remove" action, test for all supported file extensions * update statistics when primary video file is updated * reflect that collections are "sourced" from nfo * implement most scanning actions * cleanup * fix startup * cross-platform scanner testspull/1213/head
24 changed files with 2413 additions and 96 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@ |
|||||||
|
namespace ErsatzTV.Core.Domain |
||||||
|
{ |
||||||
|
public enum MetadataSource |
||||||
|
{ |
||||||
|
Fallback = 0, |
||||||
|
Sidecar = 1 |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
using ErsatzTV.Core.Metadata; |
||||||
|
using LanguageExt; |
||||||
|
|
||||||
|
namespace ErsatzTV.Core.Interfaces.Metadata |
||||||
|
{ |
||||||
|
public interface ILocalMediaSourcePlanner |
||||||
|
{ |
||||||
|
public Seq<LocalMediaSourcePlan> DetermineActions( |
||||||
|
MediaType mediaType, |
||||||
|
Seq<MediaItem> mediaItems, |
||||||
|
Seq<string> files); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
namespace ErsatzTV.Core.Metadata |
||||||
|
{ |
||||||
|
public record ActionPlan(string TargetPath, ScanningAction TargetAction); |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
using System.Collections.Generic; |
||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
using LanguageExt; |
||||||
|
|
||||||
|
namespace ErsatzTV.Core.Metadata |
||||||
|
{ |
||||||
|
public record LocalMediaSourcePlan(Either<string, MediaItem> Source, List<ActionPlan> ActionPlans) |
||||||
|
{ |
||||||
|
public Either<string, MediaItem> Source { get; set; } = Source; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,179 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.IO; |
||||||
|
using System.Linq; |
||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
using ErsatzTV.Core.Interfaces.Metadata; |
||||||
|
using LanguageExt; |
||||||
|
using static LanguageExt.Prelude; |
||||||
|
|
||||||
|
namespace ErsatzTV.Core.Metadata |
||||||
|
{ |
||||||
|
// TODO: this needs a better name
|
||||||
|
public class LocalMediaSourcePlanner : ILocalMediaSourcePlanner |
||||||
|
{ |
||||||
|
private static readonly Seq<string> ImageFileExtensions = Seq("jpg", "jpeg", "png", "gif", "tbn"); |
||||||
|
private readonly ILocalFileSystem _localFileSystem; |
||||||
|
|
||||||
|
public LocalMediaSourcePlanner(ILocalFileSystem localFileSystem) => _localFileSystem = localFileSystem; |
||||||
|
|
||||||
|
public Seq<LocalMediaSourcePlan> DetermineActions( |
||||||
|
MediaType mediaType, |
||||||
|
Seq<MediaItem> mediaItems, |
||||||
|
Seq<string> files) |
||||||
|
{ |
||||||
|
var results = new IntermediateResults(); |
||||||
|
Seq<string> videoFiles = files.Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f))) |
||||||
|
.Filter(f => !IsExtra(f)); |
||||||
|
|
||||||
|
(Seq<string> newFiles, Seq<MediaItem> existingMediaItems) = videoFiles.Map( |
||||||
|
s => mediaItems.Find(i => i.Path == s).ToEither(s)) |
||||||
|
.Partition(); |
||||||
|
|
||||||
|
// new files
|
||||||
|
foreach (string file in newFiles) |
||||||
|
{ |
||||||
|
results.Add(file, new ActionPlan(file, ScanningAction.Add)); |
||||||
|
results.Add(file, new ActionPlan(file, ScanningAction.Statistics)); |
||||||
|
|
||||||
|
Option<string> maybeNfoFile = LocateNfoFile(mediaType, files, file); |
||||||
|
maybeNfoFile.BiIter( |
||||||
|
nfoFile => |
||||||
|
{ |
||||||
|
results.Add(file, new ActionPlan(nfoFile, ScanningAction.SidecarMetadata)); |
||||||
|
results.Add(file, new ActionPlan(nfoFile, ScanningAction.Collections)); |
||||||
|
}, |
||||||
|
() => |
||||||
|
{ |
||||||
|
results.Add(file, new ActionPlan(file, ScanningAction.FallbackMetadata)); |
||||||
|
results.Add(file, new ActionPlan(file, ScanningAction.Collections)); |
||||||
|
}); |
||||||
|
|
||||||
|
Option<string> maybePoster = LocatePoster(mediaType, files, file); |
||||||
|
maybePoster.IfSome( |
||||||
|
posterFile => results.Add(file, new ActionPlan(posterFile, ScanningAction.Poster))); |
||||||
|
} |
||||||
|
|
||||||
|
// existing media items
|
||||||
|
foreach (MediaItem mediaItem in existingMediaItems) |
||||||
|
{ |
||||||
|
if ((mediaItem.LastWriteTime ?? DateTime.MinValue) < _localFileSystem.GetLastWriteTime(mediaItem.Path)) |
||||||
|
{ |
||||||
|
results.Add(mediaItem, new ActionPlan(mediaItem.Path, ScanningAction.Statistics)); |
||||||
|
} |
||||||
|
|
||||||
|
Option<string> maybeNfoFile = LocateNfoFile(mediaType, files, mediaItem.Path); |
||||||
|
maybeNfoFile.IfSome( |
||||||
|
nfoFile => |
||||||
|
{ |
||||||
|
if (mediaItem.Metadata == null || mediaItem.Metadata.Source == MetadataSource.Fallback || |
||||||
|
(mediaItem.Metadata.LastWriteTime ?? DateTime.MinValue) < |
||||||
|
_localFileSystem.GetLastWriteTime(nfoFile)) |
||||||
|
{ |
||||||
|
results.Add(mediaItem, new ActionPlan(nfoFile, ScanningAction.SidecarMetadata)); |
||||||
|
results.Add(mediaItem, new ActionPlan(nfoFile, ScanningAction.Collections)); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
Option<string> maybePoster = LocatePoster(mediaType, files, mediaItem.Path); |
||||||
|
maybePoster.IfSome( |
||||||
|
posterFile => |
||||||
|
{ |
||||||
|
if (string.IsNullOrWhiteSpace(mediaItem.Poster) || |
||||||
|
(mediaItem.PosterLastWriteTime ?? DateTime.MinValue) < |
||||||
|
_localFileSystem.GetLastWriteTime(posterFile)) |
||||||
|
{ |
||||||
|
results.Add(mediaItem, new ActionPlan(posterFile, ScanningAction.Poster)); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
// missing media items
|
||||||
|
foreach (MediaItem mediaItem in mediaItems.Where(i => !files.Contains(i.Path))) |
||||||
|
{ |
||||||
|
results.Add(mediaItem, new ActionPlan(mediaItem.Path, ScanningAction.Remove)); |
||||||
|
} |
||||||
|
|
||||||
|
return results.Summarize(); |
||||||
|
} |
||||||
|
|
||||||
|
private static bool IsExtra(string path) |
||||||
|
{ |
||||||
|
string folder = Path.GetFileName(Path.GetDirectoryName(path) ?? string.Empty); |
||||||
|
string file = Path.GetFileNameWithoutExtension(path); |
||||||
|
return ExtraDirectories.Contains(folder, StringComparer.OrdinalIgnoreCase) |
||||||
|
|| ExtraFiles.Any(f => file.EndsWith(f, StringComparison.OrdinalIgnoreCase)); |
||||||
|
} |
||||||
|
|
||||||
|
private static Option<string> LocateNfoFile(MediaType mediaType, Seq<string> files, string file) |
||||||
|
{ |
||||||
|
switch (mediaType) |
||||||
|
{ |
||||||
|
case MediaType.Movie: |
||||||
|
string movieAsNfo = Path.ChangeExtension(file, "nfo"); |
||||||
|
string movieNfo = Path.Combine(Path.GetDirectoryName(file) ?? string.Empty, "movie.nfo"); |
||||||
|
return Seq(movieAsNfo, movieNfo) |
||||||
|
.Filter(s => files.Contains(s)) |
||||||
|
.HeadOrNone(); |
||||||
|
case MediaType.TvShow: |
||||||
|
string episodeAsNfo = Path.ChangeExtension(file, "nfo"); |
||||||
|
return Optional(episodeAsNfo) |
||||||
|
.Filter(s => files.Contains(s)) |
||||||
|
.HeadOrNone(); |
||||||
|
} |
||||||
|
|
||||||
|
return None; |
||||||
|
} |
||||||
|
|
||||||
|
private static Option<string> LocatePoster(MediaType mediaType, Seq<string> files, string file) |
||||||
|
{ |
||||||
|
string folder = Path.GetDirectoryName(file) ?? string.Empty; |
||||||
|
|
||||||
|
switch (mediaType) |
||||||
|
{ |
||||||
|
case MediaType.Movie: |
||||||
|
IEnumerable<string> possibleMoviePosters = ImageFileExtensions.Collect( |
||||||
|
ext => new[] { $"poster.{ext}", Path.GetFileNameWithoutExtension(file) + $"-poster.{ext}" }) |
||||||
|
.Map(f => Path.Combine(folder, f)); |
||||||
|
return possibleMoviePosters.Filter(p => files.Contains(p)).HeadOrNone(); |
||||||
|
case MediaType.TvShow: |
||||||
|
string parentFolder = Directory.GetParent(folder)?.FullName ?? string.Empty; |
||||||
|
IEnumerable<string> possibleTvPosters = ImageFileExtensions |
||||||
|
.Collect(ext => new[] { $"poster.{ext}" }) |
||||||
|
.Map(f => Path.Combine(parentFolder, f)); |
||||||
|
return possibleTvPosters.Filter(p => files.Contains(p)).HeadOrNone(); |
||||||
|
} |
||||||
|
|
||||||
|
return None; |
||||||
|
} |
||||||
|
|
||||||
|
private class IntermediateResults |
||||||
|
{ |
||||||
|
private readonly List<Tuple<Either<string, MediaItem>, ActionPlan>> _rawResults = new(); |
||||||
|
|
||||||
|
public void Add(Either<string, MediaItem> source, ActionPlan plan) => |
||||||
|
_rawResults.Add(Tuple(source, plan)); |
||||||
|
|
||||||
|
public Seq<LocalMediaSourcePlan> Summarize() => |
||||||
|
_rawResults |
||||||
|
.GroupBy(t => t.Item1) |
||||||
|
.Select(g => new LocalMediaSourcePlan(g.Key, g.Select(g2 => g2.Item2).ToList())) |
||||||
|
.ToSeq(); |
||||||
|
} |
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
private static readonly Seq<string> VideoFileExtensions = Seq( |
||||||
|
".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".ogg", ".mp4", |
||||||
|
".m4p", ".m4v", ".avi", ".wmv", ".mov", ".mkv", ".ts"); |
||||||
|
|
||||||
|
private static readonly Seq<string> ExtraDirectories = Seq( |
||||||
|
"behind the scenes", "deleted scenes", "featurettes", |
||||||
|
"interviews", "scenes", "shorts", "trailers", "other", |
||||||
|
"extras", "specials"); |
||||||
|
|
||||||
|
private static readonly Seq<string> ExtraFiles = Seq( |
||||||
|
"behindthescenes", "deleted", "featurette", |
||||||
|
"interview", "scene", "short", "trailer", "other"); |
||||||
|
// @formatter:on
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
namespace ErsatzTV.Core.Metadata |
||||||
|
{ |
||||||
|
public enum ScanningAction |
||||||
|
{ |
||||||
|
None = 0, |
||||||
|
Add = 1, |
||||||
|
Remove = 2, |
||||||
|
Statistics = 3, |
||||||
|
SidecarMetadata = 4, |
||||||
|
FallbackMetadata = 5, |
||||||
|
Collections = 6, |
||||||
|
Poster = 7 |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,905 @@ |
|||||||
|
// <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("20210215153541_MetadataOptimizations")] |
||||||
|
partial class MetadataOptimizations |
||||||
|
{ |
||||||
|
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<int>("MediaItemId") |
||||||
|
.HasColumnType("INTEGER"); |
||||||
|
|
||||||
|
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.Property<DateTime?>("PosterLastWriteTime") |
||||||
|
.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<DateTime?>("LastWriteTime") |
||||||
|
.HasColumnType("TEXT"); |
||||||
|
|
||||||
|
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<int>("Source") |
||||||
|
.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
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
using System; |
||||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Migrations |
||||||
|
{ |
||||||
|
public partial class MetadataOptimizations : Migration |
||||||
|
{ |
||||||
|
protected override void Up(MigrationBuilder migrationBuilder) |
||||||
|
{ |
||||||
|
migrationBuilder.AddColumn<DateTime>( |
||||||
|
"Metadata_LastWriteTime", |
||||||
|
"MediaItems", |
||||||
|
"TEXT", |
||||||
|
nullable: true); |
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>( |
||||||
|
"Metadata_Source", |
||||||
|
"MediaItems", |
||||||
|
"INTEGER", |
||||||
|
nullable: true); |
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>( |
||||||
|
"PosterLastWriteTime", |
||||||
|
"MediaItems", |
||||||
|
"TEXT", |
||||||
|
nullable: true); |
||||||
|
} |
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder) |
||||||
|
{ |
||||||
|
migrationBuilder.DropColumn( |
||||||
|
"Metadata_LastWriteTime", |
||||||
|
"MediaItems"); |
||||||
|
|
||||||
|
migrationBuilder.DropColumn( |
||||||
|
"Metadata_Source", |
||||||
|
"MediaItems"); |
||||||
|
|
||||||
|
migrationBuilder.DropColumn( |
||||||
|
"PosterLastWriteTime", |
||||||
|
"MediaItems"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue