From 2d45d6422d56d082a26502230aca452c60f51b46 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Wed, 14 Jul 2021 01:50:05 +0200 Subject: [PATCH] Implementing the tvdb provider --- Kyoo.Common/Controllers/IMetadataProvider.cs | 2 +- Kyoo.TheTvdb/Convertors.cs | 157 +++++++++++ Kyoo.TheTvdb/PluginTVDB.cs | 6 +- Kyoo.TheTvdb/ProviderTVDB.cs | 260 ++++++------------- 4 files changed, 245 insertions(+), 180 deletions(-) create mode 100644 Kyoo.TheTvdb/Convertors.cs diff --git a/Kyoo.Common/Controllers/IMetadataProvider.cs b/Kyoo.Common/Controllers/IMetadataProvider.cs index 9c260030..f17e2d8d 100644 --- a/Kyoo.Common/Controllers/IMetadataProvider.cs +++ b/Kyoo.Common/Controllers/IMetadataProvider.cs @@ -35,7 +35,7 @@ namespace Kyoo.Controllers /// If this metadata provider does not support . /// /// A new containing metadata from your provider - [ItemNotNull] + [ItemCanBeNull] Task Get([NotNull] T item) where T : class, IResource; diff --git a/Kyoo.TheTvdb/Convertors.cs b/Kyoo.TheTvdb/Convertors.cs new file mode 100644 index 00000000..b7edd2ea --- /dev/null +++ b/Kyoo.TheTvdb/Convertors.cs @@ -0,0 +1,157 @@ +using System; +using System.Globalization; +using System.Linq; +using Kyoo.Models; +using TvDbSharper.Dto; + +namespace Kyoo.TheTvdb +{ + /// + /// A set of extensions methods used to convert tvdb models to Kyoo models. + /// + public static class Convertors + { + /// + /// Convert the string representation of the status in the tvdb API to a Kyoo's enum. + /// + /// The string representing the status. + /// A kyoo value or null. + private static Status? GetStatus(string status) + { + return status switch + { + "Ended" => Status.Finished, + "Continuing" => Status.Airing, + _ => null + }; + } + + /// + /// Parse a TVDB date and return a or null if the string is invalid. + /// + /// The date string to parse + /// The parsed or null. + private static DateTime ParseDate(string date) + { + DateTime.TryParseExact(date, "yyyy-mm-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsed); + return parsed; + } + + /// + /// Convert a series search to a show. + /// + /// The search result + /// The provider representing the tvdb inside kyoo + /// A show representing the given search result. + public static Show ToShow(this SeriesSearchResult result, Provider provider) + { + return new() + { + Slug = result.Slug, + Title = result.SeriesName, + Aliases = result.Aliases, + Overview = result.Overview, + Status = GetStatus(result.Status), + StartAir = ParseDate(result.FirstAired), + Poster = result.Poster != null ? $"https://www.thetvdb.com{result.Poster}" : null, + ExternalIDs = new[] + { + new MetadataID + { + DataID = result.Id.ToString(), + Link = $"https://www.thetvdb.com/series/{result.Slug}", + Second = provider + } + } + }; + } + + /// + /// Convert a tvdb series to a kyoo show. + /// + /// The series to convert + /// The provider representing the tvdb inside kyoo + /// A show representing the given series. + public static Show ToShow(this Series series, Provider provider) + { + return new() + { + Slug = series.Slug, + Title = series.SeriesName, + Aliases = series.Aliases, + 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, + Genres = series.Genre.Select(y => new Genre(y)).ToList(), + ExternalIDs = new[] + { + new MetadataID + { + DataID = series.Id.ToString(), + Link = $"https://www.thetvdb.com/series/{series.Slug}", + Second = provider + } + } + }; + } + + /// + /// Convert a tvdb actor to a kyoo . + /// + /// The actor to convert + /// The provider representing the tvdb inside kyoo + /// A people role representing the given actor in the role they played. + public static PeopleRole ToPeopleRole(this Actor actor, Provider provider) + { + return new() + { + People = new People + { + Slug = Utility.ToSlug(actor.Name), + Name = actor.Name, + Poster = actor.Image != null ? $"https://www.thetvdb.com/banners/{actor.Image}" : null, + ExternalIDs = new [] + { + new MetadataID() + { + DataID = actor.Id.ToString(), + Link = $"https://www.thetvdb.com/people/{actor.Id}", + Second = provider + } + } + }, + Role = actor.Role + }; + } + + /// + /// Convert a tvdb episode to a kyoo . + /// + /// The episode to convert + /// The provider representing the tvdb inside kyoo + /// A episode representing the given tvdb episode. + public static Episode ToEpisode(this EpisodeRecord episode, Provider provider) + { + return new() + { + SeasonNumber = episode.AiredSeason, + EpisodeNumber = episode.AiredEpisodeNumber, + AbsoluteNumber = episode.AbsoluteNumber, + Title = episode.EpisodeName, + Overview = episode.Overview, + Thumb = episode.Filename != null ? $"https://www.thetvdb.com/banners/{episode.Filename}" : null, + ExternalIDs = new[] + { + new MetadataID + { + DataID = episode.Id.ToString(), + Link = $"https://www.thetvdb.com/series/{episode.SeriesId}/episodes/{episode.Id}", + Second = provider + } + } + }; + } + } +} \ No newline at end of file diff --git a/Kyoo.TheTvdb/PluginTVDB.cs b/Kyoo.TheTvdb/PluginTVDB.cs index 6484b51d..ea4cab62 100644 --- a/Kyoo.TheTvdb/PluginTVDB.cs +++ b/Kyoo.TheTvdb/PluginTVDB.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using Autofac; using Kyoo.Controllers; -using Microsoft.Extensions.DependencyInjection; namespace Kyoo.TheTvdb { @@ -33,9 +33,9 @@ namespace Kyoo.TheTvdb /// - public void Configure(IServiceCollection services, ICollection availableTypes) + public void Configure(ContainerBuilder builder) { - // services.AddProvider(); + builder.RegisterProvider(); } } } \ No newline at end of file diff --git a/Kyoo.TheTvdb/ProviderTVDB.cs b/Kyoo.TheTvdb/ProviderTVDB.cs index eeb065c9..44841326 100644 --- a/Kyoo.TheTvdb/ProviderTVDB.cs +++ b/Kyoo.TheTvdb/ProviderTVDB.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading.Tasks; +using JetBrains.Annotations; using Kyoo.Controllers; using Kyoo.Models; using TvDbSharper; @@ -15,189 +15,97 @@ namespace Kyoo.TheTvdb /// public class ProviderTvdb : IMetadataProvider { - public Provider Provider { get; } - public Task Get(T item) where T : class, IResource + /// + /// The internal tvdb client used to make requests. + /// + private readonly TvDbClient _client = new(); + + /// + /// The API key used to authenticate with the tvdb API. + /// + private readonly string _apiKey; + + /// + public Provider Provider => new() { - throw new NotImplementedException(); + Slug = "the-tvdb", + Name = "TheTVDB", + LogoExtension = "png", + Logo = "https://www.thetvdb.com/images/logo.png" + }; + + + public ProviderTvdb(string apiKey) + { + _apiKey = apiKey; } - public Task> Search(string query) where T : class, IResource + private Task _Authenticate() { - throw new NotImplementedException(); + if (_client.Authentication.Token == null) + return _client.Authentication.AuthenticateAsync(_apiKey); + return _client.Authentication.RefreshTokenAsync(); + } + + /// + public async Task Get(T item) + where T : class, IResource + { + await _Authenticate(); + return item switch + { + Show show => await _GetShow(show) as T, + Episode episode => await _GetEpisode(episode) as T, + _ => throw new NotSupportedException() + }; + } + + [ItemCanBeNull] + private async Task _GetShow([NotNull] Show show) + { + if (!int.TryParse(show.GetID(Provider.Slug), out int id)) + return (await _SearchShow(show.Title)).FirstOrDefault(); + TvDbResponse series = await _client.Series.GetAsync(id); + return series.Data.ToShow(Provider); } - public Task> GetPeople(Show show) + [ItemCanBeNull] + private async Task _GetEpisode([NotNull] Episode episode) { + if (!int.TryParse(episode.Show?.GetID(Provider.Slug), out int id)) + return null; + EpisodeQuery query = episode.AbsoluteNumber != null + ? new EpisodeQuery {AbsoluteNumber = episode.AbsoluteNumber} + : new EpisodeQuery {AiredSeason = episode.SeasonNumber, AiredEpisode = episode.EpisodeNumber}; + TvDbResponse episodes = await _client.Series.GetEpisodesAsync(id, 0, query); + return episodes.Data.FirstOrDefault()?.ToEpisode(Provider); + } + + /// + public async Task> Search(string query) + where T : class, IResource + { + await _Authenticate(); + if (typeof(T) == typeof(Show)) + return (await _SearchShow(query) as ICollection)!; throw new NotImplementedException(); } + + [ItemNotNull] + private async Task> _SearchShow(string query) + { + TvDbResponse shows = await _client.Search.SearchSeriesByNameAsync(query); + return shows.Data.Select(x => x.ToShow(Provider)).ToArray(); + } + + /// + public async Task> GetPeople(Show show) + { + if (!int.TryParse(show?.GetID(Provider.Name), out int id)) + return null; + await _Authenticate(); + TvDbResponse people = await _client.Series.GetActorsAsync(id); + return people.Data.Select(x => x.ToPeopleRole(Provider)).ToArray(); + } } - - - // public class Old - // { - // private static readonly ProviderID _provider = new() - // { - // Slug = "the-tvdb", - // Name = "TheTVDB", - // LogoExtension = "png", - // Logo = "https://www.thetvdb.com/images/logo.png" - // }; - // public ProviderID Provider => _provider; - // - // - // private readonly TvDbClient _client = new(); - // - // private Task Authentificate() - // { - // if (_client.Authentication.Token == null) - // return _client.Authentication.AuthenticateAsync(APIKey); - // return _client.Authentication.RefreshTokenAsync(); - // } - // - // public Task GetCollectionFromName(string name) - // { - // return Task.FromResult(null); - // } - // - // public async Task> SearchShows(string showName, bool isMovie) - // { - // await Authentificate(); - // - // if (isMovie) - // return null; //There is no movie search API for now on TheTVDB. - // TvDbResponse shows = await _client.Search.SearchSeriesAsync(showName, SearchParameter.Name); - // return shows.Data.Select(x => x.ToShow(Provider)).ToArray(); - // } - // - // public async Task GetShowByID(Show show) - // { - // if (!int.TryParse(show?.GetID(Provider.Name), out int id)) - // return await Task.FromResult(null); - // await Authentificate(); - // TvDbResponse serie = await _client.Series.GetAsync(id); - // return serie.Data.ToShow(Provider); - // } - // - // public async Task> GetPeople(Show show) - // { - // if (!int.TryParse(show?.GetID(Provider.Name), out int id)) - // return null; - // await Authentificate(); - // TvDbResponse people = await _client.Series.GetActorsAsync(id); - // return people.Data.Select(x => x.ToPeopleRole(Provider)).ToArray(); - // } - // - // public Task GetSeason(Show show, int seasonNumber) - // { - // return Task.FromResult(null); - // } - // - // public async Task GetEpisode(Show show, int seasonNumber, int episodeNumber, int absoluteNumber) - // { - // if (!int.TryParse(show?.GetID(Provider.Name), out int id)) - // return null; - // await Authentificate(); - // TvDbResponse episodes = absoluteNumber != -1 - // ? await _client.Series.GetEpisodesAsync(id, 0, new EpisodeQuery {AbsoluteNumber = absoluteNumber}) - // : await _client.Series.GetEpisodesAsync(id, 0, new EpisodeQuery {AiredSeason = seasonNumber, AiredEpisode = episodeNumber}); - // EpisodeRecord x = episodes.Data[0]; - // - // if (absoluteNumber == -1) - // absoluteNumber = x.AbsoluteNumber ?? -1; - // else - // { - // seasonNumber = x.AiredSeason ?? -1; - // episodeNumber = x.AiredEpisodeNumber ?? -1; - // } - // - // return new Episode(seasonNumber, - // episodeNumber, - // absoluteNumber, - // x.EpisodeName, - // x.Overview, - // DateTime.ParseExact(x.FirstAired, "yyyy-MM-dd", CultureInfo.InvariantCulture), - // -1, - // x.Filename != null ? "https://www.thetvdb.com/banners/" + x.Filename : null, - // new [] - // { - // new MetadataID(Provider, x.Id.ToString(), $"https://www.thetvdb.com/series/{id}/episodes/{x.Id}") - // }); - // } - // } - - // public static class Convertors - // { - // private static int? GetYear(string firstAired) - // { - // if (firstAired?.Length >= 4 && int.TryParse(firstAired.Substring(0, 4), out int year)) - // return year; - // return null; - // } - // - // private static Status? GetStatus(string status) - // { - // return status switch - // { - // "Ended" => Status.Finished, - // "Continuing" => Status.Airing, - // _ => null - // }; - // } - // - // public static Show ToShow(this SeriesSearchResult x, ProviderID provider) - // { - // Show ret = new(x.Slug, - // x.SeriesName, - // x.Aliases, - // null, - // x.Overview, - // null, - // null, - // GetStatus(x.Status), - // GetYear(x.FirstAired), - // null, - // new[] - // { - // new MetadataID(provider, x.Id.ToString(), $"https://www.thetvdb.com/series/{x.Slug}") - // }); - // if (x.Poster != null) - // Utility.SetImage(ret, $"https://www.thetvdb.com{x.Poster}", ImageType.Poster); - // return ret; - // } - // - // public static Show ToShow(this Series x, ProviderID provider) - // { - // return new(x.Slug, - // x.SeriesName, - // x.Aliases, - // null, - // x.Overview, - // null, - // x.Genre.Select(y => new Genre(Utility.ToSlug(y), y)), - // GetStatus(x.Status), - // GetYear(x.FirstAired), - // null, - // new[] - // { - // new MetadataID(provider, x.Id.ToString(),$"https://www.thetvdb.com/series/{x.Slug}") - // }) - // { - // Poster = x.Poster != null ? $"https://www.thetvdb.com/banners/{x.Poster}" : null, - // Backdrop = x.FanArt != null ? $"https://www.thetvdb.com/banners/{x.FanArt}" : null - // }; - // } - // - // public static PeopleRole ToPeopleRole(this Actor x, ProviderID provider) - // { - // return new (Utility.ToSlug(x.Name), - // x.Name, - // x.Role, - // null, - // x.Image != null ? $"https://www.thetvdb.com/banners/{x.Image}" : null, - // new[] - // { - // new MetadataID(provider, x.Id.ToString(), $"https://www.thetvdb.com/people/{x.Id}") - // }); - // } - // } } \ No newline at end of file