diff --git a/Kyoo.Common/Controllers/IThumbnailsManager.cs b/Kyoo.Common/Controllers/IThumbnailsManager.cs
index 465d4c62..023e566c 100644
--- a/Kyoo.Common/Controllers/IThumbnailsManager.cs
+++ b/Kyoo.Common/Controllers/IThumbnailsManager.cs
@@ -23,37 +23,18 @@ namespace Kyoo.Controllers
/// The type of the item
/// true if an image has been downloaded, false otherwise.
Task DownloadImages([NotNull] T item, bool alwaysDownload = false)
- where T : IResource;
-
+ where T : IThumbnails;
+
///
/// Retrieve the local path of the poster of the given item.
///
/// The item to retrieve the poster from.
+ /// The ID of the image. See for values.
/// The type of the item
/// If the type does not have a poster
/// The path of the poster for the given resource (it might or might not exists).
- Task GetPoster([NotNull] T item)
- where T : IResource;
-
- ///
- /// Retrieve the local path of the logo of the given item.
- ///
- /// The item to retrieve the logo from.
- /// The type of the item
- /// If the type does not have a logo
- /// The path of the logo for the given resource (it might or might not exists).
- Task GetLogo([NotNull] T item)
- where T : IResource;
-
- ///
- /// Retrieve the local path of the thumbnail of the given item.
- ///
- /// The item to retrieve the thumbnail from.
- /// The type of the item
- /// If the type does not have a thumbnail
- /// The path of the thumbnail for the given resource (it might or might not exists).
- Task GetThumbnail([NotNull] T item)
- where T : IResource;
+ Task GetImagePath([NotNull] T item, int imageID)
+ where T : IThumbnails;
}
}
diff --git a/Kyoo.Common/Models/Resources/Collection.cs b/Kyoo.Common/Models/Resources/Collection.cs
index f7c5db3f..a8b796f2 100644
--- a/Kyoo.Common/Models/Resources/Collection.cs
+++ b/Kyoo.Common/Models/Resources/Collection.cs
@@ -8,7 +8,7 @@ namespace Kyoo.Models
/// A class representing collections of .
/// A collection can also be stored in a .
///
- public class Collection : IResource, IMetadata
+ public class Collection : IResource, IMetadata, IThumbnails
{
///
public int ID { get; set; }
@@ -20,14 +20,18 @@ namespace Kyoo.Models
/// The name of this collection.
///
public string Name { get; set; }
+
+ ///
+ public Dictionary Images { get; set; }
///
/// 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.
///
- [SerializeAs("{HOST}/api/collection/{Slug}/poster")] public string Poster { get; set; }
-
+ [SerializeAs("{HOST}/api/collection/{Slug}/poster")]
+ public string Poster => Images[Thumbnails.Poster];
+
///
/// The description of this collection.
///
@@ -44,7 +48,7 @@ namespace Kyoo.Models
[LoadableRelation] public ICollection Libraries { get; set; }
///
- public ICollection ExternalIDs { get; set; }
+ [EditableRelation] [LoadableRelation] public ICollection ExternalIDs { get; set; }
#if ENABLE_INTERNAL_LINKS
diff --git a/Kyoo.Common/Models/Resources/Episode.cs b/Kyoo.Common/Models/Resources/Episode.cs
index 687e88fb..ad2b79bb 100644
--- a/Kyoo.Common/Models/Resources/Episode.cs
+++ b/Kyoo.Common/Models/Resources/Episode.cs
@@ -10,7 +10,7 @@ namespace Kyoo.Models
///
/// A class to represent a single show's episode.
///
- public class Episode : IResource, IMetadata
+ public class Episode : IResource, IMetadata, IThumbnails
{
///
public int ID { get; set; }
@@ -98,13 +98,17 @@ namespace Kyoo.Models
/// The path of the video file for this episode. Any format supported by a is allowed.
///
[SerializeIgnore] public string Path { get; set; }
+
+ ///
+ public Dictionary Images { get; set; }
///
/// 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.
///
- [SerializeAs("{HOST}/api/episodes/{Slug}/thumb")] public string Thumb { get; set; }
+ [SerializeAs("{HOST}/api/episodes/{Slug}/thumb")]
+ public string Thumb => Images[Thumbnails.Thumbnail];
///
/// The title of this episode.
@@ -122,7 +126,7 @@ namespace Kyoo.Models
public DateTime? ReleaseDate { get; set; }
///
- 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/Interfaces/IMetadata.cs b/Kyoo.Common/Models/Resources/Interfaces/IMetadata.cs
index 469d3dbb..1c937715 100644
--- a/Kyoo.Common/Models/Resources/Interfaces/IMetadata.cs
+++ b/Kyoo.Common/Models/Resources/Interfaces/IMetadata.cs
@@ -11,6 +11,7 @@ namespace Kyoo.Models
///
/// 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; }
}
}
\ No newline at end of file
diff --git a/Kyoo.Common/Models/Resources/Interfaces/IThumbnails.cs b/Kyoo.Common/Models/Resources/Interfaces/IThumbnails.cs
new file mode 100644
index 00000000..074751c4
--- /dev/null
+++ b/Kyoo.Common/Models/Resources/Interfaces/IThumbnails.cs
@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using Kyoo.Controllers;
+
+namespace Kyoo.Models
+{
+ ///
+ /// An interface representing items that contains images (like posters, thumbnails, logo, banners...)
+ ///
+ public interface IThumbnails
+ {
+ ///
+ /// The list of images mapped to a certain index.
+ /// The string value should be a path supported by the .
+ ///
+ ///
+ /// An arbitrary index should not be used, instead use indexes from
+ ///
+ public Dictionary Images { get; set; }
+
+ // TODO remove Posters properties add them via the json serializer for every IThumbnails
+ }
+
+ ///
+ /// A class containing constant values for images. To be used as index of a .
+ ///
+ public static class Thumbnails
+ {
+ ///
+ /// A poster is a 9/16 format image with the cover of the resource.
+ ///
+ public const int Poster = 0;
+
+ ///
+ /// A thumbnail is a 16/9 format image, it could ether be used as a background or as a preview but it usually
+ /// is not an official image.
+ ///
+ public const int Thumbnail = 1;
+
+ ///
+ /// A logo is a small image representing the resource.
+ ///
+ public const int Logo = 2;
+
+ ///
+ /// A video of a few minutes that tease the content.
+ ///
+ public const int Trailer = 3;
+ }
+}
\ No newline at end of file
diff --git a/Kyoo.Common/Models/Resources/People.cs b/Kyoo.Common/Models/Resources/People.cs
index ff2b8ed1..5b7f6864 100644
--- a/Kyoo.Common/Models/Resources/People.cs
+++ b/Kyoo.Common/Models/Resources/People.cs
@@ -6,7 +6,7 @@ namespace Kyoo.Models
///
/// An actor, voice actor, writer, animator, somebody who worked on a .
///
- public class People : IResource, IMetadata
+ public class People : IResource, IMetadata, IThumbnails
{
///
public int ID { get; set; }
@@ -19,15 +19,19 @@ namespace Kyoo.Models
///
public string Name { get; set; }
+ ///
+ public Dictionary Images { get; set; }
+
///
/// 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.
///
- [SerializeAs("{HOST}/api/people/{Slug}/poster")] public string Poster { get; set; }
+ [SerializeAs("{HOST}/api/people/{Slug}/poster")]
+ public string Poster => Images[Thumbnails.Poster];
///
- 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 e13a9be4..e931479a 100644
--- a/Kyoo.Common/Models/Resources/Provider.cs
+++ b/Kyoo.Common/Models/Resources/Provider.cs
@@ -9,7 +9,7 @@ namespace Kyoo.Models
/// This class contains metadata about .
/// You can have providers even if you don't have the corresponding .
///
- public class Provider : IResource
+ public class Provider : IResource, IThumbnails
{
///
public int ID { get; set; }
@@ -22,12 +22,16 @@ namespace Kyoo.Models
///
public string Name { get; set; }
+ ///
+ public Dictionary Images { get; set; }
+
///
/// 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.
///
- [SerializeAs("{HOST}/api/providers/{Slug}/logo")] public string Logo { get; set; }
+ [SerializeAs("{HOST}/api/providers/{Slug}/logo")]
+ public string Logo => Images[Thumbnails.Logo];
///
/// The extension of the logo. This is used for http responses.
@@ -61,7 +65,7 @@ namespace Kyoo.Models
{
Slug = Utility.ToSlug(name);
Name = name;
- Logo = logo;
+ Images[Thumbnails.Logo] = logo;
}
}
}
\ No newline at end of file
diff --git a/Kyoo.Common/Models/Resources/Season.cs b/Kyoo.Common/Models/Resources/Season.cs
index b390f712..fddb7eb4 100644
--- a/Kyoo.Common/Models/Resources/Season.cs
+++ b/Kyoo.Common/Models/Resources/Season.cs
@@ -10,7 +10,7 @@ namespace Kyoo.Models
///
/// A season of a .
///
- public class Season : IResource, IMetadata
+ public class Season : IResource, IMetadata, IThumbnails
{
///
public int ID { get; set; }
@@ -74,15 +74,19 @@ namespace Kyoo.Models
///
public DateTime? EndDate { get; set; }
+ ///
+ public Dictionary Images { get; set; }
+
///
/// 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.
///
- [SerializeAs("{HOST}/api/seasons/{Slug}/thumb")] public string Poster { get; set; }
+ [SerializeAs("{HOST}/api/seasons/{Slug}/thumb")]
+ public string Poster => Images[Thumbnails.Poster];
///
- 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 85c5b959..a32a0169 100644
--- a/Kyoo.Common/Models/Resources/Show.cs
+++ b/Kyoo.Common/Models/Resources/Show.cs
@@ -11,7 +11,7 @@ namespace Kyoo.Models
///
/// A series or a movie.
///
- public class Show : IResource, IMetadata, IOnMerge
+ public class Show : IResource, IMetadata, IOnMerge, IThumbnails
{
///
public int ID { get; set; }
@@ -44,12 +44,12 @@ namespace Kyoo.Models
/// Is this show airing, not aired yet or finished?
///
public Status Status { get; set; }
-
+
///
/// An URL to a trailer. This could be any path supported by the .
///
/// 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 string TrailerUrl => Images[Thumbnails.Trailer];
///
/// The date this show started airing. It can be null if this is unknown.
@@ -63,26 +63,32 @@ namespace Kyoo.Models
///
public DateTime? EndAir { get; set; }
+ ///
+ public Dictionary Images { get; set; }
+
///
/// 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.
///
- [SerializeAs("{HOST}/api/shows/{Slug}/poster")] public string Poster { get; set; }
-
+ [SerializeAs("{HOST}/api/shows/{Slug}/poster")]
+ public string Poster => Images[Thumbnails.Poster];
+
///
/// 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.
///
- [SerializeAs("{HOST}/api/shows/{Slug}/logo")] public string Logo { get; set; }
-
+ [SerializeAs("{HOST}/api/shows/{Slug}/logo")]
+ public string Logo => Images[Thumbnails.Logo];
+
///
/// 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.
///
- [SerializeAs("{HOST}/api/shows/{Slug}/backdrop")] public string Backdrop { get; set; }
+ [SerializeAs("{HOST}/api/shows/{Slug}/backdrop")]
+ public string Backdrop => Images[Thumbnails.Thumbnail];
///
/// True if this show represent a movie, false otherwise.
@@ -90,7 +96,7 @@ namespace Kyoo.Models
public bool IsMovie { get; set; }
///
- public ICollection ExternalIDs { get; set; }
+ [EditableRelation] [LoadableRelation] public ICollection ExternalIDs { get; set; }
///
/// The ID of the Studio that made this show.
diff --git a/Kyoo.Common/Models/Resources/Studio.cs b/Kyoo.Common/Models/Resources/Studio.cs
index 80195317..03ec04df 100644
--- a/Kyoo.Common/Models/Resources/Studio.cs
+++ b/Kyoo.Common/Models/Resources/Studio.cs
@@ -25,7 +25,7 @@ namespace Kyoo.Models
[LoadableRelation] public ICollection Shows { get; set; }
///
- public ICollection ExternalIDs { get; set; }
+ [EditableRelation] [LoadableRelation] public ICollection ExternalIDs { get; set; }
///
/// Create a new, empty, .
diff --git a/Kyoo.Tests/Database/TestSample.cs b/Kyoo.Tests/Database/TestSample.cs
index e0ff955f..d81612ad 100644
--- a/Kyoo.Tests/Database/TestSample.cs
+++ b/Kyoo.Tests/Database/TestSample.cs
@@ -41,7 +41,10 @@ namespace Kyoo.Tests
Slug = "collection",
Name = "Collection",
Overview = "A nice collection for tests",
- Poster = "Poster"
+ Images =
+ {
+ [Thumbnails.Poster] = "Poster"
+ }
}
},
{
@@ -61,12 +64,14 @@ namespace Kyoo.Tests
"In time, however, these childhood friends drifted apart, and when they became high " +
"school students, they had long ceased to think of each other as friends.",
Status = Status.Finished,
- TrailerUrl = null,
StartAir = new DateTime(2011, 1, 1),
EndAir = new DateTime(2011, 1, 1),
- Poster = "poster",
- Logo = "logo",
- Backdrop = "backdrop",
+ Images =
+ {
+ [Thumbnails.Poster] = "Poster",
+ [Thumbnails.Logo] = "Logo",
+ [Thumbnails.Thumbnail] = "Thumbnail"
+ },
IsMovie = false,
Studio = null
}
@@ -83,7 +88,12 @@ namespace Kyoo.Tests
Overview = "The first season",
StartDate = new DateTime(2020, 06, 05),
EndDate = new DateTime(2020, 07, 05),
- Poster = "poster"
+ Images =
+ {
+ [Thumbnails.Poster] = "Poster",
+ [Thumbnails.Logo] = "Logo",
+ [Thumbnails.Thumbnail] = "Thumbnail"
+ },
}
},
{
@@ -98,7 +108,12 @@ namespace Kyoo.Tests
EpisodeNumber = 1,
AbsoluteNumber = 1,
Path = "/home/kyoo/anohana-s1e1",
- Thumb = "thumbnail",
+ Images =
+ {
+ [Thumbnails.Poster] = "Poster",
+ [Thumbnails.Logo] = "Logo",
+ [Thumbnails.Thumbnail] = "Thumbnail"
+ },
Title = "Episode 1",
Overview = "Summary of the first episode",
ReleaseDate = new DateTime(2020, 06, 05)
@@ -129,7 +144,12 @@ namespace Kyoo.Tests
ID = 1,
Slug = "the-actor",
Name = "The Actor",
- Poster = "NicePoster"
+ Images =
+ {
+ [Thumbnails.Poster] = "Poster",
+ [Thumbnails.Logo] = "Logo",
+ [Thumbnails.Thumbnail] = "Thumbnail"
+ },
}
},
{
@@ -138,7 +158,7 @@ namespace Kyoo.Tests
{
ID = 1,
Slug = "hyper-studio",
- Name = "Hyper studio"
+ Name = "Hyper studio",
}
},
{
@@ -157,7 +177,12 @@ namespace Kyoo.Tests
ID = 1,
Slug = "tvdb",
Name = "The TVDB",
- Logo = "path/tvdb.svg",
+ Images =
+ {
+ [Thumbnails.Poster] = "Poster",
+ [Thumbnails.Logo] = "path/tvdb.svg",
+ [Thumbnails.Thumbnail] = "Thumbnail"
+ },
LogoExtension = "svg"
}
},
@@ -257,7 +282,12 @@ namespace Kyoo.Tests
EpisodeNumber = null,
AbsoluteNumber = 3,
Path = "/home/kyoo/anohana-3",
- Thumb = "thumbnail",
+ Images =
+ {
+ [Thumbnails.Poster] = "Poster",
+ [Thumbnails.Logo] = "Logo",
+ [Thumbnails.Thumbnail] = "Thumbnail"
+ },
Title = "Episode 3",
Overview = "Summary of the third absolute episode",
ReleaseDate = new DateTime(2020, 06, 05)
@@ -272,7 +302,12 @@ namespace Kyoo.Tests
ShowSlug = "anohana",
ShowID = 1,
Path = "/home/kyoo/john-wick",
- Thumb = "thumb",
+ Images =
+ {
+ [Thumbnails.Poster] = "Poster",
+ [Thumbnails.Logo] = "Logo",
+ [Thumbnails.Thumbnail] = "Thumbnail"
+ },
Title = "John wick",
Overview = "A movie episode test",
ReleaseDate = new DateTime(1595, 05, 12)
diff --git a/Kyoo.TheMovieDb/Convertors.cs b/Kyoo.TheMovieDb/Convertors.cs
index 0cf5fe98..b6fd4ad1 100644
--- a/Kyoo.TheMovieDb/Convertors.cs
+++ b/Kyoo.TheMovieDb/Convertors.cs
@@ -26,17 +26,21 @@ namespace Kyoo.TheMovieDb
Title = movie.Title,
Aliases = movie.AlternativeTitles.Titles.Select(x => x.Title).ToArray(),
Overview = movie.Overview,
- TrailerUrl = movie.Videos?.Results.Where(x => x.Type is "Trailer" or "Teaser" && x.Site == "YouTube")
- .Select(x => "https://www.youtube.com/watch?v=" + x.Key).FirstOrDefault(),
Status = movie.Status == "Released" ? Status.Finished : Status.Planned,
StartAir = movie.ReleaseDate,
EndAir = movie.ReleaseDate,
- Poster = movie.PosterPath != null
- ? $"https://image.tmdb.org/t/p/original{movie.PosterPath}"
- : null,
- Backdrop = movie.BackdropPath != null
- ? $"https://image.tmdb.org/t/p/original{movie.BackdropPath}"
- : null,
+ Images =
+ {
+ [Thumbnails.Poster] = movie.PosterPath != null
+ ? $"https://image.tmdb.org/t/p/original{movie.PosterPath}"
+ : null,
+ [Thumbnails.Thumbnail] = movie.BackdropPath != null
+ ? $"https://image.tmdb.org/t/p/original{movie.BackdropPath}"
+ : null,
+ [Thumbnails.Trailer] = movie.Videos?.Results
+ .Where(x => x.Type is "Trailer" or "Teaser" && x.Site == "YouTube")
+ .Select(x => "https://www.youtube.com/watch?v=" + x.Key).FirstOrDefault(),
+ },
Genres = movie.Genres.Select(x => new Genre(x.Name)).ToArray(),
Studio = !string.IsNullOrEmpty(movie.ProductionCompanies.FirstOrDefault()?.Name)
? new Studio(movie.ProductionCompanies.First().Name)
@@ -72,17 +76,21 @@ namespace Kyoo.TheMovieDb
Title = tv.Name,
Aliases = tv.AlternativeTitles.Results.Select(x => x.Title).ToArray(),
Overview = tv.Overview,
- TrailerUrl = tv.Videos?.Results.Where(x => x.Type is "Trailer" or "Teaser" && x.Site == "YouTube")
- .Select(x => "https://www.youtube.com/watch?v=" + x.Key).FirstOrDefault(),
Status = tv.Status == "Ended" ? Status.Finished : Status.Planned,
StartAir = tv.FirstAirDate,
EndAir = tv.LastAirDate,
- Poster = tv.PosterPath != null
- ? $"https://image.tmdb.org/t/p/original{tv.PosterPath}"
- : null,
- Backdrop = tv.BackdropPath != null
- ? $"https://image.tmdb.org/t/p/original{tv.BackdropPath}"
- : null,
+ Images =
+ {
+ [Thumbnails.Poster] = tv.PosterPath != null
+ ? $"https://image.tmdb.org/t/p/original{tv.PosterPath}"
+ : null,
+ [Thumbnails.Thumbnail] = tv.BackdropPath != null
+ ? $"https://image.tmdb.org/t/p/original{tv.BackdropPath}"
+ : null,
+ [Thumbnails.Trailer] = tv.Videos?.Results
+ .Where(x => x.Type is "Trailer" or "Teaser" && x.Site == "YouTube")
+ .Select(x => "https://www.youtube.com/watch?v=" + x.Key).FirstOrDefault()
+ },
Genres = tv.Genres.Select(x => new Genre(x.Name)).ToArray(),
Studio = !string.IsNullOrEmpty(tv.ProductionCompanies.FirstOrDefault()?.Name)
? new Studio(tv.ProductionCompanies.First().Name)
@@ -117,7 +125,15 @@ namespace Kyoo.TheMovieDb
{
Slug = Utility.ToSlug(collection.Name),
Name = collection.Name,
- Poster = $"https://image.tmdb.org/t/p/original{collection.PosterPath}"
+ Images =
+ {
+ [Thumbnails.Poster] = collection.PosterPath != null
+ ? $"https://image.tmdb.org/t/p/original{collection.PosterPath}"
+ : null,
+ [Thumbnails.Thumbnail] = collection.BackdropPath != null
+ ? $"https://image.tmdb.org/t/p/original{collection.BackdropPath}"
+ : null
+ }
};
}
@@ -136,12 +152,15 @@ namespace Kyoo.TheMovieDb
Overview = movie.Overview,
StartAir = movie.ReleaseDate,
EndAir = movie.ReleaseDate,
- Poster = movie.PosterPath != null
- ? $"https://image.tmdb.org/t/p/original{movie.PosterPath}"
- : null,
- Backdrop = movie.BackdropPath != null
- ? $"https://image.tmdb.org/t/p/original{movie.BackdropPath}"
- : null,
+ Images =
+ {
+ [Thumbnails.Poster] = movie.PosterPath != null
+ ? $"https://image.tmdb.org/t/p/original{movie.PosterPath}"
+ : null,
+ [Thumbnails.Thumbnail] = movie.BackdropPath != null
+ ? $"https://image.tmdb.org/t/p/original{movie.BackdropPath}"
+ : null,
+ },
IsMovie = true,
ExternalIDs = new []
{
@@ -169,12 +188,15 @@ namespace Kyoo.TheMovieDb
Title = tv.Name,
Overview = tv.Overview,
StartAir = tv.FirstAirDate,
- Poster = tv.PosterPath != null
- ? $"https://image.tmdb.org/t/p/original{tv.PosterPath}"
- : null,
- Backdrop = tv.BackdropPath != null
- ? $"https://image.tmdb.org/t/p/original{tv.BackdropPath}"
- : null,
+ Images =
+ {
+ [Thumbnails.Poster] = tv.PosterPath != null
+ ? $"https://image.tmdb.org/t/p/original{tv.PosterPath}"
+ : null,
+ [Thumbnails.Thumbnail] = tv.BackdropPath != null
+ ? $"https://image.tmdb.org/t/p/original{tv.BackdropPath}"
+ : null,
+ },
IsMovie = true,
ExternalIDs = new []
{
@@ -202,7 +224,12 @@ namespace Kyoo.TheMovieDb
{
Slug = Utility.ToSlug(cast.Name),
Name = cast.Name,
- Poster = cast.ProfilePath != null ? $"https://image.tmdb.org/t/p/original{cast.ProfilePath}" : null,
+ Images =
+ {
+ [Thumbnails.Poster] = cast.ProfilePath != null
+ ? $"https://image.tmdb.org/t/p/original{cast.ProfilePath}"
+ : null
+ },
ExternalIDs = new[]
{
new MetadataID
@@ -232,7 +259,12 @@ namespace Kyoo.TheMovieDb
{
Slug = Utility.ToSlug(cast.Name),
Name = cast.Name,
- Poster = cast.ProfilePath != null ? $"https://image.tmdb.org/t/p/original{cast.ProfilePath}" : null,
+ Images =
+ {
+ [Thumbnails.Poster] = cast.ProfilePath != null
+ ? $"https://image.tmdb.org/t/p/original{cast.ProfilePath}"
+ : null
+ },
ExternalIDs = new[]
{
new MetadataID
@@ -262,7 +294,12 @@ namespace Kyoo.TheMovieDb
{
Slug = Utility.ToSlug(crew.Name),
Name = crew.Name,
- Poster = crew.ProfilePath != null ? $"https://image.tmdb.org/t/p/original{crew.ProfilePath}" : null,
+ Images =
+ {
+ [Thumbnails.Poster] = crew.ProfilePath != null
+ ? $"https://image.tmdb.org/t/p/original{crew.ProfilePath}"
+ : null
+ },
ExternalIDs = new[]
{
new MetadataID
diff --git a/Kyoo.TheMovieDb/ProviderTmdb.cs b/Kyoo.TheMovieDb/ProviderTmdb.cs
index b613e5f3..bde0a748 100644
--- a/Kyoo.TheMovieDb/ProviderTmdb.cs
+++ b/Kyoo.TheMovieDb/ProviderTmdb.cs
@@ -29,7 +29,11 @@ namespace Kyoo.TheMovieDb
Slug = "the-moviedb",
Name = "TheMovieDB",
LogoExtension = "svg",
- Logo = "https://www.themoviedb.org/assets/2/v4/logos/v2/blue_short-8e7b30f73a4020692ccca9c88bafe5dcb6f8a62a4c6bc55cd9ba82bb2cd95f6c.svg"
+ Images =
+ {
+ [Thumbnails.Logo] = "https://www.themoviedb.org/assets/2/v4/logos/v2/" +
+ "blue_short-8e7b30f73a4020692ccca9c88bafe5dcb6f8a62a4c6bc55cd9ba82bb2cd95f6c.svg"
+ }
};
///
diff --git a/Kyoo.TheTvdb/Convertors.cs b/Kyoo.TheTvdb/Convertors.cs
index 6aad939b..b992481d 100644
--- a/Kyoo.TheTvdb/Convertors.cs
+++ b/Kyoo.TheTvdb/Convertors.cs
@@ -55,7 +55,12 @@ namespace Kyoo.TheTvdb
Overview = result.Overview,
Status = _GetStatus(result.Status),
StartAir = _ParseDate(result.FirstAired),
- Poster = result.Poster != null ? $"https://www.thetvdb.com{result.Poster}" : null,
+ Images =
+ {
+ [Thumbnails.Poster] = result.Poster != null
+ ? $"https://www.thetvdb.com{result.Poster}"
+ : null,
+ },
ExternalIDs = new[]
{
new MetadataID
@@ -84,8 +89,15 @@ namespace Kyoo.TheTvdb
Overview = series.Overview,
Status = _GetStatus(series.Status),
StartAir = _ParseDate(series.FirstAired),
- Poster = series.Poster != null ? $"https://www.thetvdb.com/banners/{series.Poster}" : null,
- Backdrop = series.FanArt != null ? $"https://www.thetvdb.com/banners/{series.FanArt}" : null,
+ Images=
+ {
+ [Thumbnails.Poster] = series.Poster != null
+ ? $"https://www.thetvdb.com/banners/{series.Poster}"
+ : null,
+ [Thumbnails.Thumbnail] = series.FanArt != null
+ ? $"https://www.thetvdb.com/banners/{series.FanArt}"
+ : null
+ },
Genres = series.Genre.Select(y => new Genre(y)).ToList(),
ExternalIDs = new[]
{
@@ -113,7 +125,12 @@ namespace Kyoo.TheTvdb
{
Slug = Utility.ToSlug(actor.Name),
Name = actor.Name,
- Poster = actor.Image != null ? $"https://www.thetvdb.com/banners/{actor.Image}" : null,
+ Images =
+ {
+ [Thumbnails.Poster] = actor.Image != null
+ ? $"https://www.thetvdb.com/banners/{actor.Image}"
+ : null
+ },
ExternalIDs = new []
{
new MetadataID
@@ -144,7 +161,12 @@ namespace Kyoo.TheTvdb
AbsoluteNumber = episode.AbsoluteNumber,
Title = episode.EpisodeName,
Overview = episode.Overview,
- Thumb = episode.Filename != null ? $"https://www.thetvdb.com/banners/{episode.Filename}" : null,
+ Images =
+ {
+ [Thumbnails.Thumbnail] = episode.Filename != null
+ ? $"https://www.thetvdb.com/banners/{episode.Filename}"
+ : null
+ },
ExternalIDs = new[]
{
new MetadataID
diff --git a/Kyoo.TheTvdb/ProviderTvdb.cs b/Kyoo.TheTvdb/ProviderTvdb.cs
index 47183fb7..b2ba5683 100644
--- a/Kyoo.TheTvdb/ProviderTvdb.cs
+++ b/Kyoo.TheTvdb/ProviderTvdb.cs
@@ -33,7 +33,10 @@ namespace Kyoo.TheTvdb
Slug = "the-tvdb",
Name = "TheTVDB",
LogoExtension = "png",
- Logo = "https://www.thetvdb.com/images/logo.png"
+ Images =
+ {
+ [Thumbnails.Logo] = "https://www.thetvdb.com/images/logo.png"
+ }
};
diff --git a/Kyoo/Controllers/ThumbnailsManager.cs b/Kyoo/Controllers/ThumbnailsManager.cs
index 389891b3..61d14613 100644
--- a/Kyoo/Controllers/ThumbnailsManager.cs
+++ b/Kyoo/Controllers/ThumbnailsManager.cs
@@ -2,7 +2,6 @@
using System;
using System.IO;
using System.Threading.Tasks;
-using JetBrains.Annotations;
using Kyoo.Models.Options;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -55,23 +54,6 @@ namespace Kyoo.Controllers
});
}
- ///
- public Task DownloadImages(T item, bool alwaysDownload = false)
- where T : IResource
- {
- if (item == null)
- throw new ArgumentNullException(nameof(item));
- return item switch
- {
- Show show => _Validate(show, alwaysDownload),
- Season season => _Validate(season, alwaysDownload),
- Episode episode => _Validate(episode, alwaysDownload),
- People people => _Validate(people, alwaysDownload),
- Provider provider => _Validate(provider, alwaysDownload),
- _ => Task.FromResult(false)
- };
- }
-
///
/// An helper function to download an image using a .
///
@@ -97,196 +79,72 @@ namespace Kyoo.Controllers
return false;
}
}
-
- ///
- /// Download images of a specified show.
- ///
- ///
- /// The item to cache images.
- ///
- ///
- /// true if images should be downloaded even if they already exists locally, false otherwise.
- ///
- /// true if an image has been downloaded, false otherwise.
- private async Task _Validate([NotNull] Show show, bool alwaysDownload)
+
+ ///
+ public async Task DownloadImages(T item, bool alwaysDownload = false)
+ where T : IThumbnails
{
- bool ret = false;
-
- if (show.Poster != null)
- {
- string posterPath = await GetPoster(show);
- if (alwaysDownload || !await _files.Exists(posterPath))
- ret |= await _DownloadImage(show.Poster, posterPath, $"The poster of {show.Title}");
- }
- if (show.Logo != null)
- {
- string logoPath = await GetLogo(show);
- if (alwaysDownload || !await _files.Exists(logoPath))
- ret |= await _DownloadImage(show.Logo, logoPath, $"The logo of {show.Title}");
- }
- if (show.Backdrop != null)
- {
- string backdropPath = await GetThumbnail(show);
- if (alwaysDownload || !await _files.Exists(backdropPath))
- ret |= await _DownloadImage(show.Backdrop, backdropPath, $"The backdrop of {show.Title}");
- }
+ if (item == null)
+ throw new ArgumentNullException(nameof(item));
+ string name = item is IResource res ? res.Slug : "???";
+ bool ret = false;
+
+ foreach ((int id, string image) in item.Images)
+ {
+ string localPath = await GetImagePath(item, id);
+ if (alwaysDownload || !await _files.Exists(localPath))
+ ret |= await _DownloadImage(image, localPath, $"The image n°{id} of {name}");
+ }
+
return ret;
}
-
- ///
- /// Download images of a specified person.
- ///
- ///
- /// The item to cache images.
- ///
- ///
- /// true if images should be downloaded even if they already exists locally, false otherwise.
- ///
- /// true if an image has been downloaded, false otherwise.
- private async Task _Validate([NotNull] People people, bool alwaysDownload)
- {
- if (people == null)
- throw new ArgumentNullException(nameof(people));
- if (people.Poster == null)
- return false;
- string localPath = await GetPoster(people);
- if (alwaysDownload || !await _files.Exists(localPath))
- return await _DownloadImage(people.Poster, localPath, $"The profile picture of {people.Name}");
- return false;
- }
-
- ///
- /// Download images of a specified season.
- ///
- ///
- /// The item to cache images.
- ///
- ///
- /// true if images should be downloaded even if they already exists locally, false otherwise.
- ///
- /// true if an image has been downloaded, false otherwise.
- private async Task _Validate([NotNull] Season season, bool alwaysDownload)
- {
- if (season.Poster == null)
- return false;
-
- string localPath = await GetPoster(season);
- if (alwaysDownload || !await _files.Exists(localPath))
- return await _DownloadImage(season.Poster, localPath, $"The poster of {season.Slug}");
- return false;
- }
- ///
- /// Download images of a specified episode.
- ///
- ///
- /// The item to cache images.
- ///
- ///
- /// true if images should be downloaded even if they already exists locally, false otherwise.
- ///
- /// true if an image has been downloaded, false otherwise.
- private async Task _Validate([NotNull] Episode episode, bool alwaysDownload)
- {
- if (episode.Thumb == null)
- return false;
-
- string localPath = await _GetEpisodeThumb(episode);
- if (alwaysDownload || !await _files.Exists(localPath))
- return await _DownloadImage(episode.Thumb, localPath, $"The thumbnail of {episode.Slug}");
- return false;
- }
-
- ///
- /// Download images of a specified provider.
- ///
- ///
- /// The item to cache images.
- ///
- ///
- /// true if images should be downloaded even if they already exists locally, false otherwise.
- ///
- /// true if an image has been downloaded, false otherwise.
- private async Task _Validate([NotNull] Provider provider, bool alwaysDownload)
- {
- if (provider.Logo == null)
- return false;
-
- string localPath = await GetLogo(provider);
- if (alwaysDownload || !await _files.Exists(localPath))
- return await _DownloadImage(provider.Logo, localPath, $"The logo of {provider.Slug}");
- return false;
- }
-
///
- public Task GetPoster(T item)
- where T : IResource
+ public async Task GetImagePath(T item, int imageID)
+ where T : IThumbnails
{
if (item == null)
throw new ArgumentNullException(nameof(item));
- return item switch
+ string imageName = imageID switch
{
- Show show => Task.FromResult(_files.Combine(_files.GetExtraDirectory(show), "poster.jpg")),
- Season season => _GetSeasonPoster(season),
- People actor => Task.FromResult(_files.Combine(_options.CurrentValue.PeoplePath, $"{actor.Slug}.jpg")),
- _ => throw new NotSupportedException($"The type {typeof(T).Name} does not have a poster.")
+ Thumbnails.Poster => "poster.jpg",
+ Thumbnails.Logo => "logo.jpg",
+ Thumbnails.Thumbnail => "thumbnail.jpg",
+ _ => $"{imageID}.jpg"
};
- }
-
- ///
- /// Retrieve the path of a season's poster.
- ///
- /// The season to retrieve the poster from.
- /// The path of the season's poster.
- private async Task _GetSeasonPoster(Season season)
- {
- if (season.Show == null)
- await _library.Value.Load(season, x => x.Show);
- return _files.Combine(_files.GetExtraDirectory(season.Show), $"season-{season.SeasonNumber}.jpg");
- }
-
- ///
- public Task GetThumbnail(T item)
- where T : IResource
- {
- if (item == null)
- throw new ArgumentNullException(nameof(item));
- return item switch
+
+ // TODO implement a generic way, probably need to rework IFileManager.GetExtraDirectory too.
+ switch (item)
{
- Show show => Task.FromResult(_files.Combine(_files.GetExtraDirectory(show), "backdrop.jpg")),
- Episode episode => _GetEpisodeThumb(episode),
- _ => throw new NotSupportedException($"The type {typeof(T).Name} does not have a thumbnail.")
- };
- }
-
- ///
- /// Get the path for an episode's thumbnail.
- ///
- /// The episode to retrieve the thumbnail from
- /// The path of the given episode's thumbnail.
- private async Task _GetEpisodeThumb(Episode episode)
- {
- if (episode.Show == null)
- await _library.Value.Load(episode, x => x.Show);
- string dir = _files.Combine(_files.GetExtraDirectory(episode.Show), "Thumbnails");
- await _files.CreateDirectory(dir);
- return _files.Combine(dir, $"{Path.GetFileNameWithoutExtension(episode.Path)}.jpg");
- }
-
- ///
- public Task GetLogo(T item)
- where T : IResource
- {
- if (item == null)
- throw new ArgumentNullException(nameof(item));
- return Task.FromResult(item switch
- {
- Show show => _files.Combine(_files.GetExtraDirectory(show), "logo.png"),
- Provider provider => _files.Combine(_options.CurrentValue.ProviderPath,
- $"{provider.Slug}.{provider.LogoExtension}"),
- _ => throw new NotSupportedException($"The type {typeof(T).Name} does not have a thumbnail.")
- });
+ case Show show:
+ return _files.Combine(_files.GetExtraDirectory(show), imageName);
+
+ case Season season:
+ if (season.Show == null)
+ await _library.Value.Load(season, x => x.Show);
+ return _files.Combine(
+ _files.GetExtraDirectory(season.Show!),
+ $"season-{season.SeasonNumber}-{imageName}");
+
+ case Episode episode:
+ if (episode.Show == null)
+ await _library.Value.Load(episode, x => x.Show);
+ string dir = _files.Combine(_files.GetExtraDirectory(episode.Show!), "Thumbnails");
+ await _files.CreateDirectory(dir);
+ return _files.Combine(dir, $"{Path.GetFileNameWithoutExtension(episode.Path)}-{imageName}");
+
+ case People actor:
+ return _files.Combine(_options.CurrentValue.PeoplePath, $"{actor.Slug}-{imageName}");
+
+ case Provider provider:
+ return _files.Combine(
+ _options.CurrentValue.ProviderPath,
+ $"{provider.Slug}-{imageName[..^4]}{provider.LogoExtension}");
+
+ default:
+ throw new NotSupportedException($"The type {typeof(T).Name} is not supported.");
+ }
}
}
}
diff --git a/Kyoo/Tasks/RegisterEpisode.cs b/Kyoo/Tasks/RegisterEpisode.cs
index 9df8b1f7..033bfceb 100644
--- a/Kyoo/Tasks/RegisterEpisode.cs
+++ b/Kyoo/Tasks/RegisterEpisode.cs
@@ -163,7 +163,7 @@ namespace Kyoo.Tasks
/// The type of the item
/// The existing or filled item.
private async Task _RegisterAndFill(T item)
- where T : class, IResource
+ where T : class, IResource, IThumbnails
{
if (item == null || string.IsNullOrEmpty(item.Slug))
return null;
diff --git a/Kyoo/Views/EpisodeApi.cs b/Kyoo/Views/EpisodeApi.cs
index b7d83e27..beaf0273 100644
--- a/Kyoo/Views/EpisodeApi.cs
+++ b/Kyoo/Views/EpisodeApi.cs
@@ -195,7 +195,7 @@ namespace Kyoo.Api
try
{
Episode episode = await _libraryManager.Get(id);
- return _files.FileResult(await _thumbnails.GetThumbnail(episode));
+ return _files.FileResult(await _thumbnails.GetImagePath(episode, Thumbnails.Thumbnail));
}
catch (ItemNotFoundException)
{
@@ -210,7 +210,7 @@ namespace Kyoo.Api
try
{
Episode episode = await _libraryManager.Get(slug);
- return _files.FileResult(await _thumbnails.GetThumbnail(episode));
+ return _files.FileResult(await _thumbnails.GetImagePath(episode, Thumbnails.Thumbnail));
}
catch (ItemNotFoundException)
{
diff --git a/Kyoo/Views/PeopleApi.cs b/Kyoo/Views/PeopleApi.cs
index bb757a60..194504ff 100644
--- a/Kyoo/Views/PeopleApi.cs
+++ b/Kyoo/Views/PeopleApi.cs
@@ -94,7 +94,7 @@ namespace Kyoo.Api
People people = await _libraryManager.GetOrDefault(id);
if (people == null)
return NotFound();
- return _files.FileResult(await _thumbs.GetPoster(people));
+ return _files.FileResult(await _thumbs.GetImagePath(people, Thumbnails.Poster));
}
[HttpGet("{slug}/poster")]
@@ -103,7 +103,7 @@ namespace Kyoo.Api
People people = await _libraryManager.GetOrDefault(slug);
if (people == null)
return NotFound();
- return _files.FileResult(await _thumbs.GetPoster(people));
+ return _files.FileResult(await _thumbs.GetImagePath(people, Thumbnails.Poster));
}
}
}
\ No newline at end of file
diff --git a/Kyoo/Views/ProviderApi.cs b/Kyoo/Views/ProviderApi.cs
index 026b79ef..06c06eb8 100644
--- a/Kyoo/Views/ProviderApi.cs
+++ b/Kyoo/Views/ProviderApi.cs
@@ -36,7 +36,7 @@ namespace Kyoo.Api
Provider provider = await _libraryManager.GetOrDefault(id);
if (provider == null)
return NotFound();
- return _files.FileResult(await _thumbnails.GetLogo(provider));
+ return _files.FileResult(await _thumbnails.GetImagePath(provider, Thumbnails.Logo));
}
[HttpGet("{slug}/logo")]
@@ -45,7 +45,7 @@ namespace Kyoo.Api
Provider provider = await _libraryManager.GetOrDefault(slug);
if (provider == null)
return NotFound();
- return _files.FileResult(await _thumbnails.GetLogo(provider));
+ return _files.FileResult(await _thumbnails.GetImagePath(provider, Thumbnails.Logo));
}
}
}
\ No newline at end of file
diff --git a/Kyoo/Views/SeasonApi.cs b/Kyoo/Views/SeasonApi.cs
index 8d08dd0c..0241de55 100644
--- a/Kyoo/Views/SeasonApi.cs
+++ b/Kyoo/Views/SeasonApi.cs
@@ -151,7 +151,7 @@ namespace Kyoo.Api
if (season == null)
return NotFound();
await _libraryManager.Load(season, x => x.Show);
- return _files.FileResult(await _thumbs.GetPoster(season));
+ return _files.FileResult(await _thumbs.GetImagePath(season, Thumbnails.Poster));
}
[HttpGet("{slug}/poster")]
@@ -161,7 +161,7 @@ namespace Kyoo.Api
if (season == null)
return NotFound();
await _libraryManager.Load(season, x => x.Show);
- return _files.FileResult(await _thumbs.GetPoster(season));
+ return _files.FileResult(await _thumbs.GetImagePath(season, Thumbnails.Poster));
}
}
}
\ No newline at end of file
diff --git a/Kyoo/Views/ShowApi.cs b/Kyoo/Views/ShowApi.cs
index 04854849..d7162792 100644
--- a/Kyoo/Views/ShowApi.cs
+++ b/Kyoo/Views/ShowApi.cs
@@ -417,7 +417,7 @@ namespace Kyoo.Api
try
{
Show show = await _libraryManager.Get(slug);
- return _files.FileResult(await _thumbs.GetPoster(show));
+ return _files.FileResult(await _thumbs.GetImagePath(show, Thumbnails.Poster));
}
catch (ItemNotFoundException)
{
@@ -431,7 +431,7 @@ namespace Kyoo.Api
try
{
Show show = await _libraryManager.Get(slug);
- return _files.FileResult(await _thumbs.GetLogo(show));
+ return _files.FileResult(await _thumbs.GetImagePath(show, Thumbnails.Logo));
}
catch (ItemNotFoundException)
{
@@ -446,7 +446,7 @@ namespace Kyoo.Api
try
{
Show show = await _libraryManager.Get(slug);
- return _files.FileResult(await _thumbs.GetThumbnail(show));
+ return _files.FileResult(await _thumbs.GetImagePath(show, Thumbnails.Thumbnail));
}
catch (ItemNotFoundException)
{