mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Adding more documentation
This commit is contained in:
parent
2bc559424c
commit
c7569691e2
@ -8,7 +8,8 @@ namespace Kyoo.Models.Attributes
|
||||
/// An attribute to inform that the service will be injected automatically by a service provider.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It should only be used on <see cref="ITask"/> and will be injected before calling <see cref="ITask.Run"/>
|
||||
/// It should only be used on <see cref="ITask"/> and will be injected before calling <see cref="ITask.Run"/>.
|
||||
/// It can also be used on <see cref="IPlugin"/> and it will be injected before calling <see cref="IPlugin.ConfigureAspNet"/>.
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
[MeansImplicitUse(ImplicitUseKindFlags.Assign)]
|
||||
|
@ -2,17 +2,41 @@ using System;
|
||||
|
||||
namespace Kyoo.Models.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Remove an property from the serialization pipeline. It will simply be skipped.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public class SerializeIgnoreAttribute : Attribute {}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a property from the deserialization pipeline. The user can't input value for this property.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public class DeserializeIgnoreAttribute : Attribute {}
|
||||
|
||||
/// <summary>
|
||||
/// Change the way the field is serialized. It allow one to use a string format like formatting instead of the default value.
|
||||
/// This can be disabled for a request by setting the "internal" query string parameter to true.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public class SerializeAsAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The format string to use.
|
||||
/// </summary>
|
||||
public string Format { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="SerializeAsAttribute"/> with the selected format.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The format string can contains any property within {}. It will be replaced by the actual value of the property.
|
||||
/// You can also use the special value {HOST} that will put the webhost address.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// The show's poster serialized uses this format string: <code>{HOST}/api/shows/{Slug}/poster</code>
|
||||
/// </example>
|
||||
/// <param name="format">The format to use</param>
|
||||
public SerializeAsAttribute(string format)
|
||||
{
|
||||
Format = format;
|
||||
|
@ -5,6 +5,9 @@ using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of item, ether a show, a movie or a collection.
|
||||
/// </summary>
|
||||
public enum ItemType
|
||||
{
|
||||
Show,
|
||||
@ -12,16 +15,50 @@ namespace Kyoo.Models
|
||||
Collection
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A type union between <see cref="Show"/> and <see cref="Collection"/>.
|
||||
/// This is used to list content put inside a library.
|
||||
/// </summary>
|
||||
public class LibraryItem : IResource
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The title of the show or collection.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The summary of the show or collection.
|
||||
/// </summary>
|
||||
public string Overview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this show airing, not aired yet or finished? This is only applicable for shows.
|
||||
/// </summary>
|
||||
public Status? Status { get; set; }
|
||||
public string TrailerUrl { get; set; }
|
||||
public int? StartYear { get; set; }
|
||||
public int? EndYear { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date this show or collection started airing. It can be null if this is unknown.
|
||||
/// </summary>
|
||||
public DateTime? StartAir { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date this show or collection finished airing.
|
||||
/// It must be after the <see cref="StartAir"/> but can be the same (example: for movies).
|
||||
/// It can also be null if this is unknown.
|
||||
/// </summary>
|
||||
public DateTime? EndAir { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of this item's 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/{_type}/{Slug}/poster")] public string Poster { get; set; }
|
||||
[UsedImplicitly] private string _type => Type == ItemType.Collection ? "collection" : "show";
|
||||
public ItemType Type { get; set; }
|
||||
@ -35,9 +72,8 @@ namespace Kyoo.Models
|
||||
Title = show.Title;
|
||||
Overview = show.Overview;
|
||||
Status = show.Status;
|
||||
TrailerUrl = show.TrailerUrl;
|
||||
StartYear = show.StartYear;
|
||||
EndYear = show.EndYear;
|
||||
StartAir = show.StartAir;
|
||||
EndAir = show.EndAir;
|
||||
Poster = show.Poster;
|
||||
Type = show.IsMovie ? ItemType.Movie : ItemType.Show;
|
||||
}
|
||||
@ -49,9 +85,8 @@ namespace Kyoo.Models
|
||||
Title = collection.Name;
|
||||
Overview = collection.Overview;
|
||||
Status = Models.Status.Unknown;
|
||||
TrailerUrl = null;
|
||||
StartYear = null;
|
||||
EndYear = null;
|
||||
StartAir = null;
|
||||
EndAir = null;
|
||||
Poster = collection.Poster;
|
||||
Type = ItemType.Collection;
|
||||
}
|
||||
@ -63,9 +98,8 @@ namespace Kyoo.Models
|
||||
Title = x.Title,
|
||||
Overview = x.Overview,
|
||||
Status = x.Status,
|
||||
TrailerUrl = x.TrailerUrl,
|
||||
StartYear = x.StartYear,
|
||||
EndYear = x.EndYear,
|
||||
StartAir = x.StartAir,
|
||||
EndAir = x.EndAir,
|
||||
Poster= x.Poster,
|
||||
Type = x.IsMovie ? ItemType.Movie : ItemType.Show
|
||||
};
|
||||
@ -77,10 +111,9 @@ namespace Kyoo.Models
|
||||
Title = x.Name,
|
||||
Overview = x.Overview,
|
||||
Status = Models.Status.Unknown,
|
||||
TrailerUrl = null,
|
||||
StartYear = null,
|
||||
EndYear = null,
|
||||
Poster= x.Poster,
|
||||
StartAir = null,
|
||||
EndAir = null,
|
||||
Poster = x.Poster,
|
||||
Type = ItemType.Collection
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Kyoo.Models
|
||||
@ -55,13 +54,12 @@ namespace Kyoo.Models
|
||||
where T1 : class, IResource
|
||||
where T2 : class, IResource
|
||||
{
|
||||
public virtual T1 First { get; set; }
|
||||
public virtual T2 Second { get; set; }
|
||||
public T1 First { get; set; }
|
||||
public T2 Second { get; set; }
|
||||
|
||||
|
||||
public Link() {}
|
||||
|
||||
[SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")]
|
||||
public Link(T1 first, T2 second, bool privateItems = false)
|
||||
: base(first, second)
|
||||
{
|
||||
|
@ -6,19 +6,19 @@ namespace Kyoo.Models
|
||||
{
|
||||
[SerializeIgnore] public int ID { get; set; }
|
||||
[SerializeIgnore] public int ProviderID { get; set; }
|
||||
public virtual Provider Provider {get; set; }
|
||||
public Provider Provider {get; set; }
|
||||
|
||||
[SerializeIgnore] public int? ShowID { get; set; }
|
||||
[SerializeIgnore] public virtual Show Show { get; set; }
|
||||
[SerializeIgnore] public Show Show { get; set; }
|
||||
|
||||
[SerializeIgnore] public int? EpisodeID { get; set; }
|
||||
[SerializeIgnore] public virtual Episode Episode { get; set; }
|
||||
[SerializeIgnore] public Episode Episode { get; set; }
|
||||
|
||||
[SerializeIgnore] public int? SeasonID { get; set; }
|
||||
[SerializeIgnore] public virtual Season Season { get; set; }
|
||||
[SerializeIgnore] public Season Season { get; set; }
|
||||
|
||||
[SerializeIgnore] public int? PeopleID { get; set; }
|
||||
[SerializeIgnore] public virtual People People { get; set; }
|
||||
[SerializeIgnore] public People People { get; set; }
|
||||
|
||||
public string DataID { get; set; }
|
||||
public string Link { get; set; }
|
||||
|
@ -1,53 +1,168 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Kyoo.Common.Models.Attributes;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A series or a movie.
|
||||
/// </summary>
|
||||
public class Show : IResource, IOnMerge
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The title of this show.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of alternative titles of this show.
|
||||
/// </summary>
|
||||
[EditableRelation] public string[] Aliases { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of the root directory of this show.
|
||||
/// This can be any kind of path supported by <see cref="IFileManager"/>
|
||||
/// </summary>
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The summary of this show.
|
||||
/// </summary>
|
||||
public string Overview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this show airing, not aired yet or finished?
|
||||
/// </summary>
|
||||
public Status? Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An URL to a trailer. This could be any path supported by the <see cref="IFileManager"/>.
|
||||
/// </summary>
|
||||
/// TODO for now, this is set to a youtube url. It should be cached and converted to a local file.
|
||||
public string TrailerUrl { get; set; }
|
||||
|
||||
public int? StartYear { get; set; }
|
||||
public int? EndYear { get; set; }
|
||||
/// <summary>
|
||||
/// The date this show started airing. It can be null if this is unknown.
|
||||
/// </summary>
|
||||
public DateTime? StartAir { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date this show finished airing.
|
||||
/// It must be after the <see cref="StartAir"/> but can be the same (example: for movies).
|
||||
/// It can also be null if this is unknown.
|
||||
/// </summary>
|
||||
public DateTime? EndAir { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of this show's 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/shows/{Slug}/poster")] public string Poster { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of this show'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/shows/{Slug}/logo")] public string Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of this show's backdrop.
|
||||
/// By default, the http path for this backdrop is returned from the public API.
|
||||
/// This can be disabled using the internal query flag.
|
||||
/// </summary>
|
||||
[SerializeAs("{HOST}/api/shows/{Slug}/backdrop")] public string Backdrop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if this show represent a movie, false otherwise.
|
||||
/// </summary>
|
||||
public bool IsMovie { get; set; }
|
||||
|
||||
[EditableRelation] [LoadableRelation] public virtual ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The link to metadata providers that this show has. See <see cref="MetadataID"/> for more information.
|
||||
/// </summary>
|
||||
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the Studio that made this show. This value is only set when the <see cref="Studio"/> has been loaded.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int? StudioID { get; set; }
|
||||
[LoadableRelation(nameof(StudioID))] [EditableRelation] public virtual Studio Studio { get; set; }
|
||||
[LoadableRelation] [EditableRelation] public virtual ICollection<Genre> Genres { get; set; }
|
||||
[LoadableRelation] [EditableRelation] public virtual ICollection<PeopleRole> People { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Season> Seasons { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Episode> Episodes { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Library> Libraries { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Collection> Collections { get; set; }
|
||||
/// <summary>
|
||||
/// The Studio that made this show. This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||
/// </summary>
|
||||
[LoadableRelation(nameof(StudioID))] [EditableRelation] public Studio Studio { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of genres (themes) this show has.
|
||||
/// </summary>
|
||||
[LoadableRelation] [EditableRelation] public ICollection<Genre> Genres { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of people that made this show.
|
||||
/// </summary>
|
||||
[LoadableRelation] [EditableRelation] public ICollection<PeopleRole> People { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The different seasons in this show. If this is a movie, this list is always null or empty.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Season> Seasons { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of episodes in this show. If this is a movie, there will be a unique episode (with the seasonNumber and episodeNumber set to -1).
|
||||
/// Having an episode is necessary to store metadata and tracks.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Episode> Episodes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of libraries that contains this show.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Library> Libraries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of collections that contains this show.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Collection> Collections { get; set; }
|
||||
|
||||
#if ENABLE_INTERNAL_LINKS
|
||||
[SerializeIgnore] public virtual ICollection<Link<Library, Show>> LibraryLinks { get; set; }
|
||||
[SerializeIgnore] public virtual ICollection<Link<Collection, Show>> CollectionLinks { get; set; }
|
||||
[SerializeIgnore] public virtual ICollection<Link<Show, Genre>> GenreLinks { get; set; }
|
||||
/// <summary>
|
||||
/// The internal link between this show and libraries in the <see cref="Libraries"/> list.
|
||||
/// </summary>
|
||||
[Link] public ICollection<Link<Library, Show>> LibraryLinks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The internal link between this show and collections in the <see cref="Collections"/> list.
|
||||
/// </summary>
|
||||
[Link] public ICollection<Link<Collection, Show>> CollectionLinks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The internal link between this show and genres in the <see cref="Genres"/> list.
|
||||
/// </summary>
|
||||
[Link] public ICollection<Link<Show, Genre>> GenreLinks { get; set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the internal provider's ID of a show using it's provider slug.
|
||||
/// </summary>
|
||||
/// <remarks>This method will never return anything if the <see cref="ExternalIDs"/> are not loaded.</remarks>
|
||||
/// <param name="provider">The slug of the provider</param>
|
||||
/// <returns>The <see cref="MetadataID.DataID"/> field of the asked provider.</returns>
|
||||
public string GetID(string provider)
|
||||
{
|
||||
return ExternalIDs?.FirstOrDefault(x => x.Provider.Name == provider)?.DataID;
|
||||
return ExternalIDs?.FirstOrDefault(x => x.Provider.Slug == provider)?.DataID;
|
||||
}
|
||||
|
||||
public virtual void OnMerge(object merged)
|
||||
/// <inheritdoc />
|
||||
public void OnMerge(object merged)
|
||||
{
|
||||
if (ExternalIDs != null)
|
||||
foreach (MetadataID id in ExternalIDs)
|
||||
@ -64,5 +179,8 @@ namespace Kyoo.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The enum containing show's status.
|
||||
/// </summary>
|
||||
public enum Status { Finished, Airing, Planned, Unknown }
|
||||
}
|
||||
|
@ -3,31 +3,40 @@ using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A studio that make shows.
|
||||
/// </summary>
|
||||
public class Studio : IResource
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of this studio.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
[LoadableRelation] public virtual ICollection<Show> Shows { get; set; }
|
||||
/// <summary>
|
||||
/// The list of shows that are made by this studio.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Show> Shows { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, empty, <see cref="Studio"/>.
|
||||
/// </summary>
|
||||
public Studio() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Studio"/> with a specific name, the slug is calculated automatically.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the studio.</param>
|
||||
public Studio(string name)
|
||||
{
|
||||
Slug = Utility.ToSlug(name);
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public Studio(string slug, string name)
|
||||
{
|
||||
Slug = slug;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public static Studio Default()
|
||||
{
|
||||
return new Studio("unknown", "Unknown Studio");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
using Kyoo.Models.Watch;
|
||||
using System.Globalization;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of available stream types.
|
||||
/// Attachments are only used temporarily by the transcoder but are not stored in a database.
|
||||
/// </summary>
|
||||
public enum StreamType
|
||||
{
|
||||
Unknown = 0,
|
||||
@ -15,82 +17,15 @@ namespace Kyoo.Models
|
||||
Attachment = 4
|
||||
}
|
||||
|
||||
namespace Watch
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
public class Stream
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Language { get; set; }
|
||||
public string Codec { get; set; }
|
||||
[MarshalAs(UnmanagedType.I1)] public bool isDefault;
|
||||
[MarshalAs(UnmanagedType.I1)] public bool isForced;
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
[SerializeIgnore] public StreamType Type { get; set; }
|
||||
|
||||
public Stream() {}
|
||||
|
||||
public Stream(string title, string language, string codec, bool isDefault, bool isForced, string path, StreamType type)
|
||||
{
|
||||
Title = title;
|
||||
Language = language;
|
||||
Codec = codec;
|
||||
this.isDefault = isDefault;
|
||||
this.isForced = isForced;
|
||||
Path = path;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public Stream(Stream stream)
|
||||
{
|
||||
Title = stream.Title;
|
||||
Language = stream.Language;
|
||||
isDefault = stream.isDefault;
|
||||
isForced = stream.isForced;
|
||||
Codec = stream.Codec;
|
||||
Path = stream.Path;
|
||||
Type = stream.Type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Track : Stream, IResource
|
||||
/// <summary>
|
||||
/// A video, audio or subtitle track for an episode.
|
||||
/// </summary>
|
||||
public class Track : IResource
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
[SerializeIgnore] public int EpisodeID { get; set; }
|
||||
public int TrackIndex { get; set; }
|
||||
public bool IsDefault
|
||||
{
|
||||
get => isDefault;
|
||||
set => isDefault = value;
|
||||
}
|
||||
public bool IsForced
|
||||
{
|
||||
get => isForced;
|
||||
set => isForced = value;
|
||||
}
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
string language = GetLanguage(Language);
|
||||
|
||||
if (language == null)
|
||||
return $"Unknown (index: {TrackIndex})";
|
||||
CultureInfo info = CultureInfo.GetCultures(CultureTypes.NeutralCultures)
|
||||
.FirstOrDefault(x => x.ThreeLetterISOLanguageName == language);
|
||||
string name = info?.EnglishName ?? language;
|
||||
if (IsForced)
|
||||
name += " Forced";
|
||||
if (IsExternal)
|
||||
name += " (External)";
|
||||
if (Title != null && Title.Length > 1)
|
||||
name += " - " + Title;
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug
|
||||
{
|
||||
get
|
||||
@ -113,33 +48,89 @@ namespace Kyoo.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The title of the stream.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The language of this stream (as a ISO-639-2 language code)
|
||||
/// </summary>
|
||||
public string Language { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The codec of this stream.
|
||||
/// </summary>
|
||||
public string Codec { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Is this stream the default one of it's type?
|
||||
/// </summary>
|
||||
public bool IsDefault { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this stream tagged as forced?
|
||||
/// </summary>
|
||||
public bool IsForced { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this track extern to the episode's file?
|
||||
/// </summary>
|
||||
public bool IsExternal { get; set; }
|
||||
[LoadableRelation(nameof(EpisodeID))] public virtual Episode Episode { get; set; }
|
||||
|
||||
public Track() { }
|
||||
/// <summary>
|
||||
/// The path of this track.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
|
||||
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)
|
||||
/// <summary>
|
||||
/// The type of this stream.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public StreamType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the episode that uses this track. This value is only set when the <see cref="Episode"/> has been loaded.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int EpisodeID { get; set; }
|
||||
/// <summary>
|
||||
/// The episode that uses this track.
|
||||
/// </summary>
|
||||
[LoadableRelation(nameof(EpisodeID))] public Episode Episode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The index of this track on the episode.
|
||||
/// </summary>
|
||||
public int TrackIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A user-friendly name for this track. It does not include the track type.
|
||||
/// </summary>
|
||||
public string DisplayName
|
||||
{
|
||||
IsExternal = isExternal;
|
||||
}
|
||||
get
|
||||
{
|
||||
string language = GetLanguage(Language);
|
||||
|
||||
public Track(Stream stream)
|
||||
: base(stream)
|
||||
{
|
||||
IsExternal = false;
|
||||
if (language == null)
|
||||
return $"Unknown (index: {TrackIndex})";
|
||||
CultureInfo info = CultureInfo.GetCultures(CultureTypes.NeutralCultures)
|
||||
.FirstOrDefault(x => x.ThreeLetterISOLanguageName == language);
|
||||
string name = info?.EnglishName ?? language;
|
||||
if (IsForced)
|
||||
name += " Forced";
|
||||
if (IsExternal)
|
||||
name += " (External)";
|
||||
if (Title is {Length: > 1})
|
||||
name += " - " + Title;
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
//Converting mkv track language to c# system language tag.
|
||||
private static string GetLanguage(string mkvLanguage)
|
||||
{
|
||||
// TODO delete this and have a real way to get the language string from the ISO-639-2.
|
||||
return mkvLanguage switch
|
||||
{
|
||||
"fre" => "fra",
|
||||
|
@ -20,6 +20,7 @@ namespace Kyoo
|
||||
/// <typeparam name="T2">The type of items in the returned list</typeparam>
|
||||
/// <returns>The list mapped.</returns>
|
||||
/// <exception cref="ArgumentNullException">The list or the mapper can't be null</exception>
|
||||
[LinqTunnel]
|
||||
public static IEnumerable<T2> Map<T, T2>([NotNull] this IEnumerable<T> self,
|
||||
[NotNull] Func<T, int, T2> mapper)
|
||||
{
|
||||
@ -52,6 +53,7 @@ namespace Kyoo
|
||||
/// <typeparam name="T2">The type of items in the returned list</typeparam>
|
||||
/// <returns>The list mapped as an AsyncEnumerable</returns>
|
||||
/// <exception cref="ArgumentNullException">The list or the mapper can't be null</exception>
|
||||
[LinqTunnel]
|
||||
public static IAsyncEnumerable<T2> MapAsync<T, T2>([NotNull] this IEnumerable<T> self,
|
||||
[NotNull] Func<T, int, Task<T2>> mapper)
|
||||
{
|
||||
@ -84,6 +86,7 @@ namespace Kyoo
|
||||
/// <typeparam name="T2">The type of items in the returned list</typeparam>
|
||||
/// <returns>The list mapped as an AsyncEnumerable</returns>
|
||||
/// <exception cref="ArgumentNullException">The list or the mapper can't be null</exception>
|
||||
[LinqTunnel]
|
||||
public static IAsyncEnumerable<T2> SelectAsync<T, T2>([NotNull] this IEnumerable<T> self,
|
||||
[NotNull] Func<T, Task<T2>> mapper)
|
||||
{
|
||||
@ -110,6 +113,7 @@ namespace Kyoo
|
||||
/// <typeparam name="T">The type of items in the async list and in the returned list.</typeparam>
|
||||
/// <returns>A task that will return a simple list</returns>
|
||||
/// <exception cref="ArgumentNullException">The list can't be null</exception>
|
||||
[LinqTunnel]
|
||||
public static Task<List<T>> ToListAsync<T>([NotNull] this IAsyncEnumerable<T> self)
|
||||
{
|
||||
if (self == null)
|
||||
@ -134,6 +138,7 @@ namespace Kyoo
|
||||
/// <typeparam name="T">The type of items inside the list</typeparam>
|
||||
/// <exception cref="ArgumentNullException">The iterable and the action can't be null.</exception>
|
||||
/// <returns>The iterator proxied, there is no dual iterations.</returns>
|
||||
[LinqTunnel]
|
||||
public static IEnumerable<T> IfEmpty<T>([NotNull] this IEnumerable<T> self, [NotNull] Action action)
|
||||
{
|
||||
if (self == null)
|
||||
@ -236,6 +241,7 @@ namespace Kyoo
|
||||
/// <param name="countPerList">The number of items in each chunk</param>
|
||||
/// <typeparam name="T">The type of data in the initial list.</typeparam>
|
||||
/// <returns>A list of chunks</returns>
|
||||
[LinqTunnel]
|
||||
public static IEnumerable<List<T>> BatchBy<T>(this List<T> list, int countPerList)
|
||||
{
|
||||
for (int i = 0; i < list.Count; i += countPerList)
|
||||
@ -249,6 +255,7 @@ namespace Kyoo
|
||||
/// <param name="countPerList">The number of items in each chunk</param>
|
||||
/// <typeparam name="T">The type of data in the initial list.</typeparam>
|
||||
/// <returns>A list of chunks</returns>
|
||||
[LinqTunnel]
|
||||
public static IEnumerable<T[]> BatchBy<T>(this IEnumerable<T> list, int countPerList)
|
||||
{
|
||||
T[] ret = new T[countPerList];
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
using Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
@ -15,6 +16,7 @@ namespace Kyoo.Tests
|
||||
/// <param name="expected">The value to check against</param>
|
||||
/// <param name="value">The value to check</param>
|
||||
/// <typeparam name="T">The type to check</typeparam>
|
||||
[AssertionMethod]
|
||||
public static void DeepEqual<T>(T expected, T value)
|
||||
{
|
||||
foreach (PropertyInfo property in typeof(T).GetProperties())
|
||||
@ -24,6 +26,7 @@ namespace Kyoo.Tests
|
||||
/// <summary>
|
||||
/// Explicitly fail a test.
|
||||
/// </summary>
|
||||
[AssertionMethod]
|
||||
public static void Fail()
|
||||
{
|
||||
throw new XunitException();
|
||||
@ -33,6 +36,7 @@ namespace Kyoo.Tests
|
||||
/// Explicitly fail a test.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that will be seen in the test report</param>
|
||||
[AssertionMethod]
|
||||
public static void Fail(string message)
|
||||
{
|
||||
throw new XunitException(message);
|
||||
|
@ -26,8 +26,8 @@ namespace Kyoo.Tests
|
||||
"school students, they had long ceased to think of each other as friends.",
|
||||
Status = Status.Finished,
|
||||
TrailerUrl = null,
|
||||
StartYear = 2011,
|
||||
EndYear = 2011,
|
||||
StartAir = new DateTime(2011),
|
||||
EndAir = new DateTime(2011),
|
||||
Poster = "poster",
|
||||
Logo = "logo",
|
||||
Backdrop = "backdrop",
|
||||
|
@ -57,7 +57,7 @@ namespace Kyoo.Controllers
|
||||
Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr);
|
||||
if (stream!.Type != StreamType.Unknown)
|
||||
{
|
||||
tracks[j] = new Track(stream);
|
||||
tracks[j] = stream.ToTrack();
|
||||
j++;
|
||||
}
|
||||
streamsPtr += size;
|
||||
|
67
Kyoo/Models/Stream.cs
Normal file
67
Kyoo/Models/Stream.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models.Watch
|
||||
{
|
||||
/// <summary>
|
||||
/// The unmanaged stream that the transcoder will return.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
public class Stream
|
||||
{
|
||||
/// <summary>
|
||||
/// The title of the stream.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The language of this stream (as a ISO-639-2 language code)
|
||||
/// </summary>
|
||||
public string Language { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The codec of this stream.
|
||||
/// </summary>
|
||||
public string Codec { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this stream the default one of it's type?
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)] public bool IsDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Is this stream tagged as forced?
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)] public bool IsForced;
|
||||
|
||||
/// <summary>
|
||||
/// The path of this track.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of this stream.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public StreamType Type { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a track from this stream.
|
||||
/// </summary>
|
||||
/// <returns>A new track that represent this stream.</returns>
|
||||
public Track ToTrack()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Title = Title,
|
||||
Language = Language,
|
||||
Codec = Codec,
|
||||
IsDefault = IsDefault,
|
||||
IsForced = IsForced,
|
||||
Path = Path,
|
||||
Type = Type,
|
||||
IsExternal = false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -292,13 +292,19 @@ namespace Kyoo.Tasks
|
||||
catch (DuplicatedItemException)
|
||||
{
|
||||
old = await libraryManager.GetOrDefault<Show>(show.Slug);
|
||||
if (old.Path == showPath)
|
||||
if (old != null && old.Path == showPath)
|
||||
{
|
||||
await libraryManager.Load(old, x => x.ExternalIDs);
|
||||
return old;
|
||||
}
|
||||
show.Slug += $"-{show.StartYear}";
|
||||
await libraryManager.Create(show);
|
||||
|
||||
if (show.StartAir != null)
|
||||
{
|
||||
show.Slug += $"-{show.StartAir.Value.Year}";
|
||||
await libraryManager.Create(show);
|
||||
}
|
||||
else
|
||||
throw;
|
||||
}
|
||||
await ThumbnailsManager.Validate(show);
|
||||
return show;
|
||||
|
Loading…
x
Reference in New Issue
Block a user