diff --git a/Kyoo.Common/Controllers/IRepository.cs b/Kyoo.Common/Controllers/IRepository.cs index a83d17fa..c5fe7a7c 100644 --- a/Kyoo.Common/Controllers/IRepository.cs +++ b/Kyoo.Common/Controllers/IRepository.cs @@ -590,10 +590,10 @@ namespace Kyoo.Controllers /// Pagination information (where to start and how many to get) /// The type of metadata to retrieve /// A filtered list of external ids. - Task>> GetMetadataID(Expression, bool>> where = null, - Sort> sort = default, + Task> GetMetadataID(Expression> where = null, + Sort sort = default, Pagination limit = default) - where T : class, IResource; + where T : class, IMetadata; /// /// Get a list of external ids that match all filters @@ -602,11 +602,11 @@ namespace Kyoo.Controllers /// A sort by expression /// Pagination information (where to start and how many to get) /// A filtered list of external ids. - Task>> GetMetadataID([Optional] Expression, bool>> where, - Expression, object>> sort, + Task> GetMetadataID([Optional] Expression> where, + Expression> sort, Pagination limit = default - ) where T : class, IResource - => GetMetadataID(where, new Sort>(sort), limit); + ) where T : class, IMetadata + => GetMetadataID(where, new Sort(sort), limit); } /// diff --git a/Kyoo.Common/Controllers/Implementations/LibraryManager.cs b/Kyoo.Common/Controllers/Implementations/LibraryManager.cs index 12ea0b2a..5b051ed8 100644 --- a/Kyoo.Common/Controllers/Implementations/LibraryManager.cs +++ b/Kyoo.Common/Controllers/Implementations/LibraryManager.cs @@ -231,7 +231,12 @@ namespace Kyoo.Controllers .Then(x => l.Collections = x), - (Collection c, nameof(Library.Shows)) => ShowRepository + (Collection c, nameof(Collection.ExternalIDs)) => SetRelation(c, + ProviderRepository.GetMetadataID(x => x.ResourceID == obj.ID), + (x, y) => x.ExternalIDs = y, + (x, y) => { x.ResourceID = y.ID; }), + + (Collection c, nameof(Collection.Shows)) => ShowRepository .GetAll(x => x.Collections.Any(y => y.ID == obj.ID)) .Then(x => c.Shows = x), @@ -241,9 +246,9 @@ namespace Kyoo.Controllers (Show s, nameof(Show.ExternalIDs)) => SetRelation(s, - ProviderRepository.GetMetadataID(x => x.FirstID == obj.ID), + ProviderRepository.GetMetadataID(x => x.ResourceID == obj.ID), (x, y) => x.ExternalIDs = y, - (x, y) => { x.First = y; x.FirstID = y.ID; }), + (x, y) => { x.ResourceID = y.ID; }), (Show s, nameof(Show.Genres)) => GenreRepository .GetAll(x => x.Shows.Any(y => y.ID == obj.ID)) @@ -281,9 +286,9 @@ namespace Kyoo.Controllers (Season s, nameof(Season.ExternalIDs)) => SetRelation(s, - ProviderRepository.GetMetadataID(x => x.FirstID == obj.ID), + ProviderRepository.GetMetadataID(x => x.ResourceID == obj.ID), (x, y) => x.ExternalIDs = y, - (x, y) => { x.First = y; x.FirstID = y.ID; }), + (x, y) => { x.ResourceID = y.ID; }), (Season s, nameof(Season.Episodes)) => SetRelation(s, EpisodeRepository.GetAll(x => x.Season.ID == obj.ID), @@ -300,9 +305,9 @@ namespace Kyoo.Controllers (Episode e, nameof(Episode.ExternalIDs)) => SetRelation(e, - ProviderRepository.GetMetadataID(x => x.FirstID == obj.ID), + ProviderRepository.GetMetadataID(x => x.ResourceID == obj.ID), (x, y) => x.ExternalIDs = y, - (x, y) => { x.First = y; x.FirstID = y.ID; }), + (x, y) => { x.ResourceID = y.ID; }), (Episode e, nameof(Episode.Tracks)) => SetRelation(e, TrackRepository.GetAll(x => x.Episode.ID == obj.ID), @@ -344,11 +349,16 @@ namespace Kyoo.Controllers .GetAll(x => x.Studio.ID == obj.ID) .Then(x => s.Shows = x), + (Studio s, nameof(Studio.ExternalIDs)) => SetRelation(s, + ProviderRepository.GetMetadataID(x => x.ResourceID == obj.ID), + (x, y) => x.ExternalIDs = y, + (x, y) => { x.ResourceID = y.ID; }), + (People p, nameof(People.ExternalIDs)) => SetRelation(p, - ProviderRepository.GetMetadataID(x => x.FirstID == obj.ID), + ProviderRepository.GetMetadataID(x => x.ResourceID == obj.ID), (x, y) => x.ExternalIDs = y, - (x, y) => { x.First = y; x.FirstID = y.ID; }), + (x, y) => { x.ResourceID = y.ID; }), (People p, nameof(People.Roles)) => PeopleRepository .GetFromPeople(obj.ID) diff --git a/Kyoo.Common/Models/Link.cs b/Kyoo.Common/Models/Link.cs index 09c519da..1f4cf4ac 100644 --- a/Kyoo.Common/Models/Link.cs +++ b/Kyoo.Common/Models/Link.cs @@ -92,7 +92,7 @@ namespace Kyoo.Models get { return x => new {First = x.FirstID, Second = x.SecondID}; - } + } } } @@ -157,7 +157,7 @@ namespace Kyoo.Models get { return x => new {First = x.FirstID, Second = x.SecondID}; - } + } } } } \ No newline at end of file diff --git a/Kyoo.Common/Models/MetadataID.cs b/Kyoo.Common/Models/MetadataID.cs index de6ac817..e3bb42a6 100644 --- a/Kyoo.Common/Models/MetadataID.cs +++ b/Kyoo.Common/Models/MetadataID.cs @@ -1,15 +1,34 @@ using System; using System.Linq.Expressions; +using Kyoo.Models.Attributes; namespace Kyoo.Models { /// /// ID and link of an item on an external provider. /// - /// - public class MetadataID : Link - where T : class, IResource + public class MetadataID { + /// + /// The ID of the resource which possess the metadata. + /// + [SerializeIgnore] public int ResourceID { get; set; } + + /// + /// The name of the resource type. This is only used internally to discriminate types. + /// + [SerializeIgnore] public string ResourceType { get; set; } + + /// + /// The ID of the provider. + /// + [SerializeIgnore] public int ProviderID { get; set; } + + /// + /// The provider that can do something with this ID. + /// + public Provider Provider { get; set; } + /// /// The ID of the resource on the external provider. /// @@ -20,20 +39,14 @@ namespace Kyoo.Models /// public string Link { get; set; } - /// - /// A shortcut to access the provider of this metadata. - /// Unlike the property, this is serializable. - /// - public Provider Provider => Second; - /// /// The expression to retrieve the unique ID of a MetadataID. This is an aggregate of the two resources IDs. /// - public new static Expression, object>> PrimaryKey + public static Expression> PrimaryKey { get { - return x => new {First = x.FirstID, Second = x.SecondID}; + return x => new {First = x.ResourceID, Second = x.ProviderID, Type = x.ResourceType}; } } } diff --git a/Kyoo.Common/Models/Resources/Collection.cs b/Kyoo.Common/Models/Resources/Collection.cs index 8162ff16..f7c5db3f 100644 --- a/Kyoo.Common/Models/Resources/Collection.cs +++ b/Kyoo.Common/Models/Resources/Collection.cs @@ -8,7 +8,7 @@ namespace Kyoo.Models /// A class representing collections of . /// A collection can also be stored in a . /// - public class Collection : IResource + public class Collection : IResource, IMetadata { /// public int ID { get; set; } @@ -42,6 +42,9 @@ namespace Kyoo.Models /// The list of libraries that contains this collection. /// [LoadableRelation] public ICollection Libraries { get; set; } + + /// + public ICollection ExternalIDs { get; set; } #if ENABLE_INTERNAL_LINKS diff --git a/Kyoo.Common/Models/Resources/Episode.cs b/Kyoo.Common/Models/Resources/Episode.cs index 7f76a8a4..687e88fb 100644 --- a/Kyoo.Common/Models/Resources/Episode.cs +++ b/Kyoo.Common/Models/Resources/Episode.cs @@ -10,7 +10,7 @@ namespace Kyoo.Models /// /// A class to represent a single show's episode. /// - public class Episode : IResource + public class Episode : IResource, IMetadata { /// public int ID { get; set; } @@ -121,10 +121,8 @@ namespace Kyoo.Models /// public DateTime? ReleaseDate { get; set; } - /// - /// The link to metadata providers that this episode has. See for more information. - /// - [EditableRelation] [LoadableRelation] public ICollection> ExternalIDs { get; set; } + /// + public ICollection ExternalIDs { get; set; } /// /// The list of tracks this episode has. This lists video, audio and subtitles available. diff --git a/Kyoo.Common/Models/Resources/Interfaces/IMetadata.cs b/Kyoo.Common/Models/Resources/Interfaces/IMetadata.cs new file mode 100644 index 00000000..469d3dbb --- /dev/null +++ b/Kyoo.Common/Models/Resources/Interfaces/IMetadata.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Kyoo.Models.Attributes; + +namespace Kyoo.Models +{ + /// + /// An interface applied to resources containing external metadata. + /// + public interface IMetadata + { + /// + /// The link to metadata providers that this show has. See for more information. + /// + [EditableRelation] [LoadableRelation] public ICollection ExternalIDs { get; set; } + } +} \ No newline at end of file diff --git a/Kyoo.Common/Models/Resources/IResource.cs b/Kyoo.Common/Models/Resources/Interfaces/IResource.cs similarity index 100% rename from Kyoo.Common/Models/Resources/IResource.cs rename to Kyoo.Common/Models/Resources/Interfaces/IResource.cs diff --git a/Kyoo.Common/Models/Resources/People.cs b/Kyoo.Common/Models/Resources/People.cs index 7ae04613..ff2b8ed1 100644 --- a/Kyoo.Common/Models/Resources/People.cs +++ b/Kyoo.Common/Models/Resources/People.cs @@ -6,7 +6,7 @@ namespace Kyoo.Models /// /// An actor, voice actor, writer, animator, somebody who worked on a . /// - public class People : IResource + public class People : IResource, IMetadata { /// public int ID { get; set; } @@ -26,10 +26,8 @@ namespace Kyoo.Models /// [SerializeAs("{HOST}/api/people/{Slug}/poster")] public string Poster { get; set; } - /// - /// The link to metadata providers that this person has. See for more information. - /// - [EditableRelation] [LoadableRelation] public ICollection> ExternalIDs { get; set; } + /// + public ICollection ExternalIDs { get; set; } /// /// The list of roles this person has played in. See for more information. diff --git a/Kyoo.Common/Models/Resources/Season.cs b/Kyoo.Common/Models/Resources/Season.cs index 9b292020..b390f712 100644 --- a/Kyoo.Common/Models/Resources/Season.cs +++ b/Kyoo.Common/Models/Resources/Season.cs @@ -10,7 +10,7 @@ namespace Kyoo.Models /// /// A season of a . /// - public class Season : IResource + public class Season : IResource, IMetadata { /// public int ID { get; set; } @@ -81,10 +81,8 @@ namespace Kyoo.Models /// [SerializeAs("{HOST}/api/seasons/{Slug}/thumb")] public string Poster { get; set; } - /// - /// The link to metadata providers that this episode has. See for more information. - /// - [EditableRelation] [LoadableRelation] public ICollection> ExternalIDs { get; set; } + /// + public ICollection ExternalIDs { get; set; } /// /// The list of episodes that this season contains. diff --git a/Kyoo.Common/Models/Resources/Show.cs b/Kyoo.Common/Models/Resources/Show.cs index 57c9fcef..85c5b959 100644 --- a/Kyoo.Common/Models/Resources/Show.cs +++ b/Kyoo.Common/Models/Resources/Show.cs @@ -11,7 +11,7 @@ namespace Kyoo.Models /// /// A series or a movie. /// - public class Show : IResource, IOnMerge + public class Show : IResource, IMetadata, IOnMerge { /// public int ID { get; set; } @@ -89,11 +89,9 @@ namespace Kyoo.Models /// public bool IsMovie { get; set; } - /// - /// The link to metadata providers that this show has. See for more information. - /// - [EditableRelation] [LoadableRelation] public ICollection> ExternalIDs { get; set; } - + /// + public ICollection ExternalIDs { get; set; } + /// /// The ID of the Studio that made this show. /// @@ -157,19 +155,16 @@ namespace Kyoo.Models /// /// This method will never return anything if the are not loaded. /// The slug of the provider - /// The field of the asked provider. + /// The field of the asked provider. [CanBeNull] public string GetID(string provider) { - return ExternalIDs?.FirstOrDefault(x => x.Second.Slug == provider)?.DataID; + return ExternalIDs?.FirstOrDefault(x => x.Provider.Slug == provider)?.DataID; } /// public void OnMerge(object merged) { - if (ExternalIDs != null) - foreach (MetadataID id in ExternalIDs) - id.First = this; if (People != null) foreach (PeopleRole link in People) link.Show = this; diff --git a/Kyoo.Common/Models/Resources/Studio.cs b/Kyoo.Common/Models/Resources/Studio.cs index ebc3c4c1..80195317 100644 --- a/Kyoo.Common/Models/Resources/Studio.cs +++ b/Kyoo.Common/Models/Resources/Studio.cs @@ -6,7 +6,7 @@ namespace Kyoo.Models /// /// A studio that make shows. /// - public class Studio : IResource + public class Studio : IResource, IMetadata { /// public int ID { get; set; } @@ -24,6 +24,9 @@ namespace Kyoo.Models /// [LoadableRelation] public ICollection Shows { get; set; } + /// + public ICollection ExternalIDs { get; set; } + /// /// Create a new, empty, . /// diff --git a/Kyoo.CommonAPI/DatabaseContext.cs b/Kyoo.CommonAPI/DatabaseContext.cs index 36cdc47b..d0600da1 100644 --- a/Kyoo.CommonAPI/DatabaseContext.cs +++ b/Kyoo.CommonAPI/DatabaseContext.cs @@ -61,6 +61,10 @@ namespace Kyoo /// public DbSet Providers { get; set; } /// + /// All metadata ids, not discriminated by type. See . + /// + public DbSet MetadataIDs { get; set; } + /// /// The list of registered users. /// public DbSet Users { get; set; } @@ -83,17 +87,6 @@ namespace Kyoo /// public DbSet LibraryItems { get; set; } - /// - /// Get all metadataIDs (ExternalIDs) of a given resource. See . - /// - /// The metadata of this type will be returned. - /// A queryable of metadata ids for a type. - public DbSet> MetadataIds() - where T : class, IResource - { - return Set>(); - } - /// /// Get a generic link between two resource types. /// @@ -132,6 +125,22 @@ namespace Kyoo optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); } + /// + /// Build the metadata model for the given type. + /// + /// The database model builder + /// The type to add metadata to. + private void _HasMetadata(ModelBuilder modelBuilder) + where T : class, IMetadata + { + modelBuilder.Entity() + .HasMany(x => x.ExternalIDs) + .WithOne() + .HasForeignKey(x => x.ResourceID) + .OnDelete(DeleteBehavior.Cascade); + } + + /// /// Set database parameters to support every types of Kyoo. /// @@ -234,43 +243,21 @@ namespace Kyoo .WithMany(x => x.ShowLinks), y => y.HasKey(Link.PrimaryKey)); - modelBuilder.Entity>() - .HasKey(MetadataID.PrimaryKey); - modelBuilder.Entity>() - .HasOne(x => x.First) - .WithMany(x => x.ExternalIDs) - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity>() - .HasKey(MetadataID.PrimaryKey); - modelBuilder.Entity>() - .HasOne(x => x.First) - .WithMany(x => x.ExternalIDs) - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity>() - .HasKey(MetadataID.PrimaryKey); - modelBuilder.Entity>() - .HasOne(x => x.First) - .WithMany(x => x.ExternalIDs) - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity>() - .HasKey(MetadataID.PrimaryKey); - modelBuilder.Entity>() - .HasOne(x => x.First) - .WithMany(x => x.ExternalIDs) - .OnDelete(DeleteBehavior.Cascade); - - - modelBuilder.Entity>().HasOne(x => x.Second).WithMany() - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity>().HasOne(x => x.Second).WithMany() - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity>().HasOne(x => x.Second).WithMany() - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity>().HasOne(x => x.Second).WithMany() - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity>().HasOne(x => x.Second).WithMany() - .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() + .HasKey(MetadataID.PrimaryKey); + modelBuilder.Entity() + .Property(x => x.ResourceType) + .IsRequired(); + modelBuilder.Entity() + .HasDiscriminator(x => x.ResourceType); + _HasMetadata(modelBuilder); + _HasMetadata(modelBuilder); + _HasMetadata(modelBuilder); + _HasMetadata(modelBuilder); + _HasMetadata(modelBuilder); + _HasMetadata(modelBuilder); + modelBuilder.Entity() .HasKey(x => new {First = x.FirstID, Second = x.SecondID}); diff --git a/Kyoo.Tests/Database/RepositoryActivator.cs b/Kyoo.Tests/Database/RepositoryActivator.cs index ee6aa4df..25530f78 100644 --- a/Kyoo.Tests/Database/RepositoryActivator.cs +++ b/Kyoo.Tests/Database/RepositoryActivator.cs @@ -22,9 +22,9 @@ namespace Kyoo.Tests ProviderRepository provider = new(_database); LibraryRepository library = new(_database, provider); - CollectionRepository collection = new(_database); + CollectionRepository collection = new(_database, provider); GenreRepository genre = new(_database); - StudioRepository studio = new(_database); + StudioRepository studio = new(_database, provider); PeopleRepository people = new(_database, provider, new Lazy(() => LibraryManager.ShowRepository)); ShowRepository show = new(_database, studio, people, genre, provider); diff --git a/Kyoo.Tests/Database/SpecificTests/ShowTests.cs b/Kyoo.Tests/Database/SpecificTests/ShowTests.cs index 63207710..42ab693c 100644 --- a/Kyoo.Tests/Database/SpecificTests/ShowTests.cs +++ b/Kyoo.Tests/Database/SpecificTests/ShowTests.cs @@ -149,10 +149,9 @@ namespace Kyoo.Tests.Database Show value = await _repository.Get(TestSample.Get().Slug); value.ExternalIDs = new[] { - new MetadataID() + new MetadataID { - First = value, - Second = new Provider("test", "test.png"), + Provider = new Provider("test", "test.png"), DataID = "1234" } }; @@ -160,19 +159,19 @@ namespace Kyoo.Tests.Database Assert.Equal(value.Slug, edited.Slug); Assert.Equal( - value.ExternalIDs.Select(x => new {x.DataID, x.Second.Slug}), - edited.ExternalIDs.Select(x => new {x.DataID, x.Second.Slug})); + value.ExternalIDs.Select(x => new {x.DataID, x.Provider.Slug}), + edited.ExternalIDs.Select(x => new {x.DataID, x.Provider.Slug})); await using DatabaseContext database = Repositories.Context.New(); Show show = await database.Shows .Include(x => x.ExternalIDs) - .ThenInclude(x => x.Second) + .ThenInclude(x => x.Provider) .FirstAsync(); Assert.Equal(value.Slug, show.Slug); Assert.Equal( - value.ExternalIDs.Select(x => new {x.DataID, x.Second.Slug}), - show.ExternalIDs.Select(x => new {x.DataID, x.Second.Slug})); + value.ExternalIDs.Select(x => new {x.DataID, x.Provider.Slug}), + show.ExternalIDs.Select(x => new {x.DataID, x.Provider.Slug})); } [Fact] @@ -209,10 +208,9 @@ namespace Kyoo.Tests.Database expected.Slug = "created-relation-test"; expected.ExternalIDs = new[] { - new MetadataID + new MetadataID { - First = expected, - Second = new Provider("provider", "provider.png"), + Provider = new Provider("provider", "provider.png"), DataID = "ID" } }; diff --git a/Kyoo.TheMovieDb/Convertors.cs b/Kyoo.TheMovieDb/Convertors.cs index cb5b4412..0cf5fe98 100644 --- a/Kyoo.TheMovieDb/Convertors.cs +++ b/Kyoo.TheMovieDb/Convertors.cs @@ -48,9 +48,9 @@ namespace Kyoo.TheMovieDb .ToArray(), ExternalIDs = new [] { - new MetadataID + new MetadataID { - Second = provider, + Provider = provider, Link = $"https://www.themoviedb.org/movie/{movie.Id}", DataID = movie.Id.ToString() } @@ -94,9 +94,9 @@ namespace Kyoo.TheMovieDb .ToArray(), ExternalIDs = new [] { - new MetadataID + new MetadataID { - Second = provider, + Provider = provider, Link = $"https://www.themoviedb.org/movie/{tv.Id}", DataID = tv.Id.ToString() } @@ -145,9 +145,9 @@ namespace Kyoo.TheMovieDb IsMovie = true, ExternalIDs = new [] { - new MetadataID + new MetadataID { - Second = provider, + Provider = provider, Link = $"https://www.themoviedb.org/movie/{movie.Id}", DataID = movie.Id.ToString() } @@ -178,9 +178,9 @@ namespace Kyoo.TheMovieDb IsMovie = true, ExternalIDs = new [] { - new MetadataID + new MetadataID { - Second = provider, + Provider = provider, Link = $"https://www.themoviedb.org/movie/{tv.Id}", DataID = tv.Id.ToString() } @@ -205,9 +205,9 @@ namespace Kyoo.TheMovieDb Poster = cast.ProfilePath != null ? $"https://image.tmdb.org/t/p/original{cast.ProfilePath}" : null, ExternalIDs = new[] { - new MetadataID + new MetadataID { - Second = provider, + Provider = provider, DataID = cast.Id.ToString(), Link = $"https://www.themoviedb.org/person/{cast.Id}" } @@ -235,9 +235,9 @@ namespace Kyoo.TheMovieDb Poster = cast.ProfilePath != null ? $"https://image.tmdb.org/t/p/original{cast.ProfilePath}" : null, ExternalIDs = new[] { - new MetadataID + new MetadataID { - Second = provider, + Provider = provider, DataID = cast.Id.ToString(), Link = $"https://www.themoviedb.org/person/{cast.Id}" } @@ -265,9 +265,9 @@ namespace Kyoo.TheMovieDb Poster = crew.ProfilePath != null ? $"https://image.tmdb.org/t/p/original{crew.ProfilePath}" : null, ExternalIDs = new[] { - new MetadataID + new MetadataID { - Second = provider, + Provider = provider, DataID = crew.Id.ToString(), Link = $"https://www.themoviedb.org/person/{crew.Id}" } diff --git a/Kyoo.TheTvdb/Convertors.cs b/Kyoo.TheTvdb/Convertors.cs index bc31b5fd..6aad939b 100644 --- a/Kyoo.TheTvdb/Convertors.cs +++ b/Kyoo.TheTvdb/Convertors.cs @@ -58,11 +58,11 @@ namespace Kyoo.TheTvdb Poster = result.Poster != null ? $"https://www.thetvdb.com{result.Poster}" : null, ExternalIDs = new[] { - new MetadataID + new MetadataID { DataID = result.Id.ToString(), Link = $"https://www.thetvdb.com/series/{result.Slug}", - Second = provider + Provider = provider } } }; @@ -89,11 +89,11 @@ namespace Kyoo.TheTvdb Genres = series.Genre.Select(y => new Genre(y)).ToList(), ExternalIDs = new[] { - new MetadataID + new MetadataID { DataID = series.Id.ToString(), Link = $"https://www.thetvdb.com/series/{series.Slug}", - Second = provider + Provider = provider } } }; @@ -116,11 +116,11 @@ namespace Kyoo.TheTvdb Poster = actor.Image != null ? $"https://www.thetvdb.com/banners/{actor.Image}" : null, ExternalIDs = new [] { - new MetadataID() + new MetadataID { DataID = actor.Id.ToString(), Link = $"https://www.thetvdb.com/people/{actor.Id}", - Second = provider + Provider = provider } } }, @@ -147,11 +147,11 @@ namespace Kyoo.TheTvdb Thumb = episode.Filename != null ? $"https://www.thetvdb.com/banners/{episode.Filename}" : null, ExternalIDs = new[] { - new MetadataID + new MetadataID { DataID = episode.Id.ToString(), Link = $"https://www.thetvdb.com/series/{episode.SeriesId}/episodes/{episode.Id}", - Second = provider + Provider = provider } } }; diff --git a/Kyoo/Controllers/Repositories/CollectionRepository.cs b/Kyoo/Controllers/Repositories/CollectionRepository.cs index e0bc7843..6b546f07 100644 --- a/Kyoo/Controllers/Repositories/CollectionRepository.cs +++ b/Kyoo/Controllers/Repositories/CollectionRepository.cs @@ -18,6 +18,11 @@ namespace Kyoo.Controllers /// private readonly DatabaseContext _database; + /// + /// A provider repository to handle externalID creation and deletion + /// + private readonly IProviderRepository _providers; + /// protected override Expression> DefaultSort => x => x.Name; @@ -25,10 +30,12 @@ namespace Kyoo.Controllers /// Create a new . /// /// The database handle to use - public CollectionRepository(DatabaseContext database) + /// /// A provider repository + public CollectionRepository(DatabaseContext database, IProviderRepository providers) : base(database) { _database = database; + _providers = providers; } /// @@ -46,9 +53,35 @@ namespace Kyoo.Controllers { await base.Create(obj); _database.Entry(obj).State = EntityState.Added; + obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Added); await _database.SaveChangesAsync($"Trying to insert a duplicated collection (slug {obj.Slug} already exists)."); return obj; } + + /// + protected override async Task Validate(Collection resource) + { + await base.Validate(resource); + await resource.ExternalIDs.ForEachAsync(async x => + { + x.Provider = await _providers.CreateIfNotExists(x.Provider); + x.ProviderID = x.Provider.ID; + x.ResourceType = nameof(Collection); + _database.Entry(x.Provider).State = EntityState.Detached; + }); + } + + /// + protected override async Task EditRelations(Collection resource, Collection changed, bool resetOld) + { + if (changed.ExternalIDs != null || resetOld) + { + await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync(); + resource.ExternalIDs = changed.ExternalIDs; + } + + await base.EditRelations(resource, changed, resetOld); + } /// public override async Task Delete(Collection obj) diff --git a/Kyoo/Controllers/Repositories/EpisodeRepository.cs b/Kyoo/Controllers/Repositories/EpisodeRepository.cs index 3c1393eb..7a2b0479 100644 --- a/Kyoo/Controllers/Repositories/EpisodeRepository.cs +++ b/Kyoo/Controllers/Repositories/EpisodeRepository.cs @@ -160,9 +160,10 @@ namespace Kyoo.Controllers await base.Validate(resource); await resource.ExternalIDs.ForEachAsync(async x => { - x.Second = await _providers.CreateIfNotExists(x.Second); - x.SecondID = x.Second.ID; - _database.Entry(x.Second).State = EntityState.Detached; + x.Provider = await _providers.CreateIfNotExists(x.Provider); + x.ProviderID = x.Provider.ID; + x.ResourceType = nameof(Episode); + _database.Entry(x.Provider).State = EntityState.Detached; }); } diff --git a/Kyoo/Controllers/Repositories/PeopleRepository.cs b/Kyoo/Controllers/Repositories/PeopleRepository.cs index 9adb37ee..6267f52f 100644 --- a/Kyoo/Controllers/Repositories/PeopleRepository.cs +++ b/Kyoo/Controllers/Repositories/PeopleRepository.cs @@ -73,9 +73,10 @@ namespace Kyoo.Controllers await base.Validate(resource); await resource.ExternalIDs.ForEachAsync(async id => { - id.Second = await _providers.CreateIfNotExists(id.Second); - id.SecondID = id.Second.ID; - _database.Entry(id.Second).State = EntityState.Detached; + id.Provider = await _providers.CreateIfNotExists(id.Provider); + id.ProviderID = id.Provider.ID; + id.ResourceType = nameof(People); + _database.Entry(id.Provider).State = EntityState.Detached; }); await resource.Roles.ForEachAsync(async role => { diff --git a/Kyoo/Controllers/Repositories/ProviderRepository.cs b/Kyoo/Controllers/Repositories/ProviderRepository.cs index e48d4f50..13525c9c 100644 --- a/Kyoo/Controllers/Repositories/ProviderRepository.cs +++ b/Kyoo/Controllers/Repositories/ProviderRepository.cs @@ -9,21 +9,18 @@ using Microsoft.EntityFrameworkCore; namespace Kyoo.Controllers { /// - /// A local repository to handle providers. + /// A local repository to handle providers. /// public class ProviderRepository : LocalRepository, IProviderRepository { /// - /// The database handle + /// The database handle /// private readonly DatabaseContext _database; - - /// - protected override Expression> DefaultSort => x => x.Slug; /// - /// Create a new . + /// Create a new . /// /// The database handle public ProviderRepository(DatabaseContext database) @@ -32,6 +29,9 @@ namespace Kyoo.Controllers _database = database; } + /// + protected override Expression> DefaultSort => x => x.Slug; + /// public override async Task> Search(string query) { @@ -47,7 +47,8 @@ namespace Kyoo.Controllers { await base.Create(obj); _database.Entry(obj).State = EntityState.Added; - await _database.SaveChangesAsync($"Trying to insert a duplicated provider (slug {obj.Slug} already exists)."); + await _database.SaveChangesAsync("Trying to insert a duplicated provider " + + $"(slug {obj.Slug} already exists)."); return obj; } @@ -56,20 +57,24 @@ namespace Kyoo.Controllers { if (obj == null) throw new ArgumentNullException(nameof(obj)); - + _database.Entry(obj).State = EntityState.Deleted; await _database.SaveChangesAsync(); } /// - public Task>> GetMetadataID(Expression, bool>> where = null, - Sort> sort = default, + public Task> GetMetadataID(Expression> where = null, + Sort sort = default, Pagination limit = default) - where T : class, IResource + where T : class, IMetadata { - return ApplyFilters(_database.MetadataIds().Include(y => y.Second), - x => _database.MetadataIds().FirstOrDefaultAsync(y => y.FirstID == x), - x => x.FirstID, + string discriminator = typeof(T).Name; + return ApplyFilters(_database.MetadataIDs + .Include(y => y.Provider) + .Where(x => x.ResourceType == discriminator), + x => _database.MetadataIDs.FirstOrDefaultAsync(y => y.ResourceID == x + && y.ResourceType == discriminator), + x => x.ResourceID, where, sort, limit); diff --git a/Kyoo/Controllers/Repositories/SeasonRepository.cs b/Kyoo/Controllers/Repositories/SeasonRepository.cs index e0036982..45aa4554 100644 --- a/Kyoo/Controllers/Repositories/SeasonRepository.cs +++ b/Kyoo/Controllers/Repositories/SeasonRepository.cs @@ -106,9 +106,10 @@ namespace Kyoo.Controllers await base.Validate(resource); await resource.ExternalIDs.ForEachAsync(async id => { - id.Second = await _providers.CreateIfNotExists(id.Second); - id.SecondID = id.Second.ID; - _database.Entry(id.Second).State = EntityState.Detached; + id.Provider = await _providers.CreateIfNotExists(id.Provider); + id.ProviderID = id.Provider.ID; + id.ResourceType = nameof(Season); + _database.Entry(id.Provider).State = EntityState.Detached; }); } diff --git a/Kyoo/Controllers/Repositories/ShowRepository.cs b/Kyoo/Controllers/Repositories/ShowRepository.cs index 769cb232..37921c4d 100644 --- a/Kyoo/Controllers/Repositories/ShowRepository.cs +++ b/Kyoo/Controllers/Repositories/ShowRepository.cs @@ -101,9 +101,10 @@ namespace Kyoo.Controllers }); await resource.ExternalIDs.ForEachAsync(async id => { - id.Second = await _providers.CreateIfNotExists(id.Second); - id.SecondID = id.Second.ID; - _database.Entry(id.Second).State = EntityState.Detached; + id.Provider = await _providers.CreateIfNotExists(id.Provider); + id.ProviderID = id.Provider.ID; + id.ResourceType = nameof(Show); + _database.Entry(id.Provider).State = EntityState.Detached; }); await resource.People.ForEachAsync(async role => { diff --git a/Kyoo/Controllers/Repositories/StudioRepository.cs b/Kyoo/Controllers/Repositories/StudioRepository.cs index 516b7c08..0422c382 100644 --- a/Kyoo/Controllers/Repositories/StudioRepository.cs +++ b/Kyoo/Controllers/Repositories/StudioRepository.cs @@ -18,6 +18,11 @@ namespace Kyoo.Controllers /// private readonly DatabaseContext _database; + /// + /// A provider repository to handle externalID creation and deletion + /// + private readonly IProviderRepository _providers; + /// protected override Expression> DefaultSort => x => x.Name; @@ -26,10 +31,12 @@ namespace Kyoo.Controllers /// Create a new . /// /// The database handle - public StudioRepository(DatabaseContext database) + /// A provider repository + public StudioRepository(DatabaseContext database, IProviderRepository providers) : base(database) { _database = database; + _providers = providers; } /// @@ -50,6 +57,31 @@ namespace Kyoo.Controllers await _database.SaveChangesAsync($"Trying to insert a duplicated studio (slug {obj.Slug} already exists)."); return obj; } + + /// + protected override async Task Validate(Studio resource) + { + await base.Validate(resource); + await resource.ExternalIDs.ForEachAsync(async x => + { + x.Provider = await _providers.CreateIfNotExists(x.Provider); + x.ProviderID = x.Provider.ID; + x.ResourceType = nameof(Studio); + _database.Entry(x.Provider).State = EntityState.Detached; + }); + } + + /// + protected override async Task EditRelations(Studio resource, Studio changed, bool resetOld) + { + if (changed.ExternalIDs != null || resetOld) + { + await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync(); + resource.ExternalIDs = changed.ExternalIDs; + } + + await base.EditRelations(resource, changed, resetOld); + } /// public override async Task Delete(Studio obj)