diff --git a/Kyoo.Common/Controllers/Implementations/LibraryManager.cs b/Kyoo.Common/Controllers/Implementations/LibraryManager.cs index c14f1251..6fbffa72 100644 --- a/Kyoo.Common/Controllers/Implementations/LibraryManager.cs +++ b/Kyoo.Common/Controllers/Implementations/LibraryManager.cs @@ -306,10 +306,13 @@ namespace Kyoo.Controllers (Show s, nameof(Show.Collections)) => CollectionRepository .GetAll(x => x.Shows.Any(y => y.ID == obj.ID)) .Then(x => s.Collections = x), - // TODO Studio loading does not work. (Show s, nameof(Show.Studio)) => StudioRepository .Get(x => x.Shows.Any(y => y.ID == obj.ID)) - .Then(x => s.Studio = x), + .Then(x => + { + s.Studio = x; + s.StudioID = x?.ID ?? 0; + }), (Season s, nameof(Season.ExternalIDs)) => ProviderRepository .GetMetadataID(x => x.SeasonID == obj.ID) @@ -319,7 +322,11 @@ namespace Kyoo.Controllers .Then(x => s.Episodes = x), (Season s, nameof(Season.Show)) => ShowRepository .Get(x => x.Seasons.Any(y => y.ID == obj.ID)) - .Then(x => s.Show = x), + .Then(x => + { + s.Show = x; + s.ShowID = x?.ID ?? 0; + }), (Episode e, nameof(Episode.ExternalIDs)) => ProviderRepository .GetMetadataID(x => x.EpisodeID == obj.ID) @@ -329,14 +336,26 @@ namespace Kyoo.Controllers .Then(x => e.Tracks = x), (Episode e, nameof(Episode.Show)) => ShowRepository .Get(x => x.Episodes.Any(y => y.ID == obj.ID)) - .Then(x => e.Show = x), + .Then(x => + { + e.Show = x; + e.ShowID = x?.ID ?? 0; + }), (Episode e, nameof(Episode.Season)) => SeasonRepository .Get(x => x.Episodes.Any(y => y.ID == e.ID)) - .Then(x => e.Season = x), + .Then(x => + { + e.Season = x; + e.SeasonID = x?.ID ?? 0; + }), (Track t, nameof(Track.Episode)) => EpisodeRepository .Get(x => x.Tracks.Any(y => y.ID == obj.ID)) - .Then(x => t.Episode = x), + .Then(x => + { + t.Episode = x; + t.EpisodeID = x?.ID ?? 0; + }), (Genre g, nameof(Genre.Shows)) => ShowRepository .GetAll(x => x.Genres.Any(y => y.ID == obj.ID)) diff --git a/Kyoo.Common/Models/Attributes/RelationAttributes.cs b/Kyoo.Common/Models/Attributes/RelationAttributes.cs index 36edbbf2..aac0e633 100644 --- a/Kyoo.Common/Models/Attributes/RelationAttributes.cs +++ b/Kyoo.Common/Models/Attributes/RelationAttributes.cs @@ -4,7 +4,17 @@ namespace Kyoo.Models.Attributes { [AttributeUsage(AttributeTargets.Property, Inherited = false)] public class EditableRelationAttribute : Attribute { } - + [AttributeUsage(AttributeTargets.Property)] - public class LoadableRelationAttribute : Attribute { } + public class LoadableRelationAttribute : Attribute + { + public string RelationID { get; } + + public LoadableRelationAttribute() {} + + public LoadableRelationAttribute(string relationID) + { + RelationID = relationID; + } + } } \ No newline at end of file diff --git a/Kyoo.Common/Models/Resources/Episode.cs b/Kyoo.Common/Models/Resources/Episode.cs index 9ef8c684..42863a5d 100644 --- a/Kyoo.Common/Models/Resources/Episode.cs +++ b/Kyoo.Common/Models/Resources/Episode.cs @@ -10,10 +10,10 @@ namespace Kyoo.Models { public int ID { get; set; } public string Slug => Show != null ? GetSlug(Show.Slug, SeasonNumber, EpisodeNumber) : ID.ToString(); - public int ShowID { get; set; } - [LoadableRelation] public virtual Show Show { get; set; } - public int? SeasonID { get; set; } - [LoadableRelation] public virtual Season Season { get; set; } + [SerializeIgnore] public int ShowID { get; set; } + [LoadableRelation(nameof(ShowID))] public virtual Show Show { get; set; } + [SerializeIgnore] public int? SeasonID { get; set; } + [LoadableRelation(nameof(SeasonID))] public virtual Season Season { get; set; } public int SeasonNumber { get; set; } = -1; public int EpisodeNumber { get; set; } = -1; diff --git a/Kyoo.Common/Models/Resources/Season.cs b/Kyoo.Common/Models/Resources/Season.cs index ecaff2e4..adba3b9f 100644 --- a/Kyoo.Common/Models/Resources/Season.cs +++ b/Kyoo.Common/Models/Resources/Season.cs @@ -8,7 +8,7 @@ namespace Kyoo.Models public class Season : IResource { public int ID { get; set; } - public int ShowID { get; set; } + [SerializeIgnore] public int ShowID { get; set; } public int SeasonNumber { get; set; } = -1; @@ -21,7 +21,7 @@ namespace Kyoo.Models public string Thumb => $"/api/seasons/{Slug}/thumb"; [EditableRelation] [LoadableRelation] public virtual ICollection ExternalIDs { get; set; } - [LoadableRelation] public virtual Show Show { get; set; } + [LoadableRelation(nameof(ShowID))] public virtual Show Show { get; set; } [LoadableRelation] public virtual ICollection Episodes { get; set; } public Season() { } diff --git a/Kyoo.Common/Models/Resources/Show.cs b/Kyoo.Common/Models/Resources/Show.cs index 520cfeb7..a3cda2dd 100644 --- a/Kyoo.Common/Models/Resources/Show.cs +++ b/Kyoo.Common/Models/Resources/Show.cs @@ -28,7 +28,7 @@ namespace Kyoo.Models [SerializeIgnore] public int? StudioID { get; set; } - [LoadableRelation] [EditableRelation] public virtual Studio Studio { get; set; } + [LoadableRelation(nameof(StudioID))] [EditableRelation] public virtual Studio Studio { get; set; } [LoadableRelation] [EditableRelation] public virtual ICollection Genres { get; set; } [LoadableRelation] [EditableRelation] public virtual ICollection People { get; set; } [LoadableRelation] public virtual ICollection Seasons { get; set; } diff --git a/Kyoo.Common/Models/Resources/Track.cs b/Kyoo.Common/Models/Resources/Track.cs index f4b82fc7..7895bba6 100644 --- a/Kyoo.Common/Models/Resources/Track.cs +++ b/Kyoo.Common/Models/Resources/Track.cs @@ -57,7 +57,7 @@ namespace Kyoo.Models public class Track : Stream, IResource { public int ID { get; set; } - public int EpisodeID { get; set; } + [SerializeIgnore] public int EpisodeID { get; set; } public bool IsDefault { get => isDefault; @@ -114,11 +114,18 @@ namespace Kyoo.Models } public bool IsExternal { get; set; } - [LoadableRelation] public virtual Episode Episode { get; set; } + [LoadableRelation(nameof(EpisodeID))] public virtual Episode Episode { get; set; } public Track() { } - public Track(StreamType type, string title, string language, bool isDefault, bool isForced, string codec, bool isExternal, string path) + public Track(StreamType type, + string title, + string language, + bool isDefault, + bool isForced, + string codec, + bool isExternal, + string path) : base(title, language, codec, isDefault, isForced, path, type) { IsExternal = isExternal; diff --git a/Kyoo.Common/Utility.cs b/Kyoo.Common/Utility.cs index f50d7018..521abc11 100644 --- a/Kyoo.Common/Utility.cs +++ b/Kyoo.Common/Utility.cs @@ -33,6 +33,20 @@ namespace Kyoo : ex.Body as MemberExpression; return member!.Member.Name; } + + public static object GetValue([NotNull] this MemberInfo member, [NotNull] object obj) + { + if (member == null) + throw new ArgumentNullException(nameof(member)); + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + return member switch + { + PropertyInfo property => property.GetValue(obj), + FieldInfo field => field.GetValue(obj), + _ => throw new ArgumentException($"Can't get value of a non property/field (member: {member}).") + }; + } public static string ToSlug(string str) { diff --git a/Kyoo.CommonAPI/JsonSerializer.cs b/Kyoo.CommonAPI/JsonSerializer.cs index d0d30975..df200c26 100644 --- a/Kyoo.CommonAPI/JsonSerializer.cs +++ b/Kyoo.CommonAPI/JsonSerializer.cs @@ -10,9 +10,21 @@ namespace Kyoo.Controllers protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); - - if (member?.GetCustomAttribute() != null) - property.NullValueHandling = NullValueHandling.Ignore; + + LoadableRelationAttribute relation = member?.GetCustomAttribute(); + if (relation != null) + { + if (relation.RelationID == null) + property.ShouldSerialize = x => member.GetValue(x) != null; + else + property.ShouldSerialize = x => + { + if (member.GetValue(x) != null) + return true; + return x.GetType().GetProperty(relation.RelationID)?.GetValue(x) != null; + }; + } + if (member?.GetCustomAttribute() != null) property.ShouldSerialize = _ => false; if (member?.GetCustomAttribute() != null) diff --git a/Kyoo.CommonAPI/ResourceViewAttribute.cs b/Kyoo.CommonAPI/ResourceViewAttribute.cs index 7a60da55..a05a8fec 100644 --- a/Kyoo.CommonAPI/ResourceViewAttribute.cs +++ b/Kyoo.CommonAPI/ResourceViewAttribute.cs @@ -75,7 +75,6 @@ namespace Kyoo.CommonApi Type pageType = Utility.GetGenericDefinition(result.DeclaredType, typeof(Page<>)); - // TODO loading is case sensitive. Maybe convert them in the first check. if (pageType != null) { foreach (IResource resource in ((dynamic)result.Value).Items)