using Kyoo.InternalAPI.MetadataProvider.TheTvDB; using Kyoo.Models; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Threading.Tasks; using System.Web; namespace Kyoo.InternalAPI.MetadataProvider { [MetaProvider] public class ProviderTheTvDB : HelperTvDB, IMetadataProvider { private struct SearchTbDB { public string seriesName; public string overview; public string slug; public string network; public string status; public int id; public string firstAired; public string banner; public string[] aliases; } private struct DataTvDb { public string seriesName; public string overview; public string slug; public string network; public string status; public int id; public string seriesId; public string imdbId; public string zap2itId; public string firstAired; public string banner; public string[] aliases; public string[] genre; public string added; public string airsDayOfWeek; public string airsTime; public string lastUpdated; public string runtime; public string networkId; public string rating; public float siteRating; public int siteRatingCount; } private struct RatingInfo { public float average; public int count; } private struct ImageTvDb { public string fileName; public int id; public string keyType; public int languageId; public RatingInfo ratingsInfo; public string resolution; public string subKey; public string thumbnail; } private struct ErrorsTvDB { public string[] invalidFilters; public string invalidLanguage; public string[] invalidQueryParams; } public async Task GetShowFromName(string showName, string showPath) { string token = await Authentificate(); if (token != null) { WebRequest request = WebRequest.Create("https://api.thetvdb.com/search/series?name=" + HttpUtility.UrlEncode(showName)); request.Method = "GET"; request.Timeout = 12000; request.ContentType = "application/json"; request.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token); try { HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync(); if (response.StatusCode == HttpStatusCode.OK) { Stream stream = response.GetResponseStream(); using (StreamReader reader = new StreamReader(stream)) { string content = await reader.ReadToEndAsync(); stream.Close(); response.Close(); var model = new { data = new SearchTbDB[0] }; SearchTbDB data = JsonConvert.DeserializeAnonymousType(content, model).data[0]; Show show = new Show(-1, ToSlug(showName), data.seriesName, data.aliases, showPath, data.overview, null, //genres (no info with this request) GetStatus(data.status), GetYear(data.firstAired), null, //endYear string.Format("{0}={1}|", Provider, data.id)); return (await GetShowByID(GetID(show.ExternalIDs))).Set(show.Slug, show.Path) ?? show; } } else { Debug.WriteLine("&TheTvDB Provider couldn't work for this show: " + showName + ".\nError Code: " + response.StatusCode + " Message: " + response.StatusDescription); response.Close(); } } catch (WebException ex) { Debug.WriteLine("&TheTvDB Provider couldn't work for this show: " + showName + ".\nError Code: " + ex.Status); } } return new Show() { Slug = ToSlug(showName), Title = showName, Path = showPath }; } public async Task GetShowByID(string id) { string token = await Authentificate(); if (token == null) return null; WebRequest request = WebRequest.Create("https://api.thetvdb.com/series/" + id); request.Method = "GET"; request.Timeout = 12000; request.ContentType = "application/json"; request.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token); try { HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync(); if (response.StatusCode == HttpStatusCode.OK) { Stream stream = response.GetResponseStream(); using (StreamReader reader = new StreamReader(stream)) { string content = await reader.ReadToEndAsync(); stream.Close(); response.Close(); var model = new { data = new DataTvDb(), errors = new ErrorsTvDB() }; DataTvDb data = JsonConvert.DeserializeAnonymousType(content, model).data; Show show = new Show(-1, null, //Slug data.seriesName, data.aliases, null, //Path data.overview, GetGenres(data.genre), GetStatus(data.status), GetYear(data.firstAired), null, //endYear string.Format("TvDB={0}|", id)); await GetImages(show); return show; } } else { Debug.WriteLine("&TheTvDB Provider couldn't work for the show with the id: " + id + ".\nError Code: " + response.StatusCode + " Message: " + response.StatusDescription); response.Close(); return null; } } catch(WebException ex) { Debug.WriteLine("&TheTvDB Provider couldn't work for the show with the id: " + id + ".\nError Code: " + ex.Status); return null; } } public async Task GetImages(Show show) { Debug.WriteLine("&Getting images for: " + show.Title); string id = GetID(show.ExternalIDs); if (id == null) return show; string token = await Authentificate(); if (token == null) return show; Dictionary imageTypes = new Dictionary { { ImageType.Poster, "poster" }, { ImageType.Background, "fanart" } }; foreach (KeyValuePair type in imageTypes) { try { WebRequest request = WebRequest.Create("https://api.thetvdb.com/series/" + id + "/images/query?keyType=" + type.Value); request.Method = "GET"; request.Timeout = 12000; request.ContentType = "application/json"; request.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token); HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync(); if (response.StatusCode == HttpStatusCode.OK) { Stream stream = response.GetResponseStream(); using (StreamReader reader = new StreamReader(stream)) { string content = await reader.ReadToEndAsync(); stream.Close(); response.Close(); var model = new { data = new ImageTvDb[0], error = new ErrorsTvDB() }; //Should implement language selection here ImageTvDb data = JsonConvert.DeserializeAnonymousType(content, model).data.OrderByDescending(x => x.ratingsInfo.average).ThenByDescending(x => x.ratingsInfo.count).FirstOrDefault(); IEnumerable datas = JsonConvert.DeserializeAnonymousType(content, model).data.OrderByDescending(x => x.ratingsInfo.average).ThenByDescending(x => x.ratingsInfo.count); SetImage(show, "https://www.thetvdb.com/banners/" + data.fileName, type.Key); } } else { Debug.WriteLine("&TheTvDB Provider couldn't get " + type + " for the show with the id: " + id + ".\nError Code: " + response.StatusCode + " Message: " + response.StatusDescription); response.Close(); } } catch (WebException ex) { Debug.WriteLine("&TheTvDB Provider couldn't get " + type + " for the show with the id: " + id + ".\nError Code: " + ex.Status); } } return show; } #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously public async Task GetSeason(string showName, long seasonNumber) #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { return new Season(-1, -1, seasonNumber, "Season " + seasonNumber, null, null, null, null); } public Task GetSeasonImage(string showName, long seasonNumber) { return null; } public async Task GetEpisode(string externalIDs, long seasonNumber, long episodeNumber, string episodePath) { string id = GetID(externalIDs); if (id == null) return null; string token = await Authentificate(); if (token == null) return null; int page = (int)episodeNumber / 100 + 1; int index = (int)episodeNumber % 100 - 1; //The -1 is for array binding WebRequest request = WebRequest.Create("https://api.thetvdb.com/series/" + id + "/episodes?page=" + page); request.Method = "GET"; request.Timeout = 12000; request.ContentType = "application/json"; request.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token); try { HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync(); if (response.StatusCode == HttpStatusCode.OK) { Stream stream = response.GetResponseStream(); using (StreamReader reader = new StreamReader(stream)) { string content = await reader.ReadToEndAsync(); stream.Close(); response.Close(); dynamic data = JsonConvert.DeserializeObject(content); dynamic episode = data.data[index]; DateTime dateTime = DateTime.ParseExact((string)episode.firstAired, "yyyy-MM-dd", CultureInfo.InvariantCulture); return new Episode(seasonNumber, episodeNumber, (string)episode.episodeName, (string)episode.overview, dateTime, -1, "https://www.thetvdb.com/banners/" + episode.filename, string.Format("TvDB={0}|", episode.id)); } } else { Debug.WriteLine("&TheTvDB Provider couldn't work for the episode number: " + episodeNumber + ".\nError Code: " + response.StatusCode + " Message: " + response.StatusDescription); response.Close(); return null; } } catch (WebException ex) { Debug.WriteLine("&TheTvDB Provider couldn't work for the episode number: " + episodeNumber + ".\nError Code: " + ex.Status); return null; } } public async Task> GetPeople(string externalIDs) { string id = GetID(externalIDs); if (id == null) return null; string token = await Authentificate(); if (token == null) return null; WebRequest request = WebRequest.Create("https://api.thetvdb.com/series/" + id + "/actors"); request.Method = "GET"; request.Timeout = 12000; request.ContentType = "application/json"; request.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token); try { HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync(); if (response.StatusCode == HttpStatusCode.OK) { Stream stream = response.GetResponseStream(); using (StreamReader reader = new StreamReader(stream)) { string content = await reader.ReadToEndAsync(); stream.Close(); response.Close(); dynamic data = JsonConvert.DeserializeObject(content); return (((IEnumerable)data.data).OrderBy(x => x.sortOrder)).ToList().ConvertAll(x => { return new People(-1, ToSlug((string)x.name), (string)x.name, (string)x.role, null, "https://www.thetvdb.com/banners/" + (string)x.image, string.Format("TvDB={0}|", x.id)); }); } } else { Debug.WriteLine("&TheTvDB Provider couldn't work for the actors of the show: " + id + ".\nError Code: " + response.StatusCode + " Message: " + response.StatusDescription); response.Close(); return null; } } catch (WebException ex) { Debug.WriteLine("&TheTvDB Provider couldn't work for the actors of the show: " + id + ".\nError Code: " + ex.Status); return null; } } } }