From d2a38c9b3d29722fda9a28f6acc19c229a1f62ee Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Mon, 15 Mar 2021 23:52:11 +0100 Subject: [PATCH] Fixing tracks editing --- Kyoo.Common/Models/Resources/Episode.cs | 4 ++-- Kyoo.Common/Utility.cs | 18 +++++++++++++++++- Kyoo.CommonAPI/LocalRepository.cs | 11 ++++++++--- .../Repositories/EpisodeRepository.cs | 17 +++++++++++++++-- .../Repositories/TrackRepository.cs | 9 ++++++++- Kyoo/Models/DatabaseContext.cs | 7 ++++++- Kyoo/Startup.cs | 6 +++--- 7 files changed, 59 insertions(+), 13 deletions(-) diff --git a/Kyoo.Common/Models/Resources/Episode.cs b/Kyoo.Common/Models/Resources/Episode.cs index 0515da46..c346be0e 100644 --- a/Kyoo.Common/Models/Resources/Episode.cs +++ b/Kyoo.Common/Models/Resources/Episode.cs @@ -27,9 +27,9 @@ namespace Kyoo.Models public int Runtime { get; set; } //This runtime variable should be in minutes - [LoadableRelation] public virtual ICollection ExternalIDs { get; set; } + [EditableRelation] [LoadableRelation] public virtual ICollection ExternalIDs { get; set; } - [LoadableRelation] public virtual ICollection Tracks { get; set; } + [EditableRelation] [LoadableRelation] public virtual ICollection Tracks { get; set; } public Episode() { } diff --git a/Kyoo.Common/Utility.cs b/Kyoo.Common/Utility.cs index 521abc11..75e2ce3b 100644 --- a/Kyoo.Common/Utility.cs +++ b/Kyoo.Common/Utility.cs @@ -143,7 +143,7 @@ namespace Kyoo ? Activator.CreateInstance(property.PropertyType) : null; - if (value?.Equals(defaultValue) == false) + if (value?.Equals(defaultValue) == false && value != property.GetValue(first)) property.SetValue(first, value); } @@ -303,6 +303,22 @@ namespace Kyoo foreach (T i in self) action(i); } + + public static async Task ForEachAsync([CanBeNull] this IEnumerable self, Func action) + { + if (self == null) + return; + foreach (T i in self) + await action(i); + } + + public static async Task ForEachAsync([CanBeNull] this IAsyncEnumerable self, Action action) + { + if (self == null) + return; + await foreach (T i in self) + action(i); + } private static MethodInfo GetMethod(Type type, BindingFlags flag, string name, Type[] generics, object[] args) { diff --git a/Kyoo.CommonAPI/LocalRepository.cs b/Kyoo.CommonAPI/LocalRepository.cs index d5c7f790..cf6ed4e3 100644 --- a/Kyoo.CommonAPI/LocalRepository.cs +++ b/Kyoo.CommonAPI/LocalRepository.cs @@ -43,6 +43,11 @@ namespace Kyoo.Controllers { return Database.Set().FirstOrDefaultAsync(x => x.ID == id); } + + public virtual Task GetWithTracking(int id) + { + return Database.Set().AsTracking().FirstOrDefaultAsync(x => x.ID == id); + } public virtual Task Get(string slug) { @@ -159,7 +164,7 @@ namespace Kyoo.Controllers Database.ChangeTracker.LazyLoadingEnabled = false; try { - T old = await Get(edited.ID); + T old = await GetWithTracking(edited.ID); if (old == null) throw new ItemNotFound($"No resource found with the ID {edited.ID}."); @@ -180,8 +185,8 @@ namespace Kyoo.Controllers await navigation.LoadAsync(); // TODO this may be usless for lists since the API does not return IDs but the // TODO LinkEquality does not check slugs (their are lazy loaded and only the ID is available) - if (Utility.ResourceEquals(getter.GetClrValue(edited), getter.GetClrValue(old))) - navigation.Metadata.PropertyInfo.SetValue(edited, default); + // if (Utility.ResourceEquals(getter.GetClrValue(edited), getter.GetClrValue(old))) + // navigation.Metadata.PropertyInfo.SetValue(edited, default); } else navigation.Metadata.PropertyInfo.SetValue(edited, default); diff --git a/Kyoo/Controllers/Repositories/EpisodeRepository.cs b/Kyoo/Controllers/Repositories/EpisodeRepository.cs index 222e18de..7efadd95 100644 --- a/Kyoo/Controllers/Repositories/EpisodeRepository.cs +++ b/Kyoo/Controllers/Repositories/EpisodeRepository.cs @@ -15,15 +15,20 @@ namespace Kyoo.Controllers private readonly DatabaseContext _database; private readonly IProviderRepository _providers; private readonly IShowRepository _shows; + private readonly ITrackRepository _tracks; protected override Expression> DefaultSort => x => x.EpisodeNumber; - public EpisodeRepository(DatabaseContext database, IProviderRepository providers, IShowRepository shows) + public EpisodeRepository(DatabaseContext database, + IProviderRepository providers, + IShowRepository shows, + ITrackRepository tracks) : base(database) { _database = database; _providers = providers; _shows = shows; + _tracks = tracks; } @@ -161,6 +166,14 @@ namespace Kyoo.Controllers await base.Validate(resource); + if (resource.Tracks != null) + { + // TODO remove old values + resource.Tracks = await resource.Tracks + .SelectAsync(x => _tracks.CreateIfNotExists(x, true)) + .ToListAsync(); + } + if (resource.ExternalIDs != null) { foreach (MetadataID link in resource.ExternalIDs) @@ -181,10 +194,10 @@ namespace Kyoo.Controllers throw new ArgumentNullException(nameof(obj)); _database.Entry(obj).State = EntityState.Deleted; + await obj.Tracks.ForEachAsync(x => _tracks.CreateIfNotExists(x, true)); if (obj.ExternalIDs != null) foreach (MetadataID entry in obj.ExternalIDs) _database.Entry(entry).State = EntityState.Deleted; - // Since Tracks & Episodes are on the same database and handled by dotnet-ef, we can't use the repository to delete them. await _database.SaveChangesAsync(); } } diff --git a/Kyoo/Controllers/Repositories/TrackRepository.cs b/Kyoo/Controllers/Repositories/TrackRepository.cs index 413b202f..26b47d47 100644 --- a/Kyoo/Controllers/Repositories/TrackRepository.cs +++ b/Kyoo/Controllers/Repositories/TrackRepository.cs @@ -36,7 +36,12 @@ namespace Kyoo.Controllers await _database.DisposeAsync(); } - public Task Get(string slug, StreamType type = StreamType.Unknown) + public override Task Get(string slug) + { + return Get(slug, StreamType.Unknown); + } + + public Task Get(string slug, StreamType type) { Match match = Regex.Match(slug, @"(?.*)-s(?\d+)e(?\d+)\.(?.{0,3})(?-forced)?(\..*)?"); @@ -93,6 +98,8 @@ namespace Kyoo.Controllers return obj; } + + protected override Task Validate(Track resource) { return Task.CompletedTask; diff --git a/Kyoo/Models/DatabaseContext.cs b/Kyoo/Models/DatabaseContext.cs index 250c381b..6122d40a 100644 --- a/Kyoo/Models/DatabaseContext.cs +++ b/Kyoo/Models/DatabaseContext.cs @@ -12,7 +12,10 @@ namespace Kyoo { public class DatabaseContext : DbContext { - public DatabaseContext(DbContextOptions options) : base(options) { } + public DatabaseContext(DbContextOptions options) : base(options) + { + ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + } public DbSet Libraries { get; set; } public DbSet Collections { get; set; } @@ -35,6 +38,8 @@ namespace Kyoo NpgsqlConnection.GlobalTypeMapper.MapEnum(); NpgsqlConnection.GlobalTypeMapper.MapEnum(); NpgsqlConnection.GlobalTypeMapper.MapEnum(); + + ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index ae5ec44b..52a628fe 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -58,9 +58,9 @@ namespace Kyoo services.AddDbContext(options => { - options.UseNpgsql(_configuration.GetConnectionString("Database")); - // .EnableSensitiveDataLogging() - // .UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole())); + options.UseNpgsql(_configuration.GetConnectionString("Database")) + .EnableSensitiveDataLogging() + .UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole())); }, ServiceLifetime.Transient); services.AddDbContext(options =>