add preferred language (#109)

* add explicit warning for zero/invalid duration media items

* set dateadded on plex media versions

* add media stream table

* save local media streams to db

* save plex media streams to db

* add preferred language settings (no validation)

* use preferred language if possible

* code cleanup

* proper language code validation

* force scan of all libraries to pull in media streams
This commit is contained in:
Jason Dove
2021-03-28 21:54:48 +00:00
committed by GitHub
parent 51b671dec7
commit d303bc0158
52 changed files with 6265 additions and 181 deletions
@@ -0,0 +1,11 @@
using ErsatzTV.Core.Domain;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ErsatzTV.Infrastructure.Data.Configurations
{
public class MediaStreamConfiguration : IEntityTypeConfiguration<MediaStream>
{
public void Configure(EntityTypeBuilder<MediaStream> builder) => builder.ToTable("MediaStream");
}
}
@@ -14,6 +14,11 @@ namespace ErsatzTV.Infrastructure.Data.Configurations
.WithOne(f => f.MediaVersion)
.HasForeignKey(f => f.MediaVersionId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasMany(v => v.Streams)
.WithOne(s => s.MediaVersion)
.HasForeignKey(s => s.MediaVersionId)
.OnDelete(DeleteBehavior.Cascade);
}
}
}
@@ -1,11 +1,13 @@
using System;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using Dapper;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
using Microsoft.EntityFrameworkCore;
using static LanguageExt.Prelude;
namespace ErsatzTV.Infrastructure.Data.Repositories
{
@@ -44,15 +46,66 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
return await dbContext.SaveChangesAsync() > 0;
}
public async Task<bool> UpdateLocalStatistics(MediaVersion mediaVersion)
public async Task<bool> UpdateLocalStatistics(
int mediaVersionId,
MediaVersion incoming,
bool updateVersion = true)
{
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
dbContext.Entry(mediaVersion).State = EntityState.Modified;
return await dbContext.SaveChangesAsync() > 0;
Option<MediaVersion> maybeVersion = await dbContext.MediaVersions
.Include(v => v.Streams)
.OrderBy(v => v.Id)
.SingleOrDefaultAsync(v => v.Id == mediaVersionId)
.Map(Optional);
return await maybeVersion.Match(
async existing =>
{
if (updateVersion)
{
existing.DateUpdated = incoming.DateUpdated;
existing.Duration = incoming.Duration;
existing.SampleAspectRatio = incoming.SampleAspectRatio;
existing.DisplayAspectRatio = incoming.DisplayAspectRatio;
existing.Width = incoming.Width;
existing.Height = incoming.Height;
existing.VideoScanKind = incoming.VideoScanKind;
}
var toAdd = incoming.Streams.Filter(s => existing.Streams.All(es => es.Index != s.Index)).ToList();
var toRemove = existing.Streams.Filter(es => incoming.Streams.All(s => s.Index != es.Index))
.ToList();
var toUpdate = incoming.Streams.Except(toAdd).ToList();
// add
existing.Streams.AddRange(toAdd);
// remove
existing.Streams.RemoveAll(s => toRemove.Contains(s));
// update
foreach (MediaStream incomingStream in toUpdate)
{
MediaStream existingStream = existing.Streams.First(s => s.Index == incomingStream.Index);
existingStream.Codec = incomingStream.Codec;
existingStream.Profile = incomingStream.Profile;
existingStream.MediaStreamKind = incomingStream.MediaStreamKind;
existingStream.Language = incomingStream.Language;
existingStream.Channels = incomingStream.Channels;
existingStream.Title = incomingStream.Title;
existingStream.Default = incomingStream.Default;
existingStream.Forced = incomingStream.Forced;
}
return await dbContext.SaveChangesAsync() > 0;
},
() => Task.FromResult(false));
}
public Task<bool> UpdatePlexStatistics(MediaVersion mediaVersion) =>
_dbConnection.ExecuteAsync(
public async Task<bool> UpdatePlexStatistics(int mediaVersionId, MediaVersion incoming)
{
bool updatedVersion = await _dbConnection.ExecuteAsync(
@"UPDATE MediaVersion SET
SampleAspectRatio = @SampleAspectRatio,
VideoScanKind = @VideoScanKind,
@@ -60,12 +113,15 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
WHERE Id = @MediaVersionId",
new
{
mediaVersion.SampleAspectRatio,
mediaVersion.VideoScanKind,
mediaVersion.DateUpdated,
MediaVersionId = mediaVersion.Id
incoming.SampleAspectRatio,
incoming.VideoScanKind,
incoming.DateUpdated,
MediaVersionId = mediaVersionId
}).Map(result => result > 0);
return await UpdateLocalStatistics(mediaVersionId, incoming, false) || updatedVersion;
}
public Task<Unit> UpdateArtworkPath(Artwork artwork) =>
_dbConnection.ExecuteAsync(
"UPDATE Artwork SET Path = @Path, DateUpdated = @DateUpdated WHERE Id = @Id",
@@ -64,6 +64,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.ThenInclude(lp => lp.Library)
.Include(i => i.MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.Include(i => i.MediaVersions)
.ThenInclude(mv => mv.Streams)
.OrderBy(i => i.MediaVersions.First().MediaFiles.First().Path)
.SingleOrDefaultAsync(i => i.MediaVersions.First().MediaFiles.First().Path == path);
@@ -91,6 +93,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.ThenInclude(mm => mm.Artwork)
.Include(i => i.MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.Include(i => i.MediaVersions)
.ThenInclude(mv => mv.Streams)
.Include(i => i.LibraryPath)
.ThenInclude(lp => lp.Library)
.OrderBy(i => i.Key)
@@ -223,7 +227,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
MediaFiles = new List<MediaFile>
{
new() { Path = path }
}
},
Streams = new List<MediaStream>()
}
}
};
@@ -57,8 +57,14 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.ThenInclude(mi => (mi as Episode).MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.Include(i => i.MediaItem)
.ThenInclude(mi => (mi as Episode).MediaVersions)
.ThenInclude(mv => mv.Streams)
.Include(i => i.MediaItem)
.ThenInclude(mi => (mi as Movie).MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.Include(i => i.MediaItem)
.ThenInclude(mi => (mi as Movie).MediaVersions)
.ThenInclude(mv => mv.Streams)
.AsNoTracking()
.SingleOrDefaultAsync()
.Map(Optional);
@@ -283,6 +283,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.ThenInclude(em => em.Artwork)
.Include(i => i.MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.Include(i => i.MediaVersions)
.ThenInclude(mv => mv.Streams)
.OrderBy(i => i.MediaVersions.First().MediaFiles.First().Path)
.SingleOrDefaultAsync(i => i.MediaVersions.First().MediaFiles.First().Path == path);
@@ -414,6 +416,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.ThenInclude(mm => mm.Artwork)
.Include(i => i.MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.Include(i => i.MediaVersions)
.ThenInclude(mv => mv.Streams)
.OrderBy(i => i.Key)
.SingleOrDefaultAsync(i => i.Key == item.Key);
@@ -582,7 +586,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
MediaFiles = new List<MediaFile>
{
new() { Path = path }
}
},
Streams = new List<MediaStream>()
}
}
};