mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Finishing the reflection based validation
This commit is contained in:
parent
8ce6e1cc1b
commit
500ba56943
@ -3,4 +3,9 @@ using System;
|
||||
namespace Kyoo.Models.Attributes
|
||||
{
|
||||
public class NotMergableAttribute : Attribute { }
|
||||
|
||||
public interface IOnMerge
|
||||
{
|
||||
void OnMerge(object merged);
|
||||
}
|
||||
}
|
@ -15,22 +15,22 @@ namespace Kyoo.Models
|
||||
|
||||
public IEnumerable<ProviderID> Providers
|
||||
{
|
||||
get => ProviderLinks.Select(x => x.Provider);
|
||||
set => ProviderLinks = value.Select(x => new ProviderLink(x, this));
|
||||
get => ProviderLinks?.Select(x => x.Provider);
|
||||
set => ProviderLinks = value.Select(x => new ProviderLink(x, this)).ToList();
|
||||
}
|
||||
[NotMergable] [JsonIgnore] public virtual IEnumerable<ProviderLink> ProviderLinks { get; set; }
|
||||
[NotMergable] [JsonIgnore] public virtual IEnumerable<LibraryLink> Links { get; set; }
|
||||
|
||||
[JsonIgnore] public IEnumerable<Show> Shows
|
||||
{
|
||||
get => Links.Where(x => x.Show != null).Select(x => x.Show);
|
||||
get => Links?.Where(x => x.Show != null).Select(x => x.Show);
|
||||
set => Links = Utility.MergeLists(
|
||||
value?.Select(x => new LibraryLink(this, x)),
|
||||
Links?.Where(x => x.Show == null));
|
||||
}
|
||||
[JsonIgnore] public IEnumerable<Collection> Collections
|
||||
{
|
||||
get => Links.Where(x => x.Collection != null).Select(x => x.Collection);
|
||||
get => Links?.Where(x => x.Collection != null).Select(x => x.Collection);
|
||||
set => Links = Utility.MergeLists(
|
||||
value?.Select(x => new LibraryLink(this, x)),
|
||||
Links?.Where(x => x.Collection == null));
|
||||
|
@ -5,7 +5,7 @@ using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class Show
|
||||
public class Show : IOnMerge
|
||||
{
|
||||
[JsonIgnore] public long ID { get; set; }
|
||||
|
||||
@ -102,6 +102,25 @@ namespace Kyoo.Models
|
||||
{
|
||||
return ExternalIDs?.FirstOrDefault(x => x.Provider.Name == provider)?.DataID;
|
||||
}
|
||||
|
||||
public void OnMerge(object merged)
|
||||
{
|
||||
if (ExternalIDs != null)
|
||||
foreach (MetadataID id in ExternalIDs)
|
||||
id.Show = this;
|
||||
if (GenreLinks != null)
|
||||
foreach (GenreLink genre in GenreLinks)
|
||||
genre.Show = this;
|
||||
if (People != null)
|
||||
foreach (PeopleLink link in People)
|
||||
link.Show = this;
|
||||
if (Seasons != null)
|
||||
foreach (Season season in Seasons)
|
||||
season.Show = this;
|
||||
if (Episodes != null)
|
||||
foreach (Episode episode in Episodes)
|
||||
episode.Show = this;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Status { Finished, Airing, Planned }
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
@ -7,6 +8,8 @@ namespace Kyoo.Models
|
||||
[JsonIgnore] public long ID { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public virtual IEnumerable<Show> Shows { get; set; }
|
||||
|
||||
public Studio() { }
|
||||
|
||||
|
@ -124,6 +124,8 @@ namespace Kyoo
|
||||
}
|
||||
}
|
||||
|
||||
if (first is IOnMerge)
|
||||
((IOnMerge)first).OnMerge(second);
|
||||
return first;
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Kyoo.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
@ -184,27 +184,6 @@ namespace Kyoo.Controllers
|
||||
ValidateNewEntry(_database.Entry(obj));
|
||||
}
|
||||
|
||||
private void ValidateNewEntry(EntityEntry entry)
|
||||
{
|
||||
if (entry.State != EntityState.Detached)
|
||||
return;
|
||||
entry.State = EntityState.Added;
|
||||
foreach (NavigationEntry navigation in entry.Navigations)
|
||||
{
|
||||
ValidateNavigation(navigation);
|
||||
if (navigation.CurrentValue == null)
|
||||
continue;
|
||||
if (navigation.Metadata.IsCollection())
|
||||
{
|
||||
IEnumerable entities = (IEnumerable)navigation.CurrentValue;
|
||||
foreach (object childEntry in entities)
|
||||
ValidateNewEntry(_database.Entry(childEntry));
|
||||
}
|
||||
else
|
||||
ValidateNewEntry(_database.Entry(navigation.CurrentValue));
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterShowLinks(Library library, Collection collection, Show show)
|
||||
{
|
||||
if (collection != null)
|
||||
@ -239,43 +218,6 @@ namespace Kyoo.Controllers
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateChanges()
|
||||
{
|
||||
_database.ChangeTracker.AutoDetectChangesEnabled = false;
|
||||
try
|
||||
{
|
||||
foreach (EntityEntry sourceEntry in _database.ChangeTracker.Entries())
|
||||
{
|
||||
if (sourceEntry.State != EntityState.Added && sourceEntry.State != EntityState.Modified)
|
||||
continue;
|
||||
|
||||
foreach (NavigationEntry navigation in sourceEntry.Navigations)
|
||||
ValidateNavigation(navigation);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_database.ChangeTracker.AutoDetectChangesEnabled = true;
|
||||
_database.ChangeTracker.DetectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateNavigation(NavigationEntry navigation)
|
||||
{
|
||||
// if (navigation.IsModified == false)
|
||||
// return;
|
||||
|
||||
object oldValue = navigation.CurrentValue;
|
||||
if (oldValue == null)
|
||||
return;
|
||||
object newValue = Validate(oldValue);
|
||||
if (oldValue == newValue)
|
||||
return;
|
||||
navigation.CurrentValue = newValue;
|
||||
if (!navigation.Metadata.IsCollection())
|
||||
_database.Entry(oldValue).State = EntityState.Detached;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Edit
|
||||
@ -476,30 +418,82 @@ namespace Kyoo.Controllers
|
||||
#endregion
|
||||
|
||||
#region ValidateValue
|
||||
private T Validate<T>(T obj) where T : class
|
||||
private void ValidateChanges()
|
||||
{
|
||||
_database.ChangeTracker.AutoDetectChangesEnabled = false;
|
||||
try
|
||||
{
|
||||
foreach (EntityEntry sourceEntry in _database.ChangeTracker.Entries())
|
||||
{
|
||||
if (sourceEntry.State != EntityState.Added && sourceEntry.State != EntityState.Modified)
|
||||
continue;
|
||||
|
||||
foreach (NavigationEntry navigation in sourceEntry.Navigations)
|
||||
ValidateNavigation(navigation, EntityState.Added);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_database.ChangeTracker.AutoDetectChangesEnabled = true;
|
||||
_database.ChangeTracker.DetectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateNewEntry(EntityEntry entry)
|
||||
{
|
||||
if (entry.State != EntityState.Detached)
|
||||
return;
|
||||
entry.State = EntityState.Added;
|
||||
foreach (NavigationEntry navigation in entry.Navigations)
|
||||
{
|
||||
ValidateNavigation(navigation, EntityState.Detached);
|
||||
if (navigation.CurrentValue == null)
|
||||
continue;
|
||||
if (navigation.Metadata.IsCollection())
|
||||
{
|
||||
IEnumerable entities = (IEnumerable)navigation.CurrentValue;
|
||||
foreach (object childEntry in entities)
|
||||
ValidateNewEntry(_database.Entry(childEntry));
|
||||
}
|
||||
else
|
||||
ValidateNewEntry(_database.Entry(navigation.CurrentValue));
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateNavigation(NavigationEntry navigation, EntityState? skipOtherState)
|
||||
{
|
||||
object oldValue = navigation.CurrentValue;
|
||||
if (oldValue == null)
|
||||
return;
|
||||
object newValue = Validate(oldValue, skipOtherState);
|
||||
if (oldValue == newValue)
|
||||
return;
|
||||
navigation.CurrentValue = newValue;
|
||||
if (!navigation.Metadata.IsCollection())
|
||||
_database.Entry(oldValue).State = EntityState.Detached;
|
||||
}
|
||||
|
||||
private T Validate<T>(T obj, EntityState? skipOtherState) where T : class
|
||||
{
|
||||
if (obj == null)
|
||||
return null;
|
||||
|
||||
if (!(obj is IEnumerable) && _database.Entry(obj).State != EntityState.Added)
|
||||
if (skipOtherState != null && !(obj is IEnumerable) && _database.Entry(obj).State != skipOtherState)
|
||||
return obj;
|
||||
|
||||
switch(obj)
|
||||
{
|
||||
case ProviderLink link:
|
||||
link.Provider = ValidateLink(link.Provider);
|
||||
link.Library = ValidateLink(link.Library);
|
||||
_database.Entry(link).State = EntityState.Added;
|
||||
link.Provider = ValidateLink(link.Provider, skipOtherState);
|
||||
link.Library = ValidateLink(link.Library, skipOtherState);
|
||||
return obj;
|
||||
case GenreLink link:
|
||||
link.Show = ValidateLink(link.Show);
|
||||
link.Genre = ValidateLink(link.Genre);
|
||||
_database.Entry(link).State = EntityState.Added;
|
||||
link.Show = ValidateLink(link.Show, skipOtherState);
|
||||
link.Genre = ValidateLink(link.Genre, skipOtherState);
|
||||
return obj;
|
||||
case PeopleLink link:
|
||||
link.Show = ValidateLink(link.Show);
|
||||
link.People = ValidateLink(link.People);
|
||||
_database.Entry(link).State = EntityState.Added;
|
||||
link.Show = ValidateLink(link.Show, skipOtherState);
|
||||
link.People = ValidateLink(link.People, skipOtherState);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -509,27 +503,27 @@ namespace Kyoo.Controllers
|
||||
Collection collection => GetCollection(collection.Slug) ?? collection,
|
||||
Show show => GetShow(show.Slug) ?? show,
|
||||
Season season => GetSeason(season.Show.Slug, season.SeasonNumber) ?? season,
|
||||
Episode episode => GetEpisode(episode.Show.Slug, episode.SeasonNumber, episode.SeasonNumber) ?? episode,
|
||||
Episode episode => GetEpisode(episode.Show.Slug, episode.SeasonNumber, episode.EpisodeNumber) ?? episode,
|
||||
Studio studio => GetStudio(studio.Slug) ?? studio,
|
||||
People people => GetPeople(people.Slug) ?? people,
|
||||
Genre genre => GetGenre(genre.Slug) ?? genre,
|
||||
ProviderID provider => GetProvider(provider.Name) ?? provider,
|
||||
|
||||
IEnumerable<dynamic> list => Utility.RunGenericMethod(this, "ValidateList",
|
||||
Utility.GetEnumerableType(list), new [] {list}),
|
||||
Utility.GetEnumerableType(list), new object[] {list, skipOtherState}),
|
||||
_ => obj
|
||||
});
|
||||
}
|
||||
|
||||
public IEnumerable<T> ValidateList<T>(IEnumerable<T> list) where T : class
|
||||
public IEnumerable<T> ValidateList<T>(IEnumerable<T> list, EntityState? skipOtherState) where T : class
|
||||
{
|
||||
return list.Select(x =>
|
||||
{
|
||||
T tmp = Validate(x);
|
||||
T tmp = Validate(x, skipOtherState);
|
||||
if (tmp != x)
|
||||
_database.Entry(x).State = EntityState.Detached;
|
||||
return tmp ?? x;
|
||||
}).GroupBy(GetSlug).Select(x => x.First()).Where(x => x != null).ToList();
|
||||
})/*.GroupBy(GetSlug).Select(x => x.First()).Where(x => x != null)*/.ToList();
|
||||
}
|
||||
|
||||
private static object GetSlug(object obj)
|
||||
@ -556,9 +550,9 @@ namespace Kyoo.Controllers
|
||||
};
|
||||
}
|
||||
|
||||
private T ValidateLink<T>(T oldValue) where T : class
|
||||
private T ValidateLink<T>(T oldValue, EntityState? skipOtherState) where T : class
|
||||
{
|
||||
T newValue = Validate(oldValue);
|
||||
T newValue = Validate(oldValue, skipOtherState);
|
||||
if (!ReferenceEquals(oldValue, newValue))
|
||||
_database.Entry(oldValue).State = EntityState.Detached;
|
||||
return newValue;
|
||||
|
@ -15,7 +15,7 @@ namespace Kyoo.Controllers
|
||||
_providers = pluginManager.GetPlugins<IMetadataProvider>();
|
||||
}
|
||||
|
||||
private async Task<T> GetMetadata<T>(Func<IMetadataProvider, Task<T>> providerCall, Library library, string what)
|
||||
private async Task<T> GetMetadata<T>(Func<IMetadataProvider, Task<T>> providerCall, Library library, string what)
|
||||
where T : new()
|
||||
{
|
||||
T ret = new T();
|
||||
@ -32,13 +32,17 @@ namespace Kyoo.Controllers
|
||||
ret = Utility.Merge(ret, await providerCall(provider));
|
||||
} catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"\tThe provider {provider.Provider.Name} coudln't work for {what}. Exception: {ex.Message}");
|
||||
await Console.Error.WriteLineAsync(
|
||||
$"The provider {provider.Provider.Name} coudln't work for {what}. Exception: {ex.Message}");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<T>> GetMetadata<T>(Func<IMetadataProvider, Task<IEnumerable<T>>> providerCall, Library library, string what)
|
||||
private async Task<List<T>> GetMetadata<T>(
|
||||
Func<IMetadataProvider, Task<IEnumerable<T>>> providerCall,
|
||||
Library library,
|
||||
string what)
|
||||
{
|
||||
List<T> ret = new List<T>();
|
||||
|
||||
@ -54,7 +58,8 @@ namespace Kyoo.Controllers
|
||||
ret.AddRange(await providerCall(provider) ?? new List<T>());
|
||||
} catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"\tThe provider {provider.Provider.Name} coudln't work for {what}. Exception: {ex.Message}");
|
||||
await Console.Error.WriteLineAsync(
|
||||
$"The provider {provider.Provider.Name} coudln't work for {what}. Exception: {ex.Message}");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@ -62,7 +67,10 @@ namespace Kyoo.Controllers
|
||||
|
||||
public async Task<Collection> GetCollectionFromName(string name, Library library)
|
||||
{
|
||||
Collection collection = await GetMetadata(provider => provider.GetCollectionFromName(name), library, $"the collection {name}");
|
||||
Collection collection = await GetMetadata(
|
||||
provider => provider.GetCollectionFromName(name),
|
||||
library,
|
||||
$"the collection {name}");
|
||||
collection.Name ??= name;
|
||||
collection.Slug ??= Utility.ToSlug(name);
|
||||
return collection;
|
||||
@ -85,12 +93,17 @@ namespace Kyoo.Controllers
|
||||
show.Slug = Utility.ToSlug(showName);
|
||||
show.Title ??= showName;
|
||||
show.IsMovie = isMovie;
|
||||
show.GenreLinks = show.GenreLinks?.GroupBy(x => x.Genre.Slug).Select(x => x.First()).ToList();
|
||||
show.People = show.People?.GroupBy(x => x.Slug).Select(x => x.First()).ToList();
|
||||
return show;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Show>> SearchShows(string showName, bool isMovie, Library library)
|
||||
{
|
||||
IEnumerable<Show> shows = await GetMetadata(provider => provider.SearchShows(showName, isMovie), library, $"the show {showName}");
|
||||
IEnumerable<Show> shows = await GetMetadata(
|
||||
provider => provider.SearchShows(showName, isMovie),
|
||||
library,
|
||||
$"the show {showName}");
|
||||
return shows.Select(show =>
|
||||
{
|
||||
show.Slug = Utility.ToSlug(showName);
|
||||
@ -102,16 +115,27 @@ namespace Kyoo.Controllers
|
||||
|
||||
public async Task<Season> GetSeason(Show show, long seasonNumber, Library library)
|
||||
{
|
||||
Season season = await GetMetadata(provider => provider.GetSeason(show, seasonNumber), library, $"the season {seasonNumber} of {show.Title}");
|
||||
Season season = await GetMetadata(
|
||||
provider => provider.GetSeason(show, seasonNumber),
|
||||
library,
|
||||
$"the season {seasonNumber} of {show.Title}");
|
||||
season.Show = show;
|
||||
season.SeasonNumber = season.SeasonNumber == -1 ? seasonNumber : season.SeasonNumber;
|
||||
season.Title ??= $"Season {season.SeasonNumber}";
|
||||
return season;
|
||||
}
|
||||
|
||||
public async Task<Episode> GetEpisode(Show show, string episodePath, long seasonNumber, long episodeNumber, long absoluteNumber, Library library)
|
||||
public async Task<Episode> GetEpisode(Show show,
|
||||
string episodePath,
|
||||
long seasonNumber,
|
||||
long episodeNumber,
|
||||
long absoluteNumber,
|
||||
Library library)
|
||||
{
|
||||
Episode episode = await GetMetadata(provider => provider.GetEpisode(show, seasonNumber, episodeNumber, absoluteNumber), library, "an episode");
|
||||
Episode episode = await GetMetadata(
|
||||
provider => provider.GetEpisode(show, seasonNumber, episodeNumber, absoluteNumber),
|
||||
library,
|
||||
"an episode");
|
||||
episode.Show = show;
|
||||
episode.Path = episodePath;
|
||||
episode.SeasonNumber = episode.SeasonNumber != -1 ? episode.SeasonNumber : seasonNumber;
|
||||
@ -122,8 +146,17 @@ namespace Kyoo.Controllers
|
||||
|
||||
public async Task<IEnumerable<PeopleLink>> GetPeople(Show show, Library library)
|
||||
{
|
||||
IEnumerable<PeopleLink> people = await GetMetadata(provider => provider.GetPeople(show), library, $"a cast member of {show.Title}");
|
||||
return people;
|
||||
List<PeopleLink> people = await GetMetadata(
|
||||
provider => provider.GetPeople(show),
|
||||
library,
|
||||
$"a cast member of {show.Title}");
|
||||
return people?.GroupBy(x => x.Slug)
|
||||
.Select(x => x.First())
|
||||
.Select(x =>
|
||||
{
|
||||
x.Show = show;
|
||||
return x;
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,9 +52,9 @@ namespace Kyoo
|
||||
services.AddDbContext<DatabaseContext>(options =>
|
||||
{
|
||||
options.UseLazyLoadingProxies()
|
||||
.UseSqlite(_configuration.GetConnectionString("Database"));
|
||||
//.EnableSensitiveDataLogging();
|
||||
//.UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole()));
|
||||
.UseSqlite(_configuration.GetConnectionString("Database"))
|
||||
.EnableSensitiveDataLogging()
|
||||
.UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole()));
|
||||
});
|
||||
|
||||
services.AddDbContext<IdentityDatabase>(options =>
|
||||
|
@ -174,9 +174,7 @@ namespace Kyoo.Controllers
|
||||
return show;
|
||||
show = await _metadataProvider.SearchShow(showTitle, isMovie, library);
|
||||
show.Path = showPath;
|
||||
show.People = (await _metadataProvider.GetPeople(show, library))
|
||||
.GroupBy(x => x.Slug)
|
||||
.Select(x => x.First());
|
||||
show.People = await _metadataProvider.GetPeople(show, library);
|
||||
await _thumbnailsManager.Validate(show.People);
|
||||
await _thumbnailsManager.Validate(show);
|
||||
return show;
|
||||
|
Loading…
x
Reference in New Issue
Block a user