Browse Source

add basic plex movie synchronization

pull/34/head
Jason Dove 6 years ago
parent
commit
063579c60b
  1. 6
      ErsatzTV.Application/Libraries/LibraryViewModel.cs
  2. 3
      ErsatzTV.Application/Libraries/LocalLibraryViewModel.cs
  3. 11
      ErsatzTV.Application/Libraries/Mapper.cs
  4. 7
      ErsatzTV.Application/Libraries/PlexLibraryViewModel.cs
  5. 2
      ErsatzTV.Application/Libraries/Queries/GetAllLibraries.cs
  6. 31
      ErsatzTV.Application/Libraries/Queries/GetAllLibrariesHandler.cs
  7. 22
      ErsatzTV.Application/Libraries/Queries/GetLocalLibrariesHandler.cs
  8. 5
      ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryById.cs
  9. 12
      ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryByIdHandler.cs
  10. 8
      ErsatzTV.Core/Domain/MediaItem/PlexMediaFile.cs
  11. 12
      ErsatzTV.Core/Domain/MediaItem/PlexMediaItemPart.cs
  12. 1
      ErsatzTV.Core/Domain/MediaItem/PlexMovie.cs
  13. 2
      ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs
  14. 1
      ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs
  15. 2
      ErsatzTV.Core/Iptv/ChannelGuide.cs
  16. 11
      ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMediaFileConfiguration.cs
  17. 2
      ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMovieConfiguration.cs
  18. 4
      ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs
  19. 12
      ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs
  20. 11
      ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs
  21. 2
      ErsatzTV.Infrastructure/Data/TvContext.cs
  22. 1456
      ErsatzTV.Infrastructure/Migrations/20210228210815_Add_PlexMediaFile.Designer.cs
  23. 84
      ErsatzTV.Infrastructure/Migrations/20210228210815_Add_PlexMediaFile.cs
  24. 428
      ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs
  25. 1
      ErsatzTV.Infrastructure/Plex/Models/PlexLibraryResponse.cs
  26. 52
      ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs
  27. 20
      ErsatzTV/Pages/Libraries.razor

6
ErsatzTV.Application/Libraries/LibraryViewModel.cs

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Libraries
{
public record LibraryViewModel(string LibraryKind, int Id, string Name, LibraryMediaKind MediaKind);
}

3
ErsatzTV.Application/Libraries/LocalLibraryViewModel.cs

@ -2,5 +2,6 @@ @@ -2,5 +2,6 @@
namespace ErsatzTV.Application.Libraries
{
public record LocalLibraryViewModel(int Id, string Name, LibraryMediaKind MediaKind);
public record LocalLibraryViewModel(int Id, string Name, LibraryMediaKind MediaKind)
: LibraryViewModel("Local", Id, Name, MediaKind);
}

11
ErsatzTV.Application/Libraries/Mapper.cs

@ -1,9 +1,18 @@ @@ -1,9 +1,18 @@
using ErsatzTV.Core.Domain;
using System;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Libraries
{
internal static class Mapper
{
public static LibraryViewModel ProjectToViewModel(Library library) =>
library switch
{
LocalLibrary l => ProjectToViewModel(l),
PlexLibrary p => new PlexLibraryViewModel(p.Id, p.Name, p.MediaKind),
_ => throw new ArgumentOutOfRangeException(nameof(library))
};
public static LocalLibraryViewModel ProjectToViewModel(LocalLibrary library) =>
new(library.Id, library.Name, library.MediaKind);

7
ErsatzTV.Application/Libraries/PlexLibraryViewModel.cs

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Libraries
{
public record PlexLibraryViewModel(int Id, string Name, LibraryMediaKind MediaKind)
: LibraryViewModel("Plex", Id, Name, MediaKind);
}

2
ErsatzTV.Application/Libraries/Queries/GetLocalLibraries.cs → ErsatzTV.Application/Libraries/Queries/GetAllLibraries.cs

@ -3,5 +3,5 @@ using MediatR; @@ -3,5 +3,5 @@ using MediatR;
namespace ErsatzTV.Application.Libraries.Queries
{
public record GetLocalLibraries : IRequest<List<LocalLibraryViewModel>>;
public record GetAllLibraries : IRequest<List<LibraryViewModel>>;
}

31
ErsatzTV.Application/Libraries/Queries/GetAllLibrariesHandler.cs

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
using MediatR;
using static ErsatzTV.Application.Libraries.Mapper;
namespace ErsatzTV.Application.Libraries.Queries
{
public class GetAllLibrariesHandler : IRequestHandler<GetAllLibraries, List<LibraryViewModel>>
{
private readonly ILibraryRepository _libraryRepository;
public GetAllLibrariesHandler(ILibraryRepository libraryRepository) => _libraryRepository = libraryRepository;
public Task<List<LibraryViewModel>> Handle(GetAllLibraries request, CancellationToken cancellationToken) =>
_libraryRepository.GetAll()
.Map(list => list.Filter(ShouldIncludeLibrary).Map(ProjectToViewModel).ToList());
private static bool ShouldIncludeLibrary(Library library) =>
library switch
{
LocalLibrary => true,
PlexLibrary plex => plex.ShouldSyncItems,
_ => false
};
}
}

22
ErsatzTV.Application/Libraries/Queries/GetLocalLibrariesHandler.cs

@ -1,22 +0,0 @@ @@ -1,22 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
using MediatR;
using static ErsatzTV.Application.Libraries.Mapper;
namespace ErsatzTV.Application.Libraries.Queries
{
public class GetLocalLibrariesHandler : IRequestHandler<GetLocalLibraries, List<LocalLibraryViewModel>>
{
private readonly ILibraryRepository _libraryRepository;
public GetLocalLibrariesHandler(ILibraryRepository libraryRepository) => _libraryRepository = libraryRepository;
public Task<List<LocalLibraryViewModel>>
Handle(GetLocalLibraries request, CancellationToken cancellationToken) =>
_libraryRepository.GetAllLocal().Map(list => list.Map(ProjectToViewModel).ToList());
}
}

5
ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryById.cs

@ -6,19 +6,18 @@ namespace ErsatzTV.Application.Plex.Commands @@ -6,19 +6,18 @@ namespace ErsatzTV.Application.Plex.Commands
{
public interface ISynchronizePlexLibraryById : IRequest<Either<BaseError, string>>, IBackgroundServiceRequest
{
int PlexMediaSourceId { get; }
int PlexLibraryId { get; }
bool ForceScan { get; }
}
public record SynchronizePlexLibraryByIdIfNeeded
(int PlexMediaSourceId, int PlexLibraryId) : ISynchronizePlexLibraryById
(int PlexLibraryId) : ISynchronizePlexLibraryById
{
public bool ForceScan => false;
}
public record ForceSynchronizePlexLibraryById
(int PlexMediaSourceId, int PlexLibraryId) : ISynchronizePlexLibraryById
(int PlexLibraryId) : ISynchronizePlexLibraryById
{
public bool ForceScan => true;
}

12
ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryByIdHandler.cs

@ -82,7 +82,7 @@ namespace ErsatzTV.Application.Plex.Commands @@ -82,7 +82,7 @@ namespace ErsatzTV.Application.Plex.Commands
parameters.Library.Name);
}
// _entityLocker.UnlockMediaSource(parameters.MediaSource.Id);
_entityLocker.UnlockLibrary(parameters.Library.Id);
return Unit.Default;
}
@ -103,8 +103,10 @@ namespace ErsatzTV.Application.Plex.Commands @@ -103,8 +103,10 @@ namespace ErsatzTV.Application.Plex.Commands
private Task<Validation<BaseError, PlexMediaSource>> PlexMediaSourceMustExist(
ISynchronizePlexLibraryById request) =>
_mediaSourceRepository.GetPlex(request.PlexMediaSourceId)
.Map(v => v.ToValidation<BaseError>($"Plex media source {request.PlexMediaSourceId} does not exist."));
_mediaSourceRepository.GetPlexByLibraryId(request.PlexLibraryId)
.Map(
v => v.ToValidation<BaseError>(
$"Plex media source for library {request.PlexLibraryId} does not exist."));
private Validation<BaseError, ConnectionParameters> MediaSourceMustHaveActiveConnection(
PlexMediaSource plexMediaSource)
@ -134,9 +136,7 @@ namespace ErsatzTV.Application.Plex.Commands @@ -134,9 +136,7 @@ namespace ErsatzTV.Application.Plex.Commands
PlexLibrary Library,
bool ForceScan);
private record ConnectionParameters(
PlexMediaSource PlexMediaSource,
PlexConnection ActiveConnection)
private record ConnectionParameters(PlexMediaSource PlexMediaSource, PlexConnection ActiveConnection)
{
public PlexServerAuthToken PlexServerAuthToken { get; set; }
}

8
ErsatzTV.Core/Domain/MediaItem/PlexMediaFile.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
namespace ErsatzTV.Core.Domain
{
public class PlexMediaFile : MediaFile
{
public int PlexId { get; set; }
public string Key { get; set; }
}
}

12
ErsatzTV.Core/Domain/MediaItem/PlexMediaItemPart.cs

@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
namespace ErsatzTV.Core.Domain
{
public class PlexMediaItemPart
{
public int Id { get; set; }
public int PlexId { get; set; }
public string Key { get; set; }
public int Duration { get; set; }
public string File { get; set; }
public int Size { get; set; }
}
}

1
ErsatzTV.Core/Domain/MediaItem/PlexMovie.cs

@ -3,6 +3,5 @@ @@ -3,6 +3,5 @@
public class PlexMovie : Movie
{
public string Key { get; set; }
public PlexMediaItemPart Part { get; set; }
}
}

2
ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs

@ -10,7 +10,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories @@ -10,7 +10,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories
Task<LibraryPath> Add(LibraryPath libraryPath);
Task<Option<Library>> Get(int libraryId);
Task<Option<LocalLibrary>> GetLocal(int libraryId);
Task<List<LocalLibrary>> GetAllLocal();
Task<List<Library>> GetAll();
Task<Unit> UpdateLastScan(Library library);
Task<List<LibraryPath>> GetLocalPaths(int libraryId);
Task<Option<LibraryPath>> GetPath(int libraryPathId);

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

@ -15,6 +15,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories @@ -15,6 +15,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories
Task<Option<PlexLibrary>> GetPlexLibrary(int plexLibraryId);
Task<Option<MediaSource>> Get(int id);
Task<Option<PlexMediaSource>> GetPlex(int id);
Task<Option<PlexMediaSource>> GetPlexByLibraryId(int plexLibraryId);
Task<int> CountMediaItems(int id);
Task Update(LocalMediaSource localMediaSource);
Task Update(PlexMediaSource plexMediaSource);

2
ErsatzTV.Core/Iptv/ChannelGuide.cs

@ -108,7 +108,7 @@ namespace ErsatzTV.Core.Iptv @@ -108,7 +108,7 @@ namespace ErsatzTV.Core.Iptv
xml.WriteAttributeString("system", "xmltv_ns");
xml.WriteString($"{s - 1}.{e - 1}.0/1");
xml.WriteEndElement(); // episode-num
xml.WriteStartElement("episode-num");
xml.WriteAttributeString("system", "onscreen");
xml.WriteString($"S{s:00}E{e:00}");

11
ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMediaFileConfiguration.cs

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
using ErsatzTV.Core.Domain;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ErsatzTV.Infrastructure.Data.Configurations
{
public class PlexMediaFileConfiguration : IEntityTypeConfiguration<PlexMediaFile>
{
public void Configure(EntityTypeBuilder<PlexMediaFile> builder) => builder.ToTable("PlexMediaFile");
}
}

2
ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMovieMediaItemConfiguration.cs → ErsatzTV.Infrastructure/Data/Configurations/MediaItem/PlexMovieConfiguration.cs

@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ErsatzTV.Infrastructure.Data.Configurations
{
public class PlexMovieMediaItemConfiguration : IEntityTypeConfiguration<PlexMovie>
public class PlexMovieConfiguration : IEntityTypeConfiguration<PlexMovie>
{
public void Configure(EntityTypeBuilder<PlexMovie> builder) => builder.ToTable("PlexMovie");
}

4
ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs

@ -49,10 +49,10 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -49,10 +49,10 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.Map(Optional);
}
public Task<List<LocalLibrary>> GetAllLocal()
public Task<List<Library>> GetAll()
{
using TvContext context = _dbContextFactory.CreateDbContext();
return context.LocalLibraries.ToListAsync();
return context.Libraries.ToListAsync();
}
public Task<Unit> UpdateLastScan(Library library) => _dbConnection.ExecuteAsync(

12
ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs

@ -72,6 +72,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -72,6 +72,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
{
using TvContext context = _dbContextFactory.CreateDbContext();
return context.PlexLibraries
.Include(l => l.Paths)
.OrderBy(l => l.Id) // https://github.com/dotnet/efcore/issues/22579
.SingleOrDefaultAsync(l => l.Id == plexLibraryId)
.Map(Optional);
@ -97,6 +98,17 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -97,6 +98,17 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.Map(Optional);
}
public Task<Option<PlexMediaSource>> GetPlexByLibraryId(int plexLibraryId)
{
using TvContext context = _dbContextFactory.CreateDbContext();
return context.PlexMediaSources
.Include(p => p.Connections)
.Include(p => p.Libraries)
.Where(p => p.Libraries.Any(l => l.Id == plexLibraryId))
.SingleOrDefaultAsync()
.Map(Optional);
}
public Task<int> CountMediaItems(int id)
{
using TvContext context = _dbContextFactory.CreateDbContext();

11
ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs

@ -58,9 +58,10 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -58,9 +58,10 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
public async Task<Either<BaseError, PlexMovie>> GetOrAdd(PlexLibrary library, PlexMovie item)
{
Option<PlexMovie> maybeExisting = await _dbContext.PlexMovieMediaItems
Option<PlexMovie> maybeExisting = await _dbContext.PlexMovies
.Include(i => i.MovieMetadata)
.Include(i => i.Part)
.Include(i => i.MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.OrderBy(i => i.Key)
.SingleOrDefaultAsync(i => i.Key == item.Key);
@ -119,15 +120,13 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -119,15 +120,13 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
}
}
private async Task<Either<BaseError, PlexMovie>> AddPlexMovie(
PlexLibrary library,
PlexMovie item)
private async Task<Either<BaseError, PlexMovie>> AddPlexMovie(PlexLibrary library, PlexMovie item)
{
try
{
item.LibraryPathId = library.Paths.Head().Id;
await _dbContext.PlexMovieMediaItems.AddAsync(item);
await _dbContext.PlexMovies.AddAsync(item);
await _dbContext.SaveChangesAsync();
await _dbContext.Entry(item).Reference(i => i.LibraryPath).LoadAsync();
return item;

2
ErsatzTV.Infrastructure/Data/TvContext.cs

@ -31,7 +31,7 @@ namespace ErsatzTV.Infrastructure.Data @@ -31,7 +31,7 @@ namespace ErsatzTV.Infrastructure.Data
public DbSet<Season> Seasons { get; set; }
public DbSet<Episode> Episodes { get; set; }
public DbSet<EpisodeMetadata> EpisodeMetadata { get; set; }
public DbSet<PlexMovie> PlexMovieMediaItems { get; set; }
public DbSet<PlexMovie> PlexMovies { get; set; }
public DbSet<Collection> Collections { get; set; }
public DbSet<ProgramSchedule> ProgramSchedules { get; set; }
public DbSet<Playout> Playouts { get; set; }

1456
ErsatzTV.Infrastructure/Migrations/20210228210815_Add_PlexMediaFile.Designer.cs generated

File diff suppressed because it is too large Load Diff

84
ErsatzTV.Infrastructure/Migrations/20210228210815_Add_PlexMediaFile.cs

@ -0,0 +1,84 @@ @@ -0,0 +1,84 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace ErsatzTV.Infrastructure.Migrations
{
public partial class Add_PlexMediaFile : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
"FK_PlexMovie_PlexMediaItemPart_PartId",
"PlexMovie");
migrationBuilder.DropTable(
"PlexMediaItemPart");
migrationBuilder.DropIndex(
"IX_PlexMovie_PartId",
"PlexMovie");
migrationBuilder.DropColumn(
"PartId",
"PlexMovie");
migrationBuilder.CreateTable(
"PlexMediaFile",
table => new
{
Id = table.Column<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
PlexId = table.Column<int>("INTEGER", nullable: false),
Key = table.Column<string>("TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PlexMediaFile", x => x.Id);
table.ForeignKey(
"FK_PlexMediaFile_MediaFile_Id",
x => x.Id,
"MediaFile",
"Id",
onDelete: ReferentialAction.Cascade);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
"PlexMediaFile");
migrationBuilder.AddColumn<int>(
"PartId",
"PlexMovie",
"INTEGER",
nullable: true);
migrationBuilder.CreateTable(
"PlexMediaItemPart",
table => new
{
Id = table.Column<int>("INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Duration = table.Column<int>("INTEGER", nullable: false),
File = table.Column<string>("TEXT", nullable: true),
Key = table.Column<string>("TEXT", nullable: true),
PlexId = table.Column<int>("INTEGER", nullable: false),
Size = table.Column<int>("INTEGER", nullable: false)
},
constraints: table => { table.PrimaryKey("PK_PlexMediaItemPart", x => x.Id); });
migrationBuilder.CreateIndex(
"IX_PlexMovie_PartId",
"PlexMovie",
"PartId");
migrationBuilder.AddForeignKey(
"FK_PlexMovie_PlexMediaItemPart_PartId",
"PlexMovie",
"PartId",
"PlexMediaItemPart",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
}
}

428
ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs

@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
// <auto-generated />
using System;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace ErsatzTV.Infrastructure.Migrations
{
[DbContext(typeof(TvContext))]
partial class TvContextModelSnapshot : ModelSnapshot
internal class TvContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
@ -16,7 +16,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -16,7 +16,9 @@ namespace ErsatzTV.Infrastructure.Migrations
modelBuilder
.HasAnnotation("ProductVersion", "5.0.3");
modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Artwork",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -64,7 +66,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -64,7 +66,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Artwork");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Channel",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -95,7 +99,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -95,7 +99,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Channel");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Collection",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -109,7 +115,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -109,7 +115,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Collection");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.CollectionItem",
b =>
{
b.Property<int>("CollectionId")
.HasColumnType("INTEGER");
@ -124,7 +132,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -124,7 +132,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("CollectionItem");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ConfigElement",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -144,7 +154,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -144,7 +154,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("ConfigElement");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.EpisodeMetadata",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -193,7 +205,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -193,7 +205,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("EpisodeMetadata");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.FFmpegProfile",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -257,7 +271,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -257,7 +271,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("FFmpegProfile");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Library",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -282,7 +298,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -282,7 +298,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Library");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.LibraryPath",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -301,7 +319,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -301,7 +319,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("LibraryPath");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.MediaFile",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -323,7 +343,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -323,7 +343,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("MediaFile");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.MediaItem",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -339,7 +361,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -339,7 +361,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("MediaItem");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.MediaSource",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -350,7 +374,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -350,7 +374,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("MediaSource");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.MediaVersion",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -404,7 +430,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -404,7 +430,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("MediaVersion");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.MovieMetadata",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -453,7 +481,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -453,7 +481,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("MovieMetadata");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Playout",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -477,7 +507,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -477,7 +507,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Playout");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlayoutItem",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -504,7 +536,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -504,7 +536,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlayoutItem");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -538,7 +572,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -538,7 +572,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlayoutProgramScheduleAnchor");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlexConnection",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -560,33 +596,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -560,33 +596,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlexConnection");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaItemPart", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Duration")
.HasColumnType("INTEGER");
b.Property<string>("File")
.HasColumnType("TEXT");
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<int>("PlexId")
.HasColumnType("INTEGER");
b.Property<int>("Size")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("PlexMediaItemPart");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramSchedule",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -606,7 +618,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -606,7 +618,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("ProgramSchedule");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramScheduleItem",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -641,7 +655,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -641,7 +655,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("ProgramScheduleItem");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Resolution",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -661,7 +677,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -661,7 +677,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Resolution");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.SeasonMetadata",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -704,7 +722,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -704,7 +722,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("SeasonMetadata");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ShowMetadata",
b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -753,14 +773,18 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -753,14 +773,18 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("ShowMetadata");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.LocalLibrary",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.Library");
b.ToTable("LocalLibrary");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlexLibrary",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.Library");
@ -773,7 +797,24 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -773,7 +797,24 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlexLibrary");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlexMediaFile",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.MediaFile");
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<int>("PlexId")
.HasColumnType("INTEGER");
b.ToTable("PlexMediaFile");
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Episode",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
@ -788,14 +829,18 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -788,14 +829,18 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Episode");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Movie",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
b.ToTable("Movie");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Season",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
@ -810,21 +855,27 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -810,21 +855,27 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Season");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Show",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
b.ToTable("Show");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.LocalMediaSource",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
b.ToTable("LocalMediaSource");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlexMediaSource",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
@ -840,7 +891,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -840,7 +891,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlexMediaSource");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramScheduleItemDuration",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
@ -853,14 +906,18 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -853,14 +906,18 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("ProgramScheduleDurationItem");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramScheduleItemFlood",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
b.ToTable("ProgramScheduleFloodItem");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramScheduleItemMultiple",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
@ -870,29 +927,30 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -870,29 +927,30 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("ProgramScheduleMultipleItem");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramScheduleItemOne",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
b.ToTable("ProgramScheduleOneItem");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlexMovie",
b =>
{
b.HasBaseType("ErsatzTV.Core.Domain.Movie");
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<int?>("PartId")
.HasColumnType("INTEGER");
b.HasIndex("PartId");
b.ToTable("PlexMovie");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Artwork",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Channel", null)
.WithMany("Artwork")
@ -920,7 +978,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -920,7 +978,9 @@ namespace ErsatzTV.Infrastructure.Migrations
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Channel",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile")
.WithMany()
@ -931,7 +991,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -931,7 +991,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("FFmpegProfile");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.CollectionItem",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection")
.WithMany("CollectionItems")
@ -950,7 +1012,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -950,7 +1012,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("MediaItem");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.EpisodeMetadata",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode")
.WithMany("EpisodeMetadata")
@ -961,7 +1025,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -961,7 +1025,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Episode");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.FFmpegProfile",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution")
.WithMany()
@ -972,7 +1038,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -972,7 +1038,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Resolution");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Library",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource")
.WithMany("Libraries")
@ -983,7 +1051,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -983,7 +1051,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("MediaSource");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.LibraryPath",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Library", "Library")
.WithMany("Paths")
@ -994,7 +1064,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -994,7 +1064,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Library");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.MediaFile",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion")
.WithMany("MediaFiles")
@ -1005,7 +1077,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1005,7 +1077,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("MediaVersion");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.MediaItem",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath")
.WithMany("MediaItems")
@ -1016,7 +1090,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1016,7 +1090,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("LibraryPath");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.MediaVersion",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Episode", null)
.WithMany("MediaVersions")
@ -1029,7 +1105,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1029,7 +1105,9 @@ namespace ErsatzTV.Infrastructure.Migrations
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.MovieMetadata",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie")
.WithMany("MovieMetadata")
@ -1040,7 +1118,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1040,7 +1118,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Movie");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Playout",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel")
.WithMany("Playouts")
@ -1054,7 +1134,10 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1054,7 +1134,10 @@ namespace ErsatzTV.Infrastructure.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 =>
b.OwnsOne(
"ErsatzTV.Core.Domain.PlayoutAnchor",
"Anchor",
b1 =>
{
b1.Property<int>("PlayoutId")
.HasColumnType("INTEGER");
@ -1090,7 +1173,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1090,7 +1173,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("ProgramSchedule");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlayoutItem",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem")
.WithMany()
@ -1109,7 +1194,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1109,7 +1194,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Playout");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection")
.WithMany()
@ -1133,7 +1220,10 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1133,7 +1220,10 @@ namespace ErsatzTV.Infrastructure.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 =>
b.OwnsOne(
"ErsatzTV.Core.Domain.CollectionEnumeratorState",
"EnumeratorState",
b1 =>
{
b1.Property<int>("PlayoutProgramScheduleAnchorId")
.HasColumnType("INTEGER");
@ -1163,7 +1253,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1163,7 +1253,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("ProgramSchedule");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlexConnection",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource")
.WithMany("Connections")
@ -1174,7 +1266,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1174,7 +1266,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("PlexMediaSource");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramScheduleItem",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection")
.WithMany()
@ -1199,7 +1293,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1199,7 +1293,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("ProgramSchedule");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.SeasonMetadata",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Season", "Season")
.WithMany("SeasonMetadata")
@ -1210,7 +1306,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1210,7 +1306,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Season");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ShowMetadata",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Show", "Show")
.WithMany("ShowMetadata")
@ -1221,7 +1319,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1221,7 +1319,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Show");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.LocalLibrary",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Library", null)
.WithOne()
@ -1230,7 +1330,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1230,7 +1330,9 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired();
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlexLibrary",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Library", null)
.WithOne()
@ -1239,7 +1341,20 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1239,7 +1341,20 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired();
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlexMediaFile",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.MediaFile", null)
.WithOne()
.HasForeignKey("ErsatzTV.Core.Domain.PlexMediaFile", "Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Episode",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
.WithOne()
@ -1256,7 +1371,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1256,7 +1371,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Season");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Movie",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
.WithOne()
@ -1265,7 +1382,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1265,7 +1382,9 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired();
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Season",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
.WithOne()
@ -1282,7 +1401,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1282,7 +1401,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Show");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Show",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
.WithOne()
@ -1291,7 +1412,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1291,7 +1412,9 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired();
});
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.LocalMediaSource",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
.WithOne()
@ -1300,7 +1423,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1300,7 +1423,9 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired();
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlexMediaSource",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
.WithOne()
@ -1309,7 +1434,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1309,7 +1434,9 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired();
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramScheduleItemDuration",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
.WithOne()
@ -1318,7 +1445,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1318,7 +1445,9 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired();
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramScheduleItemFlood",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
.WithOne()
@ -1327,7 +1456,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1327,7 +1456,9 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired();
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramScheduleItemMultiple",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
.WithOne()
@ -1336,7 +1467,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1336,7 +1467,9 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired();
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramScheduleItemOne",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
.WithOne()
@ -1345,124 +1478,101 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1345,124 +1478,101 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired();
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.PlexMovie",
b =>
{
b.HasOne("ErsatzTV.Core.Domain.Movie", null)
.WithOne()
.HasForeignKey("ErsatzTV.Core.Domain.PlexMovie", "Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ErsatzTV.Core.Domain.PlexMediaItemPart", "Part")
.WithMany()
.HasForeignKey("PartId");
b.Navigation("Part");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Channel",
b =>
{
b.Navigation("Artwork");
b.Navigation("Playouts");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b =>
{
b.Navigation("CollectionItems");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => { b.Navigation("CollectionItems"); });
modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b =>
{
b.Navigation("Artwork");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => { b.Navigation("Artwork"); });
modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b =>
{
b.Navigation("Paths");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => { b.Navigation("Paths"); });
modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b =>
{
b.Navigation("MediaItems");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => { b.Navigation("MediaItems"); });
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b =>
{
b.Navigation("CollectionItems");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => { b.Navigation("CollectionItems"); });
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b =>
{
b.Navigation("Libraries");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => { b.Navigation("Libraries"); });
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b =>
{
b.Navigation("MediaFiles");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => { b.Navigation("MediaFiles"); });
modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b =>
{
b.Navigation("Artwork");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => { b.Navigation("Artwork"); });
modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Playout",
b =>
{
b.Navigation("Items");
b.Navigation("ProgramScheduleAnchors");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.ProgramSchedule",
b =>
{
b.Navigation("Items");
b.Navigation("Playouts");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b =>
{
b.Navigation("Artwork");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => { b.Navigation("Artwork"); });
modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b =>
{
b.Navigation("Artwork");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => { b.Navigation("Artwork"); });
modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Episode",
b =>
{
b.Navigation("EpisodeMetadata");
b.Navigation("MediaVersions");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Movie",
b =>
{
b.Navigation("MediaVersions");
b.Navigation("MovieMetadata");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Season",
b =>
{
b.Navigation("Episodes");
b.Navigation("SeasonMetadata");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b =>
modelBuilder.Entity(
"ErsatzTV.Core.Domain.Show",
b =>
{
b.Navigation("Seasons");
b.Navigation("ShowMetadata");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
{
b.Navigation("Connections");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => { b.Navigation("Connections"); });
#pragma warning restore 612, 618
}
}

1
ErsatzTV.Infrastructure/Plex/Models/PlexLibraryResponse.cs

@ -6,5 +6,6 @@ @@ -6,5 +6,6 @@
public string Title { get; set; }
public string Type { get; set; }
public int Hidden { get; set; }
public string Uuid { get; set; }
}
}

52
ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs

@ -62,14 +62,16 @@ namespace ErsatzTV.Infrastructure.Plex @@ -62,14 +62,16 @@ namespace ErsatzTV.Infrastructure.Plex
Key = response.Key,
Name = response.Title,
MediaKind = LibraryMediaKind.Shows,
ShouldSyncItems = false
ShouldSyncItems = false,
Paths = new List<LibraryPath> { new() { Path = $"plex://{response.Uuid}" } }
},
"movie" => new PlexLibrary
{
Key = response.Key,
Name = response.Title,
MediaKind = LibraryMediaKind.Movies,
ShouldSyncItems = false
ShouldSyncItems = false,
Paths = new List<LibraryPath> { new() { Path = $"plex://{response.Uuid}" } }
},
// TODO: "artist" for music libraries
_ => None
@ -127,40 +129,40 @@ namespace ErsatzTV.Infrastructure.Plex @@ -127,40 +129,40 @@ namespace ErsatzTV.Infrastructure.Plex
{
Title = response.Title,
Plot = response.Summary,
ReleaseDate = new DateTime(response.Year, 1, 1), // TODO: actual release date?
ReleaseDate = DateTime.Parse(response.OriginallyAvailableAt),
Year = response.Year,
Tagline = response.Tagline,
DateAdded = DateTime.UtcNow, // TODO: actual date added?
DateAdded = DateTimeOffset.FromUnixTimeSeconds(response.AddedAt).DateTime,
DateUpdated = lastWriteTime
};
// TODO: artwork
var version = new MediaVersion
{
Name = "Main",
Duration = TimeSpan.FromMilliseconds(media.Duration),
Width = media.Width,
Height = media.Height,
AudioCodec = media.AudioCodec,
VideoCodec = media.VideoCodec,
// TODO: aspect ratio
MediaFiles = new List<MediaFile>
{
new PlexMediaFile
{
PlexId = part.Id,
Key = part.Key,
Path = part.File
}
}
};
var movie = new PlexMovie
{
Key = response.Key,
// DateUpdated = lastWriteTime,
MovieMetadata = new List<MovieMetadata> { metadata },
// TODO: versions
// Statistics = new MediaItemStatistics
// {
// Duration = TimeSpan.FromMilliseconds(media.Duration),
// Width = media.Width,
// Height = media.Height,
// // TODO: aspect ratio
// AudioCodec = media.AudioCodec,
// VideoCodec = media.VideoCodec,
// LastWriteTime = lastWriteTime
// },
// TODO: multi-part movies?
Part = new PlexMediaItemPart
{
PlexId = part.Id,
Key = part.Key,
File = part.File,
Duration = part.Duration,
Size = part.Size
}
MediaVersions = new List<MediaVersion> { version }
};
return movie;

20
ErsatzTV/Pages/Libraries.razor

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
@using ErsatzTV.Application.Libraries
@using ErsatzTV.Application.Libraries.Queries
@using ErsatzTV.Application.MediaSources.Commands
@using ErsatzTV.Application.Plex.Commands
@implements IDisposable
@inject IMediator Mediator
@inject IEntityLocker Locker
@ -25,7 +26,7 @@ @@ -25,7 +26,7 @@
<MudTh/>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Library Kind">Local</MudTd>
<MudTd DataLabel="Library Kind">@context.LibraryKind</MudTd>
<MudTd DataLabel="Name">@context.Name</MudTd>
<MudTd DataLabel="Media Kind">@context.MediaKind</MudTd>
<MudTd>
@ -61,7 +62,7 @@ @@ -61,7 +62,7 @@
</MudContainer>
@code {
private IList<LocalLibraryViewModel> _libraries;
private IList<LibraryViewModel> _libraries;
protected override void OnInitialized() =>
Locker.OnLibraryChanged += LockChanged;
@ -69,13 +70,22 @@ @@ -69,13 +70,22 @@
protected override async Task OnParametersSetAsync() => await LoadLibraries();
private async Task LoadLibraries() =>
_libraries = await Mediator.Send(new GetLocalLibraries());
_libraries = await Mediator.Send(new GetAllLibraries());
private async Task ScanLibrary(LocalLibraryViewModel library)
private async Task ScanLibrary(LibraryViewModel library)
{
if (Locker.LockLibrary(library.Id))
{
await Channel.WriteAsync(new ForceScanLocalLibrary(library.Id));
switch (library)
{
case LocalLibraryViewModel:
await Channel.WriteAsync(new ForceScanLocalLibrary(library.Id));
break;
case PlexLibraryViewModel:
await Channel.WriteAsync(new ForceSynchronizePlexLibraryById(library.Id));
break;
}
StateHasChanged();
}
}

Loading…
Cancel
Save