diff --git a/Kyoo.Common/Controllers/IRepository.cs b/Kyoo.Common/Controllers/IRepository.cs index dad2d5e3..3db8d475 100644 --- a/Kyoo.Common/Controllers/IRepository.cs +++ b/Kyoo.Common/Controllers/IRepository.cs @@ -631,10 +631,12 @@ namespace Kyoo.Controllers /// A predicate to add arbitrary filter /// Sort information (sort order & sort by) /// 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> where = null, - Sort sort = default, - Pagination limit = default); + Task>> GetMetadataID(Expression, bool>> where = null, + Sort> sort = default, + Pagination limit = default) + where T : class, IResource; /// /// Get a list of external ids that match all filters @@ -643,10 +645,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> where, - Expression> sort, + Task>> GetMetadataID([Optional] Expression, bool>> where, + Expression, object>> sort, Pagination limit = default - ) => GetMetadataID(where, new Sort(sort), limit); + ) where T : class, IResource + => GetMetadataID(where, new Sort>(sort), limit); } /// diff --git a/Kyoo.Common/Controllers/Implementations/LibraryManager.cs b/Kyoo.Common/Controllers/Implementations/LibraryManager.cs index ce34f267..66581157 100644 --- a/Kyoo.Common/Controllers/Implementations/LibraryManager.cs +++ b/Kyoo.Common/Controllers/Implementations/LibraryManager.cs @@ -250,9 +250,9 @@ namespace Kyoo.Controllers (Show s, nameof(Show.ExternalIDs)) => SetRelation(s, - ProviderRepository.GetMetadataID(x => x.ShowID == obj.ID), + ProviderRepository.GetMetadataID(x => x.FirstID == obj.ID), (x, y) => x.ExternalIDs = y, - (x, y) => { x.Show = y; x.ShowID = y.ID; }), + (x, y) => { x.First = y; x.FirstID = y.ID; }), (Show s, nameof(Show.Genres)) => GenreRepository .GetAll(x => x.Shows.Any(y => y.ID == obj.ID)) @@ -290,9 +290,9 @@ namespace Kyoo.Controllers (Season s, nameof(Season.ExternalIDs)) => SetRelation(s, - ProviderRepository.GetMetadataID(x => x.SeasonID == obj.ID), + ProviderRepository.GetMetadataID(x => x.FirstID == obj.ID), (x, y) => x.ExternalIDs = y, - (x, y) => { x.Season = y; x.SeasonID = y.ID; }), + (x, y) => { x.First = y; x.FirstID = y.ID; }), (Season s, nameof(Season.Episodes)) => SetRelation(s, EpisodeRepository.GetAll(x => x.Season.ID == obj.ID), @@ -309,9 +309,9 @@ namespace Kyoo.Controllers (Episode e, nameof(Episode.ExternalIDs)) => SetRelation(e, - ProviderRepository.GetMetadataID(x => x.EpisodeID == obj.ID), + ProviderRepository.GetMetadataID(x => x.FirstID == obj.ID), (x, y) => x.ExternalIDs = y, - (x, y) => { x.Episode = y; x.EpisodeID = y.ID; }), + (x, y) => { x.First = y; x.FirstID = y.ID; }), (Episode e, nameof(Episode.Tracks)) => SetRelation(e, TrackRepository.GetAll(x => x.Episode.ID == obj.ID), @@ -355,9 +355,9 @@ namespace Kyoo.Controllers (People p, nameof(People.ExternalIDs)) => SetRelation(p, - ProviderRepository.GetMetadataID(x => x.PeopleID == obj.ID), + ProviderRepository.GetMetadataID(x => x.FirstID == obj.ID), (x, y) => x.ExternalIDs = y, - (x, y) => { x.People = y; x.PeopleID = y.ID; }), + (x, y) => { x.First = y; x.FirstID = y.ID; }), (People p, nameof(People.Roles)) => PeopleRepository .GetFromPeople(obj.ID) diff --git a/Kyoo.Common/Models/MetadataID.cs b/Kyoo.Common/Models/MetadataID.cs index 7621830c..d9ebd933 100644 --- a/Kyoo.Common/Models/MetadataID.cs +++ b/Kyoo.Common/Models/MetadataID.cs @@ -1,33 +1,20 @@ -using Kyoo.Models.Attributes; - namespace Kyoo.Models { /// /// ID and link of an item on an external provider. /// - public class MetadataID + /// + public class MetadataID : Link + where T : class, IResource { /// - /// The unique ID of this metadata. This is the equivalent of . + /// The ID of the resource on the external provider. /// - [SerializeIgnore] public int ID { get; set; } - - [SerializeIgnore] public int ProviderID { get; set; } - public Provider Provider {get; set; } - - [SerializeIgnore] public int? ShowID { get; set; } - [SerializeIgnore] public Show Show { get; set; } - - [SerializeIgnore] public int? EpisodeID { get; set; } - [SerializeIgnore] public Episode Episode { get; set; } - - [SerializeIgnore] public int? SeasonID { get; set; } - [SerializeIgnore] public Season Season { get; set; } - - [SerializeIgnore] public int? PeopleID { get; set; } - [SerializeIgnore] public People People { get; set; } - public string DataID { get; set; } + + /// + /// The URL of the resource on the external provider. + /// public string Link { get; set; } } } \ No newline at end of file diff --git a/Kyoo.Common/Models/Page.cs b/Kyoo.Common/Models/Page.cs index 71f9c7cd..023aac3e 100644 --- a/Kyoo.Common/Models/Page.cs +++ b/Kyoo.Common/Models/Page.cs @@ -3,22 +3,45 @@ using System.Linq; namespace Kyoo.Models { + /// + /// A page of resource that contains information about the pagination of resources. + /// + /// The type of resource contained in this page. public class Page where T : IResource { - public string This { get; set; } - public string First { get; set; } - public string Next { get; set; } + /// + /// The link of the current page. + /// + public string This { get; } + + /// + /// The link of the first page. + /// + public string First { get; } + + /// + /// The link of the next page. + /// + public string Next { get; } + /// + /// The number of items in the current page. + /// public int Count => Items.Count; - public ICollection Items { get; set; } - - public Page() { } - - public Page(ICollection items) - { - Items = items; - } - + + /// + /// The list of items in the page. + /// + public ICollection Items { get; } + + + /// + /// Create a new . + /// + /// The list of items in the page. + /// The link of the current page. + /// The link of the next page. + /// The link of the first page. public Page(ICollection items, string @this, string next, string first) { Items = items; @@ -27,7 +50,14 @@ namespace Kyoo.Models First = first; } - public Page(ICollection items, + /// + /// Create a new and compute the urls. + /// + /// The list of items in the page. + /// The base url of the resources available from this page. + /// The list of query strings of the current page + /// The number of items requested for the current page. + public Page(ICollection items, string url, Dictionary query, int limit) diff --git a/Kyoo.Common/Models/PeopleRole.cs b/Kyoo.Common/Models/PeopleRole.cs index be48abd1..062bb3e8 100644 --- a/Kyoo.Common/Models/PeopleRole.cs +++ b/Kyoo.Common/Models/PeopleRole.cs @@ -1,17 +1,55 @@ -using Kyoo.Models.Attributes; - namespace Kyoo.Models { + /// + /// A role a person played for a show. It can be an actor, musician, voice actor, director, writer... + /// + /// + /// This class is not serialized like other classes. + /// Based on the field, it is serialized like + /// a show with two extra fields ( and ). + /// public class PeopleRole : IResource { - [SerializeIgnore] public int ID { get; set; } - [SerializeIgnore] public string Slug => ForPeople ? Show.Slug : People.Slug; - [SerializeIgnore] public bool ForPeople; - [SerializeIgnore] public int PeopleID { get; set; } - [SerializeIgnore] public virtual People People { get; set; } - [SerializeIgnore] public int ShowID { get; set; } - [SerializeIgnore] public virtual Show Show { get; set; } - public string Role { get; set; } + /// + public int ID { get; set; } + + /// + public string Slug => ForPeople ? Show.Slug : People.Slug; + + /// + /// Should this role be used as a Show substitute (the value is false) or + /// as a People substitute (the value is true). + /// + public bool ForPeople { get; set; } + + /// + /// The ID of the People playing the role. + /// + public int PeopleID { get; set; } + /// + /// The people that played this role. + /// + public People People { get; set; } + + /// + /// The ID of the Show where the People playing in. + /// + public int ShowID { get; set; } + /// + /// The show where the People played in. + /// + public Show Show { get; set; } + + /// + /// The type of work the person has done for the show. + /// That can be something like "Actor", "Writer", "Music", "Voice Actor"... + /// public string Type { get; set; } + + /// + /// The role the People played. + /// This is mostly used to inform witch character was played for actor and voice actors. + /// + public string Role { get; set; } } } \ No newline at end of file diff --git a/Kyoo.Common/Models/Resources/Episode.cs b/Kyoo.Common/Models/Resources/Episode.cs index e5d0d7f1..3a8e3b60 100644 --- a/Kyoo.Common/Models/Resources/Episode.cs +++ b/Kyoo.Common/Models/Resources/Episode.cs @@ -86,9 +86,9 @@ namespace Kyoo.Models public DateTime? ReleaseDate { get; set; } /// - /// The link to metadata providers that this episode has. See for more information. + /// The link to metadata providers that this episode has. See for more information. /// - [EditableRelation] [LoadableRelation] public ICollection ExternalIDs { get; set; } + [EditableRelation] [LoadableRelation] 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/People.cs b/Kyoo.Common/Models/Resources/People.cs index 9fea0112..7ae04613 100644 --- a/Kyoo.Common/Models/Resources/People.cs +++ b/Kyoo.Common/Models/Resources/People.cs @@ -27,9 +27,9 @@ 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. + /// The link to metadata providers that this person has. See for more information. /// - [EditableRelation] [LoadableRelation] public ICollection ExternalIDs { get; set; } + [EditableRelation] [LoadableRelation] 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/Provider.cs b/Kyoo.Common/Models/Resources/Provider.cs index ac7d9ffc..e13a9be4 100644 --- a/Kyoo.Common/Models/Resources/Provider.cs +++ b/Kyoo.Common/Models/Resources/Provider.cs @@ -44,11 +44,6 @@ namespace Kyoo.Models /// The internal link between this provider and libraries in the list. /// [Link] public ICollection> LibraryLinks { get; set; } - - /// - /// The internal link between this provider and related . - /// - [Link] public ICollection MetadataLinks { get; set; } #endif /// diff --git a/Kyoo.Common/Models/Resources/Season.cs b/Kyoo.Common/Models/Resources/Season.cs index 369763ea..3c8f21a9 100644 --- a/Kyoo.Common/Models/Resources/Season.cs +++ b/Kyoo.Common/Models/Resources/Season.cs @@ -63,9 +63,9 @@ 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. + /// The link to metadata providers that this episode has. See for more information. /// - [EditableRelation] [LoadableRelation] public ICollection ExternalIDs { get; set; } + [EditableRelation] [LoadableRelation] 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 7656401f..788ffff3 100644 --- a/Kyoo.Common/Models/Resources/Show.cs +++ b/Kyoo.Common/Models/Resources/Show.cs @@ -89,9 +89,9 @@ namespace Kyoo.Models public bool IsMovie { get; set; } /// - /// The link to metadata providers that this show has. See for more information. + /// The link to metadata providers that this show has. See for more information. /// - [EditableRelation] [LoadableRelation] public ICollection ExternalIDs { get; set; } + [EditableRelation] [LoadableRelation] public ICollection> ExternalIDs { get; set; } /// /// The ID of the Studio that made this show. This value is only set when the has been loaded. @@ -155,18 +155,18 @@ 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. public string GetID(string provider) { - return ExternalIDs?.FirstOrDefault(x => x.Provider.Slug == provider)?.DataID; + return ExternalIDs?.FirstOrDefault(x => x.Second.Slug == provider)?.DataID; } /// public void OnMerge(object merged) { if (ExternalIDs != null) - foreach (MetadataID id in ExternalIDs) - id.Show = this; + 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/SearchResult.cs b/Kyoo.Common/Models/SearchResult.cs index 42a25bcf..9ec2bc37 100644 --- a/Kyoo.Common/Models/SearchResult.cs +++ b/Kyoo.Common/Models/SearchResult.cs @@ -2,14 +2,44 @@ namespace Kyoo.Models { + /// + /// Results of a search request. + /// public class SearchResult { - public string Query; - public IEnumerable Collections; - public IEnumerable Shows; - public IEnumerable Episodes; - public IEnumerable People; - public IEnumerable Genres; - public IEnumerable Studios; + /// + /// The query of the search request. + /// + public string Query { get; init; } + + /// + /// The collections that matched the search. + /// + public ICollection Collections { get; init; } + + /// + /// The shows that matched the search. + /// + public ICollection Shows { get; init; } + + /// + /// The episodes that matched the search. + /// + public ICollection Episodes { get; init; } + + /// + /// The people that matched the search. + /// + public ICollection People { get; init; } + + /// + /// The genres that matched the search. + /// + public ICollection Genres { get; init; } + + /// + /// The studios that matched the search. + /// + public ICollection Studios { get; init; } } } diff --git a/Kyoo.Common/Utility/Utility.cs b/Kyoo.Common/Utility/Utility.cs index e7d9600a..28699035 100644 --- a/Kyoo.Common/Utility/Utility.cs +++ b/Kyoo.Common/Utility/Utility.cs @@ -7,7 +7,6 @@ using System.Reflection; using System.Runtime.ExceptionServices; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using JetBrains.Annotations; namespace Kyoo diff --git a/Kyoo.CommonAPI/DatabaseContext.cs b/Kyoo.CommonAPI/DatabaseContext.cs index 584230b9..97e0421c 100644 --- a/Kyoo.CommonAPI/DatabaseContext.cs +++ b/Kyoo.CommonAPI/DatabaseContext.cs @@ -61,10 +61,6 @@ namespace Kyoo /// public DbSet Providers { get; set; } /// - /// All metadataIDs (ExternalIDs) of Kyoo. See . - /// - public DbSet MetadataIds { get; set; } - /// /// The list of registered users. /// public DbSet Users { get; set; } @@ -79,6 +75,17 @@ namespace Kyoo /// public DbSet WatchedEpisodes { 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. /// @@ -205,25 +212,33 @@ namespace Kyoo .WithMany(x => x.ShowLinks), y => y.HasKey(Link.PrimaryKey)); - modelBuilder.Entity() - .HasOne(x => x.Show) + modelBuilder.Entity>() + .HasOne(x => x.First) .WithMany(x => x.ExternalIDs) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() - .HasOne(x => x.Season) + modelBuilder.Entity>() + .HasOne(x => x.First) .WithMany(x => x.ExternalIDs) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() - .HasOne(x => x.Episode) + modelBuilder.Entity>() + .HasOne(x => x.First) .WithMany(x => x.ExternalIDs) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() - .HasOne(x => x.People) + modelBuilder.Entity>() + .HasOne(x => x.First) .WithMany(x => x.ExternalIDs) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() - .HasOne(x => x.Provider) - .WithMany(x => x.MetadataLinks) + + + 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() diff --git a/Kyoo/Controllers/Repositories/EpisodeRepository.cs b/Kyoo/Controllers/Repositories/EpisodeRepository.cs index 6487e6b1..46c8d62c 100644 --- a/Kyoo/Controllers/Repositories/EpisodeRepository.cs +++ b/Kyoo/Controllers/Repositories/EpisodeRepository.cs @@ -234,9 +234,9 @@ namespace Kyoo.Controllers await base.Validate(resource); resource.ExternalIDs = await resource.ExternalIDs.SelectAsync(async x => { - x.Provider = await _providers.CreateIfNotExists(x.Provider); - x.ProviderID = x.Provider.ID; - _database.Entry(x.Provider).State = EntityState.Detached; + x.Second = await _providers.CreateIfNotExists(x.Second); + x.SecondID = x.Second.ID; + _database.Entry(x.Second).State = EntityState.Detached; return x; }).ToListAsync(); } diff --git a/Kyoo/Controllers/Repositories/PeopleRepository.cs b/Kyoo/Controllers/Repositories/PeopleRepository.cs index 452f59eb..9adb37ee 100644 --- a/Kyoo/Controllers/Repositories/PeopleRepository.cs +++ b/Kyoo/Controllers/Repositories/PeopleRepository.cs @@ -73,9 +73,9 @@ namespace Kyoo.Controllers await base.Validate(resource); await resource.ExternalIDs.ForEachAsync(async id => { - id.Provider = await _providers.CreateIfNotExists(id.Provider); - id.ProviderID = id.Provider.ID; - _database.Entry(id.Provider).State = EntityState.Detached; + id.Second = await _providers.CreateIfNotExists(id.Second); + id.SecondID = id.Second.ID; + _database.Entry(id.Second).State = EntityState.Detached; }); await resource.Roles.ForEachAsync(async role => { diff --git a/Kyoo/Controllers/Repositories/ProviderRepository.cs b/Kyoo/Controllers/Repositories/ProviderRepository.cs index 135e8148..e48d4f50 100644 --- a/Kyoo/Controllers/Repositories/ProviderRepository.cs +++ b/Kyoo/Controllers/Repositories/ProviderRepository.cs @@ -58,18 +58,18 @@ namespace Kyoo.Controllers throw new ArgumentNullException(nameof(obj)); _database.Entry(obj).State = EntityState.Deleted; - obj.MetadataLinks.ForEach(x => _database.Entry(x).State = EntityState.Deleted); await _database.SaveChangesAsync(); } /// - public Task> GetMetadataID(Expression> where = null, - Sort sort = default, + public Task>> GetMetadataID(Expression, bool>> where = null, + Sort> sort = default, Pagination limit = default) + where T : class, IResource { - return ApplyFilters(_database.MetadataIds.Include(y => y.Provider), - x => _database.MetadataIds.FirstOrDefaultAsync(y => y.ID == x), - x => x.ID, + return ApplyFilters(_database.MetadataIds().Include(y => y.Second), + x => _database.MetadataIds().FirstOrDefaultAsync(y => y.FirstID == x), + x => x.FirstID, where, sort, limit); diff --git a/Kyoo/Controllers/Repositories/SeasonRepository.cs b/Kyoo/Controllers/Repositories/SeasonRepository.cs index 289ca08f..1f31ace7 100644 --- a/Kyoo/Controllers/Repositories/SeasonRepository.cs +++ b/Kyoo/Controllers/Repositories/SeasonRepository.cs @@ -160,9 +160,9 @@ namespace Kyoo.Controllers await base.Validate(resource); await resource.ExternalIDs.ForEachAsync(async id => { - id.Provider = await _providers.CreateIfNotExists(id.Provider); - id.ProviderID = id.Provider.ID; - _database.Entry(id.Provider).State = EntityState.Detached; + id.Second = await _providers.CreateIfNotExists(id.Second); + id.SecondID = id.Second.ID; + _database.Entry(id.Second).State = EntityState.Detached; }); } diff --git a/Kyoo/Controllers/Repositories/ShowRepository.cs b/Kyoo/Controllers/Repositories/ShowRepository.cs index 595f2156..ba88a639 100644 --- a/Kyoo/Controllers/Repositories/ShowRepository.cs +++ b/Kyoo/Controllers/Repositories/ShowRepository.cs @@ -111,9 +111,9 @@ namespace Kyoo.Controllers .ToList(); await resource.ExternalIDs.ForEachAsync(async id => { - id.Provider = await _providers.CreateIfNotExists(id.Provider); - id.ProviderID = id.Provider.ID; - _database.Entry(id.Provider).State = EntityState.Detached; + id.Second = await _providers.CreateIfNotExists(id.Second); + id.SecondID = id.Second.ID; + _database.Entry(id.Second).State = EntityState.Detached; }); await resource.People.ForEachAsync(async role => { @@ -196,7 +196,7 @@ namespace Kyoo.Controllers _database.Entry(entry).State = EntityState.Deleted; if (obj.ExternalIDs != null) - foreach (MetadataID entry in obj.ExternalIDs) + foreach (MetadataID entry in obj.ExternalIDs) _database.Entry(entry).State = EntityState.Deleted; await _database.SaveChangesAsync(); diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index 27c05c56..e52aaee1 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -4,7 +4,6 @@ using Kyoo.Authentication; using Kyoo.Controllers; using Kyoo.Models; using Kyoo.Models.Options; -using Kyoo.Postgresql; using Kyoo.SqLite; using Kyoo.Tasks; using Microsoft.AspNetCore.Builder;