mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Splitting task tests and adding documentation to resources
This commit is contained in:
parent
d6630f29ea
commit
2bc559424c
@ -242,6 +242,9 @@ namespace Kyoo.Controllers
|
|||||||
/// <typeparam name="T">The type of the source object</typeparam>
|
/// <typeparam name="T">The type of the source object</typeparam>
|
||||||
/// <typeparam name="T2">The related resource's type</typeparam>
|
/// <typeparam name="T2">The related resource's type</typeparam>
|
||||||
/// <returns>The param <see cref="obj"/></returns>
|
/// <returns>The param <see cref="obj"/></returns>
|
||||||
|
/// <seealso cref="Load{T,T2}(T,System.Linq.Expressions.Expression{System.Func{T,System.Collections.Generic.ICollection{T2}}})"/>
|
||||||
|
/// <seealso cref="Load{T}(T, System.String)"/>
|
||||||
|
/// <seealso cref="Load(IResource, string)"/>
|
||||||
Task<T> Load<T, T2>([NotNull] T obj, Expression<Func<T, T2>> member)
|
Task<T> Load<T, T2>([NotNull] T obj, Expression<Func<T, T2>> member)
|
||||||
where T : class, IResource
|
where T : class, IResource
|
||||||
where T2 : class, IResource, new();
|
where T2 : class, IResource, new();
|
||||||
@ -254,6 +257,9 @@ namespace Kyoo.Controllers
|
|||||||
/// <typeparam name="T">The type of the source object</typeparam>
|
/// <typeparam name="T">The type of the source object</typeparam>
|
||||||
/// <typeparam name="T2">The related resource's type</typeparam>
|
/// <typeparam name="T2">The related resource's type</typeparam>
|
||||||
/// <returns>The param <see cref="obj"/></returns>
|
/// <returns>The param <see cref="obj"/></returns>
|
||||||
|
/// <seealso cref="Load{T,T2}(T,System.Linq.Expressions.Expression{System.Func{T,T2}})"/>
|
||||||
|
/// <seealso cref="Load{T}(T, System.String)"/>
|
||||||
|
/// <seealso cref="Load(IResource, string)"/>
|
||||||
Task<T> Load<T, T2>([NotNull] T obj, Expression<Func<T, ICollection<T2>>> member)
|
Task<T> Load<T, T2>([NotNull] T obj, Expression<Func<T, ICollection<T2>>> member)
|
||||||
where T : class, IResource
|
where T : class, IResource
|
||||||
where T2 : class, new();
|
where T2 : class, new();
|
||||||
@ -265,6 +271,9 @@ namespace Kyoo.Controllers
|
|||||||
/// <param name="memberName">The name of the resource to load (case sensitive)</param>
|
/// <param name="memberName">The name of the resource to load (case sensitive)</param>
|
||||||
/// <typeparam name="T">The type of the source object</typeparam>
|
/// <typeparam name="T">The type of the source object</typeparam>
|
||||||
/// <returns>The param <see cref="obj"/></returns>
|
/// <returns>The param <see cref="obj"/></returns>
|
||||||
|
/// <seealso cref="Load{T,T2}(T,System.Linq.Expressions.Expression{System.Func{T,T2}})"/>
|
||||||
|
/// <seealso cref="Load{T,T2}(T,System.Linq.Expressions.Expression{System.Func{T,System.Collections.Generic.ICollection{T2}}})"/>
|
||||||
|
/// <seealso cref="Load(IResource, string)"/>
|
||||||
Task<T> Load<T>([NotNull] T obj, string memberName)
|
Task<T> Load<T>([NotNull] T obj, string memberName)
|
||||||
where T : class, IResource;
|
where T : class, IResource;
|
||||||
|
|
||||||
@ -273,6 +282,9 @@ namespace Kyoo.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The source object.</param>
|
/// <param name="obj">The source object.</param>
|
||||||
/// <param name="memberName">The name of the resource to load (case sensitive)</param>
|
/// <param name="memberName">The name of the resource to load (case sensitive)</param>
|
||||||
|
/// <seealso cref="Load{T,T2}(T,System.Linq.Expressions.Expression{System.Func{T,T2}})"/>
|
||||||
|
/// <seealso cref="Load{T,T2}(T,System.Linq.Expressions.Expression{System.Func{T,System.Collections.Generic.ICollection{T2}}})"/>
|
||||||
|
/// <seealso cref="Load{T}(T, System.String)"/>
|
||||||
Task Load([NotNull] IResource obj, string memberName);
|
Task Load([NotNull] IResource obj, string memberName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
13
Kyoo.Common/Models/Attributes/LinkAttribute.cs
Normal file
13
Kyoo.Common/Models/Attributes/LinkAttribute.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Kyoo.Models.Attributes;
|
||||||
|
|
||||||
|
namespace Kyoo.Common.Models.Attributes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An attribute to mark Link properties on resource.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
|
[MeansImplicitUse]
|
||||||
|
public class LinkAttribute : SerializeIgnoreAttribute { }
|
||||||
|
}
|
@ -1,17 +1,34 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Kyoo.Controllers;
|
||||||
|
|
||||||
namespace Kyoo.Models.Attributes
|
namespace Kyoo.Models.Attributes
|
||||||
{
|
{
|
||||||
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
|
/// <summary>
|
||||||
|
/// The targeted relation can be edited via calls to the repository's <see cref="IRepository{T}.Edit"/> method.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public class EditableRelationAttribute : Attribute { }
|
public class EditableRelationAttribute : Attribute { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The targeted relation can be loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||||
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public class LoadableRelationAttribute : Attribute
|
public class LoadableRelationAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the field containing the related resource's ID.
|
||||||
|
/// </summary>
|
||||||
public string RelationID { get; }
|
public string RelationID { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="LoadableRelationAttribute"/>.
|
||||||
|
/// </summary>
|
||||||
public LoadableRelationAttribute() {}
|
public LoadableRelationAttribute() {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="LoadableRelationAttribute"/> with a baking relationID field.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="relationID">The name of the RelationID field.</param>
|
||||||
public LoadableRelationAttribute(string relationID)
|
public LoadableRelationAttribute(string relationID)
|
||||||
{
|
{
|
||||||
RelationID = relationID;
|
RelationID = relationID;
|
||||||
|
@ -1,31 +1,59 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Kyoo.Common.Models.Attributes;
|
||||||
using Kyoo.Models.Attributes;
|
using Kyoo.Models.Attributes;
|
||||||
|
|
||||||
namespace Kyoo.Models
|
namespace Kyoo.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A class representing collections of <see cref="Show"/>.
|
||||||
|
/// A collection can also be stored in a <see cref="Library"/>.
|
||||||
|
/// </summary>
|
||||||
public class Collection : IResource
|
public class Collection : IResource
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string Slug { get; set; }
|
public string Slug { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of this collection.
|
||||||
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path of this poster.
|
||||||
|
/// By default, the http path for this poster is returned from the public API.
|
||||||
|
/// This can be disabled using the internal query flag.
|
||||||
|
/// </summary>
|
||||||
[SerializeAs("{HOST}/api/collection/{Slug}/poster")] public string Poster { get; set; }
|
[SerializeAs("{HOST}/api/collection/{Slug}/poster")] public string Poster { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The description of this collection.
|
||||||
|
/// </summary>
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
[LoadableRelation] public virtual ICollection<Show> Shows { get; set; }
|
|
||||||
[LoadableRelation] public virtual ICollection<Library> Libraries { get; set; }
|
/// <summary>
|
||||||
|
/// The list of shows contained in this collection.
|
||||||
|
/// </summary>
|
||||||
|
[LoadableRelation] public ICollection<Show> Shows { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of libraries that contains this collection.
|
||||||
|
/// </summary>
|
||||||
|
[LoadableRelation] public ICollection<Library> Libraries { get; set; }
|
||||||
|
|
||||||
#if ENABLE_INTERNAL_LINKS
|
#if ENABLE_INTERNAL_LINKS
|
||||||
[SerializeIgnore] public virtual ICollection<Link<Collection, Show>> ShowLinks { get; set; }
|
|
||||||
[SerializeIgnore] public virtual ICollection<Link<Library, Collection>> LibraryLinks { get; set; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public Collection() { }
|
/// <summary>
|
||||||
|
/// The internal link between this collection and shows in the <see cref="Shows"/> list.
|
||||||
public Collection(string slug, string name, string overview, string poster)
|
/// </summary>
|
||||||
{
|
[Link] public ICollection<Link<Collection, Show>> ShowLinks { get; set; }
|
||||||
Slug = slug;
|
|
||||||
Name = name;
|
/// <summary>
|
||||||
Overview = overview;
|
/// The internal link between this collection and libraries in the <see cref="Libraries"/> list.
|
||||||
Poster = poster;
|
/// </summary>
|
||||||
}
|
[Link] public ICollection<Link<Library, Collection>> LibraryLinks { get; set; }
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,126 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Kyoo.Controllers;
|
||||||
using Kyoo.Models.Attributes;
|
using Kyoo.Models.Attributes;
|
||||||
|
|
||||||
namespace Kyoo.Models
|
namespace Kyoo.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A class to represent a single show's episode.
|
||||||
|
/// This is also used internally for movies (their number is juste set to -1).
|
||||||
|
/// </summary>
|
||||||
public class Episode : IResource, IOnMerge
|
public class Episode : IResource, IOnMerge
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string Slug => GetSlug(ShowSlug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
public string Slug => GetSlug(ShowSlug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The slug of the Show that contain this episode. If this is not set, this episode is ill-formed.
|
||||||
|
/// </summary>
|
||||||
[SerializeIgnore] public string ShowSlug { private get; set; }
|
[SerializeIgnore] public string ShowSlug { private get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the Show containing this episode. This value is only set when the <see cref="Show"/> has been loaded.
|
||||||
|
/// </summary>
|
||||||
[SerializeIgnore] public int ShowID { get; set; }
|
[SerializeIgnore] public int ShowID { get; set; }
|
||||||
[LoadableRelation(nameof(ShowID))] public virtual Show Show { get; set; }
|
/// <summary>
|
||||||
|
/// The show that contains this episode. This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||||
|
/// </summary>
|
||||||
|
[LoadableRelation(nameof(ShowID))] public Show Show { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the Season containing this episode. This value is only set when the <see cref="Season"/> has been loaded.
|
||||||
|
/// </summary>
|
||||||
[SerializeIgnore] public int? SeasonID { get; set; }
|
[SerializeIgnore] public int? SeasonID { get; set; }
|
||||||
[LoadableRelation(nameof(SeasonID))] public virtual Season Season { get; set; }
|
/// <summary>
|
||||||
|
/// The season that contains this episode. This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||||
|
/// This can be null if the season is unknown and the episode is only identified by it's <see cref="AbsoluteNumber"/>.
|
||||||
|
/// </summary>
|
||||||
|
[LoadableRelation(nameof(SeasonID))] public Season Season { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The season in witch this episode is in. This defaults to -1 if not specified.
|
||||||
|
/// </summary>
|
||||||
public int SeasonNumber { get; set; } = -1;
|
public int SeasonNumber { get; set; } = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of this episode is it's season. This defaults to -1 if not specified.
|
||||||
|
/// </summary>
|
||||||
public int EpisodeNumber { get; set; } = -1;
|
public int EpisodeNumber { get; set; } = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The absolute number of this episode. It's an episode number that is not reset to 1 after a new season.
|
||||||
|
/// This defaults to -1 if not specified.
|
||||||
|
/// </summary>
|
||||||
public int AbsoluteNumber { get; set; } = -1;
|
public int AbsoluteNumber { get; set; } = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path of the video file for this episode. Any format supported by a <see cref="IFileManager"/> is allowed.
|
||||||
|
/// </summary>
|
||||||
[SerializeIgnore] public string Path { get; set; }
|
[SerializeIgnore] public string Path { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path of this episode's thumbnail.
|
||||||
|
/// By default, the http path for the thumbnail is returned from the public API.
|
||||||
|
/// This can be disabled using the internal query flag.
|
||||||
|
/// </summary>
|
||||||
[SerializeAs("{HOST}/api/episodes/{Slug}/thumb")] public string Thumb { get; set; }
|
[SerializeAs("{HOST}/api/episodes/{Slug}/thumb")] public string Thumb { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The title of this episode.
|
||||||
|
/// </summary>
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The overview of this episode.
|
||||||
|
/// </summary>
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The release date of this episode. It can be null if unknown.
|
||||||
|
/// </summary>
|
||||||
public DateTime? ReleaseDate { get; set; }
|
public DateTime? ReleaseDate { get; set; }
|
||||||
|
|
||||||
public int Runtime { get; set; } //This runtime variable should be in minutes
|
/// <summary>
|
||||||
|
/// The link to metadata providers that this episode has. See <see cref="MetadataID"/> for more information.
|
||||||
|
/// </summary>
|
||||||
|
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||||
|
|
||||||
[EditableRelation] [LoadableRelation] public virtual ICollection<MetadataID> ExternalIDs { get; set; }
|
/// <summary>
|
||||||
|
/// The list of tracks this episode has. This lists video, audio and subtitles available.
|
||||||
[EditableRelation] [LoadableRelation] public virtual ICollection<Track> Tracks { get; set; }
|
/// </summary>
|
||||||
|
[EditableRelation] [LoadableRelation] public ICollection<Track> Tracks { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public static string GetSlug(string showSlug, int seasonNumber, int episodeNumber, int absoluteNumber)
|
/// <summary>
|
||||||
|
/// Get the slug of an episode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="showSlug">The slug of the show. It can't be null.</param>
|
||||||
|
/// <param name="seasonNumber">
|
||||||
|
/// The season in which the episode is.
|
||||||
|
/// If this is a movie or if the episode should be referred by it's absolute number, set this to -1.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="episodeNumber">
|
||||||
|
/// The number of the episode in it's season.
|
||||||
|
/// If this is a movie or if the episode should be referred by it's absolute number, set this to -1.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="absoluteNumber">
|
||||||
|
/// The absolute number of this show.
|
||||||
|
/// If you don't know it or this is a movie, use -1
|
||||||
|
/// </param>
|
||||||
|
/// <returns>The slug corresponding to the given arguments</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">The given show slug was null.</exception>
|
||||||
|
public static string GetSlug([NotNull] string showSlug,
|
||||||
|
int seasonNumber = -1,
|
||||||
|
int episodeNumber = -1,
|
||||||
|
int absoluteNumber = -1)
|
||||||
{
|
{
|
||||||
if (showSlug == null)
|
if (showSlug == null)
|
||||||
throw new ArgumentException("Show's slug is null. Can't find episode's slug.");
|
throw new ArgumentNullException(nameof(showSlug));
|
||||||
return seasonNumber switch
|
return seasonNumber switch
|
||||||
{
|
{
|
||||||
-1 when absoluteNumber == -1 => showSlug,
|
-1 when absoluteNumber == -1 => showSlug,
|
||||||
@ -43,6 +129,7 @@ namespace Kyoo.Models
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void OnMerge(object merged)
|
public void OnMerge(object merged)
|
||||||
{
|
{
|
||||||
Episode other = (Episode)merged;
|
Episode other = (Episode)merged;
|
||||||
|
@ -1,40 +1,51 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Kyoo.Common.Models.Attributes;
|
||||||
using Kyoo.Models.Attributes;
|
using Kyoo.Models.Attributes;
|
||||||
|
|
||||||
namespace Kyoo.Models
|
namespace Kyoo.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A genre that allow one to specify categories for shows.
|
||||||
|
/// </summary>
|
||||||
public class Genre : IResource
|
public class Genre : IResource
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string Slug { get; set; }
|
public string Slug { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of this genre.
|
||||||
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
[LoadableRelation] public virtual ICollection<Show> Shows { get; set; }
|
/// <summary>
|
||||||
|
/// The list of shows that have this genre.
|
||||||
|
/// </summary>
|
||||||
|
[LoadableRelation] public ICollection<Show> Shows { get; set; }
|
||||||
|
|
||||||
#if ENABLE_INTERNAL_LINKS
|
#if ENABLE_INTERNAL_LINKS
|
||||||
[SerializeIgnore] public virtual ICollection<Link<Show, Genre>> ShowLinks { get; set; }
|
/// <summary>
|
||||||
|
/// The internal link between this genre and shows in the <see cref="Shows"/> list.
|
||||||
|
/// </summary>
|
||||||
|
[Link] public ICollection<Link<Show, Genre>> ShowLinks { get; set; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new, empty <see cref="Genre"/>.
|
||||||
|
/// </summary>
|
||||||
public Genre() {}
|
public Genre() {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="Genre"/> and specify it's <see cref="Name"/>.
|
||||||
|
/// The <see cref="Slug"/> is automatically calculated from it's name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of this genre.</param>
|
||||||
public Genre(string name)
|
public Genre(string name)
|
||||||
{
|
{
|
||||||
Slug = Utility.ToSlug(name);
|
Slug = Utility.ToSlug(name);
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Genre(string slug, string name)
|
|
||||||
{
|
|
||||||
Slug = slug;
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Genre(int id, string slug, string name)
|
|
||||||
{
|
|
||||||
ID = id;
|
|
||||||
Slug = slug;
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
using Kyoo.Controllers;
|
||||||
|
|
||||||
namespace Kyoo.Models
|
namespace Kyoo.Models
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -8,6 +10,10 @@ namespace Kyoo.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A unique ID for this type of resource. This can't be changed and duplicates are not allowed.
|
/// A unique ID for this type of resource. This can't be changed and duplicates are not allowed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// You don't need to specify an ID manually when creating a new resource,
|
||||||
|
/// this field is automatically assigned by the <see cref="IRepository{T}"/>.
|
||||||
|
/// </remarks>
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,24 +1,60 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Kyoo.Common.Models.Attributes;
|
||||||
using Kyoo.Models.Attributes;
|
using Kyoo.Models.Attributes;
|
||||||
|
|
||||||
namespace Kyoo.Models
|
namespace Kyoo.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A library containing <see cref="Show"/> and <see cref="Collection"/>.
|
||||||
|
/// </summary>
|
||||||
public class Library : IResource
|
public class Library : IResource
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string Slug { get; set; }
|
public string Slug { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of this library.
|
||||||
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of paths that this library is responsible for. This is mainly used by the Scan task.
|
||||||
|
/// </summary>
|
||||||
public string[] Paths { get; set; }
|
public string[] Paths { get; set; }
|
||||||
|
|
||||||
[EditableRelation] [LoadableRelation] public virtual ICollection<Provider> Providers { get; set; }
|
/// <summary>
|
||||||
|
/// The list of <see cref="Provider"/> used for items in this library.
|
||||||
|
/// </summary>
|
||||||
|
[EditableRelation] [LoadableRelation] public ICollection<Provider> Providers { get; set; }
|
||||||
|
|
||||||
[LoadableRelation] public virtual ICollection<Show> Shows { get; set; }
|
/// <summary>
|
||||||
[LoadableRelation] public virtual ICollection<Collection> Collections { get; set; }
|
/// The list of shows in this library.
|
||||||
|
/// </summary>
|
||||||
|
[LoadableRelation] public ICollection<Show> Shows { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of collections in this library.
|
||||||
|
/// </summary>
|
||||||
|
[LoadableRelation] public ICollection<Collection> Collections { get; set; }
|
||||||
|
|
||||||
#if ENABLE_INTERNAL_LINKS
|
#if ENABLE_INTERNAL_LINKS
|
||||||
[SerializeIgnore] public virtual ICollection<Link<Library, Provider>> ProviderLinks { get; set; }
|
/// <summary>
|
||||||
[SerializeIgnore] public virtual ICollection<Link<Library, Show>> ShowLinks { get; set; }
|
/// The internal link between this library and provider in the <see cref="Providers"/> list.
|
||||||
[SerializeIgnore] public virtual ICollection<Link<Library, Collection>> CollectionLinks { get; set; }
|
/// </summary>
|
||||||
|
[Link] public ICollection<Link<Library, Provider>> ProviderLinks { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The internal link between this library and shows in the <see cref="Shows"/> list.
|
||||||
|
/// </summary>
|
||||||
|
[Link] public ICollection<Link<Library, Show>> ShowLinks { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The internal link between this library and collection in the <see cref="Collections"/> list.
|
||||||
|
/// </summary>
|
||||||
|
[Link] public ICollection<Link<Library, Collection>> CollectionLinks { get; set; }
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,37 @@ using Kyoo.Models.Attributes;
|
|||||||
|
|
||||||
namespace Kyoo.Models
|
namespace Kyoo.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An actor, voice actor, writer, animator, somebody who worked on a <see cref="Show"/>.
|
||||||
|
/// </summary>
|
||||||
public class People : IResource
|
public class People : IResource
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
public string Slug { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
[SerializeAs("{HOST}/api/people/{Slug}/poster")] public string Poster { get; set; }
|
|
||||||
[EditableRelation] [LoadableRelation] public virtual ICollection<MetadataID> ExternalIDs { get; set; }
|
|
||||||
|
|
||||||
[EditableRelation] [LoadableRelation] public virtual ICollection<PeopleRole> Roles { get; set; }
|
/// <inheritdoc />
|
||||||
|
public string Slug { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of this person.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path of this poster.
|
||||||
|
/// By default, the http path for this poster is returned from the public API.
|
||||||
|
/// This can be disabled using the internal query flag.
|
||||||
|
/// </summary>
|
||||||
|
[SerializeAs("{HOST}/api/people/{Slug}/poster")] public string Poster { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The link to metadata providers that this person has. See <see cref="MetadataID"/> for more information.
|
||||||
|
/// </summary>
|
||||||
|
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of roles this person has played in. See <see cref="PeopleRole"/> for more information.
|
||||||
|
/// </summary>
|
||||||
|
[EditableRelation] [LoadableRelation] public ICollection<PeopleRole> Roles { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,72 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Kyoo.Common.Models.Attributes;
|
||||||
|
using Kyoo.Controllers;
|
||||||
using Kyoo.Models.Attributes;
|
using Kyoo.Models.Attributes;
|
||||||
|
|
||||||
namespace Kyoo.Models
|
namespace Kyoo.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class contains metadata about <see cref="IMetadataProvider"/>.
|
||||||
|
/// You can have providers even if you don't have the corresponding <see cref="IMetadataProvider"/>.
|
||||||
|
/// </summary>
|
||||||
public class Provider : IResource
|
public class Provider : IResource
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string Slug { get; set; }
|
public string Slug { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of this provider.
|
||||||
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path of this provider's logo.
|
||||||
|
/// By default, the http path for this logo is returned from the public API.
|
||||||
|
/// This can be disabled using the internal query flag.
|
||||||
|
/// </summary>
|
||||||
[SerializeAs("{HOST}/api/providers/{Slug}/logo")] public string Logo { get; set; }
|
[SerializeAs("{HOST}/api/providers/{Slug}/logo")] public string Logo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The extension of the logo. This is used for http responses.
|
||||||
|
/// </summary>
|
||||||
[SerializeIgnore] public string LogoExtension { get; set; }
|
[SerializeIgnore] public string LogoExtension { get; set; }
|
||||||
[LoadableRelation] public virtual ICollection<Library> Libraries { get; set; }
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of libraries that uses this provider.
|
||||||
|
/// </summary>
|
||||||
|
[LoadableRelation] public ICollection<Library> Libraries { get; set; }
|
||||||
|
|
||||||
#if ENABLE_INTERNAL_LINKS
|
#if ENABLE_INTERNAL_LINKS
|
||||||
[SerializeIgnore] public virtual ICollection<Link<Library, Provider>> LibraryLinks { get; set; }
|
/// <summary>
|
||||||
[SerializeIgnore] public virtual ICollection<MetadataID> MetadataLinks { get; set; }
|
/// The internal link between this provider and libraries in the <see cref="Libraries"/> list.
|
||||||
|
/// </summary>
|
||||||
|
[Link] public ICollection<Link<Library, Provider>> LibraryLinks { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The internal link between this provider and related <see cref="MetadataID"/>.
|
||||||
|
/// </summary>
|
||||||
|
[Link] public ICollection<MetadataID> MetadataLinks { get; set; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new, default, <see cref="Provider"/>
|
||||||
|
/// </summary>
|
||||||
public Provider() { }
|
public Provider() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="Provider"/> and specify it's <see cref="Name"/>.
|
||||||
|
/// The <see cref="Slug"/> is automatically calculated from it's name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of this provider.</param>
|
||||||
|
/// <param name="logo">The logo of this provider.</param>
|
||||||
public Provider(string name, string logo)
|
public Provider(string name, string logo)
|
||||||
{
|
{
|
||||||
Slug = Utility.ToSlug(name);
|
Slug = Utility.ToSlug(name);
|
||||||
Name = name;
|
Name = name;
|
||||||
Logo = logo;
|
Logo = logo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Provider(int id, string name, string logo)
|
|
||||||
{
|
|
||||||
ID = id;
|
|
||||||
Slug = Utility.ToSlug(name);
|
|
||||||
Name = name;
|
|
||||||
Logo = logo;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,25 +1,75 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Kyoo.Controllers;
|
||||||
using Kyoo.Models.Attributes;
|
using Kyoo.Models.Attributes;
|
||||||
|
|
||||||
namespace Kyoo.Models
|
namespace Kyoo.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A season of a <see cref="Show"/>.
|
||||||
|
/// </summary>
|
||||||
public class Season : IResource
|
public class Season : IResource
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string Slug => $"{ShowSlug}-s{SeasonNumber}";
|
public string Slug => $"{ShowSlug}-s{SeasonNumber}";
|
||||||
[SerializeIgnore] public int ShowID { get; set; }
|
|
||||||
|
/// <summary>
|
||||||
|
/// The slug of the Show that contain this episode. If this is not set, this season is ill-formed.
|
||||||
|
/// </summary>
|
||||||
[SerializeIgnore] public string ShowSlug { private get; set; }
|
[SerializeIgnore] public string ShowSlug { private get; set; }
|
||||||
[LoadableRelation(nameof(ShowID))] public virtual Show Show { get; set; }
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the Show containing this season. This value is only set when the <see cref="Show"/> has been loaded.
|
||||||
|
/// </summary>
|
||||||
|
[SerializeIgnore] public int ShowID { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The show that contains this season. This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||||
|
/// </summary>
|
||||||
|
[LoadableRelation(nameof(ShowID))] public Show Show { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of this season. This can be set to 0 to indicate specials. This defaults to -1 for unset.
|
||||||
|
/// </summary>
|
||||||
public int SeasonNumber { get; set; } = -1;
|
public int SeasonNumber { get; set; } = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The title of this season.
|
||||||
|
/// </summary>
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A quick overview of this season.
|
||||||
|
/// </summary>
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
public int? Year { get; set; }
|
|
||||||
|
/// <summary>
|
||||||
|
/// The starting air date of this season.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? StartDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ending date of this season.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? EndDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path of this poster.
|
||||||
|
/// By default, the http path for this poster is returned from the public API.
|
||||||
|
/// This can be disabled using the internal query flag.
|
||||||
|
/// </summary>
|
||||||
[SerializeAs("{HOST}/api/seasons/{Slug}/thumb")] public string Poster { get; set; }
|
[SerializeAs("{HOST}/api/seasons/{Slug}/thumb")] public string Poster { get; set; }
|
||||||
[EditableRelation] [LoadableRelation] public virtual ICollection<MetadataID> ExternalIDs { get; set; }
|
|
||||||
|
/// <summary>
|
||||||
|
/// The link to metadata providers that this episode has. See <see cref="MetadataID"/> for more information.
|
||||||
|
/// </summary>
|
||||||
|
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||||
|
|
||||||
[LoadableRelation] public virtual ICollection<Episode> Episodes { get; set; }
|
/// <summary>
|
||||||
|
/// The list of episodes that this season contains.
|
||||||
|
/// </summary>
|
||||||
|
[LoadableRelation] public ICollection<Episode> Episodes { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Kyoo.Common.Models.Attributes;
|
||||||
|
|
||||||
namespace Kyoo.Models
|
namespace Kyoo.Models
|
||||||
{
|
{
|
||||||
@ -52,7 +53,7 @@ namespace Kyoo.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Links between Users and Shows.
|
/// Links between Users and Shows.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ICollection<Link<User, Show>> ShowLinks { get; set; }
|
[Link] public ICollection<Link<User, Show>> ShowLinks { get; set; }
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
69
Kyoo.Common/Utility/TaskUtils.cs
Normal file
69
Kyoo.Common/Utility/TaskUtils.cs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace Kyoo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A class containing helper method for tasks.
|
||||||
|
/// </summary>
|
||||||
|
public static class TaskUtils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Run a method after the execution of the task.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="task">The task to wait.</param>
|
||||||
|
/// <param name="then">
|
||||||
|
/// The method to run after the task finish. This will only be run if the task finished successfully.
|
||||||
|
/// </param>
|
||||||
|
/// <typeparam name="T">The type of the item in the task.</typeparam>
|
||||||
|
/// <returns>A continuation task wrapping the initial task and adding a continuation method.</returns>
|
||||||
|
/// <exception cref="TaskCanceledException"></exception>
|
||||||
|
/// <exception cref="TaskCanceledException">The source task has been canceled.</exception>
|
||||||
|
public static Task<T> Then<T>(this Task<T> task, Action<T> then)
|
||||||
|
{
|
||||||
|
return task.ContinueWith(x =>
|
||||||
|
{
|
||||||
|
if (x.IsFaulted)
|
||||||
|
x.Exception!.InnerException!.ReThrow();
|
||||||
|
if (x.IsCanceled)
|
||||||
|
throw new TaskCanceledException();
|
||||||
|
then(x.Result);
|
||||||
|
return x.Result;
|
||||||
|
}, TaskContinuationOptions.ExecuteSynchronously);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Map the result of a task to another result.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="task">The task to map.</param>
|
||||||
|
/// <param name="map">The mapper method, it take the task's result as a parameter and should return the new result.</param>
|
||||||
|
/// <typeparam name="T">The type of returns of the given task</typeparam>
|
||||||
|
/// <typeparam name="TResult">The resulting task after the mapping method</typeparam>
|
||||||
|
/// <returns>A task wrapping the initial task and mapping the initial result.</returns>
|
||||||
|
/// <exception cref="TaskCanceledException">The source task has been canceled.</exception>
|
||||||
|
public static Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> map)
|
||||||
|
{
|
||||||
|
return task.ContinueWith(x =>
|
||||||
|
{
|
||||||
|
if (x.IsFaulted)
|
||||||
|
x.Exception!.InnerException!.ReThrow();
|
||||||
|
if (x.IsCanceled)
|
||||||
|
throw new TaskCanceledException();
|
||||||
|
return map(x.Result);
|
||||||
|
}, TaskContinuationOptions.ExecuteSynchronously);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A method to return the a default value from a task if the initial task is null.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The initial task</param>
|
||||||
|
/// <typeparam name="T">The type that the task will return</typeparam>
|
||||||
|
/// <returns>A non-null task.</returns>
|
||||||
|
[NotNull]
|
||||||
|
public static Task<T> DefaultIfNull<T>([CanBeNull] Task<T> value)
|
||||||
|
{
|
||||||
|
return value ?? Task.FromResult<T>(default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -270,43 +269,6 @@ namespace Kyoo
|
|||||||
throw new ArgumentNullException(nameof(ex));
|
throw new ArgumentNullException(nameof(ex));
|
||||||
ExceptionDispatchInfo.Capture(ex).Throw();
|
ExceptionDispatchInfo.Capture(ex).Throw();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<T> Then<T>(this Task<T> task, Action<T> map)
|
|
||||||
{
|
|
||||||
return task.ContinueWith(x =>
|
|
||||||
{
|
|
||||||
if (x.IsFaulted)
|
|
||||||
x.Exception!.InnerException!.ReThrow();
|
|
||||||
if (x.IsCanceled)
|
|
||||||
throw new TaskCanceledException();
|
|
||||||
map(x.Result);
|
|
||||||
return x.Result;
|
|
||||||
}, TaskContinuationOptions.ExecuteSynchronously);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> map)
|
|
||||||
{
|
|
||||||
return task.ContinueWith(x =>
|
|
||||||
{
|
|
||||||
if (x.IsFaulted)
|
|
||||||
x.Exception!.InnerException!.ReThrow();
|
|
||||||
if (x.IsCanceled)
|
|
||||||
throw new TaskCanceledException();
|
|
||||||
return map(x.Result);
|
|
||||||
}, TaskContinuationOptions.ExecuteSynchronously);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task<T> Cast<T>(this Task task)
|
|
||||||
{
|
|
||||||
return task.ContinueWith(x =>
|
|
||||||
{
|
|
||||||
if (x.IsFaulted)
|
|
||||||
x.Exception!.InnerException!.ReThrow();
|
|
||||||
if (x.IsCanceled)
|
|
||||||
throw new TaskCanceledException();
|
|
||||||
return (T)((dynamic)x).Result;
|
|
||||||
}, TaskContinuationOptions.ExecuteSynchronously);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a friendly type name (supporting generics)
|
/// Get a friendly type name (supporting generics)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Controllers;
|
using Kyoo.Controllers;
|
||||||
using Kyoo.Models;
|
using Kyoo.Models;
|
||||||
@ -15,20 +16,61 @@ namespace Kyoo.Tests.SpecificTests
|
|||||||
{
|
{
|
||||||
_repository = Repositories.LibraryManager.ShowRepository;
|
_repository = Repositories.LibraryManager.ShowRepository;
|
||||||
}
|
}
|
||||||
//
|
|
||||||
|
[Fact]
|
||||||
|
public async Task EditTest()
|
||||||
|
{
|
||||||
|
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||||
|
value.Path = "/super";
|
||||||
|
value.Title = "New Title";
|
||||||
|
Show edited = await _repository.Edit(value, false);
|
||||||
|
KAssert.DeepEqual(value, edited);
|
||||||
|
|
||||||
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
|
Show show = await database.Shows.FirstAsync();
|
||||||
|
|
||||||
|
KAssert.DeepEqual(show, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task EditGenreTest()
|
||||||
|
{
|
||||||
|
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||||
|
value.Genres = new[] {new Genre("test")};
|
||||||
|
Show edited = await _repository.Edit(value, false);
|
||||||
|
|
||||||
|
Assert.Equal(value.Slug, edited.Slug);
|
||||||
|
Assert.Equal(value.Genres.Select(x => new{x.Slug, x.Name}), edited.Genres.Select(x => new{x.Slug, x.Name}));
|
||||||
|
|
||||||
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
|
Show show = await database.Shows
|
||||||
|
.Include(x => x.Genres)
|
||||||
|
.FirstAsync();
|
||||||
|
|
||||||
|
Assert.Equal(value.Slug, show.Slug);
|
||||||
|
Assert.Equal(value.Genres.Select(x => new{x.Slug, x.Name}), show.Genres.Select(x => new{x.Slug, x.Name}));
|
||||||
|
}
|
||||||
|
|
||||||
// [Fact]
|
// [Fact]
|
||||||
// public async Task EditTest()
|
// public async Task EditPeopleTest()
|
||||||
// {
|
// {
|
||||||
// Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
// Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||||
// value.Path = "/super";
|
// value.People = new[] {new People
|
||||||
// value.Title = "New Title";
|
// {
|
||||||
|
// Name = "test"
|
||||||
|
// }};
|
||||||
// Show edited = await _repository.Edit(value, false);
|
// Show edited = await _repository.Edit(value, false);
|
||||||
// KAssert.DeepEqual(value, edited);
|
//
|
||||||
|
// Assert.Equal(value.Slug, edited.Slug);
|
||||||
|
// Assert.Equal(value.Genres.Select(x => new{x.Slug, x.Name}), edited.Genres.Select(x => new{x.Slug, x.Name}));
|
||||||
//
|
//
|
||||||
// await using DatabaseContext database = Repositories.Context.New();
|
// await using DatabaseContext database = Repositories.Context.New();
|
||||||
// Show show = await database.Shows.FirstAsync();
|
// Show show = await database.Shows
|
||||||
|
// .Include(x => x.Genres)
|
||||||
|
// .FirstAsync();
|
||||||
//
|
//
|
||||||
// KAssert.DeepEqual(show, value);
|
// Assert.Equal(value.Slug, show.Slug);
|
||||||
|
// Assert.Equal(value.Genres.Select(x => new{x.Slug, x.Name}), show.Genres.Select(x => new{x.Slug, x.Name}));
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
76
Kyoo.Tests/Utility/TaskTests.cs
Normal file
76
Kyoo.Tests/Utility/TaskTests.cs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Kyoo.Tests
|
||||||
|
{
|
||||||
|
public class TaskTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task DefaultIfNullTest()
|
||||||
|
{
|
||||||
|
Assert.Equal(0, await TaskUtils.DefaultIfNull<int>(null));
|
||||||
|
Assert.Equal(1, await TaskUtils.DefaultIfNull(Task.FromResult(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ThenTest()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<ArgumentException>(() => Task.FromResult(1)
|
||||||
|
.Then(_ => throw new ArgumentException()));
|
||||||
|
Assert.Equal(1, await Task.FromResult(1)
|
||||||
|
.Then(_ => {}));
|
||||||
|
|
||||||
|
static async Task<int> Faulted()
|
||||||
|
{
|
||||||
|
await Task.Delay(1);
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
await Assert.ThrowsAsync<ArgumentException>(() => Faulted().Then(_ => KAssert.Fail()));
|
||||||
|
|
||||||
|
static async Task<int> Infinite()
|
||||||
|
{
|
||||||
|
await Task.Delay(100000);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CancellationTokenSource token = new();
|
||||||
|
token.Cancel();
|
||||||
|
await Assert.ThrowsAsync<TaskCanceledException>(() => Task.Run(Infinite, token.Token)
|
||||||
|
.Then(_ => {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MapTest()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<ArgumentException>(() => Task.FromResult(1)
|
||||||
|
.Map<int, int>(_ => throw new ArgumentException()));
|
||||||
|
Assert.Equal(2, await Task.FromResult(1)
|
||||||
|
.Map(x => x + 1));
|
||||||
|
|
||||||
|
static async Task<int> Faulted()
|
||||||
|
{
|
||||||
|
await Task.Delay(1);
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
await Assert.ThrowsAsync<ArgumentException>(() => Faulted()
|
||||||
|
.Map(x =>
|
||||||
|
{
|
||||||
|
KAssert.Fail();
|
||||||
|
return x;
|
||||||
|
}));
|
||||||
|
|
||||||
|
static async Task<int> Infinite()
|
||||||
|
{
|
||||||
|
await Task.Delay(100000);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CancellationTokenSource token = new();
|
||||||
|
token.Cancel();
|
||||||
|
await Assert.ThrowsAsync<TaskCanceledException>(() => Task.Run(Infinite, token.Token)
|
||||||
|
.Map(x => x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -103,9 +103,9 @@ namespace Kyoo.Controllers
|
|||||||
await base.Validate(resource);
|
await base.Validate(resource);
|
||||||
if (resource.Studio != null)
|
if (resource.Studio != null)
|
||||||
resource.Studio = await _studios.CreateIfNotExists(resource.Studio);
|
resource.Studio = await _studios.CreateIfNotExists(resource.Studio);
|
||||||
resource.Genres = await resource.Genres
|
resource.Genres = await TaskUtils.DefaultIfNull(resource.Genres
|
||||||
.SelectAsync(x => _genres.CreateIfNotExists(x))
|
?.SelectAsync(x => _genres.CreateIfNotExists(x))
|
||||||
.ToListAsync();
|
.ToListAsync());
|
||||||
resource.GenreLinks = resource.Genres?
|
resource.GenreLinks = resource.Genres?
|
||||||
.Select(x => Link.UCreate(resource, x))
|
.Select(x => Link.UCreate(resource, x))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user