From 9729ae52a3351b389869fd032da2ebca5ab3b77e Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Tue, 29 Jan 2019 23:09:48 +0100 Subject: [PATCH 01/39] initial commit --- .../MediaBrowser.Providers.csproj | 3 +- .../TV/TheTVDB/TvdbSeriesProvider.cs | 881 ++++-------------- 2 files changed, 183 insertions(+), 701 deletions(-) diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index e6ef889c3e..1cf1ab256a 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -1,4 +1,4 @@ - + @@ -12,6 +12,7 @@ + diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 52e60a8eda..858a4fa9d1 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -12,7 +12,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -22,6 +21,9 @@ using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; +using TvDbSharper; +using TvDbSharper.Dto; +using Series = MediaBrowser.Controller.Entities.TV.Series; namespace MediaBrowser.Providers.TV.TheTVDB { @@ -37,6 +39,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localizationManager; + private readonly TvDbClient _tvDbClient; public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager) { @@ -49,15 +52,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB _xmlSettings = xmlSettings; _localizationManager = localizationManager; Current = this; + _tvDbClient = new TvDbClient(); } - public const string TvdbBaseUrl = "https://www.thetvdb.com/"; - - private const string SeriesSearchUrl = TvdbBaseUrl + "api/GetSeries.php?seriesname={0}&language={1}"; - private const string SeriesGetZip = TvdbBaseUrl + "api/{0}/series/{1}/all/{2}.zip"; - private const string GetSeriesByImdbId = TvdbBaseUrl + "api/GetSeriesByRemoteID.php?imdbid={0}&language={1}"; - private const string GetSeriesByZap2ItId = TvdbBaseUrl + "api/GetSeriesByRemoteID.php?zap2it={0}&language={1}"; - private string NormalizeLanguage(string language) { if (string.IsNullOrWhiteSpace(language)) @@ -135,29 +132,34 @@ namespace MediaBrowser.Providers.TV.TheTVDB /// Task{System.Boolean}. private void FetchSeriesData(MetadataResult result, string metadataLanguage, Dictionary seriesProviderIds, CancellationToken cancellationToken) { + _tvDbClient.AcceptedLanguage = NormalizeLanguage(metadataLanguage); var series = result.Item; + TvDbResponse searchResult; - if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out string id) && !string.IsNullOrEmpty(id)) + if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(id)) { - series.SetProviderId(MetadataProviders.Tvdb, id); + series.SetProviderId(MetadataProviders.Tvdb, tvdbId); } - if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id) && !string.IsNullOrEmpty(id)) + if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out var imdbId) && !string.IsNullOrEmpty(id)) { - series.SetProviderId(MetadataProviders.Imdb, id); + series.SetProviderId(MetadataProviders.Imdb, imdbId); + tvdbId = GetSeriesByRemoteId(imdbId, MetadataProviders.Imdb.ToString(), metadataLanguage, cancellationToken).Result.ToString(); } - if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out id) && !string.IsNullOrEmpty(id)) + if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out var zap2It) && !string.IsNullOrEmpty(id)) { - series.SetProviderId(MetadataProviders.Zap2It, id); + series.SetProviderId(MetadataProviders.Zap2It, zap2It); + tvdbId = GetSeriesByRemoteId(zap2It, MetadataProviders.Zap2It.ToString(), metadataLanguage, cancellationToken).Result.ToString(); } - var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); + var seriesResult = _tvDbClient.Series.GetAsync(Convert.ToInt32(tvdbId), cancellationToken).Result; +// var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); +// +// var seriesXmlPath = GetSeriesXmlPath(seriesProviderIds, metadataLanguage); +// var actorsXmlPath = Path.Combine(seriesDataPath, "actors.xml"); - var seriesXmlPath = GetSeriesXmlPath(seriesProviderIds, metadataLanguage); - var actorsXmlPath = Path.Combine(seriesDataPath, "actors.xml"); - - FetchSeriesInfo(result, seriesXmlPath, cancellationToken); + FetchSeriesInfo(result, searchResult, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); @@ -199,7 +201,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB if (!string.Equals(idType, "tvdb", StringComparison.OrdinalIgnoreCase)) { - seriesId = await GetSeriesByRemoteId(seriesId, idType, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); + seriesId = (await GetSeriesByRemoteId(seriesId, idType, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false)).ToString(); } // If searching by remote id came up empty, then do a regular search @@ -269,227 +271,31 @@ namespace MediaBrowser.Providers.TV.TheTVDB await ExtractEpisodes(seriesDataPath, downloadLangaugeXmlFile, lastTvDbUpdateTime).ConfigureAwait(false); } - private async Task GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken) + private async Task GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken) { - string url; + _tvDbClient.AcceptedLanguage = NormalizeLanguage(language); + TvDbResponse result; + if (string.Equals(idType, MetadataProviders.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase)) { - url = string.Format(GetSeriesByZap2ItId, id, NormalizeLanguage(language)); + result = await _tvDbClient.Search.SearchSeriesByZap2ItIdAsync(id, cancellationToken); } else { - url = string.Format(GetSeriesByImdbId, id, NormalizeLanguage(language)); + result = await _tvDbClient.Search.SearchSeriesByImdbIdAsync(id, cancellationToken); } - using (var response = await _httpClient.SendAsync(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - BufferContent = false - - }, "GET").ConfigureAwait(false)) - { - using (var result = response.Content) - { - return FindSeriesId(result); - } - } - } - - private string FindSeriesId(Stream stream) - { - using (var streamReader = new StreamReader(stream, Encoding.UTF8)) - { - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Series": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - using (var subtree = reader.ReadSubtree()) - { - return FindSeriesId(subtree); - } - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } - } - - return null; - } - - private string FindSeriesId(XmlReader reader) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "seriesid": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - return val; - } - - return null; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - - return null; + return result.Data.First().Id; } internal static bool IsValidSeries(Dictionary seriesProviderIds) { - if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out string id)) - { - // This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet. - if (!string.IsNullOrWhiteSpace(id)) - { - return true; - } - } - - if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id)) - { - // This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet. - if (!string.IsNullOrWhiteSpace(id)) - { - return true; - } - } - - if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out id)) - { - // This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet. - if (!string.IsNullOrWhiteSpace(id)) - { - return true; - } - } - return false; - } - - private SemaphoreSlim _ensureSemaphore = new SemaphoreSlim(1, 1); - internal async Task EnsureSeriesInfo(Dictionary seriesProviderIds, string seriesName, int? seriesYear, string preferredMetadataLanguage, CancellationToken cancellationToken) - { - await _ensureSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out string seriesId) && !string.IsNullOrWhiteSpace(seriesId)) - { - var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); - - // Only download if not already there - // The post-scan task will take care of updates so we don't need to re-download here - if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage)) - { - await DownloadSeriesZip(seriesId, MetadataProviders.Tvdb.ToString(), seriesName, seriesYear, seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); - } - - return seriesDataPath; - } - - if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out seriesId) && !string.IsNullOrWhiteSpace(seriesId)) - { - var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); - - // Only download if not already there - // The post-scan task will take care of updates so we don't need to re-download here - if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage)) - { - try - { - await DownloadSeriesZip(seriesId, MetadataProviders.Imdb.ToString(), seriesName, seriesYear, seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); - } - catch (ArgumentNullException) - { - // Unable to determine tvdb id based on imdb id - return null; - } - } - - return seriesDataPath; - } - - if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out seriesId) && !string.IsNullOrWhiteSpace(seriesId)) - { - var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); - - // Only download if not already there - // The post-scan task will take care of updates so we don't need to re-download here - if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage)) - { - try - { - await DownloadSeriesZip(seriesId, MetadataProviders.Zap2It.ToString(), seriesName, seriesYear, seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); - } - catch (ArgumentNullException) - { - // Unable to determine tvdb id based on Zap2It id - return null; - } - } - - return seriesDataPath; - } - - return null; - } - finally - { - _ensureSemaphore.Release(); - } + return seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out _) || + seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out _) || + seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out _); } + // TODO caching private bool IsCacheValid(string seriesDataPath, string preferredMetadataLanguage) { try @@ -543,7 +349,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB /// Task{System.String}. private async Task> FindSeries(string name, int? year, string language, CancellationToken cancellationToken) { - var results = (await FindSeriesInternal(name, language, cancellationToken).ConfigureAwait(false)); + var results = await FindSeriesInternal(name, language, cancellationToken).ConfigureAwait(false); if (results.Count == 0) { @@ -552,7 +358,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB if (!string.IsNullOrWhiteSpace(nameWithoutYear) && !string.Equals(nameWithoutYear, name, StringComparison.OrdinalIgnoreCase)) { - results = (await FindSeriesInternal(nameWithoutYear, language, cancellationToken).ConfigureAwait(false)); + results = await FindSeriesInternal(nameWithoutYear, language, cancellationToken).ConfigureAwait(false); } } @@ -570,77 +376,32 @@ namespace MediaBrowser.Providers.TV.TheTVDB private async Task> FindSeriesInternal(string name, string language, CancellationToken cancellationToken) { - var url = string.Format(SeriesSearchUrl, WebUtility.UrlEncode(name), NormalizeLanguage(language)); - + _tvDbClient.AcceptedLanguage = NormalizeLanguage(language); var comparableName = GetComparableName(name); - var list = new List, RemoteSearchResult>>(); + TvDbResponse result = await _tvDbClient.Search.SearchSeriesByNameAsync(comparableName, cancellationToken); - using (var response = await _httpClient.SendAsync(new HttpRequestOptions + foreach (var seriesSearchResult in result.Data) { - Url = url, - CancellationToken = cancellationToken, - BufferContent = false - - }, "GET").ConfigureAwait(false)) - { - using (var stream = response.Content) + var tvdbTitles = new List { - var settings = _xmlSettings.Create(false); + GetComparableName(seriesSearchResult.SeriesName) + }; + tvdbTitles.AddRange(seriesSearchResult.Aliases.Select(GetComparableName)); - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; + DateTime.TryParse(seriesSearchResult.FirstAired, out var firstAired); + var remoteSearchResult = new RemoteSearchResult + { + Name = tvdbTitles.FirstOrDefault(), + ProductionYear = firstAired.Year, + SearchProviderName = Name, + ImageUrl = seriesSearchResult.Banner - using (var streamReader = new StreamReader(stream, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Series": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - using (var subtree = reader.ReadSubtree()) - { - var searchResultInfo = GetSeriesSearchResultFromSubTree(subtree); - if (searchResultInfo != null) - { - searchResultInfo.Item2.SearchProviderName = Name; - list.Add(searchResultInfo); - } - } - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } - } - } + }; + // TODO requires another query + // remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSearchResult.Id); + remoteSearchResult.SetProviderId(MetadataProviders.Tvdb, seriesSearchResult.Id.ToString()); + list.Add(new Tuple, RemoteSearchResult>(tvdbTitles, remoteSearchResult)); } return list @@ -650,116 +411,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB .ToList(); } - private Tuple, RemoteSearchResult> GetSeriesSearchResultFromSubTree(XmlReader reader) - { - var searchResult = new RemoteSearchResult - { - SearchProviderName = Name - }; - - var tvdbTitles = new List(); - string seriesId = null; - - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "SeriesName": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - tvdbTitles.Add(GetComparableName(val)); - } - break; - } - - case "AliasNames": - { - var val = reader.ReadElementContentAsString(); - - var alias = (val ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).Select(GetComparableName); - tvdbTitles.AddRange(alias); - break; - } - - case "IMDB_ID": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - searchResult.SetProviderId(MetadataProviders.Imdb, val); - } - break; - } - - case "banner": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - searchResult.ImageUrl = TVUtils.BannerUrl + val; - } - break; - } - - case "FirstAired": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (DateTime.TryParse(val, out var date)) - { - searchResult.ProductionYear = date.Year; - } - } - break; - } - - case "id": - case "seriesid": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - seriesId = val; - } - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - - if (tvdbTitles.Count == 0) - { - return null; - } - - searchResult.Name = tvdbTitles.FirstOrDefault(); - searchResult.SetProviderId(MetadataProviders.Tvdb, seriesId); - - return new Tuple, RemoteSearchResult>(tvdbTitles, searchResult); - } - /// /// The remove /// @@ -817,85 +468,141 @@ namespace MediaBrowser.Providers.TV.TheTVDB return name.Trim(); } - private void FetchSeriesInfo(MetadataResult result, string seriesXmlPath, CancellationToken cancellationToken) + private void FetchSeriesInfo(MetadataResult result, TvDbResponse seriesResponse, CancellationToken cancellationToken) { - var settings = _xmlSettings.Create(false); + var episodeAirDates = new List(); + var series = result.Item; + Series item = result.Item; + series.SetProviderId(MetadataProviders.Tvdb, seriesResponse.Data.Id.ToString()); + series.Name = seriesResponse.Data.SeriesName; + series.Overview = (seriesResponse.Data.Overview ?? string.Empty).Trim(); + // TODO result.ResultLanguage = (seriesResponse.Data. ?? string.Empty).Trim(); + series.AirDays = TVUtils.GetAirDays(seriesResponse.Data.AirsDayOfWeek); + series.AirTime = seriesResponse.Data.AirsTime; - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - var episiodeAirDates = new List(); - - using (var fileStream = _fileSystem.GetFileStream(seriesXmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) + series.CommunityRating = (float?)seriesResponse.Data.SiteRating; + series.SetProviderId(MetadataProviders.Imdb, seriesResponse.Data.ImdbId); + series.SetProviderId(MetadataProviders.Zap2It, seriesResponse.Data.Zap2itId); + if (Enum.TryParse(seriesResponse.Data.Status, true, out SeriesStatus seriesStatus)) { - using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) + series.Status = seriesStatus; + } + + if (DateTime.TryParse(seriesResponse.Data.FirstAired, out var date)) + { + date = date.ToUniversalTime(); + + series.PremiereDate = date; + series.ProductionYear = date.Year; + } + + series.RunTimeTicks = TimeSpan.FromMinutes(Convert.ToDouble(seriesResponse.Data.Runtime)).Ticks; + + + if (!string.IsNullOrWhiteSpace(val)) + { + var vals = val + .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) + .Select(i => i.Trim()) + .Where(i => !string.IsNullOrWhiteSpace(i)) + .ToList(); + + if (vals.Count > 0) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) + item.Genres = Array.Empty(); + + foreach (var genre in vals) { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Series": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - using (var subtree = reader.ReadSubtree()) - { - FetchDataFromSeriesNode(result, subtree, cancellationToken); - } - break; - } - - case "Episode": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - using (var subtree = reader.ReadSubtree()) - { - var date = GetFirstAiredDateFromEpisodeNode(subtree, cancellationToken); - - if (date.HasValue) - { - episiodeAirDates.Add(date.Value); - } - } - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } + item.AddGenre(genre); } } } - if (result.Item.Status.HasValue && result.Item.Status.Value == SeriesStatus.Ended && episiodeAirDates.Count > 0) + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) { - result.Item.EndDate = episiodeAirDates.Max(); + var vals = val + .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) + .Select(i => i.Trim()) + .Where(i => !string.IsNullOrWhiteSpace(i)) + .ToList(); + + if (vals.Count > 0) + { + item.SetStudios(vals); + } + } + +// using (var fileStream = _fileSystem.GetFileStream(seriesXmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) +// { +// using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) +// { +// // Use XmlReader for best performance +// using (var reader = XmlReader.Create(streamReader, settings)) +// { +// reader.MoveToContent(); +// reader.Read(); +// +// // Loop through each element +// while (!reader.EOF && reader.ReadState == ReadState.Interactive) +// { +// cancellationToken.ThrowIfCancellationRequested(); +// +// if (reader.NodeType == XmlNodeType.Element) +// { +// switch (reader.Name) +// { +// case "Series": +// { +// if (reader.IsEmptyElement) +// { +// reader.Read(); +// continue; +// } +// using (var subtree = reader.ReadSubtree()) +// { +// FetchDataFromSeriesNode(result, subtree, cancellationToken); +// } +// break; +// } +// +// case "Episode": +// { +// if (reader.IsEmptyElement) +// { +// reader.Read(); +// continue; +// } +// using (var subtree = reader.ReadSubtree()) +// { +// var date = GetFirstAiredDateFromEpisodeNode(subtree, cancellationToken); +// +// if (date.HasValue) +// { +// episiodeAirDates.Add(date.Value); +// } +// } +// break; +// } +// +// default: +// reader.Skip(); +// break; +// } +// } +// else +// { +// reader.Read(); +// } +// } +// } +// } +// } + + if (result.Item.Status.HasValue && result.Item.Status.Value == SeriesStatus.Ended && episodeAirDates.Count > 0) + { + result.Item.EndDate = episodeAirDates.Max(); } } @@ -1107,237 +814,11 @@ namespace MediaBrowser.Providers.TV.TheTVDB } } - private void FetchDataFromSeriesNode(MetadataResult result, XmlReader reader, CancellationToken cancellationToken) + private void FetchDataFromSeriesNode(MetadataResult result, CancellationToken cancellationToken) { - Series item = result.Item; + cancellationToken.ThrowIfCancellationRequested(); - reader.MoveToContent(); - reader.Read(); - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "id": - { - item.SetProviderId(MetadataProviders.Tvdb.ToString(), (reader.ReadElementContentAsString() ?? string.Empty).Trim()); - break; - } - - case "SeriesName": - { - item.Name = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - break; - } - - case "Overview": - { - item.Overview = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - break; - } - - case "Language": - { - result.ResultLanguage = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - break; - } - - case "Airs_DayOfWeek": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.AirDays = TVUtils.GetAirDays(val); - } - break; - } - - case "Airs_Time": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.AirTime = val; - } - break; - } - - case "ContentRating": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.OfficialRating = val; - } - break; - } - - case "Rating": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // float.TryParse is local aware, so it can be probamatic, force us culture - if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out var rval)) - { - item.CommunityRating = rval; - } - } - break; - } - case "RatingCount": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - //item.VoteCount = rval; - } - } - - break; - } - - case "IMDB_ID": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.SetProviderId(MetadataProviders.Imdb, val); - } - - break; - } - - case "zap2it_id": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.SetProviderId(MetadataProviders.Zap2It, val); - } - - break; - } - - case "Status": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (Enum.TryParse(val, true, out SeriesStatus seriesStatus)) - item.Status = seriesStatus; - } - - break; - } - - case "FirstAired": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (DateTime.TryParse(val, out var date)) - { - date = date.ToUniversalTime(); - - item.PremiereDate = date; - item.ProductionYear = date.Year; - } - } - - break; - } - - case "Runtime": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - item.RunTimeTicks = TimeSpan.FromMinutes(rval).Ticks; - } - } - - break; - } - - case "Genre": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - var vals = val - .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) - .Select(i => i.Trim()) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .ToList(); - - if (vals.Count > 0) - { - item.Genres = Array.Empty(); - - foreach (var genre in vals) - { - item.AddGenre(genre); - } - } - } - - break; - } - - case "Network": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - var vals = val - .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) - .Select(i => i.Trim()) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .ToList(); - - if (vals.Count > 0) - { - item.SetStudios(vals); - } - } - - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } } /// From c2202be0f86f52decc926bb7d14405c23a5f6c33 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 30 Jan 2019 21:23:23 +0100 Subject: [PATCH 02/39] Some of it works??? --- MediaBrowser.Controller/Library/TVUtils.cs | 2 +- .../People/TvdbPersonImageProvider.cs | 17 +- .../TV/MissingEpisodeProvider.cs | 20 +- .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 112 +- .../TV/TheTVDB/TvdbEpisodeProvider.cs | 1770 ++++++++--------- .../TV/TheTVDB/TvdbPrescanTask.cs | 7 +- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 306 +-- .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 323 +-- .../TV/TheTVDB/TvdbSeriesProvider.cs | 879 +------- 9 files changed, 1082 insertions(+), 2354 deletions(-) diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs index 5b66e74978..3080143ce9 100644 --- a/MediaBrowser.Controller/Library/TVUtils.cs +++ b/MediaBrowser.Controller/Library/TVUtils.cs @@ -10,7 +10,7 @@ namespace MediaBrowser.Controller.Library /// /// The TVDB API key /// - public static readonly string TvdbApiKey = "72930AE1CB7E2DB3"; + public static readonly string TvdbApiKey = "OG4V3YJ3FAP7FP2K"; public static readonly string TvdbBaseUrl = "https://www.thetvdb.com/"; /// /// The banner URL diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index 181e888201..2e0c450bb7 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -80,22 +80,9 @@ namespace MediaBrowser.Providers.People private RemoteImageInfo GetImageFromSeriesData(Series series, string personName, CancellationToken cancellationToken) { - var tvdbPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds); + //var actorsResult = await _tvDbClient.Series.GetActorsAsync(Convert.ToInt32(tvdbId), cancellationToken); - var actorXmlPath = Path.Combine(tvdbPath, "actors.xml"); - - try - { - return GetImageInfo(actorXmlPath, personName, cancellationToken); - } - catch (FileNotFoundException) - { - return null; - } - catch (IOException) - { - return null; - } + return null; // GetImageInfo(actorXmlPath, personName, cancellationToken); } private RemoteImageInfo GetImageInfo(string xmlFile, string personName, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 25ad36620a..70765718c9 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -44,27 +44,15 @@ namespace MediaBrowser.Providers.TV public async Task Run(Series series, bool addNewItems, CancellationToken cancellationToken) { + // TODO cvium fixme wtfisthisandwhydoesitrunwhenoptionisdisabled + return true; var tvdbId = series.GetProviderId(MetadataProviders.Tvdb); // Todo: Support series by imdb id var seriesProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); seriesProviderIds[MetadataProviders.Tvdb.ToString()] = tvdbId; - var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); - - // Doesn't have required provider id's - if (string.IsNullOrWhiteSpace(seriesDataPath)) - { - return false; - } - - // Check this in order to avoid logging an exception due to directory not existing - if (!Directory.Exists(seriesDataPath)) - { - return false; - } - - var episodeFiles = _fileSystem.GetFilePaths(seriesDataPath) + var episodeFiles = _fileSystem.GetFilePaths("") .Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase)) .Select(Path.GetFileNameWithoutExtension) .Where(i => i.StartsWith("episode-", StringComparison.OrdinalIgnoreCase)) @@ -118,7 +106,7 @@ namespace MediaBrowser.Providers.TV if (addNewItems && series.IsMetadataFetcherEnabled(_libraryManager.GetLibraryOptions(series), TvdbSeriesProvider.Current.Name)) { - hasNewEpisodes = await AddMissingEpisodes(series, allRecursiveChildren, addMissingEpisodes, seriesDataPath, episodeLookup, cancellationToken) + hasNewEpisodes = await AddMissingEpisodes(series, allRecursiveChildren, addMissingEpisodes, "", episodeLookup, cancellationToken) .ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 102a3d4ec0..75547610db 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -1,9 +1,8 @@ +using System; using System.Collections.Generic; using System.Globalization; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Xml; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -11,23 +10,23 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; +using TvDbSharper; +using TvDbSharper.Dto; namespace MediaBrowser.Providers.TV.TheTVDB { public class TvdbEpisodeImageProvider : IRemoteImageProvider { - private readonly IServerConfigurationManager _config; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IHttpClient _httpClient; - private readonly IFileSystem _fileSystem; + private readonly TvDbClient _tvDbClient; - public TvdbEpisodeImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) + public TvdbEpisodeImageProvider(IHttpClient httpClient) { - _config = config; _httpClient = httpClient; - _fileSystem = fileSystem; + _tvDbClient = new TvDbClient(); + _tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey); } public string Name => "TheTVDB"; @@ -45,113 +44,44 @@ namespace MediaBrowser.Providers.TV.TheTVDB }; } - public Task> GetImages(BaseItem item, CancellationToken cancellationToken) + public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { var episode = (Episode)item; var series = episode.Series; if (series != null && TvdbSeriesProvider.IsValidSeries(series.ProviderIds)) { + var tvdbId = episode.GetProviderId(MetadataProviders.Tvdb); // Process images - var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds); + var episodeResult = await _tvDbClient.Episodes.GetAsync(Convert.ToInt32(tvdbId), cancellationToken); - var nodes = TvdbEpisodeProvider.Current.GetEpisodeXmlNodes(seriesDataPath, episode.GetLookupInfo()); - - var result = nodes.Select(i => GetImageInfo(i, cancellationToken)) - .Where(i => i != null) - .ToList(); - - return Task.FromResult>(result); + var image = GetImageInfo(episodeResult.Data); + return new List + { + image + }; } - return Task.FromResult>(new RemoteImageInfo[] { }); + return new RemoteImageInfo[] { }; } - private RemoteImageInfo GetImageInfo(XmlReader reader, CancellationToken cancellationToken) + private RemoteImageInfo GetImageInfo(EpisodeRecord episode) { var height = 225; var width = 400; var url = string.Empty; - // Use XmlReader for best performance - using (reader) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - cancellationToken.ThrowIfCancellationRequested(); - - switch (reader.Name) - { - case "thumb_width": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - width = rval; - } - } - break; - } - - case "thumb_height": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - height = rval; - } - } - break; - } - - case "filename": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - url = TVUtils.BannerUrl + val; - } - break; - } - default: - { - reader.Skip(); - break; - } - } - } - else - { - reader.Read(); - } - } - } - - if (string.IsNullOrEmpty(url)) + if (string.IsNullOrEmpty(episode.Filename)) { return null; } return new RemoteImageInfo { - Width = width, - Height = height, + Width = Convert.ToInt32(episode.ThumbWidth), + Height = Convert.ToInt32(episode.ThumbHeight), ProviderName = Name, - Url = url, + Url = TVUtils.BannerUrl + episode.Filename, Type = ImageType.Primary }; } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index be137e8792..7a1789e50f 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -1,898 +1,872 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Xml; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Providers.TV.TheTVDB -{ - - /// - /// Class RemoteEpisodeProvider - /// - class TvdbEpisodeProvider : IRemoteMetadataProvider - { - private static readonly string FullIdKey = MetadataProviders.Tvdb + "-Full"; - - internal static TvdbEpisodeProvider Current; - private readonly IFileSystem _fileSystem; - private readonly IServerConfigurationManager _config; - private readonly IHttpClient _httpClient; - private readonly ILogger _logger; - private readonly IXmlReaderSettingsFactory _xmlSettings; - - public TvdbEpisodeProvider(IFileSystem fileSystem, IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IXmlReaderSettingsFactory xmlSettings) - { - _fileSystem = fileSystem; - _config = config; - _httpClient = httpClient; - _logger = logger; - _xmlSettings = xmlSettings; - Current = this; - } - - public Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) - { - var list = new List(); - - // The search query must either provide an episode number or date - if (!searchInfo.IndexNumber.HasValue && !searchInfo.PremiereDate.HasValue) - { - return Task.FromResult((IEnumerable)list); - } - - if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds)) - { - var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds); - - try - { - var metadataResult = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken); - - if (metadataResult.HasMetadata) - { - var item = metadataResult.Item; - - list.Add(new RemoteSearchResult - { - IndexNumber = item.IndexNumber, - Name = item.Name, - ParentIndexNumber = item.ParentIndexNumber, - PremiereDate = item.PremiereDate, - ProductionYear = item.ProductionYear, - ProviderIds = item.ProviderIds, - SearchProviderName = Name, - IndexNumberEnd = item.IndexNumberEnd - }); - } - } - catch (FileNotFoundException) - { - // Don't fail the provider because this will just keep on going and going. - } - catch (IOException) - { - // Don't fail the provider because this will just keep on going and going. - } - } - - return Task.FromResult((IEnumerable)list); - } - - public string Name => "TheTVDB"; - - public async Task> GetMetadata(EpisodeInfo searchInfo, CancellationToken cancellationToken) - { - var result = new MetadataResult(); - result.QueriedById = true; - - if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && - (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue)) - { - var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(searchInfo.SeriesProviderIds, null, null, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false); - - if (string.IsNullOrEmpty(seriesDataPath)) - { - return result; - } - - try - { - result = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken); - } - catch (FileNotFoundException) - { - // Don't fail the provider because this will just keep on going and going. - } - catch (IOException) - { - // Don't fail the provider because this will just keep on going and going. - } - } - else - { - _logger.LogDebug("No series identity found for {0}", searchInfo.Name); - } - - return result; - } - - /// - /// Gets the episode XML files. - /// - /// The series data path. - /// The search information. - /// List{FileInfo}. - internal List GetEpisodeXmlNodes(string seriesDataPath, EpisodeInfo searchInfo) - { - var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage); - - try - { - return GetXmlNodes(seriesXmlPath, searchInfo); - } - catch (FileNotFoundException) - { - return new List(); - } - catch (IOException) - { - return new List(); - } - } - - /// - /// Fetches the episode data. - /// - /// The identifier. - /// The series data path. - /// The cancellation token. - /// Task{System.Boolean}. - private MetadataResult FetchEpisodeData(EpisodeInfo id, string seriesDataPath, CancellationToken cancellationToken) - { - var result = new MetadataResult() - { - Item = new Episode - { - IndexNumber = id.IndexNumber, - ParentIndexNumber = id.ParentIndexNumber, - IndexNumberEnd = id.IndexNumberEnd - } - }; - - var xmlNodes = GetEpisodeXmlNodes(seriesDataPath, id); - - if (xmlNodes.Count > 0) - { - FetchMainEpisodeInfo(result, xmlNodes[0], id.SeriesDisplayOrder, cancellationToken); - - result.HasMetadata = true; - } - - foreach (var node in xmlNodes.Skip(1)) - { - FetchAdditionalPartInfo(result, node, cancellationToken); - } - - return result; - } - - private List GetXmlNodes(string xmlFile, EpisodeInfo searchInfo) - { - var list = new List(); - - if (searchInfo.IndexNumber.HasValue) - { - var files = GetEpisodeXmlFiles(searchInfo.SeriesDisplayOrder, searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, Path.GetDirectoryName(xmlFile)); - - list = files.Select(GetXmlReader).ToList(); - } - - if (list.Count == 0 && searchInfo.PremiereDate.HasValue) - { - list = GetXmlNodesByPremiereDate(xmlFile, searchInfo.PremiereDate.Value); - } - - return list; - } - - private string GetEpisodeFileName(string seriesDisplayOrder, int? seasonNumber, int? episodeNumber) - { - if (string.Equals(seriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase)) - { - return string.Format("episode-abs-{0}.xml", episodeNumber); - } - else if (string.Equals(seriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase)) - { - return string.Format("episode-dvd-{0}-{1}.xml", seasonNumber.Value, episodeNumber); - } - else - { - return string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber); - } - } - - private FileSystemMetadata GetEpisodeFileInfoWithFallback(string seriesDataPath, string seriesDisplayOrder, int? seasonNumber, int? episodeNumber) - { - var file = Path.Combine(seriesDataPath, GetEpisodeFileName(seriesDisplayOrder, seasonNumber, episodeNumber)); - var fileInfo = _fileSystem.GetFileInfo(file); - - if (fileInfo.Exists) - { - return fileInfo; - } - - if (!seasonNumber.HasValue) - { - return fileInfo; - } - - // revert to aired order - if (string.Equals(seriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase) || string.Equals(seriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase)) - { - file = Path.Combine(seriesDataPath, GetEpisodeFileName(null, seasonNumber, episodeNumber)); - return _fileSystem.GetFileInfo(file); - } - - return fileInfo; - } - - private List GetEpisodeXmlFiles(string seriesDisplayOrder, int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath) - { - var files = new List(); - - if (episodeNumber == null) - { - return files; - } - - if (!seasonNumber.HasValue) - { - seriesDisplayOrder = "absolute"; - } - - var fileInfo = GetEpisodeFileInfoWithFallback(seriesDataPath, seriesDisplayOrder, seasonNumber, episodeNumber); - - if (fileInfo.Exists) - { - files.Add(fileInfo); - } - - var end = endingEpisodeNumber ?? episodeNumber; - episodeNumber++; - - while (episodeNumber <= end) - { - fileInfo = GetEpisodeFileInfoWithFallback(seriesDataPath, seriesDisplayOrder, seasonNumber, episodeNumber); - - if (fileInfo.Exists) - { - files.Add(fileInfo); - } - else - { - break; - } - - episodeNumber++; - } - - return files; - } - - private XmlReader GetXmlReader(FileSystemMetadata xmlFile) - { - return GetXmlReader(File.ReadAllText(xmlFile.FullName, Encoding.UTF8)); - } - - private XmlReader GetXmlReader(string xml) - { - var streamReader = new StringReader(xml); - - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - return XmlReader.Create(streamReader, settings); - } - - private List GetXmlNodesByPremiereDate(string xmlFile, DateTime premiereDate) - { - var list = new List(); - - using (var fileStream = _fileSystem.GetFileStream(xmlFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) - { - using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) - { - // Use XmlReader for best performance - - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Episode": - { - var outerXml = reader.ReadOuterXml(); - - var airDate = GetEpisodeAirDate(outerXml); - - if (airDate.HasValue && premiereDate.Date == airDate.Value.Date) - { - list.Add(GetXmlReader(outerXml)); - return list; - } - - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } - } - } - - return list; - } - - private DateTime? GetEpisodeAirDate(string xml) - { - using (var streamReader = new StringReader(xml)) - { - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "FirstAired": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (DateTime.TryParse(val, out var date)) - { - date = date.ToUniversalTime(); - - return date; - } - } - - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } - } - return null; - } - - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - private void FetchMainEpisodeInfo(MetadataResult result, XmlReader reader, string seriesOrder, CancellationToken cancellationToken) - { - var item = result.Item; - - int? episodeNumber = null; - int? seasonNumber = null; - int? combinedEpisodeNumber = null; - int? combinedSeasonNumber = null; - - // Use XmlReader for best performance - using (reader) - { - result.ResetPeople(); - - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "id": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.SetProviderId(MetadataProviders.Tvdb, val); - } - break; - } - - case "IMDB_ID": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.SetProviderId(MetadataProviders.Imdb, val); - } - break; - } - - case "EpisodeNumber": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - episodeNumber = rval; - } - } - - break; - } - - case "SeasonNumber": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - seasonNumber = rval; - } - } - - break; - } - - case "Combined_episodenumber": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num)) - { - combinedEpisodeNumber = Convert.ToInt32(num); - } - } - - break; - } - - case "Combined_season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num)) - { - combinedSeasonNumber = Convert.ToInt32(num); - } - } - - break; - } - - case "airsbefore_episode": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - item.AirsBeforeEpisodeNumber = rval; - } - } - - break; - } - - case "airsafter_season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - item.AirsAfterSeasonNumber = rval; - } - } - - break; - } - - case "airsbefore_season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - item.AirsBeforeSeasonNumber = rval; - } - } - - break; - } - - case "EpisodeName": - { - var val = reader.ReadElementContentAsString(); - if (!item.LockedFields.Contains(MetadataFields.Name)) - { - if (!string.IsNullOrWhiteSpace(val)) - { - item.Name = val; - } - } - break; - } - - case "Overview": - { - var val = reader.ReadElementContentAsString(); - if (!item.LockedFields.Contains(MetadataFields.Overview)) - { - if (!string.IsNullOrWhiteSpace(val)) - { - item.Overview = val; - } - } - break; - } - case "Rating": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // float.TryParse is local aware, so it can be probamatic, force us culture - if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out var rval)) - { - item.CommunityRating = rval; - } - } - break; - } - case "RatingCount": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - //item.VoteCount = rval; - } - } - - break; - } - - case "FirstAired": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (DateTime.TryParse(val, out var date)) - { - date = date.ToUniversalTime(); - - item.PremiereDate = date; - item.ProductionYear = date.Year; - } - } - - break; - } - - case "Director": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddPeople(result, val, PersonType.Director); - } - } - - break; - } - case "GuestStars": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddGuestStars(result, val); - } - } - - break; - } - case "Writer": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - //AddPeople(result, val, PersonType.Writer); - } - } - - break; - } - case "Language": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - result.ResultLanguage = val; - } - - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } - - if (string.Equals(seriesOrder, "dvd", StringComparison.OrdinalIgnoreCase)) - { - episodeNumber = combinedEpisodeNumber ?? episodeNumber; - seasonNumber = combinedSeasonNumber ?? seasonNumber; - } - - if (episodeNumber.HasValue) - { - item.IndexNumber = episodeNumber; - } - - if (seasonNumber.HasValue) - { - item.ParentIndexNumber = seasonNumber; - } - } - - private void AddPeople(MetadataResult result, string val, string personType) - { - // Sometimes tvdb actors have leading spaces - foreach (var person in val.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .Select(str => new PersonInfo { Type = personType, Name = str.Trim() })) - { - result.AddPerson(person); - } - } - - private void AddGuestStars(MetadataResult result, string val) - where T : BaseItem - { - // example: - // |Mark C. Thomas| Dennis Kiefer| David Nelson (David)| Angela Nicholas| Tzi Ma| Kevin P. Kearns (Pasco)| - var persons = val.Split('|') - .Select(i => i.Trim()) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .ToList(); - - foreach (var person in persons) - { - var index = person.IndexOf('('); - string role = null; - var name = person; - - if (index != -1) - { - role = person.Substring(index + 1).Trim().TrimEnd(')'); - - name = person.Substring(0, index).Trim(); - } - - result.AddPerson(new PersonInfo - { - Type = PersonType.GuestStar, - Name = name, - Role = role - }); - } - } - - private void FetchAdditionalPartInfo(MetadataResult result, XmlReader reader, CancellationToken cancellationToken) - { - var item = result.Item; - - // Use XmlReader for best performance - using (reader) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "EpisodeName": - { - var val = reader.ReadElementContentAsString(); - if (!item.LockedFields.Contains(MetadataFields.Name)) - { - if (!string.IsNullOrWhiteSpace(val)) - { - item.Name += ", " + val; - } - } - break; - } - - case "Overview": - { - var val = reader.ReadElementContentAsString(); - if (!item.LockedFields.Contains(MetadataFields.Overview)) - { - if (!string.IsNullOrWhiteSpace(val)) - { - item.Overview += Environment.NewLine + Environment.NewLine + val; - } - } - break; - } - case "Director": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddPeople(result, val, PersonType.Director); - } - } - - break; - } - case "GuestStars": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddGuestStars(result, val); - } - } - - break; - } - case "Writer": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - //AddPeople(result, val, PersonType.Writer); - } - } - - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } - } - - public Task GetImageResponse(string url, CancellationToken cancellationToken) - { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); - } - - public int Order => 0; - } -} +//using System; +//using System.Collections.Generic; +//using System.Globalization; +//using System.IO; +//using System.Linq; +//using System.Text; +//using System.Threading; +//using System.Threading.Tasks; +//using System.Xml; +//using MediaBrowser.Common.Net; +//using MediaBrowser.Controller.Configuration; +//using MediaBrowser.Controller.Entities; +//using MediaBrowser.Controller.Entities.TV; +//using MediaBrowser.Controller.Providers; +//using MediaBrowser.Model.Entities; +//using MediaBrowser.Model.IO; +//using MediaBrowser.Model.Providers; +//using MediaBrowser.Model.Xml; +//using Microsoft.Extensions.Logging; +// +//namespace MediaBrowser.Providers.TV +//{ +// +// /// +// /// Class RemoteEpisodeProvider +// /// +// class TvdbEpisodeProvider : IRemoteMetadataProvider +// { +// internal static TvdbEpisodeProvider Current; +// private readonly IServerConfigurationManager _config; +// private readonly IHttpClient _httpClient; +// private readonly ILogger _logger; +// +// public TvdbEpisodeProvider(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger) +// { +// _config = config; +// _httpClient = httpClient; +// _logger = logger; +// Current = this; +// } +// +// public Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) +// { +// var list = new List(); +// +// // The search query must either provide an episode number or date +// if (!searchInfo.IndexNumber.HasValue && !searchInfo.PremiereDate.HasValue) +// { +// return Task.FromResult((IEnumerable)list); +// } +// +// if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds)) +// { +// try +// { +// var metadataResult = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken); +// +// if (metadataResult.HasMetadata) +// { +// var item = metadataResult.Item; +// +// list.Add(new RemoteSearchResult +// { +// IndexNumber = item.IndexNumber, +// Name = item.Name, +// ParentIndexNumber = item.ParentIndexNumber, +// PremiereDate = item.PremiereDate, +// ProductionYear = item.ProductionYear, +// ProviderIds = item.ProviderIds, +// SearchProviderName = Name, +// IndexNumberEnd = item.IndexNumberEnd +// }); +// } +// } +// catch (FileNotFoundException) +// { +// // Don't fail the provider because this will just keep on going and going. +// } +// catch (IOException) +// { +// // Don't fail the provider because this will just keep on going and going. +// } +// } +// +// return Task.FromResult((IEnumerable)list); +// } +// +// public string Name => "TheTVDB"; +// +// public async Task> GetMetadata(EpisodeInfo searchInfo, CancellationToken cancellationToken) +// { +// var result = new MetadataResult(); +// result.QueriedById = true; +// +// if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && +// (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue)) +// { +// result = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken); +// } +// else +// { +// _logger.LogDebug("No series identity found for {0}", searchInfo.Name); +// } +// +// return result; +// } +// +// /// +// /// Gets the episode XML files. +// /// +// /// The series data path. +// /// The search information. +// /// List{FileInfo}. +// internal List GetEpisodeXmlNodes(string seriesDataPath, EpisodeInfo searchInfo) +// { +// var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage); +// +// try +// { +// return GetXmlNodes(seriesXmlPath, searchInfo); +// } +// catch (FileNotFoundException) +// { +// return new List(); +// } +// catch (IOException) +// { +// return new List(); +// } +// } +// +// /// +// /// Fetches the episode data. +// /// +// /// The identifier. +// /// The series data path. +// /// The cancellation token. +// /// Task{System.Boolean}. +// private MetadataResult FetchEpisodeData(EpisodeInfo id, string seriesDataPath, CancellationToken cancellationToken) +// { +// var result = new MetadataResult() +// { +// Item = new Episode +// { +// IndexNumber = id.IndexNumber, +// ParentIndexNumber = id.ParentIndexNumber, +// IndexNumberEnd = id.IndexNumberEnd +// } +// }; +// +// var xmlNodes = GetEpisodeXmlNodes(seriesDataPath, id); +// +// if (xmlNodes.Count > 0) +// { +// FetchMainEpisodeInfo(result, xmlNodes[0], id.SeriesDisplayOrder, cancellationToken); +// +// result.HasMetadata = true; +// } +// +// foreach (var node in xmlNodes.Skip(1)) +// { +// FetchAdditionalPartInfo(result, node, cancellationToken); +// } +// +// return result; +// } +// +// private List GetXmlNodes(string xmlFile, EpisodeInfo searchInfo) +// { +// var list = new List(); +// +// if (searchInfo.IndexNumber.HasValue) +// { +// var files = GetEpisodeXmlFiles(searchInfo.SeriesDisplayOrder, searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, Path.GetDirectoryName(xmlFile)); +// +// list = files.Select(GetXmlReader).ToList(); +// } +// +// if (list.Count == 0 && searchInfo.PremiereDate.HasValue) +// { +// list = GetXmlNodesByPremiereDate(xmlFile, searchInfo.PremiereDate.Value); +// } +// +// return list; +// } +// +// private string GetEpisodeFileName(string seriesDisplayOrder, int? seasonNumber, int? episodeNumber) +// { +// if (string.Equals(seriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase)) +// { +// return string.Format("episode-abs-{0}.xml", episodeNumber); +// } +// else if (string.Equals(seriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase)) +// { +// return string.Format("episode-dvd-{0}-{1}.xml", seasonNumber.Value, episodeNumber); +// } +// else +// { +// return string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber); +// } +// } +// +// private FileSystemMetadata GetEpisodeFileInfoWithFallback(string seriesDataPath, string seriesDisplayOrder, int? seasonNumber, int? episodeNumber) +// { +// var file = Path.Combine(seriesDataPath, GetEpisodeFileName(seriesDisplayOrder, seasonNumber, episodeNumber)); +// var fileInfo = _fileSystem.GetFileInfo(file); +// +// if (fileInfo.Exists) +// { +// return fileInfo; +// } +// +// if (!seasonNumber.HasValue) +// { +// return fileInfo; +// } +// +// // revert to aired order +// if (string.Equals(seriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase) || string.Equals(seriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase)) +// { +// file = Path.Combine(seriesDataPath, GetEpisodeFileName(null, seasonNumber, episodeNumber)); +// return _fileSystem.GetFileInfo(file); +// } +// +// return fileInfo; +// } +// +// private List GetEpisodeXmlFiles(string seriesDisplayOrder, int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath) +// { +// var files = new List(); +// +// if (episodeNumber == null) +// { +// return files; +// } +// +// if (!seasonNumber.HasValue) +// { +// seriesDisplayOrder = "absolute"; +// } +// +// var fileInfo = GetEpisodeFileInfoWithFallback(seriesDataPath, seriesDisplayOrder, seasonNumber, episodeNumber); +// +// if (fileInfo.Exists) +// { +// files.Add(fileInfo); +// } +// +// var end = endingEpisodeNumber ?? episodeNumber; +// episodeNumber++; +// +// while (episodeNumber <= end) +// { +// fileInfo = GetEpisodeFileInfoWithFallback(seriesDataPath, seriesDisplayOrder, seasonNumber, episodeNumber); +// +// if (fileInfo.Exists) +// { +// files.Add(fileInfo); +// } +// else +// { +// break; +// } +// +// episodeNumber++; +// } +// +// return files; +// } +// +// private XmlReader GetXmlReader(FileSystemMetadata xmlFile) +// { +// return GetXmlReader(File.ReadAllText(xmlFile.FullName, Encoding.UTF8)); +// } +// +// private XmlReader GetXmlReader(string xml) +// { +// var streamReader = new StringReader(xml); +// +// var settings = _xmlSettings.Create(false); +// +// settings.CheckCharacters = false; +// settings.IgnoreProcessingInstructions = true; +// settings.IgnoreComments = true; +// +// return XmlReader.Create(streamReader, settings); +// } +// +// private List GetXmlNodesByPremiereDate(string xmlFile, DateTime premiereDate) +// { +// var list = new List(); +// +// using (var fileStream = _fileSystem.GetFileStream(xmlFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) +// { +// using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) +// { +// // Use XmlReader for best performance +// +// var settings = _xmlSettings.Create(false); +// +// settings.CheckCharacters = false; +// settings.IgnoreProcessingInstructions = true; +// settings.IgnoreComments = true; +// +// using (var reader = XmlReader.Create(streamReader, settings)) +// { +// reader.MoveToContent(); +// reader.Read(); +// +// // Loop through each element +// while (!reader.EOF && reader.ReadState == ReadState.Interactive) +// { +// if (reader.NodeType == XmlNodeType.Element) +// { +// switch (reader.Name) +// { +// case "Episode": +// { +// var outerXml = reader.ReadOuterXml(); +// +// var airDate = GetEpisodeAirDate(outerXml); +// +// if (airDate.HasValue && premiereDate.Date == airDate.Value.Date) +// { +// list.Add(GetXmlReader(outerXml)); +// return list; +// } +// +// break; +// } +// +// default: +// reader.Skip(); +// break; +// } +// } +// else +// { +// reader.Read(); +// } +// } +// } +// } +// } +// +// return list; +// } +// +// private DateTime? GetEpisodeAirDate(string xml) +// { +// using (var streamReader = new StringReader(xml)) +// { +// var settings = _xmlSettings.Create(false); +// +// settings.CheckCharacters = false; +// settings.IgnoreProcessingInstructions = true; +// settings.IgnoreComments = true; +// +// // Use XmlReader for best performance +// using (var reader = XmlReader.Create(streamReader, settings)) +// { +// reader.MoveToContent(); +// reader.Read(); +// +// // Loop through each element +// while (!reader.EOF && reader.ReadState == ReadState.Interactive) +// { +// if (reader.NodeType == XmlNodeType.Element) +// { +// switch (reader.Name) +// { +// case "FirstAired": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// if (DateTime.TryParse(val, out var date)) +// { +// date = date.ToUniversalTime(); +// +// return date; +// } +// } +// +// break; +// } +// +// default: +// reader.Skip(); +// break; +// } +// } +// else +// { +// reader.Read(); +// } +// } +// } +// } +// return null; +// } +// +// private readonly CultureInfo _usCulture = new CultureInfo("en-US"); +// +// private void FetchMainEpisodeInfo(MetadataResult result, XmlReader reader, string seriesOrder, CancellationToken cancellationToken) +// { +// var item = result.Item; +// +// int? episodeNumber = null; +// int? seasonNumber = null; +// int? combinedEpisodeNumber = null; +// int? combinedSeasonNumber = null; +// +// // Use XmlReader for best performance +// using (reader) +// { +// result.ResetPeople(); +// +// reader.MoveToContent(); +// reader.Read(); +// +// // Loop through each element +// while (!reader.EOF && reader.ReadState == ReadState.Interactive) +// { +// cancellationToken.ThrowIfCancellationRequested(); +// +// if (reader.NodeType == XmlNodeType.Element) +// { +// switch (reader.Name) +// { +// case "id": +// { +// var val = reader.ReadElementContentAsString(); +// if (!string.IsNullOrWhiteSpace(val)) +// { +// item.SetProviderId(MetadataProviders.Tvdb, val); +// } +// break; +// } +// +// case "IMDB_ID": +// { +// var val = reader.ReadElementContentAsString(); +// if (!string.IsNullOrWhiteSpace(val)) +// { +// item.SetProviderId(MetadataProviders.Imdb, val); +// } +// break; +// } +// +// case "EpisodeNumber": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// // int.TryParse is local aware, so it can be probamatic, force us culture +// if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) +// { +// episodeNumber = rval; +// } +// } +// +// break; +// } +// +// case "SeasonNumber": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// // int.TryParse is local aware, so it can be probamatic, force us culture +// if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) +// { +// seasonNumber = rval; +// } +// } +// +// break; +// } +// +// case "Combined_episodenumber": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num)) +// { +// combinedEpisodeNumber = Convert.ToInt32(num); +// } +// } +// +// break; +// } +// +// case "Combined_season": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num)) +// { +// combinedSeasonNumber = Convert.ToInt32(num); +// } +// } +// +// break; +// } +// +// case "airsbefore_episode": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// // int.TryParse is local aware, so it can be probamatic, force us culture +// if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) +// { +// item.AirsBeforeEpisodeNumber = rval; +// } +// } +// +// break; +// } +// +// case "airsafter_season": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// // int.TryParse is local aware, so it can be probamatic, force us culture +// if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) +// { +// item.AirsAfterSeasonNumber = rval; +// } +// } +// +// break; +// } +// +// case "airsbefore_season": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// // int.TryParse is local aware, so it can be probamatic, force us culture +// if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) +// { +// item.AirsBeforeSeasonNumber = rval; +// } +// } +// +// break; +// } +// +// case "EpisodeName": +// { +// var val = reader.ReadElementContentAsString(); +// if (!item.LockedFields.Contains(MetadataFields.Name)) +// { +// if (!string.IsNullOrWhiteSpace(val)) +// { +// item.Name = val; +// } +// } +// break; +// } +// +// case "Overview": +// { +// var val = reader.ReadElementContentAsString(); +// if (!item.LockedFields.Contains(MetadataFields.Overview)) +// { +// if (!string.IsNullOrWhiteSpace(val)) +// { +// item.Overview = val; +// } +// } +// break; +// } +// case "Rating": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// // float.TryParse is local aware, so it can be probamatic, force us culture +// if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out var rval)) +// { +// item.CommunityRating = rval; +// } +// } +// break; +// } +// case "RatingCount": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// // int.TryParse is local aware, so it can be probamatic, force us culture +// if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) +// { +// //item.VoteCount = rval; +// } +// } +// +// break; +// } +// +// case "FirstAired": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// if (DateTime.TryParse(val, out var date)) +// { +// date = date.ToUniversalTime(); +// +// item.PremiereDate = date; +// item.ProductionYear = date.Year; +// } +// } +// +// break; +// } +// +// case "Director": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// if (!item.LockedFields.Contains(MetadataFields.Cast)) +// { +// AddPeople(result, val, PersonType.Director); +// } +// } +// +// break; +// } +// case "GuestStars": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// if (!item.LockedFields.Contains(MetadataFields.Cast)) +// { +// AddGuestStars(result, val); +// } +// } +// +// break; +// } +// case "Writer": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// if (!item.LockedFields.Contains(MetadataFields.Cast)) +// { +// //AddPeople(result, val, PersonType.Writer); +// } +// } +// +// break; +// } +// case "Language": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// result.ResultLanguage = val; +// } +// +// break; +// } +// +// default: +// reader.Skip(); +// break; +// } +// } +// else +// { +// reader.Read(); +// } +// } +// } +// +// if (string.Equals(seriesOrder, "dvd", StringComparison.OrdinalIgnoreCase)) +// { +// episodeNumber = combinedEpisodeNumber ?? episodeNumber; +// seasonNumber = combinedSeasonNumber ?? seasonNumber; +// } +// +// if (episodeNumber.HasValue) +// { +// item.IndexNumber = episodeNumber; +// } +// +// if (seasonNumber.HasValue) +// { +// item.ParentIndexNumber = seasonNumber; +// } +// } +// +// private void AddPeople(MetadataResult result, string val, string personType) +// { +// // Sometimes tvdb actors have leading spaces +// foreach (var person in val.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) +// .Where(i => !string.IsNullOrWhiteSpace(i)) +// .Select(str => new PersonInfo { Type = personType, Name = str.Trim() })) +// { +// result.AddPerson(person); +// } +// } +// +// private void AddGuestStars(MetadataResult result, string val) +// where T : BaseItem +// { +// // example: +// // |Mark C. Thomas| Dennis Kiefer| David Nelson (David)| Angela Nicholas| Tzi Ma| Kevin P. Kearns (Pasco)| +// var persons = val.Split('|') +// .Select(i => i.Trim()) +// .Where(i => !string.IsNullOrWhiteSpace(i)) +// .ToList(); +// +// foreach (var person in persons) +// { +// var index = person.IndexOf('('); +// string role = null; +// var name = person; +// +// if (index != -1) +// { +// role = person.Substring(index + 1).Trim().TrimEnd(')'); +// +// name = person.Substring(0, index).Trim(); +// } +// +// result.AddPerson(new PersonInfo +// { +// Type = PersonType.GuestStar, +// Name = name, +// Role = role +// }); +// } +// } +// +// private void FetchAdditionalPartInfo(MetadataResult result, XmlReader reader, CancellationToken cancellationToken) +// { +// var item = result.Item; +// +// // Use XmlReader for best performance +// using (reader) +// { +// reader.MoveToContent(); +// reader.Read(); +// +// // Loop through each element +// while (!reader.EOF && reader.ReadState == ReadState.Interactive) +// { +// cancellationToken.ThrowIfCancellationRequested(); +// +// if (reader.NodeType == XmlNodeType.Element) +// { +// switch (reader.Name) +// { +// case "EpisodeName": +// { +// var val = reader.ReadElementContentAsString(); +// if (!item.LockedFields.Contains(MetadataFields.Name)) +// { +// if (!string.IsNullOrWhiteSpace(val)) +// { +// item.Name += ", " + val; +// } +// } +// break; +// } +// +// case "Overview": +// { +// var val = reader.ReadElementContentAsString(); +// if (!item.LockedFields.Contains(MetadataFields.Overview)) +// { +// if (!string.IsNullOrWhiteSpace(val)) +// { +// item.Overview += Environment.NewLine + Environment.NewLine + val; +// } +// } +// break; +// } +// case "Director": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// if (!item.LockedFields.Contains(MetadataFields.Cast)) +// { +// AddPeople(result, val, PersonType.Director); +// } +// } +// +// break; +// } +// case "GuestStars": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// if (!item.LockedFields.Contains(MetadataFields.Cast)) +// { +// AddGuestStars(result, val); +// } +// } +// +// break; +// } +// case "Writer": +// { +// var val = reader.ReadElementContentAsString(); +// +// if (!string.IsNullOrWhiteSpace(val)) +// { +// if (!item.LockedFields.Contains(MetadataFields.Cast)) +// { +// //AddPeople(result, val, PersonType.Writer); +// } +// } +// +// break; +// } +// +// default: +// reader.Skip(); +// break; +// } +// } +// else +// { +// reader.Read(); +// } +// } +// } +// } +// +// public Task GetImageResponse(string url, CancellationToken cancellationToken) +// { +// return _httpClient.GetResponse(new HttpRequestOptions +// { +// CancellationToken = cancellationToken, +// Url = url +// }); +// } +// +// public int Order => 0; +// } +//} diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs index d456960578..2cc053c644 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs @@ -80,7 +80,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB /// Task. public async Task Run(IProgress progress, CancellationToken cancellationToken) { - var path = TvdbSeriesProvider.GetSeriesDataPath(_config.CommonApplicationPaths); + return; + var path = ""; + //var path = TvdbSeriesProvider.GetSeriesDataPath(_config.CommonApplicationPaths); Directory.CreateDirectory(path); @@ -392,7 +394,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB Directory.CreateDirectory(seriesDataPath); - return TvdbSeriesProvider.Current.DownloadSeriesZip(id, MetadataProviders.Tvdb.ToString(), null, null, seriesDataPath, lastTvDbUpdateTime, preferredMetadataLanguage, cancellationToken); + //return TvdbSeriesProvider.Current.DownloadSeriesZip(id, MetadataProviders.Tvdb.ToString(), null, null, seriesDataPath, lastTvDbUpdateTime, preferredMetadataLanguage, cancellationToken); + return Task.CompletedTask; } } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index 01ede44bb2..1c5fde0d20 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -1,23 +1,19 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Xml; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Xml; +using TvDbSharper; +using TvDbSharper.Dto; +using RatingType = MediaBrowser.Model.Dto.RatingType; namespace MediaBrowser.Providers.TV.TheTVDB { @@ -25,17 +21,14 @@ namespace MediaBrowser.Providers.TV.TheTVDB { private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - private readonly IServerConfigurationManager _config; private readonly IHttpClient _httpClient; - private readonly IFileSystem _fileSystem; - private readonly IXmlReaderSettingsFactory _xmlSettings; + private readonly TvDbClient _tvDbClient; - public TvdbSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings) + public TvdbSeasonImageProvider(IHttpClient httpClient) { - _config = config; _httpClient = httpClient; - _fileSystem = fileSystem; - _xmlSettings = xmlSettings; + _tvDbClient = new TvDbClient(); + _tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey); } public string Name => ProviderName; @@ -62,91 +55,68 @@ namespace MediaBrowser.Providers.TV.TheTVDB var season = (Season)item; var series = season.Series; - if (series != null && season.IndexNumber.HasValue && TvdbSeriesProvider.IsValidSeries(series.ProviderIds)) + if (series == null || !season.IndexNumber.HasValue || !TvdbSeriesProvider.IsValidSeries(series.ProviderIds)) { - var seriesProviderIds = series.ProviderIds; - var seasonNumber = season.IndexNumber.Value; - - var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(seriesProviderIds, series.Name, series.ProductionYear, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false); - - if (!string.IsNullOrEmpty(seriesDataPath)) - { - var path = Path.Combine(seriesDataPath, "banners.xml"); - - try - { - return GetImages(path, item.GetPreferredMetadataLanguage(), seasonNumber, _xmlSettings, _fileSystem, cancellationToken); - } - catch (FileNotFoundException) - { - // No tvdb data yet. Don't blow up - } - catch (IOException) - { - // No tvdb data yet. Don't blow up - } - } + return new RemoteImageInfo[] { }; } - return new RemoteImageInfo[] { }; + var seasonNumber = season.IndexNumber.Value; + var language = item.GetPreferredMetadataLanguage(); + _tvDbClient.AcceptedLanguage = language; + var remoteImages = new List(); + var keyTypes = new[] {KeyType.Season, KeyType.Seasonwide, KeyType.Fanart}; + // TODO error handling + foreach (KeyType keyType in keyTypes) + { + var imageQuery = new ImagesQuery + { + KeyType = keyType, + SubKey = seasonNumber.ToString() + }; + var imageResults = + await _tvDbClient.Series.GetImagesAsync(Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb)), imageQuery, cancellationToken); + + remoteImages.AddRange(GetImages(imageResults.Data, language)); + } + + return remoteImages; } - internal static IEnumerable GetImages(string xmlPath, string preferredLanguage, int seasonNumber, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem, CancellationToken cancellationToken) + private static IEnumerable GetImages(Image[] images, string preferredLanguage) { - var settings = xmlReaderSettingsFactory.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - var list = new List(); - using (var fileStream = fileSystem.GetFileStream(xmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) + foreach (Image image in images) { - using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) + var resolution = image.Resolution.Split('x'); + var imageInfo = new RemoteImageInfo { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); + RatingType = RatingType.Score, + CommunityRating = (double?)image.RatingsInfo.Average, + VoteCount = image.RatingsInfo.Count, + Url = TVUtils.BannerUrl + image.FileName, + ProviderName = ProviderName, + // TODO Language = image.LanguageId, + Width = Convert.ToInt32(resolution[0]), + Height = Convert.ToInt32(resolution[1]), + ThumbnailUrl = TVUtils.BannerUrl + image.Thumbnail + }; - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Banner": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - using (var subtree = reader.ReadSubtree()) - { - AddImage(subtree, list, seasonNumber); - } - break; - } - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } + if (string.Equals(image.KeyType, "season", StringComparison.OrdinalIgnoreCase)) + { + imageInfo.Type = ImageType.Primary; + } + else if (string.Equals(image.KeyType, "seasonwide", StringComparison.OrdinalIgnoreCase)) + { + imageInfo.Type = ImageType.Banner; + } + else if (string.Equals(image.KeyType, "fanart", StringComparison.OrdinalIgnoreCase)) + { + imageInfo.Type = ImageType.Backdrop; } - } + list.Add(imageInfo); + } var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase); return list.OrderByDescending(i => @@ -155,6 +125,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB { return 3; } + if (!isLanguageEn) { if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) @@ -162,177 +133,18 @@ namespace MediaBrowser.Providers.TV.TheTVDB return 2; } } + if (string.IsNullOrEmpty(i.Language)) { return isLanguageEn ? 3 : 2; } + return 0; }) .ThenByDescending(i => i.CommunityRating ?? 0) .ThenByDescending(i => i.VoteCount ?? 0); } - private static void AddImage(XmlReader reader, List images, int seasonNumber) - { - reader.MoveToContent(); - - string bannerType = null; - string bannerType2 = null; - string url = null; - int? bannerSeason = null; - int? width = null; - int? height = null; - string language = null; - double? rating = null; - int? voteCount = null; - string thumbnailUrl = null; - - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Rating": - { - var val = reader.ReadElementContentAsString() ?? string.Empty; - - if (double.TryParse(val, NumberStyles.Any, UsCulture, out var rval)) - { - rating = rval; - } - - break; - } - - case "RatingCount": - { - var val = reader.ReadElementContentAsString() ?? string.Empty; - - if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var rval)) - { - voteCount = rval; - } - - break; - } - - case "Language": - { - language = reader.ReadElementContentAsString() ?? string.Empty; - break; - } - - case "ThumbnailPath": - { - thumbnailUrl = reader.ReadElementContentAsString() ?? string.Empty; - break; - } - - case "BannerType": - { - bannerType = reader.ReadElementContentAsString() ?? string.Empty; - break; - } - - case "BannerType2": - { - bannerType2 = reader.ReadElementContentAsString() ?? string.Empty; - - // Sometimes the resolution is stuffed in here - var resolutionParts = bannerType2.Split('x'); - - if (resolutionParts.Length == 2) - { - if (int.TryParse(resolutionParts[0], NumberStyles.Integer, UsCulture, out var rval)) - { - width = rval; - } - - if (int.TryParse(resolutionParts[1], NumberStyles.Integer, UsCulture, out rval)) - { - height = rval; - } - - } - - break; - } - - case "BannerPath": - { - url = reader.ReadElementContentAsString() ?? string.Empty; - break; - } - - case "Season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - bannerSeason = int.Parse(val); - } - break; - } - - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - - if (!string.IsNullOrEmpty(url) && bannerSeason.HasValue && bannerSeason.Value == seasonNumber) - { - var imageInfo = new RemoteImageInfo - { - RatingType = RatingType.Score, - CommunityRating = rating, - VoteCount = voteCount, - Url = TVUtils.BannerUrl + url, - ProviderName = ProviderName, - Language = language, - Width = width, - Height = height - }; - - if (!string.IsNullOrEmpty(thumbnailUrl)) - { - imageInfo.ThumbnailUrl = TVUtils.BannerUrl + thumbnailUrl; - } - - if (string.Equals(bannerType, "season", StringComparison.OrdinalIgnoreCase)) - { - if (string.Equals(bannerType2, "season", StringComparison.OrdinalIgnoreCase)) - { - imageInfo.Type = ImageType.Primary; - images.Add(imageInfo); - } - else if (string.Equals(bannerType2, "seasonwide", StringComparison.OrdinalIgnoreCase)) - { - imageInfo.Type = ImageType.Banner; - images.Add(imageInfo); - } - } - else if (string.Equals(bannerType, "fanart", StringComparison.OrdinalIgnoreCase)) - { - imageInfo.Type = ImageType.Backdrop; - images.Add(imageInfo); - } - } - - } - public int Order => 0; public Task GetImageResponse(string url, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index 2b4337ed1c..999598ff82 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -1,40 +1,32 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Xml; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Xml; +using TvDbSharper; +using TvDbSharper.Dto; +using RatingType = MediaBrowser.Model.Dto.RatingType; +using Series = MediaBrowser.Controller.Entities.TV.Series; namespace MediaBrowser.Providers.TV.TheTVDB { public class TvdbSeriesImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IServerConfigurationManager _config; private readonly IHttpClient _httpClient; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private readonly IFileSystem _fileSystem; - private readonly IXmlReaderSettingsFactory _xmlReaderSettingsFactory; + private readonly TvDbClient _tvDbClient = new TvDbClient(); - public TvdbSeriesImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory) + public TvdbSeriesImageProvider(IHttpClient httpClient) { - _config = config; _httpClient = httpClient; - _fileSystem = fileSystem; - _xmlReaderSettingsFactory = xmlReaderSettingsFactory; + _tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey); } public string Name => ProviderName; @@ -58,273 +50,92 @@ namespace MediaBrowser.Providers.TV.TheTVDB public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { - if (TvdbSeriesProvider.IsValidSeries(item.ProviderIds)) + if (!TvdbSeriesProvider.IsValidSeries(item.ProviderIds)) { - var language = item.GetPreferredMetadataLanguage(); - - var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(item.ProviderIds, item.Name, item.ProductionYear, language, cancellationToken).ConfigureAwait(false); - - if (string.IsNullOrEmpty(seriesDataPath)) - { - return new RemoteImageInfo[] { }; - } - - var path = Path.Combine(seriesDataPath, "banners.xml"); - - try - { - return GetImages(path, language, cancellationToken); - } - catch (FileNotFoundException) - { - // No tvdb data yet. Don't blow up - } - catch (IOException) - { - // No tvdb data yet. Don't blow up - } + return new RemoteImageInfo[] { }; } - return new RemoteImageInfo[] { }; + var language = item.GetPreferredMetadataLanguage(); + _tvDbClient.AcceptedLanguage = language; + var remoteImages = new List(); + var keyTypes = new[] {KeyType.Poster, KeyType.Series, KeyType.Fanart}; + // TODO error handling + foreach (KeyType keyType in keyTypes) + { + var imageQuery = new ImagesQuery + { + KeyType = keyType + }; + var imageResults = + await _tvDbClient.Series.GetImagesAsync(Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb)), imageQuery, cancellationToken); + + remoteImages.AddRange(GetImages(imageResults.Data, language)); + } + return remoteImages; } - private IEnumerable GetImages(string xmlPath, string preferredLanguage, CancellationToken cancellationToken) + private IEnumerable GetImages(Image[] images, string preferredLanguage) { - var settings = _xmlReaderSettingsFactory.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - var list = new List(); - using (var fileStream = _fileSystem.GetFileStream(xmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) - { - using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Banner": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - using (var subtree = reader.ReadSubtree()) - { - AddImage(subtree, list); - } - break; - } - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } - } - } - - var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase); - - return list.OrderByDescending(i => - { - if (string.Equals(preferredLanguage, i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 3; - } - if (!isLanguageEn) - { - if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 2; - } - } - if (string.IsNullOrEmpty(i.Language)) - { - return isLanguageEn ? 3 : 2; - } - return 0; - }) - .ThenByDescending(i => i.CommunityRating ?? 0) - .ThenByDescending(i => i.VoteCount ?? 0); - } - - private void AddImage(XmlReader reader, List images) - { - reader.MoveToContent(); - - string bannerType = null; - string url = null; - int? bannerSeason = null; - int? width = null; - int? height = null; - string language = null; - double? rating = null; - int? voteCount = null; - string thumbnailUrl = null; - - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Rating": - { - var val = reader.ReadElementContentAsString() ?? string.Empty; - - if (double.TryParse(val, NumberStyles.Any, _usCulture, out var rval)) - { - rating = rval; - } - - break; - } - - case "RatingCount": - { - var val = reader.ReadElementContentAsString() ?? string.Empty; - - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - voteCount = rval; - } - - break; - } - - case "Language": - { - language = reader.ReadElementContentAsString() ?? string.Empty; - break; - } - - case "ThumbnailPath": - { - thumbnailUrl = reader.ReadElementContentAsString() ?? string.Empty; - break; - } - - case "BannerType": - { - bannerType = reader.ReadElementContentAsString() ?? string.Empty; - - break; - } - - case "BannerPath": - { - url = reader.ReadElementContentAsString() ?? string.Empty; - break; - } - - case "BannerType2": - { - var bannerType2 = reader.ReadElementContentAsString() ?? string.Empty; - - // Sometimes the resolution is stuffed in here - var resolutionParts = bannerType2.Split('x'); - - if (resolutionParts.Length == 2) - { - if (int.TryParse(resolutionParts[0], NumberStyles.Integer, _usCulture, out var rval)) - { - width = rval; - } - - if (int.TryParse(resolutionParts[1], NumberStyles.Integer, _usCulture, out rval)) - { - height = rval; - } - - } - - break; - } - - case "Season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - bannerSeason = int.Parse(val); - } - break; - } - - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - - if (!string.IsNullOrEmpty(url) && !bannerSeason.HasValue) + foreach (Image image in images) { + var resolution = image.Resolution.Split('x'); var imageInfo = new RemoteImageInfo { RatingType = RatingType.Score, - CommunityRating = rating, - VoteCount = voteCount, - Url = TVUtils.BannerUrl + url, + CommunityRating = (double?)image.RatingsInfo.Average, + VoteCount = image.RatingsInfo.Count, + Url = TVUtils.BannerUrl + image.FileName, ProviderName = Name, - Language = language, - Width = width, - Height = height + // TODO Language = image.LanguageId, + Width = Convert.ToInt32(resolution[0]), + Height = Convert.ToInt32(resolution[1]), + ThumbnailUrl = TVUtils.BannerUrl + image.Thumbnail }; - if (!string.IsNullOrEmpty(thumbnailUrl)) - { - imageInfo.ThumbnailUrl = TVUtils.BannerUrl + thumbnailUrl; - } - if (string.Equals(bannerType, "poster", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(image.KeyType, "poster", StringComparison.OrdinalIgnoreCase)) { imageInfo.Type = ImageType.Primary; - images.Add(imageInfo); } - else if (string.Equals(bannerType, "series", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(image.KeyType, "series", StringComparison.OrdinalIgnoreCase)) { imageInfo.Type = ImageType.Banner; - images.Add(imageInfo); } - else if (string.Equals(bannerType, "fanart", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(image.KeyType, "fanart", StringComparison.OrdinalIgnoreCase)) { imageInfo.Type = ImageType.Backdrop; - images.Add(imageInfo); } - } + list.Add(imageInfo); + } + var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase); + + return list.OrderByDescending(i => + { + if (string.Equals(preferredLanguage, i.Language, StringComparison.OrdinalIgnoreCase)) + { + return 3; + } + + if (!isLanguageEn) + { + if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) + { + return 2; + } + } + + if (string.IsNullOrEmpty(i.Language)) + { + return isLanguageEn ? 3 : 2; + } + + return 0; + }) + .ThenByDescending(i => i.CommunityRating ?? 0) + .ThenByDescending(i => i.VoteCount ?? 0); } public int Order => 0; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 858a4fa9d1..84c218265f 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -30,7 +30,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB public class TvdbSeriesProvider : IRemoteMetadataProvider, IHasOrder { internal static TvdbSeriesProvider Current { get; private set; } - private readonly IZipClient _zipClient; private readonly IHttpClient _httpClient; private readonly IFileSystem _fileSystem; private readonly IXmlReaderSettingsFactory _xmlSettings; @@ -41,9 +40,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILocalizationManager _localizationManager; private readonly TvDbClient _tvDbClient; - public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager) + public TvdbSeriesProvider(IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager) { - _zipClient = zipClient; _httpClient = httpClient; _fileSystem = fileSystem; _config = config; @@ -53,6 +51,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB _localizationManager = localizationManager; Current = this; _tvDbClient = new TvDbClient(); + _tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey); } private string NormalizeLanguage(string language) @@ -93,8 +92,10 @@ namespace MediaBrowser.Providers.TV.TheTVDB public async Task> GetMetadata(SeriesInfo itemId, CancellationToken cancellationToken) { - var result = new MetadataResult(); - result.QueriedById = true; + var result = new MetadataResult + { + QueriedById = true + }; if (!IsValidSeries(itemId.ProviderIds)) { @@ -106,13 +107,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB if (IsValidSeries(itemId.ProviderIds)) { - var seriesDataPath = await EnsureSeriesInfo(itemId.ProviderIds, itemId.Name, itemId.Year, itemId.MetadataLanguage, cancellationToken).ConfigureAwait(false); - - if (string.IsNullOrEmpty(seriesDataPath)) - { - return result; - } - result.Item = new Series(); result.HasMetadata = true; @@ -122,156 +116,43 @@ namespace MediaBrowser.Providers.TV.TheTVDB return result; } - /// - /// Fetches the series data. - /// - /// The result. - /// The metadata language. - /// The series provider ids. - /// The cancellation token. - /// Task{System.Boolean}. - private void FetchSeriesData(MetadataResult result, string metadataLanguage, Dictionary seriesProviderIds, CancellationToken cancellationToken) + private async Task FetchSeriesData(MetadataResult result, string metadataLanguage, Dictionary seriesProviderIds, CancellationToken cancellationToken) { _tvDbClient.AcceptedLanguage = NormalizeLanguage(metadataLanguage); var series = result.Item; - TvDbResponse searchResult; - if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(id)) + if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(tvdbId)) { series.SetProviderId(MetadataProviders.Tvdb, tvdbId); } - if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out var imdbId) && !string.IsNullOrEmpty(id)) + if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out var imdbId) && !string.IsNullOrEmpty(imdbId)) { series.SetProviderId(MetadataProviders.Imdb, imdbId); - tvdbId = GetSeriesByRemoteId(imdbId, MetadataProviders.Imdb.ToString(), metadataLanguage, cancellationToken).Result.ToString(); + tvdbId = await GetSeriesByRemoteId(imdbId, MetadataProviders.Imdb.ToString(), metadataLanguage, cancellationToken); } - if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out var zap2It) && !string.IsNullOrEmpty(id)) + if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out var zap2It) && !string.IsNullOrEmpty(zap2It)) { series.SetProviderId(MetadataProviders.Zap2It, zap2It); - tvdbId = GetSeriesByRemoteId(zap2It, MetadataProviders.Zap2It.ToString(), metadataLanguage, cancellationToken).Result.ToString(); + tvdbId = await GetSeriesByRemoteId(zap2It, MetadataProviders.Zap2It.ToString(), metadataLanguage, cancellationToken); } - var seriesResult = _tvDbClient.Series.GetAsync(Convert.ToInt32(tvdbId), cancellationToken).Result; -// var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); -// -// var seriesXmlPath = GetSeriesXmlPath(seriesProviderIds, metadataLanguage); -// var actorsXmlPath = Path.Combine(seriesDataPath, "actors.xml"); + // TODO call this function elsewhere? + var seriesResult = await _tvDbClient.Series.GetAsync(Convert.ToInt32(tvdbId), cancellationToken); - FetchSeriesInfo(result, searchResult, cancellationToken); + // TODO error handling + MapSeriesToResult(result, seriesResult.Data, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); result.ResetPeople(); - FetchActors(result, actorsXmlPath); + var actorsResult = await _tvDbClient.Series.GetActorsAsync(Convert.ToInt32(tvdbId), cancellationToken); + MapActorsToResult(result, actorsResult.Data); } - /// - /// Downloads the series zip. - /// - internal async Task DownloadSeriesZip(string seriesId, string idType, string seriesName, int? seriesYear, string seriesDataPath, long? lastTvDbUpdateTime, string preferredMetadataLanguage, CancellationToken cancellationToken) - { - try - { - await DownloadSeriesZip(seriesId, idType, seriesName, seriesYear, seriesDataPath, lastTvDbUpdateTime, preferredMetadataLanguage, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); - return; - } - catch (HttpException ex) - { - if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound) - { - throw; - } - } - - if (!string.Equals(preferredMetadataLanguage, "en", StringComparison.OrdinalIgnoreCase)) - { - await DownloadSeriesZip(seriesId, idType, seriesName, seriesYear, seriesDataPath, lastTvDbUpdateTime, "en", preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); - } - } - - private async Task DownloadSeriesZip(string seriesId, string idType, string seriesName, int? seriesYear, string seriesDataPath, long? lastTvDbUpdateTime, string preferredMetadataLanguage, string saveAsMetadataLanguage, CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(seriesId)) - { - throw new ArgumentNullException(nameof(seriesId)); - } - - if (!string.Equals(idType, "tvdb", StringComparison.OrdinalIgnoreCase)) - { - seriesId = (await GetSeriesByRemoteId(seriesId, idType, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false)).ToString(); - } - - // If searching by remote id came up empty, then do a regular search - if (string.IsNullOrWhiteSpace(seriesId) && !string.IsNullOrWhiteSpace(seriesName)) - { - var searchInfo = new SeriesInfo - { - Name = seriesName, - Year = seriesYear, - MetadataLanguage = preferredMetadataLanguage - }; - var results = await GetSearchResults(searchInfo, cancellationToken).ConfigureAwait(false); - var result = results.FirstOrDefault(); - if (result != null) - { - seriesId = result.GetProviderId(MetadataProviders.Tvdb); - } - } - - if (string.IsNullOrWhiteSpace(seriesId)) - { - throw new ArgumentNullException(nameof(seriesId)); - } - - var url = string.Format(SeriesGetZip, TVUtils.TvdbApiKey, seriesId, NormalizeLanguage(preferredMetadataLanguage)); - - using (var response = await _httpClient.SendAsync(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - BufferContent = false - - }, "GET").ConfigureAwait(false)) - { - using (var zipStream = response.Content) - { - // Delete existing files - DeleteXmlFiles(seriesDataPath); - - // Copy to memory stream because we need a seekable stream - using (var ms = new MemoryStream()) - { - await zipStream.CopyToAsync(ms).ConfigureAwait(false); - - ms.Position = 0; - _zipClient.ExtractAllFromZip(ms, seriesDataPath, true); - } - } - } - - // Sanitize all files, except for extracted episode files - foreach (var file in _fileSystem.GetFilePaths(seriesDataPath, true).ToList() - .Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase)) - .Where(i => !Path.GetFileName(i).StartsWith("episode-", StringComparison.OrdinalIgnoreCase))) - { - await SanitizeXmlFile(file).ConfigureAwait(false); - } - - var downloadLangaugeXmlFile = Path.Combine(seriesDataPath, NormalizeLanguage(preferredMetadataLanguage) + ".xml"); - var saveAsLanguageXmlFile = Path.Combine(seriesDataPath, saveAsMetadataLanguage + ".xml"); - - if (!string.Equals(downloadLangaugeXmlFile, saveAsLanguageXmlFile, StringComparison.OrdinalIgnoreCase)) - { - File.Copy(downloadLangaugeXmlFile, saveAsLanguageXmlFile, true); - } - - await ExtractEpisodes(seriesDataPath, downloadLangaugeXmlFile, lastTvDbUpdateTime).ConfigureAwait(false); - } - - private async Task GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken) + private async Task GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken) { _tvDbClient.AcceptedLanguage = NormalizeLanguage(language); TvDbResponse result; @@ -285,7 +166,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB result = await _tvDbClient.Search.SearchSeriesByImdbIdAsync(id, cancellationToken); } - return result.Data.First().Id; + return result.Data.First().Id.ToString(); } internal static bool IsValidSeries(Dictionary seriesProviderIds) @@ -395,10 +276,10 @@ namespace MediaBrowser.Providers.TV.TheTVDB Name = tvdbTitles.FirstOrDefault(), ProductionYear = firstAired.Year, SearchProviderName = Name, - ImageUrl = seriesSearchResult.Banner + ImageUrl = TVUtils.BannerUrl + seriesSearchResult.Banner }; - // TODO requires another query + // TODO requires another query, is it worth it? // remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSearchResult.Id); remoteSearchResult.SetProviderId(MetadataProviders.Tvdb, seriesSearchResult.Id.ToString()); list.Add(new Tuple, RemoteSearchResult>(tvdbTitles, remoteSearchResult)); @@ -468,27 +349,26 @@ namespace MediaBrowser.Providers.TV.TheTVDB return name.Trim(); } - private void FetchSeriesInfo(MetadataResult result, TvDbResponse seriesResponse, CancellationToken cancellationToken) + private static void MapSeriesToResult(MetadataResult result, TvDbSharper.Dto.Series tvdbSeries, CancellationToken cancellationToken) { var episodeAirDates = new List(); - var series = result.Item; - Series item = result.Item; - series.SetProviderId(MetadataProviders.Tvdb, seriesResponse.Data.Id.ToString()); - series.Name = seriesResponse.Data.SeriesName; - series.Overview = (seriesResponse.Data.Overview ?? string.Empty).Trim(); + Series series = result.Item; + series.SetProviderId(MetadataProviders.Tvdb, tvdbSeries.Id.ToString()); + series.Name = tvdbSeries.SeriesName; + series.Overview = (tvdbSeries.Overview ?? string.Empty).Trim(); // TODO result.ResultLanguage = (seriesResponse.Data. ?? string.Empty).Trim(); - series.AirDays = TVUtils.GetAirDays(seriesResponse.Data.AirsDayOfWeek); - series.AirTime = seriesResponse.Data.AirsTime; + series.AirDays = TVUtils.GetAirDays(tvdbSeries.AirsDayOfWeek); + series.AirTime = tvdbSeries.AirsTime; - series.CommunityRating = (float?)seriesResponse.Data.SiteRating; - series.SetProviderId(MetadataProviders.Imdb, seriesResponse.Data.ImdbId); - series.SetProviderId(MetadataProviders.Zap2It, seriesResponse.Data.Zap2itId); - if (Enum.TryParse(seriesResponse.Data.Status, true, out SeriesStatus seriesStatus)) + series.CommunityRating = (float?)tvdbSeries.SiteRating; + series.SetProviderId(MetadataProviders.Imdb, tvdbSeries.ImdbId); + series.SetProviderId(MetadataProviders.Zap2It, tvdbSeries.Zap2itId); + if (Enum.TryParse(tvdbSeries.Status, true, out SeriesStatus seriesStatus)) { series.Status = seriesStatus; } - if (DateTime.TryParse(seriesResponse.Data.FirstAired, out var date)) + if (DateTime.TryParse(tvdbSeries.FirstAired, out var date)) { date = date.ToUniversalTime(); @@ -496,697 +376,40 @@ namespace MediaBrowser.Providers.TV.TheTVDB series.ProductionYear = date.Year; } - series.RunTimeTicks = TimeSpan.FromMinutes(Convert.ToDouble(seriesResponse.Data.Runtime)).Ticks; - - - if (!string.IsNullOrWhiteSpace(val)) + series.RunTimeTicks = TimeSpan.FromMinutes(Convert.ToDouble(tvdbSeries.Runtime)).Ticks; + foreach (var genre in tvdbSeries.Genre) { - var vals = val - .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) - .Select(i => i.Trim()) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .ToList(); - - if (vals.Count > 0) - { - item.Genres = Array.Empty(); - - foreach (var genre in vals) - { - item.AddGenre(genre); - } - } + series.AddGenre(genre); } - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - var vals = val - .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) - .Select(i => i.Trim()) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .ToList(); - - if (vals.Count > 0) - { - item.SetStudios(vals); - } - } - -// using (var fileStream = _fileSystem.GetFileStream(seriesXmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) -// { -// using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) -// { -// // Use XmlReader for best performance -// using (var reader = XmlReader.Create(streamReader, settings)) -// { -// reader.MoveToContent(); -// reader.Read(); -// -// // Loop through each element -// while (!reader.EOF && reader.ReadState == ReadState.Interactive) -// { -// cancellationToken.ThrowIfCancellationRequested(); -// -// if (reader.NodeType == XmlNodeType.Element) -// { -// switch (reader.Name) -// { -// case "Series": -// { -// if (reader.IsEmptyElement) -// { -// reader.Read(); -// continue; -// } -// using (var subtree = reader.ReadSubtree()) -// { -// FetchDataFromSeriesNode(result, subtree, cancellationToken); -// } -// break; -// } -// -// case "Episode": -// { -// if (reader.IsEmptyElement) -// { -// reader.Read(); -// continue; -// } -// using (var subtree = reader.ReadSubtree()) -// { -// var date = GetFirstAiredDateFromEpisodeNode(subtree, cancellationToken); -// -// if (date.HasValue) -// { -// episiodeAirDates.Add(date.Value); -// } -// } -// break; -// } -// -// default: -// reader.Skip(); -// break; -// } -// } -// else -// { -// reader.Read(); -// } -// } -// } -// } -// } + // TODO is network == studio? + series.AddStudio(tvdbSeries.Network); + // TODO is this necessary? if (result.Item.Status.HasValue && result.Item.Status.Value == SeriesStatus.Ended && episodeAirDates.Count > 0) { result.Item.EndDate = episodeAirDates.Max(); } } - private DateTime? GetFirstAiredDateFromEpisodeNode(XmlReader reader, CancellationToken cancellationToken) + private static void MapActorsToResult(MetadataResult result, IEnumerable actors) { - DateTime? airDate = null; - int? seasonNumber = null; - - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) + foreach (Actor actor in actors) { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) + var personInfo = new PersonInfo { - switch (reader.Name) - { - case "FirstAired": - { - var val = reader.ReadElementContentAsString(); + Type = PersonType.Actor, + Name = (actor.Name ?? string.Empty).Trim(), + Role = actor.Role, + ImageUrl = actor.Image, + SortOrder = actor.SortOrder + }; - if (!string.IsNullOrWhiteSpace(val)) - { - if (DateTime.TryParse(val, out var date)) - { - airDate = date.ToUniversalTime(); - } - } - - break; - } - - case "SeasonNumber": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - seasonNumber = rval; - } - } - - break; - } - - default: - reader.Skip(); - break; - } - } - else + if (!string.IsNullOrWhiteSpace(personInfo.Name)) { - reader.Read(); + result.AddPerson(personInfo); } } - - if (seasonNumber.HasValue && seasonNumber.Value != 0) - { - return airDate; - } - - return null; - } - - /// - /// Fetches the actors. - /// - /// The result. - /// The actors XML path. - private void FetchActors(MetadataResult result, string actorsXmlPath) - { - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - using (var fileStream = _fileSystem.GetFileStream(actorsXmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) - { - using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Actor": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - using (var subtree = reader.ReadSubtree()) - { - FetchDataFromActorNode(result, subtree); - } - break; - } - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } - } - } - } - - /// - /// Fetches the data from actor node. - /// - /// The result. - /// The reader. - private void FetchDataFromActorNode(MetadataResult result, XmlReader reader) - { - reader.MoveToContent(); - - var personInfo = new PersonInfo(); - - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Name": - { - personInfo.Name = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - break; - } - - case "Role": - { - personInfo.Role = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - break; - } - - case "id": - { - reader.Skip(); - break; - } - - case "Image": - { - var url = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - if (!string.IsNullOrWhiteSpace(url)) - { - personInfo.ImageUrl = TVUtils.BannerUrl + url; - } - break; - } - - case "SortOrder": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) - { - personInfo.SortOrder = rval; - } - } - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - - personInfo.Type = PersonType.Actor; - - if (!string.IsNullOrWhiteSpace(personInfo.Name)) - { - result.AddPerson(personInfo); - } - } - - private void FetchDataFromSeriesNode(MetadataResult result, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - - } - - /// - /// Extracts info for each episode into invididual xml files so that they can be easily accessed without having to step through the entire series xml - /// - /// The series data path. - /// The XML file. - /// The last tv db update time. - /// Task. - private async Task ExtractEpisodes(string seriesDataPath, string xmlFile, long? lastTvDbUpdateTime) - { - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - using (var fileStream = _fileSystem.GetFileStream(xmlFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) - { - using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Episode": - { - var outerXml = reader.ReadOuterXml(); - - await SaveEpsiodeXml(seriesDataPath, outerXml, lastTvDbUpdateTime).ConfigureAwait(false); - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } - } - } - } - - private async Task SaveEpsiodeXml(string seriesDataPath, string xml, long? lastTvDbUpdateTime) - { - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - var seasonNumber = -1; - var episodeNumber = -1; - var absoluteNumber = -1; - var lastUpdateString = string.Empty; - - var dvdSeasonNumber = -1; - var dvdEpisodeNumber = -1.0; - - using (var streamReader = new StringReader(xml)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "lastupdated": - { - lastUpdateString = reader.ReadElementContentAsString(); - break; - } - - case "EpisodeNumber": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var num)) - { - episodeNumber = num; - } - } - break; - } - - case "Combined_episodenumber": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num)) - { - dvdEpisodeNumber = num; - } - } - - break; - } - - case "Combined_season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num)) - { - dvdSeasonNumber = Convert.ToInt32(num); - } - } - - break; - } - - case "absolute_number": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var num)) - { - absoluteNumber = num; - } - } - break; - } - - case "SeasonNumber": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var num)) - { - seasonNumber = num; - } - } - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } - } - - var hasEpisodeChanged = true; - if (!string.IsNullOrWhiteSpace(lastUpdateString) && lastTvDbUpdateTime.HasValue) - { - if (long.TryParse(lastUpdateString, NumberStyles.Any, _usCulture, out var num)) - { - hasEpisodeChanged = num >= lastTvDbUpdateTime.Value; - } - } - - var file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber, episodeNumber)); - - // Only save the file if not already there, or if the episode has changed - if (hasEpisodeChanged || !File.Exists(file)) - { - using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true)) - { - using (var writer = XmlWriter.Create(fileStream, new XmlWriterSettings - { - Encoding = Encoding.UTF8, - Async = true - })) - { - await writer.WriteRawAsync(xml).ConfigureAwait(false); - } - } - } - - if (absoluteNumber != -1) - { - file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", absoluteNumber)); - - // Only save the file if not already there, or if the episode has changed - if (hasEpisodeChanged || !File.Exists(file)) - { - using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true)) - { - using (var writer = XmlWriter.Create(fileStream, new XmlWriterSettings - { - Encoding = Encoding.UTF8, - Async = true - })) - { - await writer.WriteRawAsync(xml).ConfigureAwait(false); - } - } - } - } - - if (dvdSeasonNumber != -1 && dvdEpisodeNumber != -1 && (dvdSeasonNumber != seasonNumber || dvdEpisodeNumber != episodeNumber)) - { - file = Path.Combine(seriesDataPath, string.Format("episode-dvd-{0}-{1}.xml", dvdSeasonNumber, dvdEpisodeNumber)); - - // Only save the file if not already there, or if the episode has changed - if (hasEpisodeChanged || !File.Exists(file)) - { - using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true)) - { - using (var writer = XmlWriter.Create(fileStream, new XmlWriterSettings - { - Encoding = Encoding.UTF8, - Async = true - })) - { - await writer.WriteRawAsync(xml).ConfigureAwait(false); - } - } - } - } - } - - /// - /// Gets the series data path. - /// - /// The app paths. - /// The series provider ids. - /// System.String. - internal static string GetSeriesDataPath(IApplicationPaths appPaths, Dictionary seriesProviderIds) - { - if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out string seriesId) && !string.IsNullOrEmpty(seriesId)) - { - var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId); - - return seriesDataPath; - } - - if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out seriesId) && !string.IsNullOrEmpty(seriesId)) - { - var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId); - - return seriesDataPath; - } - - if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out seriesId) && !string.IsNullOrEmpty(seriesId)) - { - var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId); - - return seriesDataPath; - } - - return null; - } - - public string GetSeriesXmlPath(Dictionary seriesProviderIds, string language) - { - var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); - - var seriesXmlFilename = language.ToLowerInvariant() + ".xml"; - - return Path.Combine(seriesDataPath, seriesXmlFilename); - } - - /// - /// Gets the series data path. - /// - /// The app paths. - /// System.String. - internal static string GetSeriesDataPath(IApplicationPaths appPaths) - { - var dataPath = Path.Combine(appPaths.CachePath, "tvdb"); - - return dataPath; - } - - private void DeleteXmlFiles(string path) - { - try - { - foreach (var file in _fileSystem.GetFilePaths(path, true) - .ToList()) - { - _fileSystem.DeleteFile(file); - } - } - catch (IOException) - { - // No biggie - } - } - - /// - /// Sanitizes the XML file. - /// - /// The file. - /// Task. - private async Task SanitizeXmlFile(string file) - { - string validXml; - - using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true)) - { - using (var reader = new StreamReader(fileStream)) - { - var xml = await reader.ReadToEndAsync().ConfigureAwait(false); - - validXml = StripInvalidXmlCharacters(xml); - } - } - - using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) - { - using (var writer = new StreamWriter(fileStream)) - { - await writer.WriteAsync(validXml).ConfigureAwait(false); - } - } - } - - /// - /// Strips the invalid XML characters. - /// - /// The in string. - /// System.String. - public static string StripInvalidXmlCharacters(string inString) - { - if (inString == null) return null; - - var sbOutput = new StringBuilder(); - char ch; - - for (int i = 0; i < inString.Length; i++) - { - ch = inString[i]; - if ((ch >= 0x0020 && ch <= 0xD7FF) || - (ch >= 0xE000 && ch <= 0xFFFD) || - ch == 0x0009 || - ch == 0x000A || - ch == 0x000D) - { - sbOutput.Append(ch); - } - } - return sbOutput.ToString(); } public string Name => "TheTVDB"; From 19b6808602acfac2533830fcbf5ca03b2413e859 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 30 Jan 2019 21:32:38 +0100 Subject: [PATCH 03/39] Remove some junk from series provider --- .../TV/TheTVDB/TvdbSeriesProvider.cs | 87 +++++++++---------- 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 84c218265f..4aa2d10622 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -31,23 +31,17 @@ namespace MediaBrowser.Providers.TV.TheTVDB { internal static TvdbSeriesProvider Current { get; private set; } private readonly IHttpClient _httpClient; - private readonly IFileSystem _fileSystem; - private readonly IXmlReaderSettingsFactory _xmlSettings; - private readonly IServerConfigurationManager _config; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localizationManager; private readonly TvDbClient _tvDbClient; - public TvdbSeriesProvider(IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager) + public TvdbSeriesProvider(IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, ILocalizationManager localizationManager) { _httpClient = httpClient; - _fileSystem = fileSystem; - _config = config; _logger = logger; _libraryManager = libraryManager; - _xmlSettings = xmlSettings; _localizationManager = localizationManager; Current = this; _tvDbClient = new TvDbClient(); @@ -179,45 +173,46 @@ namespace MediaBrowser.Providers.TV.TheTVDB // TODO caching private bool IsCacheValid(string seriesDataPath, string preferredMetadataLanguage) { - try - { - var files = _fileSystem.GetFiles(seriesDataPath, new[] { ".xml" }, true, false) - .ToList(); - - var seriesXmlFilename = preferredMetadataLanguage + ".xml"; - - const int cacheHours = 12; - - var seriesFile = files.FirstOrDefault(i => string.Equals(seriesXmlFilename, i.Name, StringComparison.OrdinalIgnoreCase)); - // No need to check age if automatic updates are enabled - if (seriesFile == null || !seriesFile.Exists || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(seriesFile)).TotalHours > cacheHours) - { - return false; - } - - var actorsXml = files.FirstOrDefault(i => string.Equals("actors.xml", i.Name, StringComparison.OrdinalIgnoreCase)); - // No need to check age if automatic updates are enabled - if (actorsXml == null || !actorsXml.Exists || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(actorsXml)).TotalHours > cacheHours) - { - return false; - } - - var bannersXml = files.FirstOrDefault(i => string.Equals("banners.xml", i.Name, StringComparison.OrdinalIgnoreCase)); - // No need to check age if automatic updates are enabled - if (bannersXml == null || !bannersXml.Exists || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(bannersXml)).TotalHours > cacheHours) - { - return false; - } - return true; - } - catch (FileNotFoundException) - { - return false; - } - catch (IOException) - { - return false; - } + return true; +// try +// { +// var files = _fileSystem.GetFiles(seriesDataPath, new[] { ".xml" }, true, false) +// .ToList(); +// +// var seriesXmlFilename = preferredMetadataLanguage + ".xml"; +// +// const int cacheHours = 12; +// +// var seriesFile = files.FirstOrDefault(i => string.Equals(seriesXmlFilename, i.Name, StringComparison.OrdinalIgnoreCase)); +// // No need to check age if automatic updates are enabled +// if (seriesFile == null || !seriesFile.Exists || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(seriesFile)).TotalHours > cacheHours) +// { +// return false; +// } +// +// var actorsXml = files.FirstOrDefault(i => string.Equals("actors.xml", i.Name, StringComparison.OrdinalIgnoreCase)); +// // No need to check age if automatic updates are enabled +// if (actorsXml == null || !actorsXml.Exists || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(actorsXml)).TotalHours > cacheHours) +// { +// return false; +// } +// +// var bannersXml = files.FirstOrDefault(i => string.Equals("banners.xml", i.Name, StringComparison.OrdinalIgnoreCase)); +// // No need to check age if automatic updates are enabled +// if (bannersXml == null || !bannersXml.Exists || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(bannersXml)).TotalHours > cacheHours) +// { +// return false; +// } +// return true; +// } +// catch (FileNotFoundException) +// { +// return false; +// } +// catch (IOException) +// { +// return false; +// } } /// From 2a26760911eb70e6a8b6e1eda4e1fcf342aad573 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 30 Jan 2019 23:28:43 +0100 Subject: [PATCH 04/39] Episode provider is somewhat broken --- .../TV/TheTVDB/TvDbClientManager.cs | 64 + .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 7 +- .../TV/TheTVDB/TvdbEpisodeProvider.cs | 1067 +++-------------- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 33 +- .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 18 +- .../TV/TheTVDB/TvdbSeriesProvider.cs | 29 +- 6 files changed, 309 insertions(+), 909 deletions(-) create mode 100644 MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs new file mode 100644 index 0000000000..861cd3f5bd --- /dev/null +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -0,0 +1,64 @@ +using System; +using MediaBrowser.Controller.Library; +using TvDbSharper; + +namespace MediaBrowser.Providers.TV +{ + public sealed class TvDbClientManager + { + private static volatile TvDbClientManager instance; + private static readonly object syncRoot = new object(); + private static TvDbClient tvDbClient; + private static DateTime tokenCreatedAt; + + private TvDbClientManager() + { + tvDbClient = new TvDbClient(); + tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey); + tokenCreatedAt = DateTime.Now; + } + + public static TvDbClientManager Instance + { + get + { + if (instance != null) + { + return instance; + } + + lock (syncRoot) + { + if (instance == null) + instance = new TvDbClientManager(); + } + + return instance; + } + } + + public TvDbClient TvDbClient + { + get + { + // Refresh if necessary + if (tokenCreatedAt > DateTime.Now.Subtract(TimeSpan.FromHours(20))) + { + try + { + tvDbClient.Authentication.RefreshTokenAsync(); + } + catch + { + tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey); + } + + tokenCreatedAt = DateTime.Now; + } + // Default to English + tvDbClient.AcceptedLanguage = "en"; + return tvDbClient; + } + } + } +} diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 75547610db..bc526e42a9 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -20,13 +20,12 @@ namespace MediaBrowser.Providers.TV.TheTVDB { private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IHttpClient _httpClient; - private readonly TvDbClient _tvDbClient; + private readonly TvDbClientManager _tvDbClientManager; public TvdbEpisodeImageProvider(IHttpClient httpClient) { _httpClient = httpClient; - _tvDbClient = new TvDbClient(); - _tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey); + _tvDbClientManager = TvDbClientManager.Instance; } public string Name => "TheTVDB"; @@ -53,7 +52,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB { var tvdbId = episode.GetProviderId(MetadataProviders.Tvdb); // Process images - var episodeResult = await _tvDbClient.Episodes.GetAsync(Convert.ToInt32(tvdbId), cancellationToken); + var episodeResult = await _tvDbClientManager.TvDbClient.Episodes.GetAsync(Convert.ToInt32(tvdbId), cancellationToken); var image = GetImageInfo(episodeResult.Data); return new List diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index 7a1789e50f..c3a95f3856 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -1,872 +1,203 @@ -//using System; -//using System.Collections.Generic; -//using System.Globalization; -//using System.IO; -//using System.Linq; -//using System.Text; -//using System.Threading; -//using System.Threading.Tasks; -//using System.Xml; -//using MediaBrowser.Common.Net; -//using MediaBrowser.Controller.Configuration; -//using MediaBrowser.Controller.Entities; -//using MediaBrowser.Controller.Entities.TV; -//using MediaBrowser.Controller.Providers; -//using MediaBrowser.Model.Entities; -//using MediaBrowser.Model.IO; -//using MediaBrowser.Model.Providers; -//using MediaBrowser.Model.Xml; -//using Microsoft.Extensions.Logging; -// -//namespace MediaBrowser.Providers.TV -//{ -// -// /// -// /// Class RemoteEpisodeProvider -// /// -// class TvdbEpisodeProvider : IRemoteMetadataProvider -// { -// internal static TvdbEpisodeProvider Current; -// private readonly IServerConfigurationManager _config; -// private readonly IHttpClient _httpClient; -// private readonly ILogger _logger; -// -// public TvdbEpisodeProvider(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger) -// { -// _config = config; -// _httpClient = httpClient; -// _logger = logger; -// Current = this; -// } -// -// public Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) -// { -// var list = new List(); -// -// // The search query must either provide an episode number or date -// if (!searchInfo.IndexNumber.HasValue && !searchInfo.PremiereDate.HasValue) -// { -// return Task.FromResult((IEnumerable)list); -// } -// -// if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds)) -// { -// try -// { -// var metadataResult = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken); -// -// if (metadataResult.HasMetadata) -// { -// var item = metadataResult.Item; -// -// list.Add(new RemoteSearchResult -// { -// IndexNumber = item.IndexNumber, -// Name = item.Name, -// ParentIndexNumber = item.ParentIndexNumber, -// PremiereDate = item.PremiereDate, -// ProductionYear = item.ProductionYear, -// ProviderIds = item.ProviderIds, -// SearchProviderName = Name, -// IndexNumberEnd = item.IndexNumberEnd -// }); -// } -// } -// catch (FileNotFoundException) -// { -// // Don't fail the provider because this will just keep on going and going. -// } -// catch (IOException) -// { -// // Don't fail the provider because this will just keep on going and going. -// } -// } -// -// return Task.FromResult((IEnumerable)list); -// } -// -// public string Name => "TheTVDB"; -// -// public async Task> GetMetadata(EpisodeInfo searchInfo, CancellationToken cancellationToken) -// { -// var result = new MetadataResult(); -// result.QueriedById = true; -// -// if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && -// (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue)) -// { -// result = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken); -// } -// else -// { -// _logger.LogDebug("No series identity found for {0}", searchInfo.Name); -// } -// -// return result; -// } -// -// /// -// /// Gets the episode XML files. -// /// -// /// The series data path. -// /// The search information. -// /// List{FileInfo}. -// internal List GetEpisodeXmlNodes(string seriesDataPath, EpisodeInfo searchInfo) -// { -// var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage); -// -// try -// { -// return GetXmlNodes(seriesXmlPath, searchInfo); -// } -// catch (FileNotFoundException) -// { -// return new List(); -// } -// catch (IOException) -// { -// return new List(); -// } -// } -// -// /// -// /// Fetches the episode data. -// /// -// /// The identifier. -// /// The series data path. -// /// The cancellation token. -// /// Task{System.Boolean}. -// private MetadataResult FetchEpisodeData(EpisodeInfo id, string seriesDataPath, CancellationToken cancellationToken) -// { -// var result = new MetadataResult() -// { -// Item = new Episode -// { -// IndexNumber = id.IndexNumber, -// ParentIndexNumber = id.ParentIndexNumber, -// IndexNumberEnd = id.IndexNumberEnd -// } -// }; -// -// var xmlNodes = GetEpisodeXmlNodes(seriesDataPath, id); -// -// if (xmlNodes.Count > 0) -// { -// FetchMainEpisodeInfo(result, xmlNodes[0], id.SeriesDisplayOrder, cancellationToken); -// -// result.HasMetadata = true; -// } -// +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using Microsoft.Extensions.Logging; +using TvDbSharper.Dto; + +namespace MediaBrowser.Providers.TV +{ + + /// + /// Class RemoteEpisodeProvider + /// + class TvdbEpisodeProvider : IRemoteMetadataProvider, IHasOrder + { + internal static TvdbEpisodeProvider Current; + private readonly IHttpClient _httpClient; + private readonly ILogger _logger; + private readonly TvDbClientManager _tvDbClientManager; + + public TvdbEpisodeProvider(IHttpClient httpClient, ILogger logger) + { + _httpClient = httpClient; + _logger = logger; + Current = this; + _tvDbClientManager = TvDbClientManager.Instance; + } + + public async Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) + { + var list = new List(); + + // The search query must either provide an episode number or date + // TODO premieredate functionality is ded + if (!searchInfo.IndexNumber.HasValue || !searchInfo.PremiereDate.HasValue) + { + return list; + } + + if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds)) + { + var episodeResult = + await _tvDbClientManager.TvDbClient.Episodes.GetAsync((int)searchInfo.IndexNumber, cancellationToken); + var metadataResult = MapEpisodeToResult(searchInfo, episodeResult.Data); + + if (metadataResult.HasMetadata) + { + var item = metadataResult.Item; + + list.Add(new RemoteSearchResult + { + IndexNumber = item.IndexNumber, + Name = item.Name, + ParentIndexNumber = item.ParentIndexNumber, + PremiereDate = item.PremiereDate, + ProductionYear = item.ProductionYear, + ProviderIds = item.ProviderIds, + SearchProviderName = Name, + IndexNumberEnd = item.IndexNumberEnd + }); + } + } + + return list; + } + + public string Name => "TheTVDB"; + + public async Task> GetMetadata(EpisodeInfo searchInfo, CancellationToken cancellationToken) + { + var result = new MetadataResult + { + QueriedById = true + }; + + if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && + (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue)) + { + var episodeResult = await _tvDbClientManager.TvDbClient.Episodes.GetAsync(Convert.ToInt32(searchInfo.GetProviderId(MetadataProviders.Tvdb)), + cancellationToken); + + result = MapEpisodeToResult(searchInfo, episodeResult.Data); + } + else + { + _logger.LogDebug("No series identity found for {0}", searchInfo.Name); + } + + return result; + } + + private static MetadataResult MapEpisodeToResult(EpisodeInfo id, EpisodeRecord episode) + { + var result = new MetadataResult + { + HasMetadata = true, + Item = new Episode + { + IndexNumber = id.IndexNumber, + ParentIndexNumber = id.ParentIndexNumber, + IndexNumberEnd = id.IndexNumberEnd, + AirsBeforeEpisodeNumber = episode.AirsBeforeEpisode, + AirsAfterSeasonNumber = episode.AirsAfterSeason, + AirsBeforeSeasonNumber = episode.AirsBeforeSeason, + Name = episode.EpisodeName, + Overview = episode.Overview, + CommunityRating = (float?)episode.SiteRating, + + } + }; + result.ResetPeople(); + + var item = result.Item; + item.SetProviderId(MetadataProviders.Tvdb, episode.Id.ToString()); + item.SetProviderId(MetadataProviders.Imdb, episode.ImdbId); + + if (string.Equals(id.SeriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase)) + { + item.IndexNumber = Convert.ToInt32(episode.DvdEpisodeNumber ?? episode.AiredEpisodeNumber); + item.ParentIndexNumber = episode.DvdSeason ?? episode.AiredSeason; + } + else if (episode.AiredEpisodeNumber.HasValue) + { + item.IndexNumber = episode.AiredEpisodeNumber; + } + else if (episode.AiredSeason.HasValue) + { + item.ParentIndexNumber = episode.AiredSeason; + } + + if (DateTime.TryParse(episode.FirstAired, out var date)) + { + date = date.ToUniversalTime(); + + item.PremiereDate = date; + item.ProductionYear = date.Year; + } + + foreach (var director in episode.Directors) + { + result.AddPerson(new PersonInfo + { + Name = director, + Type = PersonType.Director + }); + } + foreach (var person in episode.GuestStars) + { + var index = person.IndexOf('('); + string role = null; + var name = person; + + if (index != -1) + { + role = person.Substring(index + 1).Trim().TrimEnd(')'); + + name = person.Substring(0, index).Trim(); + } + + result.AddPerson(new PersonInfo + { + Type = PersonType.GuestStar, + Name = name, + Role = role + }); + } + foreach (var writer in episode.Writers) + { + result.AddPerson(new PersonInfo + { + Name = writer, + Type = PersonType.Writer + }); + } + // TODO result.ResultLanguage = episode. + + // TODO wtf is additional part info? // foreach (var node in xmlNodes.Skip(1)) // { // FetchAdditionalPartInfo(result, node, cancellationToken); // } -// -// return result; -// } -// -// private List GetXmlNodes(string xmlFile, EpisodeInfo searchInfo) -// { -// var list = new List(); -// -// if (searchInfo.IndexNumber.HasValue) -// { -// var files = GetEpisodeXmlFiles(searchInfo.SeriesDisplayOrder, searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, Path.GetDirectoryName(xmlFile)); -// -// list = files.Select(GetXmlReader).ToList(); -// } -// -// if (list.Count == 0 && searchInfo.PremiereDate.HasValue) -// { -// list = GetXmlNodesByPremiereDate(xmlFile, searchInfo.PremiereDate.Value); -// } -// -// return list; -// } -// -// private string GetEpisodeFileName(string seriesDisplayOrder, int? seasonNumber, int? episodeNumber) -// { -// if (string.Equals(seriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase)) -// { -// return string.Format("episode-abs-{0}.xml", episodeNumber); -// } -// else if (string.Equals(seriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase)) -// { -// return string.Format("episode-dvd-{0}-{1}.xml", seasonNumber.Value, episodeNumber); -// } -// else -// { -// return string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber); -// } -// } -// -// private FileSystemMetadata GetEpisodeFileInfoWithFallback(string seriesDataPath, string seriesDisplayOrder, int? seasonNumber, int? episodeNumber) -// { -// var file = Path.Combine(seriesDataPath, GetEpisodeFileName(seriesDisplayOrder, seasonNumber, episodeNumber)); -// var fileInfo = _fileSystem.GetFileInfo(file); -// -// if (fileInfo.Exists) -// { -// return fileInfo; -// } -// -// if (!seasonNumber.HasValue) -// { -// return fileInfo; -// } -// -// // revert to aired order -// if (string.Equals(seriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase) || string.Equals(seriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase)) -// { -// file = Path.Combine(seriesDataPath, GetEpisodeFileName(null, seasonNumber, episodeNumber)); -// return _fileSystem.GetFileInfo(file); -// } -// -// return fileInfo; -// } -// -// private List GetEpisodeXmlFiles(string seriesDisplayOrder, int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath) -// { -// var files = new List(); -// -// if (episodeNumber == null) -// { -// return files; -// } -// -// if (!seasonNumber.HasValue) -// { -// seriesDisplayOrder = "absolute"; -// } -// -// var fileInfo = GetEpisodeFileInfoWithFallback(seriesDataPath, seriesDisplayOrder, seasonNumber, episodeNumber); -// -// if (fileInfo.Exists) -// { -// files.Add(fileInfo); -// } -// -// var end = endingEpisodeNumber ?? episodeNumber; -// episodeNumber++; -// -// while (episodeNumber <= end) -// { -// fileInfo = GetEpisodeFileInfoWithFallback(seriesDataPath, seriesDisplayOrder, seasonNumber, episodeNumber); -// -// if (fileInfo.Exists) -// { -// files.Add(fileInfo); -// } -// else -// { -// break; -// } -// -// episodeNumber++; -// } -// -// return files; -// } -// -// private XmlReader GetXmlReader(FileSystemMetadata xmlFile) -// { -// return GetXmlReader(File.ReadAllText(xmlFile.FullName, Encoding.UTF8)); -// } -// -// private XmlReader GetXmlReader(string xml) -// { -// var streamReader = new StringReader(xml); -// -// var settings = _xmlSettings.Create(false); -// -// settings.CheckCharacters = false; -// settings.IgnoreProcessingInstructions = true; -// settings.IgnoreComments = true; -// -// return XmlReader.Create(streamReader, settings); -// } -// -// private List GetXmlNodesByPremiereDate(string xmlFile, DateTime premiereDate) -// { -// var list = new List(); -// -// using (var fileStream = _fileSystem.GetFileStream(xmlFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) -// { -// using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) -// { -// // Use XmlReader for best performance -// -// var settings = _xmlSettings.Create(false); -// -// settings.CheckCharacters = false; -// settings.IgnoreProcessingInstructions = true; -// settings.IgnoreComments = true; -// -// using (var reader = XmlReader.Create(streamReader, settings)) -// { -// reader.MoveToContent(); -// reader.Read(); -// -// // Loop through each element -// while (!reader.EOF && reader.ReadState == ReadState.Interactive) -// { -// if (reader.NodeType == XmlNodeType.Element) -// { -// switch (reader.Name) -// { -// case "Episode": -// { -// var outerXml = reader.ReadOuterXml(); -// -// var airDate = GetEpisodeAirDate(outerXml); -// -// if (airDate.HasValue && premiereDate.Date == airDate.Value.Date) -// { -// list.Add(GetXmlReader(outerXml)); -// return list; -// } -// -// break; -// } -// -// default: -// reader.Skip(); -// break; -// } -// } -// else -// { -// reader.Read(); -// } -// } -// } -// } -// } -// -// return list; -// } -// -// private DateTime? GetEpisodeAirDate(string xml) -// { -// using (var streamReader = new StringReader(xml)) -// { -// var settings = _xmlSettings.Create(false); -// -// settings.CheckCharacters = false; -// settings.IgnoreProcessingInstructions = true; -// settings.IgnoreComments = true; -// -// // Use XmlReader for best performance -// using (var reader = XmlReader.Create(streamReader, settings)) -// { -// reader.MoveToContent(); -// reader.Read(); -// -// // Loop through each element -// while (!reader.EOF && reader.ReadState == ReadState.Interactive) -// { -// if (reader.NodeType == XmlNodeType.Element) -// { -// switch (reader.Name) -// { -// case "FirstAired": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// if (DateTime.TryParse(val, out var date)) -// { -// date = date.ToUniversalTime(); -// -// return date; -// } -// } -// -// break; -// } -// -// default: -// reader.Skip(); -// break; -// } -// } -// else -// { -// reader.Read(); -// } -// } -// } -// } -// return null; -// } -// -// private readonly CultureInfo _usCulture = new CultureInfo("en-US"); -// -// private void FetchMainEpisodeInfo(MetadataResult result, XmlReader reader, string seriesOrder, CancellationToken cancellationToken) -// { -// var item = result.Item; -// -// int? episodeNumber = null; -// int? seasonNumber = null; -// int? combinedEpisodeNumber = null; -// int? combinedSeasonNumber = null; -// -// // Use XmlReader for best performance -// using (reader) -// { -// result.ResetPeople(); -// -// reader.MoveToContent(); -// reader.Read(); -// -// // Loop through each element -// while (!reader.EOF && reader.ReadState == ReadState.Interactive) -// { -// cancellationToken.ThrowIfCancellationRequested(); -// -// if (reader.NodeType == XmlNodeType.Element) -// { -// switch (reader.Name) -// { -// case "id": -// { -// var val = reader.ReadElementContentAsString(); -// if (!string.IsNullOrWhiteSpace(val)) -// { -// item.SetProviderId(MetadataProviders.Tvdb, val); -// } -// break; -// } -// -// case "IMDB_ID": -// { -// var val = reader.ReadElementContentAsString(); -// if (!string.IsNullOrWhiteSpace(val)) -// { -// item.SetProviderId(MetadataProviders.Imdb, val); -// } -// break; -// } -// -// case "EpisodeNumber": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// // int.TryParse is local aware, so it can be probamatic, force us culture -// if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) -// { -// episodeNumber = rval; -// } -// } -// -// break; -// } -// -// case "SeasonNumber": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// // int.TryParse is local aware, so it can be probamatic, force us culture -// if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) -// { -// seasonNumber = rval; -// } -// } -// -// break; -// } -// -// case "Combined_episodenumber": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num)) -// { -// combinedEpisodeNumber = Convert.ToInt32(num); -// } -// } -// -// break; -// } -// -// case "Combined_season": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num)) -// { -// combinedSeasonNumber = Convert.ToInt32(num); -// } -// } -// -// break; -// } -// -// case "airsbefore_episode": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// // int.TryParse is local aware, so it can be probamatic, force us culture -// if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) -// { -// item.AirsBeforeEpisodeNumber = rval; -// } -// } -// -// break; -// } -// -// case "airsafter_season": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// // int.TryParse is local aware, so it can be probamatic, force us culture -// if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) -// { -// item.AirsAfterSeasonNumber = rval; -// } -// } -// -// break; -// } -// -// case "airsbefore_season": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// // int.TryParse is local aware, so it can be probamatic, force us culture -// if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) -// { -// item.AirsBeforeSeasonNumber = rval; -// } -// } -// -// break; -// } -// -// case "EpisodeName": -// { -// var val = reader.ReadElementContentAsString(); -// if (!item.LockedFields.Contains(MetadataFields.Name)) -// { -// if (!string.IsNullOrWhiteSpace(val)) -// { -// item.Name = val; -// } -// } -// break; -// } -// -// case "Overview": -// { -// var val = reader.ReadElementContentAsString(); -// if (!item.LockedFields.Contains(MetadataFields.Overview)) -// { -// if (!string.IsNullOrWhiteSpace(val)) -// { -// item.Overview = val; -// } -// } -// break; -// } -// case "Rating": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// // float.TryParse is local aware, so it can be probamatic, force us culture -// if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out var rval)) -// { -// item.CommunityRating = rval; -// } -// } -// break; -// } -// case "RatingCount": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// // int.TryParse is local aware, so it can be probamatic, force us culture -// if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval)) -// { -// //item.VoteCount = rval; -// } -// } -// -// break; -// } -// -// case "FirstAired": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// if (DateTime.TryParse(val, out var date)) -// { -// date = date.ToUniversalTime(); -// -// item.PremiereDate = date; -// item.ProductionYear = date.Year; -// } -// } -// -// break; -// } -// -// case "Director": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// if (!item.LockedFields.Contains(MetadataFields.Cast)) -// { -// AddPeople(result, val, PersonType.Director); -// } -// } -// -// break; -// } -// case "GuestStars": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// if (!item.LockedFields.Contains(MetadataFields.Cast)) -// { -// AddGuestStars(result, val); -// } -// } -// -// break; -// } -// case "Writer": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// if (!item.LockedFields.Contains(MetadataFields.Cast)) -// { -// //AddPeople(result, val, PersonType.Writer); -// } -// } -// -// break; -// } -// case "Language": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// result.ResultLanguage = val; -// } -// -// break; -// } -// -// default: -// reader.Skip(); -// break; -// } -// } -// else -// { -// reader.Read(); -// } -// } -// } -// -// if (string.Equals(seriesOrder, "dvd", StringComparison.OrdinalIgnoreCase)) -// { -// episodeNumber = combinedEpisodeNumber ?? episodeNumber; -// seasonNumber = combinedSeasonNumber ?? seasonNumber; -// } -// -// if (episodeNumber.HasValue) -// { -// item.IndexNumber = episodeNumber; -// } -// -// if (seasonNumber.HasValue) -// { -// item.ParentIndexNumber = seasonNumber; -// } -// } -// -// private void AddPeople(MetadataResult result, string val, string personType) -// { -// // Sometimes tvdb actors have leading spaces -// foreach (var person in val.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) -// .Where(i => !string.IsNullOrWhiteSpace(i)) -// .Select(str => new PersonInfo { Type = personType, Name = str.Trim() })) -// { -// result.AddPerson(person); -// } -// } -// -// private void AddGuestStars(MetadataResult result, string val) -// where T : BaseItem -// { -// // example: -// // |Mark C. Thomas| Dennis Kiefer| David Nelson (David)| Angela Nicholas| Tzi Ma| Kevin P. Kearns (Pasco)| -// var persons = val.Split('|') -// .Select(i => i.Trim()) -// .Where(i => !string.IsNullOrWhiteSpace(i)) -// .ToList(); -// -// foreach (var person in persons) -// { -// var index = person.IndexOf('('); -// string role = null; -// var name = person; -// -// if (index != -1) -// { -// role = person.Substring(index + 1).Trim().TrimEnd(')'); -// -// name = person.Substring(0, index).Trim(); -// } -// -// result.AddPerson(new PersonInfo -// { -// Type = PersonType.GuestStar, -// Name = name, -// Role = role -// }); -// } -// } -// -// private void FetchAdditionalPartInfo(MetadataResult result, XmlReader reader, CancellationToken cancellationToken) -// { -// var item = result.Item; -// -// // Use XmlReader for best performance -// using (reader) -// { -// reader.MoveToContent(); -// reader.Read(); -// -// // Loop through each element -// while (!reader.EOF && reader.ReadState == ReadState.Interactive) -// { -// cancellationToken.ThrowIfCancellationRequested(); -// -// if (reader.NodeType == XmlNodeType.Element) -// { -// switch (reader.Name) -// { -// case "EpisodeName": -// { -// var val = reader.ReadElementContentAsString(); -// if (!item.LockedFields.Contains(MetadataFields.Name)) -// { -// if (!string.IsNullOrWhiteSpace(val)) -// { -// item.Name += ", " + val; -// } -// } -// break; -// } -// -// case "Overview": -// { -// var val = reader.ReadElementContentAsString(); -// if (!item.LockedFields.Contains(MetadataFields.Overview)) -// { -// if (!string.IsNullOrWhiteSpace(val)) -// { -// item.Overview += Environment.NewLine + Environment.NewLine + val; -// } -// } -// break; -// } -// case "Director": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// if (!item.LockedFields.Contains(MetadataFields.Cast)) -// { -// AddPeople(result, val, PersonType.Director); -// } -// } -// -// break; -// } -// case "GuestStars": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// if (!item.LockedFields.Contains(MetadataFields.Cast)) -// { -// AddGuestStars(result, val); -// } -// } -// -// break; -// } -// case "Writer": -// { -// var val = reader.ReadElementContentAsString(); -// -// if (!string.IsNullOrWhiteSpace(val)) -// { -// if (!item.LockedFields.Contains(MetadataFields.Cast)) -// { -// //AddPeople(result, val, PersonType.Writer); -// } -// } -// -// break; -// } -// -// default: -// reader.Skip(); -// break; -// } -// } -// else -// { -// reader.Read(); -// } -// } -// } -// } -// -// public Task GetImageResponse(string url, CancellationToken cancellationToken) -// { -// return _httpClient.GetResponse(new HttpRequestOptions -// { -// CancellationToken = cancellationToken, -// Url = url -// }); -// } -// -// public int Order => 0; -// } -//} + + return result; + } + + public Task GetImageResponse(string url, CancellationToken cancellationToken) + { + return _httpClient.GetResponse(new HttpRequestOptions + { + CancellationToken = cancellationToken, + Url = url + }); + } + + public int Order => 0; + } +} diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index 1c5fde0d20..e592b20042 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -22,13 +22,12 @@ namespace MediaBrowser.Providers.TV.TheTVDB private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); private readonly IHttpClient _httpClient; - private readonly TvDbClient _tvDbClient; + private readonly TvDbClientManager _tvDbClientManager; public TvdbSeasonImageProvider(IHttpClient httpClient) { _httpClient = httpClient; - _tvDbClient = new TvDbClient(); - _tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey); + _tvDbClientManager = TvDbClientManager.Instance; } public string Name => ProviderName; @@ -62,7 +61,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB var seasonNumber = season.IndexNumber.Value; var language = item.GetPreferredMetadataLanguage(); - _tvDbClient.AcceptedLanguage = language; + _tvDbClientManager.TvDbClient.AcceptedLanguage = language; var remoteImages = new List(); var keyTypes = new[] {KeyType.Season, KeyType.Seasonwide, KeyType.Fanart}; // TODO error handling @@ -73,10 +72,18 @@ namespace MediaBrowser.Providers.TV.TheTVDB KeyType = keyType, SubKey = seasonNumber.ToString() }; - var imageResults = - await _tvDbClient.Series.GetImagesAsync(Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb)), imageQuery, cancellationToken); - - remoteImages.AddRange(GetImages(imageResults.Data, language)); + try + { + var imageResults = + await _tvDbClientManager.TvDbClient.Series.GetImagesAsync( + Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb)), imageQuery, + cancellationToken); + remoteImages.AddRange(GetImages(imageResults.Data, language)); + } + catch (TvDbServerException e) + { + // TODO log + } } return remoteImages; @@ -88,7 +95,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB foreach (Image image in images) { - var resolution = image.Resolution.Split('x'); var imageInfo = new RemoteImageInfo { RatingType = RatingType.Score, @@ -97,11 +103,16 @@ namespace MediaBrowser.Providers.TV.TheTVDB Url = TVUtils.BannerUrl + image.FileName, ProviderName = ProviderName, // TODO Language = image.LanguageId, - Width = Convert.ToInt32(resolution[0]), - Height = Convert.ToInt32(resolution[1]), ThumbnailUrl = TVUtils.BannerUrl + image.Thumbnail }; + var resolution = image.Resolution.Split('x'); + if (resolution.Length == 2) + { + imageInfo.Width = Convert.ToInt32(resolution[0]); + imageInfo.Height = Convert.ToInt32(resolution[1]); + } + if (string.Equals(image.KeyType, "season", StringComparison.OrdinalIgnoreCase)) { imageInfo.Type = ImageType.Primary; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index 999598ff82..be968e5248 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -21,12 +21,12 @@ namespace MediaBrowser.Providers.TV.TheTVDB { private readonly IHttpClient _httpClient; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private readonly TvDbClient _tvDbClient = new TvDbClient(); + private readonly TvDbClientManager _tvDbClientManager; public TvdbSeriesImageProvider(IHttpClient httpClient) { _httpClient = httpClient; - _tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey); + _tvDbClientManager = TvDbClientManager.Instance; } public string Name => ProviderName; @@ -56,7 +56,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB } var language = item.GetPreferredMetadataLanguage(); - _tvDbClient.AcceptedLanguage = language; + _tvDbClientManager.TvDbClient.AcceptedLanguage = language; var remoteImages = new List(); var keyTypes = new[] {KeyType.Poster, KeyType.Series, KeyType.Fanart}; // TODO error handling @@ -67,7 +67,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB KeyType = keyType }; var imageResults = - await _tvDbClient.Series.GetImagesAsync(Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb)), imageQuery, cancellationToken); + await _tvDbClientManager.TvDbClient.Series.GetImagesAsync(Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb)), imageQuery, cancellationToken); remoteImages.AddRange(GetImages(imageResults.Data, language)); } @@ -80,7 +80,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB foreach (Image image in images) { - var resolution = image.Resolution.Split('x'); var imageInfo = new RemoteImageInfo { RatingType = RatingType.Score, @@ -89,11 +88,16 @@ namespace MediaBrowser.Providers.TV.TheTVDB Url = TVUtils.BannerUrl + image.FileName, ProviderName = Name, // TODO Language = image.LanguageId, - Width = Convert.ToInt32(resolution[0]), - Height = Convert.ToInt32(resolution[1]), ThumbnailUrl = TVUtils.BannerUrl + image.Thumbnail }; + var resolution = image.Resolution.Split('x'); + if (resolution.Length == 2) + { + imageInfo.Width = Convert.ToInt32(resolution[0]); + imageInfo.Height = Convert.ToInt32(resolution[1]); + } + if (string.Equals(image.KeyType, "poster", StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 4aa2d10622..d0a1f5ec19 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -1,25 +1,17 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; -using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Xml; -using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; using TvDbSharper; using TvDbSharper.Dto; @@ -35,7 +27,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localizationManager; - private readonly TvDbClient _tvDbClient; + private readonly TvDbClientManager _tvDbClientManager; public TvdbSeriesProvider(IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, ILocalizationManager localizationManager) { @@ -44,8 +36,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB _libraryManager = libraryManager; _localizationManager = localizationManager; Current = this; - _tvDbClient = new TvDbClient(); - _tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey); + _tvDbClientManager = TvDbClientManager.Instance; } private string NormalizeLanguage(string language) @@ -112,7 +103,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private async Task FetchSeriesData(MetadataResult result, string metadataLanguage, Dictionary seriesProviderIds, CancellationToken cancellationToken) { - _tvDbClient.AcceptedLanguage = NormalizeLanguage(metadataLanguage); + _tvDbClientManager.TvDbClient.AcceptedLanguage = NormalizeLanguage(metadataLanguage); var series = result.Item; if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(tvdbId)) @@ -133,7 +124,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB } // TODO call this function elsewhere? - var seriesResult = await _tvDbClient.Series.GetAsync(Convert.ToInt32(tvdbId), cancellationToken); + var seriesResult = await _tvDbClientManager.TvDbClient.Series.GetAsync(Convert.ToInt32(tvdbId), cancellationToken); // TODO error handling MapSeriesToResult(result, seriesResult.Data, cancellationToken); @@ -142,22 +133,22 @@ namespace MediaBrowser.Providers.TV.TheTVDB result.ResetPeople(); - var actorsResult = await _tvDbClient.Series.GetActorsAsync(Convert.ToInt32(tvdbId), cancellationToken); + var actorsResult = await _tvDbClientManager.TvDbClient.Series.GetActorsAsync(Convert.ToInt32(tvdbId), cancellationToken); MapActorsToResult(result, actorsResult.Data); } private async Task GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken) { - _tvDbClient.AcceptedLanguage = NormalizeLanguage(language); + _tvDbClientManager.TvDbClient.AcceptedLanguage = NormalizeLanguage(language); TvDbResponse result; if (string.Equals(idType, MetadataProviders.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase)) { - result = await _tvDbClient.Search.SearchSeriesByZap2ItIdAsync(id, cancellationToken); + result = await _tvDbClientManager.TvDbClient.Search.SearchSeriesByZap2ItIdAsync(id, cancellationToken); } else { - result = await _tvDbClient.Search.SearchSeriesByImdbIdAsync(id, cancellationToken); + result = await _tvDbClientManager.TvDbClient.Search.SearchSeriesByImdbIdAsync(id, cancellationToken); } return result.Data.First().Id.ToString(); @@ -252,10 +243,10 @@ namespace MediaBrowser.Providers.TV.TheTVDB private async Task> FindSeriesInternal(string name, string language, CancellationToken cancellationToken) { - _tvDbClient.AcceptedLanguage = NormalizeLanguage(language); + _tvDbClientManager.TvDbClient.AcceptedLanguage = NormalizeLanguage(language); var comparableName = GetComparableName(name); var list = new List, RemoteSearchResult>>(); - TvDbResponse result = await _tvDbClient.Search.SearchSeriesByNameAsync(comparableName, cancellationToken); + TvDbResponse result = await _tvDbClientManager.TvDbClient.Search.SearchSeriesByNameAsync(comparableName, cancellationToken); foreach (var seriesSearchResult in result.Data) { From ecbc0538f6b0d2a3f4907687778b00d56ade363c Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Thu, 7 Feb 2019 19:54:34 +0100 Subject: [PATCH 05/39] Add some error handling, grab imdb id and other minor fixes --- MediaBrowser.Controller/Library/TVUtils.cs | 28 +++--- .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 14 ++- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 17 ++-- .../TV/TheTVDB/TvdbSeriesProvider.cs | 88 +++++++------------ 4 files changed, 58 insertions(+), 89 deletions(-) diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs index 3080143ce9..0089f65d0d 100644 --- a/MediaBrowser.Controller/Library/TVUtils.cs +++ b/MediaBrowser.Controller/Library/TVUtils.cs @@ -28,24 +28,24 @@ namespace MediaBrowser.Controller.Library { if (string.Equals(day, "Daily", StringComparison.OrdinalIgnoreCase)) { - return new DayOfWeek[] - { - DayOfWeek.Sunday, - DayOfWeek.Monday, - DayOfWeek.Tuesday, - DayOfWeek.Wednesday, - DayOfWeek.Thursday, - DayOfWeek.Friday, - DayOfWeek.Saturday - }; + return new[] + { + DayOfWeek.Sunday, + DayOfWeek.Monday, + DayOfWeek.Tuesday, + DayOfWeek.Wednesday, + DayOfWeek.Thursday, + DayOfWeek.Friday, + DayOfWeek.Saturday + }; } if (Enum.TryParse(day, true, out DayOfWeek value)) { - return new DayOfWeek[] - { - value - }; + return new[] + { + value + }; } return new DayOfWeek[] { }; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index bc526e42a9..74165e4bd2 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -18,7 +18,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB { public class TvdbEpisodeImageProvider : IRemoteImageProvider { - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IHttpClient _httpClient; private readonly TvDbClientManager _tvDbClientManager; @@ -47,6 +46,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB { var episode = (Episode)item; var series = episode.Series; + var imageResult = new List(); if (series != null && TvdbSeriesProvider.IsValidSeries(series.ProviderIds)) { @@ -55,21 +55,17 @@ namespace MediaBrowser.Providers.TV.TheTVDB var episodeResult = await _tvDbClientManager.TvDbClient.Episodes.GetAsync(Convert.ToInt32(tvdbId), cancellationToken); var image = GetImageInfo(episodeResult.Data); - return new List + if (image != null) { - image - }; + imageResult.Add(image); + } } - return new RemoteImageInfo[] { }; + return imageResult; } private RemoteImageInfo GetImageInfo(EpisodeRecord episode) { - var height = 225; - var width = 400; - var url = string.Empty; - if (string.IsNullOrEmpty(episode.Filename)) { return null; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index e592b20042..8f763f2c53 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -11,6 +11,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; +using Microsoft.Extensions.Logging; using TvDbSharper; using TvDbSharper.Dto; using RatingType = MediaBrowser.Model.Dto.RatingType; @@ -22,11 +23,13 @@ namespace MediaBrowser.Providers.TV.TheTVDB private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); private readonly IHttpClient _httpClient; + private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; - public TvdbSeasonImageProvider(IHttpClient httpClient) + public TvdbSeasonImageProvider(IHttpClient httpClient, ILogger logger) { _httpClient = httpClient; + _logger = logger; _tvDbClientManager = TvDbClientManager.Instance; } @@ -59,13 +62,14 @@ namespace MediaBrowser.Providers.TV.TheTVDB return new RemoteImageInfo[] { }; } + var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb)); var seasonNumber = season.IndexNumber.Value; var language = item.GetPreferredMetadataLanguage(); _tvDbClientManager.TvDbClient.AcceptedLanguage = language; var remoteImages = new List(); + var keyTypes = new[] {KeyType.Season, KeyType.Seasonwide, KeyType.Fanart}; - // TODO error handling - foreach (KeyType keyType in keyTypes) + foreach (var keyType in keyTypes) { var imageQuery = new ImagesQuery { @@ -74,15 +78,12 @@ namespace MediaBrowser.Providers.TV.TheTVDB }; try { - var imageResults = - await _tvDbClientManager.TvDbClient.Series.GetImagesAsync( - Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb)), imageQuery, - cancellationToken); + var imageResults = await _tvDbClientManager.TvDbClient.Series.GetImagesAsync(tvdbId, imageQuery, cancellationToken); remoteImages.AddRange(GetImages(imageResults.Data, language)); } catch (TvDbServerException e) { - // TODO log + _logger.LogInformation(e, "No images of type {KeyType} found for series {TvdbId}", keyType, tvdbId); } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index d0a1f5ec19..25bea7821f 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -95,7 +95,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB result.Item = new Series(); result.HasMetadata = true; - FetchSeriesData(result, itemId.MetadataLanguage, itemId.ProviderIds, cancellationToken); + await FetchSeriesData(result, itemId.MetadataLanguage, itemId.ProviderIds, cancellationToken); } return result; @@ -127,7 +127,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB var seriesResult = await _tvDbClientManager.TvDbClient.Series.GetAsync(Convert.ToInt32(tvdbId), cancellationToken); // TODO error handling - MapSeriesToResult(result, seriesResult.Data, cancellationToken); + MapSeriesToResult(result, seriesResult.Data); cancellationToken.ThrowIfCancellationRequested(); @@ -161,51 +161,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out _); } - // TODO caching - private bool IsCacheValid(string seriesDataPath, string preferredMetadataLanguage) - { - return true; -// try -// { -// var files = _fileSystem.GetFiles(seriesDataPath, new[] { ".xml" }, true, false) -// .ToList(); -// -// var seriesXmlFilename = preferredMetadataLanguage + ".xml"; -// -// const int cacheHours = 12; -// -// var seriesFile = files.FirstOrDefault(i => string.Equals(seriesXmlFilename, i.Name, StringComparison.OrdinalIgnoreCase)); -// // No need to check age if automatic updates are enabled -// if (seriesFile == null || !seriesFile.Exists || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(seriesFile)).TotalHours > cacheHours) -// { -// return false; -// } -// -// var actorsXml = files.FirstOrDefault(i => string.Equals("actors.xml", i.Name, StringComparison.OrdinalIgnoreCase)); -// // No need to check age if automatic updates are enabled -// if (actorsXml == null || !actorsXml.Exists || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(actorsXml)).TotalHours > cacheHours) -// { -// return false; -// } -// -// var bannersXml = files.FirstOrDefault(i => string.Equals("banners.xml", i.Name, StringComparison.OrdinalIgnoreCase)); -// // No need to check age if automatic updates are enabled -// if (bannersXml == null || !bannersXml.Exists || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(bannersXml)).TotalHours > cacheHours) -// { -// return false; -// } -// return true; -// } -// catch (FileNotFoundException) -// { -// return false; -// } -// catch (IOException) -// { -// return false; -// } - } - /// /// Finds the series. /// @@ -246,7 +201,16 @@ namespace MediaBrowser.Providers.TV.TheTVDB _tvDbClientManager.TvDbClient.AcceptedLanguage = NormalizeLanguage(language); var comparableName = GetComparableName(name); var list = new List, RemoteSearchResult>>(); - TvDbResponse result = await _tvDbClientManager.TvDbClient.Search.SearchSeriesByNameAsync(comparableName, cancellationToken); + TvDbResponse result; + try + { + result = await _tvDbClientManager.TvDbClient.Search.SearchSeriesByNameAsync(comparableName, cancellationToken); + } + catch (TvDbServerException e) + { + _logger.LogError(e, "No series results found for {Name}", comparableName); + return new List(); + } foreach (var seriesSearchResult in result.Data) { @@ -265,8 +229,18 @@ namespace MediaBrowser.Providers.TV.TheTVDB ImageUrl = TVUtils.BannerUrl + seriesSearchResult.Banner }; - // TODO requires another query, is it worth it? - // remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSearchResult.Id); + try + { + var seriesSesult = + await _tvDbClientManager.TvDbClient.Series.GetAsync(seriesSearchResult.Id, cancellationToken); + remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSesult.Data.ImdbId); + remoteSearchResult.SetProviderId(MetadataProviders.Zap2It, seriesSesult.Data.Zap2itId); + } + catch (TvDbServerException e) + { + _logger.LogError(e, "Unable to retrieve series with id {TvdbId}", seriesSearchResult.Id); + } + remoteSearchResult.SetProviderId(MetadataProviders.Tvdb, seriesSearchResult.Id.ToString()); list.Add(new Tuple, RemoteSearchResult>(tvdbTitles, remoteSearchResult)); } @@ -335,14 +309,13 @@ namespace MediaBrowser.Providers.TV.TheTVDB return name.Trim(); } - private static void MapSeriesToResult(MetadataResult result, TvDbSharper.Dto.Series tvdbSeries, CancellationToken cancellationToken) + private static void MapSeriesToResult(MetadataResult result, TvDbSharper.Dto.Series tvdbSeries) { - var episodeAirDates = new List(); Series series = result.Item; series.SetProviderId(MetadataProviders.Tvdb, tvdbSeries.Id.ToString()); series.Name = tvdbSeries.SeriesName; series.Overview = (tvdbSeries.Overview ?? string.Empty).Trim(); - // TODO result.ResultLanguage = (seriesResponse.Data. ?? string.Empty).Trim(); + result.ResultLanguage = TvDbClientManager.Instance.TvDbClient.AcceptedLanguage; series.AirDays = TVUtils.GetAirDays(tvdbSeries.AirsDayOfWeek); series.AirTime = tvdbSeries.AirsTime; @@ -368,14 +341,13 @@ namespace MediaBrowser.Providers.TV.TheTVDB series.AddGenre(genre); } - // TODO is network == studio? series.AddStudio(tvdbSeries.Network); // TODO is this necessary? - if (result.Item.Status.HasValue && result.Item.Status.Value == SeriesStatus.Ended && episodeAirDates.Count > 0) - { - result.Item.EndDate = episodeAirDates.Max(); - } + // if (result.Item.Status.HasValue && result.Item.Status.Value == SeriesStatus.Ended && episodeAirDates.Count > 0) + // { + // result.Item.EndDate = episodeAirDates.Max(); + // } } private static void MapActorsToResult(MetadataResult result, IEnumerable actors) From 75d90c8e4c2213b7428640d407583f4e1c579aa4 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Thu, 7 Feb 2019 20:28:19 +0100 Subject: [PATCH 06/39] Add caching PoC --- .../MediaBrowser.Providers.csproj | 1 + .../TV/TheTVDB/TvDbClientManager.cs | 32 +++++++++++++++++++ .../TV/TheTVDB/TvdbSeriesProvider.cs | 16 +++++----- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 1cf1ab256a..aadf31a7a7 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -11,6 +11,7 @@ + diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 861cd3f5bd..e7b2d151c4 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -1,12 +1,19 @@ using System; +using System.Threading; +using System.Threading.Tasks; using MediaBrowser.Controller.Library; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Internal; using TvDbSharper; +using TvDbSharper.Dto; namespace MediaBrowser.Providers.TV { public sealed class TvDbClientManager { private static volatile TvDbClientManager instance; + // TODO add to DI once Bond's PR is merged + private static MemoryCache _cache; private static readonly object syncRoot = new object(); private static TvDbClient tvDbClient; private static DateTime tokenCreatedAt; @@ -30,7 +37,10 @@ namespace MediaBrowser.Providers.TV lock (syncRoot) { if (instance == null) + { instance = new TvDbClientManager(); + _cache = new MemoryCache(new MemoryCacheOptions()); + } } return instance; @@ -60,5 +70,27 @@ namespace MediaBrowser.Providers.TV return tvDbClient; } } + + public async Task GetSeriesByName(string name, CancellationToken cancellationToken) + { + if (_cache.TryGetValue(name, out SeriesSearchResult[] series)) + { + return series; + } + var result = await TvDbClient.Search.SearchSeriesByNameAsync(name, cancellationToken); + _cache.Set(name, result.Data, DateTimeOffset.UtcNow.AddHours(1)); + return result.Data; + } + + public async Task GetSeriesById(int tvdbId, CancellationToken cancellationToken) + { + if (_cache.TryGetValue(tvdbId, out Series series)) + { + return series; + } + var result = await TvDbClient.Series.GetAsync(tvdbId, cancellationToken); + _cache.Set(tvdbId, result.Data, DateTimeOffset.UtcNow.AddHours(1)); + return result.Data; + } } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 25bea7821f..db7519b4eb 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -124,10 +124,10 @@ namespace MediaBrowser.Providers.TV.TheTVDB } // TODO call this function elsewhere? - var seriesResult = await _tvDbClientManager.TvDbClient.Series.GetAsync(Convert.ToInt32(tvdbId), cancellationToken); + var seriesResult = await _tvDbClientManager.GetSeriesById(Convert.ToInt32(tvdbId), cancellationToken); // TODO error handling - MapSeriesToResult(result, seriesResult.Data); + MapSeriesToResult(result, seriesResult); cancellationToken.ThrowIfCancellationRequested(); @@ -201,10 +201,10 @@ namespace MediaBrowser.Providers.TV.TheTVDB _tvDbClientManager.TvDbClient.AcceptedLanguage = NormalizeLanguage(language); var comparableName = GetComparableName(name); var list = new List, RemoteSearchResult>>(); - TvDbResponse result; + SeriesSearchResult[] result; try { - result = await _tvDbClientManager.TvDbClient.Search.SearchSeriesByNameAsync(comparableName, cancellationToken); + result = await _tvDbClientManager.GetSeriesByName(comparableName, cancellationToken); } catch (TvDbServerException e) { @@ -212,7 +212,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB return new List(); } - foreach (var seriesSearchResult in result.Data) + foreach (var seriesSearchResult in result) { var tvdbTitles = new List { @@ -232,9 +232,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB try { var seriesSesult = - await _tvDbClientManager.TvDbClient.Series.GetAsync(seriesSearchResult.Id, cancellationToken); - remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSesult.Data.ImdbId); - remoteSearchResult.SetProviderId(MetadataProviders.Zap2It, seriesSesult.Data.Zap2itId); + await _tvDbClientManager.GetSeriesById(seriesSearchResult.Id, cancellationToken); + remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSesult.ImdbId); + remoteSearchResult.SetProviderId(MetadataProviders.Zap2It, seriesSesult.Zap2itId); } catch (TvDbServerException e) { From 86940e96d51d9259fe6a9795e477f4ef0644a27e Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Thu, 7 Feb 2019 20:57:42 +0100 Subject: [PATCH 07/39] More MemoryCache PoC --- .../TV/TheTVDB/TvDbClientManager.cs | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index e7b2d151c4..ca50d4c2b5 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -13,6 +13,7 @@ namespace MediaBrowser.Providers.TV { private static volatile TvDbClientManager instance; // TODO add to DI once Bond's PR is merged + private readonly SemaphoreSlim _cacheWriteLock = new SemaphoreSlim(1, 1); private static MemoryCache _cache; private static readonly object syncRoot = new object(); private static TvDbClient tvDbClient; @@ -73,24 +74,32 @@ namespace MediaBrowser.Providers.TV public async Task GetSeriesByName(string name, CancellationToken cancellationToken) { - if (_cache.TryGetValue(name, out SeriesSearchResult[] series)) - { - return series; - } - var result = await TvDbClient.Search.SearchSeriesByNameAsync(name, cancellationToken); - _cache.Set(name, result.Data, DateTimeOffset.UtcNow.AddHours(1)); - return result.Data; + return await TryGetValue(name, + async () => (await TvDbClient.Search.SearchSeriesByNameAsync(name, cancellationToken)).Data); } public async Task GetSeriesById(int tvdbId, CancellationToken cancellationToken) { - if (_cache.TryGetValue(tvdbId, out Series series)) + return await TryGetValue(tvdbId, + async () => (await TvDbClient.Series.GetAsync(tvdbId, cancellationToken)).Data); + } + + private async Task TryGetValue(object key, Func> resultFactory) + { + if (_cache.TryGetValue(key, out T cachedValue)) { - return series; + return cachedValue; + } + using (_cacheWriteLock) + { + if (_cache.TryGetValue(key, out cachedValue)) + { + return cachedValue; + } + var result = await resultFactory.Invoke(); + _cache.Set(key, result, DateTimeOffset.UtcNow.AddHours(1)); + return result; } - var result = await TvDbClient.Series.GetAsync(tvdbId, cancellationToken); - _cache.Set(tvdbId, result.Data, DateTimeOffset.UtcNow.AddHours(1)); - return result.Data; } } } From d6835f8dd6eb4ee2d397e682bb300b9f19ce23dc Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Thu, 7 Feb 2019 21:05:58 +0100 Subject: [PATCH 08/39] Use the locking properly, this is not Python... --- .../TV/TheTVDB/TvDbClientManager.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index ca50d4c2b5..276622666d 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -90,16 +90,23 @@ namespace MediaBrowser.Providers.TV { return cachedValue; } - using (_cacheWriteLock) + + await _cacheWriteLock.WaitAsync().ConfigureAwait(false); + try { if (_cache.TryGetValue(key, out cachedValue)) { return cachedValue; } + var result = await resultFactory.Invoke(); - _cache.Set(key, result, DateTimeOffset.UtcNow.AddHours(1)); + _cache.Set(key, result, TimeSpan.FromHours(1)); return result; } + finally + { + _cacheWriteLock.Release(); + } } } } From 1f8e74f3a80cea0b1ce0bacb592bfc156d0f3e53 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Thu, 7 Feb 2019 21:59:57 +0100 Subject: [PATCH 09/39] Add caching for all tvdb requests --- .../TV/TheTVDB/TvDbClientManager.cs | 36 +++++++++++++++---- .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 2 +- .../TV/TheTVDB/TvdbEpisodeProvider.cs | 4 +-- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 2 +- .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 2 +- .../TV/TheTVDB/TvdbSeriesProvider.cs | 24 ++++++------- 6 files changed, 46 insertions(+), 24 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 276622666d..7af9863232 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -9,6 +9,7 @@ using TvDbSharper.Dto; namespace MediaBrowser.Providers.TV { + // TODO add to DI once Bond's PR is merged public sealed class TvDbClientManager { private static volatile TvDbClientManager instance; @@ -72,22 +73,45 @@ namespace MediaBrowser.Providers.TV } } - public async Task GetSeriesByName(string name, CancellationToken cancellationToken) + public Task> GetSeriesByNameAsync(string name, CancellationToken cancellationToken) { - return await TryGetValue(name, - async () => (await TvDbClient.Search.SearchSeriesByNameAsync(name, cancellationToken)).Data); + return TryGetValue(name,() => TvDbClient.Search.SearchSeriesByNameAsync(name, cancellationToken)); } - public async Task GetSeriesById(int tvdbId, CancellationToken cancellationToken) + public Task> GetSeriesByIdAsync(int tvdbId, CancellationToken cancellationToken) { - return await TryGetValue(tvdbId, - async () => (await TvDbClient.Series.GetAsync(tvdbId, cancellationToken)).Data); + return TryGetValue(tvdbId,() => TvDbClient.Series.GetAsync(tvdbId, cancellationToken)); + } + + public Task> GetEpisodesAsync(int tvdbId, CancellationToken cancellationToken) + { + return TryGetValue(tvdbId,() => TvDbClient.Episodes.GetAsync(tvdbId, cancellationToken)); + } + + public Task> GetSeriesByImdbIdAsync(string imdbId, CancellationToken cancellationToken) + { + return TryGetValue(imdbId,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(imdbId, cancellationToken)); + } + + public Task> GetSeriesByZap2ItIdAsync(string zap2ItId, CancellationToken cancellationToken) + { + return TryGetValue(zap2ItId,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(zap2ItId, cancellationToken)); + } + public Task> GetActorsAsync(int tvdbId, CancellationToken cancellationToken) + { + return TryGetValue(tvdbId,() => TvDbClient.Series.GetActorsAsync(tvdbId, cancellationToken)); + } + + public Task> GetImagesAsync(int tvdbId, ImagesQuery imageQuery, CancellationToken cancellationToken) + { + return TryGetValue(tvdbId,() => TvDbClient.Series.GetImagesAsync(tvdbId, imageQuery, cancellationToken)); } private async Task TryGetValue(object key, Func> resultFactory) { if (_cache.TryGetValue(key, out T cachedValue)) { + Console.WriteLine("Cache hit!!!"); return cachedValue; } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 74165e4bd2..3f5d415afe 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -52,7 +52,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB { var tvdbId = episode.GetProviderId(MetadataProviders.Tvdb); // Process images - var episodeResult = await _tvDbClientManager.TvDbClient.Episodes.GetAsync(Convert.ToInt32(tvdbId), cancellationToken); + var episodeResult = await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(tvdbId), cancellationToken); var image = GetImageInfo(episodeResult.Data); if (image != null) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index c3a95f3856..c4891a66a0 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -46,7 +46,7 @@ namespace MediaBrowser.Providers.TV if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds)) { var episodeResult = - await _tvDbClientManager.TvDbClient.Episodes.GetAsync((int)searchInfo.IndexNumber, cancellationToken); + await _tvDbClientManager.GetEpisodesAsync((int)searchInfo.IndexNumber, cancellationToken); var metadataResult = MapEpisodeToResult(searchInfo, episodeResult.Data); if (metadataResult.HasMetadata) @@ -82,7 +82,7 @@ namespace MediaBrowser.Providers.TV if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue)) { - var episodeResult = await _tvDbClientManager.TvDbClient.Episodes.GetAsync(Convert.ToInt32(searchInfo.GetProviderId(MetadataProviders.Tvdb)), + var episodeResult = await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(searchInfo.GetProviderId(MetadataProviders.Tvdb)), cancellationToken); result = MapEpisodeToResult(searchInfo, episodeResult.Data); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index 8f763f2c53..7040779b4d 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB }; try { - var imageResults = await _tvDbClientManager.TvDbClient.Series.GetImagesAsync(tvdbId, imageQuery, cancellationToken); + var imageResults = await _tvDbClientManager.GetImagesAsync(tvdbId, imageQuery, cancellationToken); remoteImages.AddRange(GetImages(imageResults.Data, language)); } catch (TvDbServerException e) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index be968e5248..03c5502dcd 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -67,7 +67,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB KeyType = keyType }; var imageResults = - await _tvDbClientManager.TvDbClient.Series.GetImagesAsync(Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb)), imageQuery, cancellationToken); + await _tvDbClientManager.GetImagesAsync(Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb)), imageQuery, cancellationToken); remoteImages.AddRange(GetImages(imageResults.Data, language)); } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index db7519b4eb..b431d5c7f6 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -123,17 +123,15 @@ namespace MediaBrowser.Providers.TV.TheTVDB tvdbId = await GetSeriesByRemoteId(zap2It, MetadataProviders.Zap2It.ToString(), metadataLanguage, cancellationToken); } - // TODO call this function elsewhere? - var seriesResult = await _tvDbClientManager.GetSeriesById(Convert.ToInt32(tvdbId), cancellationToken); + var seriesResult = await _tvDbClientManager.GetSeriesByIdAsync(Convert.ToInt32(tvdbId), cancellationToken); - // TODO error handling - MapSeriesToResult(result, seriesResult); + MapSeriesToResult(result, seriesResult.Data); cancellationToken.ThrowIfCancellationRequested(); result.ResetPeople(); - var actorsResult = await _tvDbClientManager.TvDbClient.Series.GetActorsAsync(Convert.ToInt32(tvdbId), cancellationToken); + var actorsResult = await _tvDbClientManager.GetActorsAsync(Convert.ToInt32(tvdbId), cancellationToken); MapActorsToResult(result, actorsResult.Data); } @@ -144,11 +142,11 @@ namespace MediaBrowser.Providers.TV.TheTVDB if (string.Equals(idType, MetadataProviders.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase)) { - result = await _tvDbClientManager.TvDbClient.Search.SearchSeriesByZap2ItIdAsync(id, cancellationToken); + result = await _tvDbClientManager.GetSeriesByZap2ItIdAsync(id, cancellationToken); } else { - result = await _tvDbClientManager.TvDbClient.Search.SearchSeriesByImdbIdAsync(id, cancellationToken); + result = await _tvDbClientManager.GetSeriesByImdbIdAsync(id, cancellationToken); } return result.Data.First().Id.ToString(); @@ -201,10 +199,10 @@ namespace MediaBrowser.Providers.TV.TheTVDB _tvDbClientManager.TvDbClient.AcceptedLanguage = NormalizeLanguage(language); var comparableName = GetComparableName(name); var list = new List, RemoteSearchResult>>(); - SeriesSearchResult[] result; + TvDbResponse result; try { - result = await _tvDbClientManager.GetSeriesByName(comparableName, cancellationToken); + result = await _tvDbClientManager.GetSeriesByNameAsync(comparableName, cancellationToken); } catch (TvDbServerException e) { @@ -212,7 +210,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB return new List(); } - foreach (var seriesSearchResult in result) + foreach (var seriesSearchResult in result.Data) { var tvdbTitles = new List { @@ -232,9 +230,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB try { var seriesSesult = - await _tvDbClientManager.GetSeriesById(seriesSearchResult.Id, cancellationToken); - remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSesult.ImdbId); - remoteSearchResult.SetProviderId(MetadataProviders.Zap2It, seriesSesult.Zap2itId); + await _tvDbClientManager.GetSeriesByIdAsync(seriesSearchResult.Id, cancellationToken); + remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSesult.Data.ImdbId); + remoteSearchResult.SetProviderId(MetadataProviders.Zap2It, seriesSesult.Data.Zap2itId); } catch (TvDbServerException e) { From 42c233c74efe94b30192fc0b50b2ec15bd43bf36 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Thu, 7 Feb 2019 22:42:02 +0100 Subject: [PATCH 10/39] Add much needed exception handling and logging --- .../TV/TheTVDB/TvDbClientManager.cs | 23 ++++--- .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 24 +++++-- .../TV/TheTVDB/TvdbEpisodeProvider.cs | 65 +++++++++++-------- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 4 +- .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 25 +++++-- .../TV/TheTVDB/TvdbSeriesProvider.cs | 44 +++++++++---- 6 files changed, 122 insertions(+), 63 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 7af9863232..e789d2ded7 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -3,7 +3,6 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Internal; using TvDbSharper; using TvDbSharper.Dto; @@ -75,43 +74,47 @@ namespace MediaBrowser.Providers.TV public Task> GetSeriesByNameAsync(string name, CancellationToken cancellationToken) { - return TryGetValue(name,() => TvDbClient.Search.SearchSeriesByNameAsync(name, cancellationToken)); + return TryGetValue("series" + name,() => TvDbClient.Search.SearchSeriesByNameAsync(name, cancellationToken)); } public Task> GetSeriesByIdAsync(int tvdbId, CancellationToken cancellationToken) { - return TryGetValue(tvdbId,() => TvDbClient.Series.GetAsync(tvdbId, cancellationToken)); + return TryGetValue("series" + tvdbId,() => TvDbClient.Series.GetAsync(tvdbId, cancellationToken)); } - public Task> GetEpisodesAsync(int tvdbId, CancellationToken cancellationToken) + public Task> GetEpisodesAsync(int episodeTvdbId, CancellationToken cancellationToken) { - return TryGetValue(tvdbId,() => TvDbClient.Episodes.GetAsync(tvdbId, cancellationToken)); + return TryGetValue("episode" + episodeTvdbId,() => TvDbClient.Episodes.GetAsync(episodeTvdbId, cancellationToken)); } public Task> GetSeriesByImdbIdAsync(string imdbId, CancellationToken cancellationToken) { - return TryGetValue(imdbId,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(imdbId, cancellationToken)); + return TryGetValue("series" + imdbId,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(imdbId, cancellationToken)); } public Task> GetSeriesByZap2ItIdAsync(string zap2ItId, CancellationToken cancellationToken) { - return TryGetValue(zap2ItId,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(zap2ItId, cancellationToken)); + return TryGetValue("series" + zap2ItId,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(zap2ItId, cancellationToken)); } public Task> GetActorsAsync(int tvdbId, CancellationToken cancellationToken) { - return TryGetValue(tvdbId,() => TvDbClient.Series.GetActorsAsync(tvdbId, cancellationToken)); + return TryGetValue("actors" + tvdbId,() => TvDbClient.Series.GetActorsAsync(tvdbId, cancellationToken)); } public Task> GetImagesAsync(int tvdbId, ImagesQuery imageQuery, CancellationToken cancellationToken) { - return TryGetValue(tvdbId,() => TvDbClient.Series.GetImagesAsync(tvdbId, imageQuery, cancellationToken)); + return TryGetValue("images" + tvdbId,() => TvDbClient.Series.GetImagesAsync(tvdbId, imageQuery, cancellationToken)); + } + + public Task> GetLanguagesAsync(CancellationToken cancellationToken) + { + return TryGetValue("languages",() => TvDbClient.Languages.GetAllAsync(cancellationToken)); } private async Task TryGetValue(object key, Func> resultFactory) { if (_cache.TryGetValue(key, out T cachedValue)) { - Console.WriteLine("Cache hit!!!"); return cachedValue; } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 3f5d415afe..caa0a5aba2 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -11,6 +11,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; +using Microsoft.Extensions.Logging; using TvDbSharper; using TvDbSharper.Dto; @@ -19,11 +20,13 @@ namespace MediaBrowser.Providers.TV.TheTVDB public class TvdbEpisodeImageProvider : IRemoteImageProvider { private readonly IHttpClient _httpClient; + private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; - public TvdbEpisodeImageProvider(IHttpClient httpClient) + public TvdbEpisodeImageProvider(IHttpClient httpClient, ILogger logger) { _httpClient = httpClient; + _logger = logger; _tvDbClientManager = TvDbClientManager.Instance; } @@ -51,13 +54,22 @@ namespace MediaBrowser.Providers.TV.TheTVDB if (series != null && TvdbSeriesProvider.IsValidSeries(series.ProviderIds)) { var tvdbId = episode.GetProviderId(MetadataProviders.Tvdb); - // Process images - var episodeResult = await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(tvdbId), cancellationToken); - var image = GetImageInfo(episodeResult.Data); - if (image != null) + // Process images + try { - imageResult.Add(image); + var episodeResult = + await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(tvdbId), cancellationToken); + + var image = GetImageInfo(episodeResult.Data); + if (image != null) + { + imageResult.Add(image); + } + } + catch (TvDbServerException e) + { + _logger.LogError(e, "Failed to retrieve episode images for {TvDbId}", tvdbId); } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index c4891a66a0..68125d50a8 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using Microsoft.Extensions.Logging; +using TvDbSharper; using TvDbSharper.Dto; namespace MediaBrowser.Providers.TV @@ -37,7 +38,7 @@ namespace MediaBrowser.Providers.TV var list = new List(); // The search query must either provide an episode number or date - // TODO premieredate functionality is ded + // TODO premieredate functionality is ded, could grab all episodes and search for it if (!searchInfo.IndexNumber.HasValue || !searchInfo.PremiereDate.HasValue) { return list; @@ -45,25 +46,32 @@ namespace MediaBrowser.Providers.TV if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds)) { - var episodeResult = - await _tvDbClientManager.GetEpisodesAsync((int)searchInfo.IndexNumber, cancellationToken); - var metadataResult = MapEpisodeToResult(searchInfo, episodeResult.Data); - - if (metadataResult.HasMetadata) + try { - var item = metadataResult.Item; + var episodeResult = + await _tvDbClientManager.GetEpisodesAsync((int)searchInfo.IndexNumber, cancellationToken); + var metadataResult = MapEpisodeToResult(searchInfo, episodeResult.Data); - list.Add(new RemoteSearchResult + if (metadataResult.HasMetadata) { - IndexNumber = item.IndexNumber, - Name = item.Name, - ParentIndexNumber = item.ParentIndexNumber, - PremiereDate = item.PremiereDate, - ProductionYear = item.ProductionYear, - ProviderIds = item.ProviderIds, - SearchProviderName = Name, - IndexNumberEnd = item.IndexNumberEnd - }); + var item = metadataResult.Item; + + list.Add(new RemoteSearchResult + { + IndexNumber = item.IndexNumber, + Name = item.Name, + ParentIndexNumber = item.ParentIndexNumber, + PremiereDate = item.PremiereDate, + ProductionYear = item.ProductionYear, + ProviderIds = item.ProviderIds, + SearchProviderName = Name, + IndexNumberEnd = item.IndexNumberEnd + }); + } + } + catch (TvDbServerException e) + { + _logger.LogError(e, "Failed to retrieve episode with id {TvDbId}", searchInfo.IndexNumber); } } @@ -82,10 +90,19 @@ namespace MediaBrowser.Providers.TV if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue)) { - var episodeResult = await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(searchInfo.GetProviderId(MetadataProviders.Tvdb)), - cancellationToken); + var tvdbId = searchInfo.GetProviderId(MetadataProviders.Tvdb); + try + { + var episodeResult = await _tvDbClientManager.GetEpisodesAsync( + Convert.ToInt32(tvdbId), + cancellationToken); - result = MapEpisodeToResult(searchInfo, episodeResult.Data); + result = MapEpisodeToResult(searchInfo, episodeResult.Data); + } + catch (TvDbServerException e) + { + _logger.LogError(e, "Failed to retrieve episode with id {TvDbId}", tvdbId); + } } else { @@ -178,14 +195,8 @@ namespace MediaBrowser.Providers.TV Type = PersonType.Writer }); } - // TODO result.ResultLanguage = episode. - - // TODO wtf is additional part info? -// foreach (var node in xmlNodes.Skip(1)) -// { -// FetchAdditionalPartInfo(result, node, cancellationToken); -// } + result.ResultLanguage = episode.Language.EpisodeName; return result; } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index 7040779b4d..42c7bc5c38 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -93,7 +93,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private static IEnumerable GetImages(Image[] images, string preferredLanguage) { var list = new List(); - + var languages = TvDbClientManager.Instance.GetLanguagesAsync(CancellationToken.None).Result.Data; foreach (Image image in images) { var imageInfo = new RemoteImageInfo @@ -103,7 +103,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB VoteCount = image.RatingsInfo.Count, Url = TVUtils.BannerUrl + image.FileName, ProviderName = ProviderName, - // TODO Language = image.LanguageId, + Language = languages.FirstOrDefault(l => l.Id == image.LanguageId)?.Abbreviation, ThumbnailUrl = TVUtils.BannerUrl + image.Thumbnail }; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index 03c5502dcd..b9caf0b3a9 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -10,6 +10,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; +using Microsoft.Extensions.Logging; using TvDbSharper; using TvDbSharper.Dto; using RatingType = MediaBrowser.Model.Dto.RatingType; @@ -20,12 +21,13 @@ namespace MediaBrowser.Providers.TV.TheTVDB public class TvdbSeriesImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClient _httpClient; - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; - public TvdbSeriesImageProvider(IHttpClient httpClient) + public TvdbSeriesImageProvider(IHttpClient httpClient, ILogger logger) { _httpClient = httpClient; + _logger = logger; _tvDbClientManager = TvDbClientManager.Instance; } @@ -59,17 +61,25 @@ namespace MediaBrowser.Providers.TV.TheTVDB _tvDbClientManager.TvDbClient.AcceptedLanguage = language; var remoteImages = new List(); var keyTypes = new[] {KeyType.Poster, KeyType.Series, KeyType.Fanart}; - // TODO error handling + var tvdbId = Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb)); foreach (KeyType keyType in keyTypes) { var imageQuery = new ImagesQuery { KeyType = keyType }; - var imageResults = - await _tvDbClientManager.GetImagesAsync(Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb)), imageQuery, cancellationToken); + try + { + var imageResults = + await _tvDbClientManager.GetImagesAsync(tvdbId, imageQuery, cancellationToken); - remoteImages.AddRange(GetImages(imageResults.Data, language)); + remoteImages.AddRange(GetImages(imageResults.Data, language)); + } + catch (TvDbServerException e) + { + _logger.LogError(e, "Failed to retrieve images of type {KeyType} for series {TvDbId}", keyType, + tvdbId); + } } return remoteImages; } @@ -77,6 +87,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private IEnumerable GetImages(Image[] images, string preferredLanguage) { var list = new List(); + var languages = TvDbClientManager.Instance.GetLanguagesAsync(CancellationToken.None).Result.Data; foreach (Image image in images) { @@ -87,7 +98,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB VoteCount = image.RatingsInfo.Count, Url = TVUtils.BannerUrl + image.FileName, ProviderName = Name, - // TODO Language = image.LanguageId, + Language = languages.FirstOrDefault(l => l.Id == image.LanguageId)?.Abbreviation, ThumbnailUrl = TVUtils.BannerUrl + image.Thumbnail }; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index b431d5c7f6..9940cf7e42 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -123,33 +123,55 @@ namespace MediaBrowser.Providers.TV.TheTVDB tvdbId = await GetSeriesByRemoteId(zap2It, MetadataProviders.Zap2It.ToString(), metadataLanguage, cancellationToken); } - var seriesResult = await _tvDbClientManager.GetSeriesByIdAsync(Convert.ToInt32(tvdbId), cancellationToken); - - MapSeriesToResult(result, seriesResult.Data); + try + { + var seriesResult = + await _tvDbClientManager.GetSeriesByIdAsync(Convert.ToInt32(tvdbId), cancellationToken); + MapSeriesToResult(result, seriesResult.Data); + } + catch (TvDbServerException e) + { + _logger.LogError(e, "Failed to retrieve series with id {TvdbId}", tvdbId); + return; + } cancellationToken.ThrowIfCancellationRequested(); result.ResetPeople(); - var actorsResult = await _tvDbClientManager.GetActorsAsync(Convert.ToInt32(tvdbId), cancellationToken); - MapActorsToResult(result, actorsResult.Data); + try + { + var actorsResult = await _tvDbClientManager.GetActorsAsync(Convert.ToInt32(tvdbId), cancellationToken); + MapActorsToResult(result, actorsResult.Data); + } + catch (TvDbServerException e) + { + _logger.LogError(e, "Failed to retrieve actors for series {TvdbId}", tvdbId); + } } private async Task GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken) { _tvDbClientManager.TvDbClient.AcceptedLanguage = NormalizeLanguage(language); - TvDbResponse result; + TvDbResponse result = null; - if (string.Equals(idType, MetadataProviders.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase)) + try { - result = await _tvDbClientManager.GetSeriesByZap2ItIdAsync(id, cancellationToken); + if (string.Equals(idType, MetadataProviders.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase)) + { + result = await _tvDbClientManager.GetSeriesByZap2ItIdAsync(id, cancellationToken); + } + else + { + result = await _tvDbClientManager.GetSeriesByImdbIdAsync(id, cancellationToken); + } } - else + catch (TvDbServerException e) { - result = await _tvDbClientManager.GetSeriesByImdbIdAsync(id, cancellationToken); + _logger.LogError(e, "Failed to retrieve series with remote id {RemoteId}", id); } - return result.Data.First().Id.ToString(); + return result?.Data.First().Id.ToString(); } internal static bool IsValidSeries(Dictionary seriesProviderIds) From 23c867f946a81385510025259d9aed8c39974e67 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Thu, 7 Feb 2019 22:45:51 +0100 Subject: [PATCH 11/39] Remove TvdbPrescanTask as it looks like it was used for pre-fetching data, which is no longer relevant --- .../TV/TheTVDB/TvdbPrescanTask.cs | 401 ------------------ MediaBrowser.Providers/TV/TvExternalIds.cs | 4 +- 2 files changed, 2 insertions(+), 403 deletions(-) delete mode 100644 MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs deleted file mode 100644 index 2cc053c644..0000000000 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs +++ /dev/null @@ -1,401 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Xml; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Providers.TV.TheTVDB -{ - /// - /// Class TvdbPrescanTask - /// - public class TvdbPrescanTask : ILibraryPostScanTask - { - public const string TvdbBaseUrl = "https://thetvdb.com/"; - - /// - /// The server time URL - /// - private const string ServerTimeUrl = TvdbBaseUrl + "api/Updates.php?type=none"; - - /// - /// The updates URL - /// - private const string UpdatesUrl = TvdbBaseUrl + "api/Updates.php?type=all&time={0}"; - - /// - /// The _HTTP client - /// - private readonly IHttpClient _httpClient; - /// - /// The _logger - /// - private readonly ILogger _logger; - /// - /// The _config - /// - private readonly IServerConfigurationManager _config; - private readonly IFileSystem _fileSystem; - private readonly ILibraryManager _libraryManager; - private readonly IXmlReaderSettingsFactory _xmlSettings; - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - /// The HTTP client. - /// The config. - public TvdbPrescanTask(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IFileSystem fileSystem, ILibraryManager libraryManager, IXmlReaderSettingsFactory xmlSettings) - { - _logger = logger; - _httpClient = httpClient; - _config = config; - _fileSystem = fileSystem; - _libraryManager = libraryManager; - _xmlSettings = xmlSettings; - } - - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - /// - /// Runs the specified progress. - /// - /// The progress. - /// The cancellation token. - /// Task. - public async Task Run(IProgress progress, CancellationToken cancellationToken) - { - return; - var path = ""; - //var path = TvdbSeriesProvider.GetSeriesDataPath(_config.CommonApplicationPaths); - - Directory.CreateDirectory(path); - - var timestampFile = Path.Combine(path, "time.txt"); - - var timestampFileInfo = _fileSystem.GetFileInfo(timestampFile); - - // Don't check for tvdb updates anymore frequently than 24 hours - if (timestampFileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(timestampFileInfo)).TotalDays < 1) - { - return; - } - - // Find out the last time we queried tvdb for updates - var lastUpdateTime = timestampFileInfo.Exists ? File.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty; - - string newUpdateTime; - - var existingDirectories = _fileSystem.GetDirectoryPaths(path) - .Select(Path.GetFileName) - .ToList(); - - var seriesList = _libraryManager.GetItemList(new InternalItemsQuery() - { - IncludeItemTypes = new[] { typeof(Series).Name }, - Recursive = true, - GroupByPresentationUniqueKey = false, - DtoOptions = new DtoOptions(false) - { - EnableImages = false - } - - }).Cast() - .ToList(); - - var seriesIdsInLibrary = seriesList - .Where(i => !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb))) - .Select(i => i.GetProviderId(MetadataProviders.Tvdb)) - .ToList(); - - var missingSeries = seriesIdsInLibrary.Except(existingDirectories, StringComparer.OrdinalIgnoreCase) - .ToList(); - - var enableInternetProviders = seriesList.Count == 0 ? false : seriesList[0].IsMetadataFetcherEnabled(_libraryManager.GetLibraryOptions(seriesList[0]), TvdbSeriesProvider.Current.Name); - if (!enableInternetProviders) - { - progress.Report(100); - return; - } - - // If this is our first time, update all series - if (string.IsNullOrEmpty(lastUpdateTime)) - { - // First get tvdb server time - using (var response = await _httpClient.SendAsync(new HttpRequestOptions - { - Url = ServerTimeUrl, - CancellationToken = cancellationToken, - EnableHttpCompression = true, - BufferContent = false - - }, "GET").ConfigureAwait(false)) - { - // First get tvdb server time - using (var stream = response.Content) - { - newUpdateTime = GetUpdateTime(stream); - } - } - - existingDirectories.AddRange(missingSeries); - - await UpdateSeries(existingDirectories, path, null, progress, cancellationToken).ConfigureAwait(false); - } - else - { - var seriesToUpdate = await GetSeriesIdsToUpdate(existingDirectories, lastUpdateTime, cancellationToken).ConfigureAwait(false); - - newUpdateTime = seriesToUpdate.Item2; - - long.TryParse(lastUpdateTime, NumberStyles.Any, UsCulture, out var lastUpdateValue); - - var nullableUpdateValue = lastUpdateValue == 0 ? (long?)null : lastUpdateValue; - - var listToUpdate = seriesToUpdate.Item1.ToList(); - listToUpdate.AddRange(missingSeries); - - await UpdateSeries(listToUpdate, path, nullableUpdateValue, progress, cancellationToken).ConfigureAwait(false); - } - - File.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8); - progress.Report(100); - } - - /// - /// Gets the update time. - /// - /// The response. - /// System.String. - private string GetUpdateTime(Stream response) - { - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - using (var streamReader = new StreamReader(response, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Time": - { - return (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - } - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } - } - - return null; - } - - /// - /// Gets the series ids to update. - /// - /// The existing series ids. - /// The last update time. - /// The cancellation token. - /// Task{IEnumerable{System.String}}. - private async Task, string>> GetSeriesIdsToUpdate(IEnumerable existingSeriesIds, string lastUpdateTime, CancellationToken cancellationToken) - { - // First get last time - using (var response = await _httpClient.SendAsync(new HttpRequestOptions - { - Url = string.Format(UpdatesUrl, lastUpdateTime), - CancellationToken = cancellationToken, - EnableHttpCompression = true, - BufferContent = false - - }, "GET").ConfigureAwait(false)) - { - using (var stream = response.Content) - { - var data = GetUpdatedSeriesIdList(stream); - - var existingDictionary = existingSeriesIds.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); - - var seriesList = data.Item1 - .Where(i => !string.IsNullOrWhiteSpace(i) && existingDictionary.ContainsKey(i)); - - return new Tuple, string>(seriesList, data.Item2); - } - } - } - - private Tuple, string> GetUpdatedSeriesIdList(Stream stream) - { - string updateTime = null; - var idList = new List(); - - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - using (var streamReader = new StreamReader(stream, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Time": - { - updateTime = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - break; - } - case "Series": - { - var id = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - idList.Add(id); - break; - } - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } - } - - return new Tuple, string>(idList, updateTime); - } - - /// - /// Updates the series. - /// - /// The series ids. - /// The series data path. - /// The last tv db update time. - /// The progress. - /// The cancellation token. - /// Task. - private async Task UpdateSeries(List seriesIds, string seriesDataPath, long? lastTvDbUpdateTime, IProgress progress, CancellationToken cancellationToken) - { - var numComplete = 0; - - var seriesList = _libraryManager.GetItemList(new InternalItemsQuery() - { - IncludeItemTypes = new[] { typeof(Series).Name }, - Recursive = true, - GroupByPresentationUniqueKey = false, - DtoOptions = new DtoOptions(false) - { - EnableImages = false - } - - }).Cast(); - - // Gather all series into a lookup by tvdb id - var allSeries = seriesList - .Where(i => !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb))) - .ToLookup(i => i.GetProviderId(MetadataProviders.Tvdb)); - - foreach (var seriesId in seriesIds) - { - // Find the preferred language(s) for the movie in the library - var languages = allSeries[seriesId] - .Select(i => i.GetPreferredMetadataLanguage()) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - - foreach (var language in languages) - { - try - { - await UpdateSeries(seriesId, seriesDataPath, lastTvDbUpdateTime, language, cancellationToken).ConfigureAwait(false); - } - catch (HttpException ex) - { - _logger.LogError(ex, "Error updating tvdb series id {ID}, language {Language}", seriesId, language); - - // Already logged at lower levels, but don't fail the whole operation, unless timed out - // We have to fail this to make it run again otherwise new episode data could potentially be missing - if (ex.IsTimedOut) - { - throw; - } - } - } - - numComplete++; - double percent = numComplete; - percent /= seriesIds.Count; - percent *= 100; - - progress.Report(percent); - } - } - - /// - /// Updates the series. - /// - /// The id. - /// The series data path. - /// The last tv db update time. - /// The preferred metadata language. - /// The cancellation token. - /// Task. - private Task UpdateSeries(string id, string seriesDataPath, long? lastTvDbUpdateTime, string preferredMetadataLanguage, CancellationToken cancellationToken) - { - _logger.LogInformation("Updating series from tvdb " + id + ", language " + preferredMetadataLanguage); - - seriesDataPath = Path.Combine(seriesDataPath, id); - - Directory.CreateDirectory(seriesDataPath); - - //return TvdbSeriesProvider.Current.DownloadSeriesZip(id, MetadataProviders.Tvdb.ToString(), null, null, seriesDataPath, lastTvDbUpdateTime, preferredMetadataLanguage, cancellationToken); - return Task.CompletedTask; - } - } -} diff --git a/MediaBrowser.Providers/TV/TvExternalIds.cs b/MediaBrowser.Providers/TV/TvExternalIds.cs index 5c246e3000..f50ebc1afd 100644 --- a/MediaBrowser.Providers/TV/TvExternalIds.cs +++ b/MediaBrowser.Providers/TV/TvExternalIds.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.TV public string Key => MetadataProviders.Tvdb.ToString(); - public string UrlFormatString => TvdbPrescanTask.TvdbBaseUrl + "?tab=series&id={0}"; + public string UrlFormatString => "https://thetvdb.com/?tab=series&id={0}"; public bool Supports(IHasProviderIds item) { @@ -53,7 +53,7 @@ namespace MediaBrowser.Providers.TV public string Key => MetadataProviders.Tvdb.ToString(); - public string UrlFormatString => TvdbPrescanTask.TvdbBaseUrl + "?tab=episode&id={0}"; + public string UrlFormatString => "https://thetvdb.com/?tab=episode&id={0}"; public bool Supports(IHasProviderIds item) { From ced9868357bfcdfb1fcdfe8e7e4a8e5ebb778076 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Thu, 7 Feb 2019 23:09:55 +0100 Subject: [PATCH 12/39] Fix a copy paste mistake and add series end date --- .../TV/TheTVDB/TvDbClientManager.cs | 14 ++++++++- .../TV/TheTVDB/TvdbSeriesProvider.cs | 31 +++++++++++++++---- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index e789d2ded7..7a83cfa186 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -94,7 +94,7 @@ namespace MediaBrowser.Providers.TV public Task> GetSeriesByZap2ItIdAsync(string zap2ItId, CancellationToken cancellationToken) { - return TryGetValue("series" + zap2ItId,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(zap2ItId, cancellationToken)); + return TryGetValue("series" + zap2ItId,() => TvDbClient.Search.SearchSeriesByZap2ItIdAsync(zap2ItId, cancellationToken)); } public Task> GetActorsAsync(int tvdbId, CancellationToken cancellationToken) { @@ -111,6 +111,18 @@ namespace MediaBrowser.Providers.TV return TryGetValue("languages",() => TvDbClient.Languages.GetAllAsync(cancellationToken)); } + public Task> GetSeriesEpisodeSummaryAsync(int tvdbId, CancellationToken cancellationToken) + { + return TryGetValue("seriesepisodesummary" + tvdbId, + () => TvDbClient.Series.GetEpisodesSummaryAsync(tvdbId, cancellationToken)); + } + + public Task> GetEpisodesPageAsync(int tvdbId, EpisodeQuery episodeQuery, CancellationToken cancellationToken) + { + return TryGetValue("episodespage" + tvdbId + episodeQuery.AiredSeason, + () => TvDbClient.Series.GetEpisodesAsync(tvdbId, 1, episodeQuery, cancellationToken)); + } + private async Task TryGetValue(object key, Func> resultFactory) { if (_cache.TryGetValue(key, out T cachedValue)) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 9940cf7e42..6b124c96d9 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -329,7 +329,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB return name.Trim(); } - private static void MapSeriesToResult(MetadataResult result, TvDbSharper.Dto.Series tvdbSeries) + private void MapSeriesToResult(MetadataResult result, TvDbSharper.Dto.Series tvdbSeries) { Series series = result.Item; series.SetProviderId(MetadataProviders.Tvdb, tvdbSeries.Id.ToString()); @@ -363,11 +363,30 @@ namespace MediaBrowser.Providers.TV.TheTVDB series.AddStudio(tvdbSeries.Network); - // TODO is this necessary? - // if (result.Item.Status.HasValue && result.Item.Status.Value == SeriesStatus.Ended && episodeAirDates.Count > 0) - // { - // result.Item.EndDate = episodeAirDates.Max(); - // } + if (result.Item.Status.HasValue && result.Item.Status.Value == SeriesStatus.Ended) + { + try + { + var episodeSummary = _tvDbClientManager + .GetSeriesEpisodeSummaryAsync(tvdbSeries.Id, CancellationToken.None).Result.Data; + var maxSeasonNumber = episodeSummary.AiredSeasons.Select(s => Convert.ToInt32(s)).Max(); + var episodeQuery = new EpisodeQuery + { + AiredSeason = maxSeasonNumber + }; + var episodesPage = + _tvDbClientManager.GetEpisodesPageAsync(tvdbSeries.Id, episodeQuery, CancellationToken.None).Result.Data; + result.Item.EndDate = episodesPage.Select(e => + { + DateTime.TryParse(e.FirstAired, out var firstAired); + return firstAired; + }).Max(); + } + catch (TvDbServerException e) + { + _logger.LogError(e, "Failed to find series end date for series {TvdbId}", tvdbSeries.Id); + } + } } private static void MapActorsToResult(MetadataResult result, IEnumerable actors) From 0d43b06042fb1359f84afc3bd17429eb5b445b65 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Thu, 7 Feb 2019 23:56:57 +0100 Subject: [PATCH 13/39] Fix MissingEpisodeProvider (almost) --- .../TV/MissingEpisodeProvider.cs | 193 +++--------------- .../TV/SeriesMetadataService.cs | 3 +- .../TV/TheTVDB/TvDbClientManager.cs | 34 ++- 3 files changed, 67 insertions(+), 163 deletions(-) diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 70765718c9..f2ab0d42d0 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -30,63 +30,46 @@ namespace MediaBrowser.Providers.TV private readonly IFileSystem _fileSystem; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private readonly IXmlReaderSettingsFactory _xmlSettings; - public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings) + public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem) { _logger = logger; _config = config; _libraryManager = libraryManager; _localization = localization; _fileSystem = fileSystem; - _xmlSettings = xmlSettings; } public async Task Run(Series series, bool addNewItems, CancellationToken cancellationToken) { - // TODO cvium fixme wtfisthisandwhydoesitrunwhenoptionisdisabled - return true; var tvdbId = series.GetProviderId(MetadataProviders.Tvdb); // Todo: Support series by imdb id - var seriesProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); - seriesProviderIds[MetadataProviders.Tvdb.ToString()] = tvdbId; + var seriesProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + [MetadataProviders.Tvdb.ToString()] = tvdbId + }; - var episodeFiles = _fileSystem.GetFilePaths("") - .Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase)) - .Select(Path.GetFileNameWithoutExtension) - .Where(i => i.StartsWith("episode-", StringComparison.OrdinalIgnoreCase)) - .ToList(); + var episodes = await TvDbClientManager.Instance.GetAllEpisodesAsync(Convert.ToInt32(tvdbId), cancellationToken); - var episodeLookup = episodeFiles + var episodeLookup = episodes .Select(i => { - var parts = i.Split('-'); - - if (parts.Length == 3) - { - if (int.TryParse(parts[1], NumberStyles.Integer, _usCulture, out var seasonNumber)) - { - if (int.TryParse(parts[2], NumberStyles.Integer, _usCulture, out var episodeNumber)) - { - return new ValueTuple(seasonNumber, episodeNumber); - } - } - } - - return new ValueTuple(-1, -1); + DateTime.TryParse(i.FirstAired, out var firstAired); + return new ValueTuple( + i.AiredSeason.GetValueOrDefault(-1), i.AiredEpisodeNumber.GetValueOrDefault(-1), firstAired); }) - .Where(i => i.Item1 != -1 && i.Item2 != -1) + .Where(i => i.Item2 != -1 && i.Item2 != -1) .ToList(); var allRecursiveChildren = series.GetRecursiveChildren(); - var hasBadData = HasInvalidContent(series, allRecursiveChildren); + var hasBadData = HasInvalidContent(allRecursiveChildren); // Be conservative here to avoid creating missing episodes for ones they already have var addMissingEpisodes = !hasBadData && _libraryManager.GetLibraryOptions(series).ImportMissingEpisodes; - var anySeasonsRemoved = RemoveObsoleteOrMissingSeasons(series, allRecursiveChildren, episodeLookup); + var anySeasonsRemoved = RemoveObsoleteOrMissingSeasons(allRecursiveChildren, episodeLookup); if (anySeasonsRemoved) { @@ -94,7 +77,7 @@ namespace MediaBrowser.Providers.TV allRecursiveChildren = series.GetRecursiveChildren(); } - var anyEpisodesRemoved = RemoveObsoleteOrMissingEpisodes(series, allRecursiveChildren, episodeLookup, addMissingEpisodes); + var anyEpisodesRemoved = RemoveObsoleteOrMissingEpisodes(allRecursiveChildren, episodeLookup, addMissingEpisodes); if (anyEpisodesRemoved) { @@ -106,7 +89,7 @@ namespace MediaBrowser.Providers.TV if (addNewItems && series.IsMetadataFetcherEnabled(_libraryManager.GetLibraryOptions(series), TvdbSeriesProvider.Current.Name)) { - hasNewEpisodes = await AddMissingEpisodes(series, allRecursiveChildren, addMissingEpisodes, "", episodeLookup, cancellationToken) + hasNewEpisodes = await AddMissingEpisodes(series, allRecursiveChildren, addMissingEpisodes, episodeLookup, cancellationToken) .ConfigureAwait(false); } @@ -122,7 +105,7 @@ namespace MediaBrowser.Providers.TV /// Returns true if a series has any seasons or episodes without season or episode numbers /// If this data is missing no virtual items will be added in order to prevent possible duplicates /// - private bool HasInvalidContent(Series series, IList allItems) + private bool HasInvalidContent(IList allItems) { return allItems.OfType().Any(i => !i.IndexNumber.HasValue) || allItems.OfType().Any(i => @@ -139,31 +122,25 @@ namespace MediaBrowser.Providers.TV private const double UnairedEpisodeThresholdDays = 2; - /// - /// Adds the missing episodes. - /// - /// The series. - /// Task. - private async Task AddMissingEpisodes(Series series, + + private async Task AddMissingEpisodes( + Series series, IList allItems, bool addMissingEpisodes, - string seriesDataPath, - IEnumerable> episodeLookup, + List> episodeLookup, CancellationToken cancellationToken) { var existingEpisodes = allItems.OfType() .ToList(); - var lookup = episodeLookup as IList> ?? episodeLookup.ToList(); - - var seasonCounts = (from e in lookup + var seasonCounts = (from e in episodeLookup group e by e.Item1 into g select g) .ToDictionary(g => g.Key, g => g.Count()); var hasChanges = false; - foreach (var tuple in lookup) + foreach (var tuple in episodeLookup) { if (tuple.Item1 <= 0) { @@ -184,32 +161,14 @@ namespace MediaBrowser.Providers.TV continue; } - var airDate = GetAirDate(seriesDataPath, tuple.Item1, tuple.Item2); + var airDate = tuple.Item3; - if (!airDate.HasValue) - { - continue; - } + var now = DateTime.UtcNow.AddDays(0 - UnairedEpisodeThresholdDays); - var now = DateTime.UtcNow; - - now = now.AddDays(0 - UnairedEpisodeThresholdDays); - - if (airDate.Value < now) - { - if (addMissingEpisodes) - { - // tvdb has a lot of nearly blank episodes - _logger.LogInformation("Creating virtual missing episode {0} {1}x{2}", series.Name, tuple.Item1, tuple.Item2); - await AddEpisode(series, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false); - - hasChanges = true; - } - } - else if (airDate.Value > now) + if (airDate < now && addMissingEpisodes || airDate > now) { // tvdb has a lot of nearly blank episodes - _logger.LogInformation("Creating virtual unaired episode {0} {1}x{2}", series.Name, tuple.Item1, tuple.Item2); + _logger.LogInformation("Creating virtual missing/unaired episode {0} {1}x{2}", series.Name, tuple.Item1, tuple.Item2); await AddEpisode(series, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false); hasChanges = true; @@ -222,9 +181,9 @@ namespace MediaBrowser.Providers.TV /// /// Removes the virtual entry after a corresponding physical version has been added /// - private bool RemoveObsoleteOrMissingEpisodes(Series series, + private bool RemoveObsoleteOrMissingEpisodes( IList allRecursiveChildren, - IEnumerable> episodeLookup, + IEnumerable> episodeLookup, bool allowMissingEpisodes) { var existingEpisodes = allRecursiveChildren.OfType() @@ -295,12 +254,11 @@ namespace MediaBrowser.Providers.TV /// /// Removes the obsolete or missing seasons. /// - /// The series. + /// /// The episode lookup. /// Task{System.Boolean}. - private bool RemoveObsoleteOrMissingSeasons(Series series, - IList allRecursiveChildren, - IEnumerable> episodeLookup) + private bool RemoveObsoleteOrMissingSeasons(IList allRecursiveChildren, + IEnumerable<(int, int, DateTime)> episodeLookup) { var existingSeasons = allRecursiveChildren.OfType().ToList(); @@ -380,7 +338,7 @@ namespace MediaBrowser.Providers.TV season = await provider.AddSeason(series, seasonNumber, true, cancellationToken).ConfigureAwait(false); } - var name = string.Format("Episode {0}", episodeNumber.ToString(_usCulture)); + var name = $"Episode {episodeNumber.ToString(_usCulture)}"; var episode = new Episode { @@ -389,7 +347,7 @@ namespace MediaBrowser.Providers.TV ParentIndexNumber = seasonNumber, Id = _libraryManager.GetNewItemId((series.Id + seasonNumber.ToString(_usCulture) + name), typeof(Episode)), IsVirtualItem = true, - SeasonId = season == null ? Guid.Empty : season.Id, + SeasonId = season?.Id ?? Guid.Empty, SeriesId = series.Id }; @@ -407,7 +365,7 @@ namespace MediaBrowser.Providers.TV /// /// The tuple. /// Episode. - private Episode GetExistingEpisode(IList existingEpisodes, Dictionary seasonCounts, ValueTuple tuple) + private Episode GetExistingEpisode(IList existingEpisodes, Dictionary seasonCounts, ValueTuple tuple) { var s = tuple.Item1; var e = tuple.Item2; @@ -434,88 +392,5 @@ namespace MediaBrowser.Providers.TV return existingEpisodes .FirstOrDefault(i => i.ParentIndexNumber == season && i.ContainsEpisodeNumber(episode)); } - - /// - /// Gets the air date. - /// - /// The series data path. - /// The season number. - /// The episode number. - /// System.Nullable{DateTime}. - private DateTime? GetAirDate(string seriesDataPath, int seasonNumber, int episodeNumber) - { - // First open up the tvdb xml file and make sure it has valid data - var filename = string.Format("episode-{0}-{1}.xml", seasonNumber.ToString(_usCulture), episodeNumber.ToString(_usCulture)); - - var xmlPath = Path.Combine(seriesDataPath, filename); - - DateTime? airDate = null; - - using (var fileStream = _fileSystem.GetFileStream(xmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) - { - // It appears the best way to filter out invalid entries is to only include those with valid air dates - using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) - { - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "EpisodeName": - { - var val = reader.ReadElementContentAsString(); - if (string.IsNullOrWhiteSpace(val)) - { - // Not valid, ignore these - return null; - } - break; - } - case "FirstAired": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (DateTime.TryParse(val, out var date)) - { - airDate = date.ToUniversalTime(); - } - } - - break; - } - default: - { - reader.Skip(); - break; - } - } - } - else - { - reader.Read(); - } - } - } - } - } - - return airDate; - } } } diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index 5f4f39d45c..b0abd7c7cb 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -36,8 +36,7 @@ namespace MediaBrowser.Providers.TV ServerConfigurationManager, LibraryManager, _localization, - FileSystem, - _xmlSettings); + FileSystem); try { diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 7a83cfa186..c02661e374 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; @@ -87,6 +88,24 @@ namespace MediaBrowser.Providers.TV return TryGetValue("episode" + episodeTvdbId,() => TvDbClient.Episodes.GetAsync(episodeTvdbId, cancellationToken)); } + public async Task> GetAllEpisodesAsync(int tvdbId, CancellationToken cancellationToken) + { + // Traverse all episode pages and join them together + var episodes = new List(); + var episodePage = await GetEpisodesPageAsync(tvdbId, new EpisodeQuery(), cancellationToken); + episodes.AddRange(episodePage.Data); + int next = episodePage.Links.Next.GetValueOrDefault(0); + int last = episodePage.Links.Last.GetValueOrDefault(0); + + for (var page = next; page <= last; ++page) + { + episodePage = await GetEpisodesPageAsync(tvdbId, page, new EpisodeQuery(), cancellationToken); + episodes.AddRange(episodePage.Data); + } + + return episodes; + } + public Task> GetSeriesByImdbIdAsync(string imdbId, CancellationToken cancellationToken) { return TryGetValue("series" + imdbId,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(imdbId, cancellationToken)); @@ -117,10 +136,21 @@ namespace MediaBrowser.Providers.TV () => TvDbClient.Series.GetEpisodesSummaryAsync(tvdbId, cancellationToken)); } + public Task> GetEpisodesPageAsync(int tvdbId, int page, EpisodeQuery episodeQuery, CancellationToken cancellationToken) + { + // Not quite as dynamic as it could be + var cacheKey = "episodespage" + tvdbId + "page" + page; + if (episodeQuery.AiredSeason.HasValue) + { + cacheKey += "airedseason" + episodeQuery.AiredSeason.Value; + } + return TryGetValue(cacheKey, + () => TvDbClient.Series.GetEpisodesAsync(tvdbId, page, episodeQuery, cancellationToken)); + } + public Task> GetEpisodesPageAsync(int tvdbId, EpisodeQuery episodeQuery, CancellationToken cancellationToken) { - return TryGetValue("episodespage" + tvdbId + episodeQuery.AiredSeason, - () => TvDbClient.Series.GetEpisodesAsync(tvdbId, 1, episodeQuery, cancellationToken)); + return GetEpisodesPageAsync(tvdbId, 1, episodeQuery, cancellationToken); } private async Task TryGetValue(object key, Func> resultFactory) From 373a1f72bff7c8192e78f854f719dd17182abfde Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Fri, 8 Feb 2019 13:30:50 +0100 Subject: [PATCH 14/39] Fix actor image provider --- .../People/TvdbPersonImageProvider.cs | 160 +++--------------- 1 file changed, 27 insertions(+), 133 deletions(-) diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index 2e0c450bb7..0f978d71fb 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -1,42 +1,35 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Xml; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Xml; using MediaBrowser.Providers.TV; -using MediaBrowser.Providers.TV.TheTVDB; +using Microsoft.Extensions.Logging; +using TvDbSharper; namespace MediaBrowser.Providers.People { public class TvdbPersonImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IServerConfigurationManager _config; - private readonly ILibraryManager _libraryManager; private readonly IHttpClient _httpClient; - private readonly IFileSystem _fileSystem; - private readonly IXmlReaderSettingsFactory _xmlSettings; + private readonly ILogger _logger; + private readonly ILibraryManager _libraryManager; + private readonly TvDbClientManager _tvDbClientManager; - public TvdbPersonImageProvider(IServerConfigurationManager config, ILibraryManager libraryManager, IHttpClient httpClient, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings) + public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClient httpClient, ILogger logger) { - _config = config; _libraryManager = libraryManager; _httpClient = httpClient; - _fileSystem = fileSystem; - _xmlSettings = xmlSettings; + _logger = logger; + _tvDbClientManager = TvDbClientManager.Instance; } public string Name => ProviderName; @@ -56,7 +49,7 @@ namespace MediaBrowser.Providers.People }; } - public Task> GetImages(BaseItem item, CancellationToken cancellationToken) + public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { var seriesWithPerson = _libraryManager.GetItemList(new InternalItemsQuery { @@ -71,139 +64,40 @@ namespace MediaBrowser.Providers.People .Where(i => TvdbSeriesProvider.IsValidSeries(i.ProviderIds)) .ToList(); - var infos = seriesWithPerson.Select(i => GetImageFromSeriesData(i, item.Name, cancellationToken)) + var infos = await Task.WhenAll(seriesWithPerson.Select(async i => await GetImageFromSeriesData(i, item.Name, cancellationToken)) .Where(i => i != null) - .Take(1); + .Take(1)); - return Task.FromResult(infos); + return infos; } - private RemoteImageInfo GetImageFromSeriesData(Series series, string personName, CancellationToken cancellationToken) + private async Task GetImageFromSeriesData(Series series, string personName, CancellationToken cancellationToken) { - //var actorsResult = await _tvDbClient.Series.GetActorsAsync(Convert.ToInt32(tvdbId), cancellationToken); + var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb)); - return null; // GetImageInfo(actorXmlPath, personName, cancellationToken); - } - - private RemoteImageInfo GetImageInfo(string xmlFile, string personName, CancellationToken cancellationToken) - { - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - using (var fileStream = _fileSystem.GetFileStream(xmlFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) + try { - using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) + var actorsResult = await _tvDbClientManager.GetActorsAsync(tvdbId, cancellationToken); + var actor = actorsResult.Data.FirstOrDefault(a => + string.Equals(a.Name, personName, StringComparison.OrdinalIgnoreCase) && + !string.IsNullOrEmpty(a.Image)); + if (actor == null) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) - { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Actor": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - using (var subtree = reader.ReadSubtree()) - { - var info = FetchImageInfoFromActorNode(personName, subtree); - - if (info != null) - { - return info; - } - } - break; - } - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - } + return null; } - } - return null; - } - - /// - /// Fetches the data from actor node. - /// - /// Name of the person. - /// The reader. - /// System.String. - private RemoteImageInfo FetchImageInfoFromActorNode(string personName, XmlReader reader) - { - string name = null; - string image = null; - - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Name": - { - name = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - break; - } - - case "Image": - { - image = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - - if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(image) && - string.Equals(name, personName, StringComparison.OrdinalIgnoreCase)) - { return new RemoteImageInfo { - Url = TVUtils.BannerUrl + image, + Url = TVUtils.BannerUrl + actor.Image, Type = ImageType.Primary, ProviderName = Name - }; } - - return null; + catch (TvDbServerException e) + { + _logger.LogError(e, "Failed to retrieve actor {ActorName} from series {SeriesTvdbId}", personName, tvdbId); + return null; + } } public int Order => 1; From 6887e790c82ea2b7b745d603f42d744ac8af52cd Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Fri, 8 Feb 2019 14:39:01 +0100 Subject: [PATCH 15/39] Minor fixes in MissingEpisodeProvider --- MediaBrowser.Providers/TV/MissingEpisodeProvider.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index f2ab0d42d0..0e0b05156d 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -43,12 +43,10 @@ namespace MediaBrowser.Providers.TV public async Task Run(Series series, bool addNewItems, CancellationToken cancellationToken) { var tvdbId = series.GetProviderId(MetadataProviders.Tvdb); - - // Todo: Support series by imdb id - var seriesProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase) + if (string.IsNullOrEmpty(tvdbId)) { - [MetadataProviders.Tvdb.ToString()] = tvdbId - }; + return false; + } var episodes = await TvDbClientManager.Instance.GetAllEpisodesAsync(Convert.ToInt32(tvdbId), cancellationToken); @@ -59,7 +57,7 @@ namespace MediaBrowser.Providers.TV return new ValueTuple( i.AiredSeason.GetValueOrDefault(-1), i.AiredEpisodeNumber.GetValueOrDefault(-1), firstAired); }) - .Where(i => i.Item2 != -1 && i.Item2 != -1) + .Where(i => i.Item1 != -1 && i.Item2 != -1) .ToList(); var allRecursiveChildren = series.GetRecursiveChildren(); From b997b12d27f365c24a019e8760fce6a1921a0d35 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Fri, 8 Feb 2019 17:42:09 +0100 Subject: [PATCH 16/39] Add names to tuple items and reduce list iterations --- .../TV/MissingEpisodeProvider.cs | 169 +++++++++--------- 1 file changed, 81 insertions(+), 88 deletions(-) diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 0e0b05156d..c54a3fa4a3 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -30,6 +30,7 @@ namespace MediaBrowser.Providers.TV private readonly IFileSystem _fileSystem; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private const double UnairedEpisodeThresholdDays = 2; public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem) { @@ -54,10 +55,13 @@ namespace MediaBrowser.Providers.TV .Select(i => { DateTime.TryParse(i.FirstAired, out var firstAired); - return new ValueTuple( - i.AiredSeason.GetValueOrDefault(-1), i.AiredEpisodeNumber.GetValueOrDefault(-1), firstAired); + var seasonNumber = i.AiredSeason.GetValueOrDefault(-1); + var episodeNumber = i.AiredEpisodeNumber.GetValueOrDefault(-1); + return (SeasonNumber: seasonNumber, EpisodeNumber: episodeNumber, firstAired: firstAired); }) - .Where(i => i.Item1 != -1 && i.Item2 != -1) + .Where(i => i.SeasonNumber != -1 && i.EpisodeNumber != -1) + .OrderBy(i => i.SeasonNumber) + .ThenBy(i => i.EpisodeNumber) .ToList(); var allRecursiveChildren = series.GetRecursiveChildren(); @@ -118,37 +122,25 @@ namespace MediaBrowser.Providers.TV }); } - private const double UnairedEpisodeThresholdDays = 2; - - private async Task AddMissingEpisodes( Series series, - IList allItems, + IEnumerable allItems, bool addMissingEpisodes, - List> episodeLookup, + IReadOnlyCollection<(int SeasonNumber, int Episodenumber, DateTime FirstAired)> episodeLookup, CancellationToken cancellationToken) { var existingEpisodes = allItems.OfType() .ToList(); - var seasonCounts = (from e in episodeLookup - group e by e.Item1 into g - select g) - .ToDictionary(g => g.Key, g => g.Count()); + var seasonCounts = episodeLookup.GroupBy(e => e.SeasonNumber).ToDictionary(g => g.Key, g => g.Count()); var hasChanges = false; foreach (var tuple in episodeLookup) { - if (tuple.Item1 <= 0) + if (tuple.SeasonNumber <= 0 || tuple.Episodenumber <= 0) { - // Ignore season zeros - continue; - } - - if (tuple.Item2 <= 0) - { - // Ignore episode zeros + // Ignore episode/season zeros continue; } @@ -159,15 +151,15 @@ namespace MediaBrowser.Providers.TV continue; } - var airDate = tuple.Item3; + var airDate = tuple.FirstAired; var now = DateTime.UtcNow.AddDays(0 - UnairedEpisodeThresholdDays); if (airDate < now && addMissingEpisodes || airDate > now) { // tvdb has a lot of nearly blank episodes - _logger.LogInformation("Creating virtual missing/unaired episode {0} {1}x{2}", series.Name, tuple.Item1, tuple.Item2); - await AddEpisode(series, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false); + _logger.LogInformation("Creating virtual missing/unaired episode {0} {1}x{2}", series.Name, tuple.SeasonNumber, tuple.Episodenumber); + await AddEpisode(series, tuple.SeasonNumber, tuple.Episodenumber, cancellationToken).ConfigureAwait(false); hasChanges = true; } @@ -180,58 +172,57 @@ namespace MediaBrowser.Providers.TV /// Removes the virtual entry after a corresponding physical version has been added /// private bool RemoveObsoleteOrMissingEpisodes( - IList allRecursiveChildren, - IEnumerable> episodeLookup, + IEnumerable allRecursiveChildren, + IEnumerable<(int SeasonNumber, int EpisodeNumber, DateTime FirstAired)> episodeLookup, bool allowMissingEpisodes) { - var existingEpisodes = allRecursiveChildren.OfType() - .ToList(); + var existingEpisodes = allRecursiveChildren.OfType(); - var physicalEpisodes = existingEpisodes - .Where(i => i.LocationType != LocationType.Virtual) - .ToList(); - - var virtualEpisodes = existingEpisodes - .Where(i => i.LocationType == LocationType.Virtual) - .ToList(); + var physicalEpisodes = new List(); + var virtualEpisodes = new List(); + foreach (var episode in existingEpisodes) + { + if (episode.LocationType == LocationType.Virtual) + { + virtualEpisodes.Add(episode); + } + else + { + physicalEpisodes.Add(episode); + } + } var episodesToRemove = virtualEpisodes .Where(i => { - if (i.IndexNumber.HasValue && i.ParentIndexNumber.HasValue) + if (!i.IndexNumber.HasValue || !i.ParentIndexNumber.HasValue) { - var seasonNumber = i.ParentIndexNumber.Value; - var episodeNumber = i.IndexNumber.Value; - - // If there's a physical episode with the same season and episode number, delete it - if (physicalEpisodes.Any(p => - p.ParentIndexNumber.HasValue && (p.ParentIndexNumber.Value) == seasonNumber && - p.ContainsEpisodeNumber(episodeNumber))) - { - return true; - } - - // If the episode no longer exists in the remote lookup, delete it - if (!episodeLookup.Any(e => e.Item1 == seasonNumber && e.Item2 == episodeNumber)) - { - return true; - } - - if (!allowMissingEpisodes && i.IsMissingEpisode) - { - // If it's missing, but not unaired, remove it - if (!i.PremiereDate.HasValue || i.PremiereDate.Value.ToLocalTime().Date.AddDays(UnairedEpisodeThresholdDays) < DateTime.Now.Date) - { - return true; - } - } - - return false; + return true; } - return true; - }) - .ToList(); + var seasonNumber = i.ParentIndexNumber.Value; + var episodeNumber = i.IndexNumber.Value; + + // If there's a physical episode with the same season and episode number, delete it + if (physicalEpisodes.Any(p => + p.ParentIndexNumber.HasValue && p.ParentIndexNumber.Value == seasonNumber && + p.ContainsEpisodeNumber(episodeNumber))) + { + return true; + } + + // If the episode no longer exists in the remote lookup, delete it + if (!episodeLookup.Any(e => e.SeasonNumber == seasonNumber && e.EpisodeNumber == episodeNumber)) + { + return true; + } + + // If it's missing, but not unaired, remove it + return !allowMissingEpisodes && i.IsMissingEpisode && + (!i.PremiereDate.HasValue || + i.PremiereDate.Value.ToLocalTime().Date.AddDays(UnairedEpisodeThresholdDays) < + DateTime.Now.Date); + }); var hasChanges = false; @@ -240,7 +231,6 @@ namespace MediaBrowser.Providers.TV _libraryManager.DeleteItem(episodeToRemove, new DeleteOptions { DeleteFileLocation = true - }, false); hasChanges = true; @@ -256,17 +246,23 @@ namespace MediaBrowser.Providers.TV /// The episode lookup. /// Task{System.Boolean}. private bool RemoveObsoleteOrMissingSeasons(IList allRecursiveChildren, - IEnumerable<(int, int, DateTime)> episodeLookup) + IEnumerable<(int SeasonNumber, int EpisodeNumber, DateTime FirstAired)> episodeLookup) { var existingSeasons = allRecursiveChildren.OfType().ToList(); - var physicalSeasons = existingSeasons - .Where(i => i.LocationType != LocationType.Virtual) - .ToList(); - - var virtualSeasons = existingSeasons - .Where(i => i.LocationType == LocationType.Virtual) - .ToList(); + var physicalSeasons = new List(); + var virtualSeasons = new List(); + foreach (var season in existingSeasons) + { + if (season.LocationType == LocationType.Virtual) + { + virtualSeasons.Add(season); + } + else + { + physicalSeasons.Add(season); + } + } var allEpisodes = allRecursiveChildren.OfType().ToList(); @@ -278,13 +274,13 @@ namespace MediaBrowser.Providers.TV var seasonNumber = i.IndexNumber.Value; // If there's a physical season with the same number, delete it - if (physicalSeasons.Any(p => p.IndexNumber.HasValue && (p.IndexNumber.Value) == seasonNumber && string.Equals(p.Series.PresentationUniqueKey, i.Series.PresentationUniqueKey, StringComparison.Ordinal))) + if (physicalSeasons.Any(p => p.IndexNumber.HasValue && p.IndexNumber.Value == seasonNumber && string.Equals(p.Series.PresentationUniqueKey, i.Series.PresentationUniqueKey, StringComparison.Ordinal))) { return true; } // If the season no longer exists in the remote lookup, delete it, but only if an existing episode doesn't require it - if (episodeLookup.All(e => e.Item1 != seasonNumber)) + if (episodeLookup.All(e => e.SeasonNumber != seasonNumber)) { if (allEpisodes.All(s => s.ParentIndexNumber != seasonNumber || s.IsInSeasonFolder)) { @@ -298,8 +294,7 @@ namespace MediaBrowser.Providers.TV // Season does not have a number // Remove if there are no episodes directly in series without a season number return allEpisodes.All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder); - }) - .ToList(); + }); var hasChanges = false; @@ -343,14 +338,12 @@ namespace MediaBrowser.Providers.TV Name = name, IndexNumber = episodeNumber, ParentIndexNumber = seasonNumber, - Id = _libraryManager.GetNewItemId((series.Id + seasonNumber.ToString(_usCulture) + name), typeof(Episode)), + Id = _libraryManager.GetNewItemId(series.Id + seasonNumber.ToString(_usCulture) + name, typeof(Episode)), IsVirtualItem = true, SeasonId = season?.Id ?? Guid.Empty, SeriesId = series.Id }; - episode.SetParent(season); - season.AddChild(episode, cancellationToken); await episode.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), cancellationToken).ConfigureAwait(false); @@ -363,21 +356,21 @@ namespace MediaBrowser.Providers.TV /// /// The tuple. /// Episode. - private Episode GetExistingEpisode(IList existingEpisodes, Dictionary seasonCounts, ValueTuple tuple) + private Episode GetExistingEpisode(IList existingEpisodes, Dictionary seasonCounts, (int SeasonNumber, int EpisodeNumber, DateTime FirstAired) episodeTuple) { - var s = tuple.Item1; - var e = tuple.Item2; + var seasonNumber = episodeTuple.SeasonNumber; + var episodeNumber = episodeTuple.EpisodeNumber; while (true) { - var episode = GetExistingEpisode(existingEpisodes, s, e); + var episode = GetExistingEpisode(existingEpisodes, seasonNumber, episodeNumber); if (episode != null) return episode; - s--; + seasonNumber--; - if (seasonCounts.ContainsKey(s)) - e += seasonCounts[s]; + if (seasonCounts.ContainsKey(seasonNumber)) + episodeNumber += seasonCounts[seasonNumber]; else break; } From 83d98ac92d7e2dc2c408f59cbff74de8b42968e2 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Fri, 8 Feb 2019 19:48:18 +0100 Subject: [PATCH 17/39] Fix episode provider --- .../TV/TheTVDB/TvDbClientManager.cs | 41 +++++++++++++++++-- .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 21 ++++++++-- .../TV/TheTVDB/TvdbEpisodeProvider.cs | 27 ++++++++++-- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 4 +- .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 4 +- 5 files changed, 83 insertions(+), 14 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index c02661e374..1bf4f6e03d 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; using Microsoft.Extensions.Caching.Memory; using TvDbSharper; using TvDbSharper.Dto; @@ -94,8 +97,13 @@ namespace MediaBrowser.Providers.TV var episodes = new List(); var episodePage = await GetEpisodesPageAsync(tvdbId, new EpisodeQuery(), cancellationToken); episodes.AddRange(episodePage.Data); - int next = episodePage.Links.Next.GetValueOrDefault(0); - int last = episodePage.Links.Last.GetValueOrDefault(0); + if (!episodePage.Links.Next.HasValue || !episodePage.Links.Last.HasValue) + { + return episodes; + } + + int next = episodePage.Links.Next.Value; + int last = episodePage.Links.Last.Value; for (var page = next; page <= last; ++page) { @@ -122,7 +130,8 @@ namespace MediaBrowser.Providers.TV public Task> GetImagesAsync(int tvdbId, ImagesQuery imageQuery, CancellationToken cancellationToken) { - return TryGetValue("images" + tvdbId,() => TvDbClient.Series.GetImagesAsync(tvdbId, imageQuery, cancellationToken)); + var cacheKey = "images" + tvdbId + "keytype" + imageQuery.KeyType + "subkey" + imageQuery.SubKey; + return TryGetValue(cacheKey,() => TvDbClient.Series.GetImagesAsync(tvdbId, imageQuery, cancellationToken)); } public Task> GetLanguagesAsync(CancellationToken cancellationToken) @@ -144,10 +153,36 @@ namespace MediaBrowser.Providers.TV { cacheKey += "airedseason" + episodeQuery.AiredSeason.Value; } + if (episodeQuery.AiredEpisode.HasValue) + { + cacheKey += "airedepisode" + episodeQuery.AiredEpisode.Value; + } return TryGetValue(cacheKey, () => TvDbClient.Series.GetEpisodesAsync(tvdbId, page, episodeQuery, cancellationToken)); } + public Task GetEpisodeTvdbId(EpisodeInfo searchInfo, CancellationToken cancellationToken) + { + searchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), + out var seriesTvdbId); + var episodeNumber = searchInfo.IndexNumber.Value; + var seasonNumber = searchInfo.ParentIndexNumber.Value; + + return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeNumber, seasonNumber, cancellationToken); + } + + public async Task GetEpisodeTvdbId(int seriesTvdbId, int episodeNumber, int seasonNumber, CancellationToken cancellationToken) + { + var episodeQuery = new EpisodeQuery + { + AiredSeason = seasonNumber, + AiredEpisode = episodeNumber + }; + var episodePage = await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), + episodeQuery, cancellationToken); + return episodePage.Data.FirstOrDefault()?.Id.ToString(); + } + public Task> GetEpisodesPageAsync(int tvdbId, EpisodeQuery episodeQuery, CancellationToken cancellationToken) { return GetEpisodesPageAsync(tvdbId, 1, episodeQuery, cancellationToken); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index caa0a5aba2..8f02b13772 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -53,13 +53,28 @@ namespace MediaBrowser.Providers.TV.TheTVDB if (series != null && TvdbSeriesProvider.IsValidSeries(series.ProviderIds)) { - var tvdbId = episode.GetProviderId(MetadataProviders.Tvdb); + var episodeTvdbId = episode.GetProviderId(MetadataProviders.Tvdb); // Process images try { + if (string.IsNullOrEmpty(episodeTvdbId)) + { + var episodeNumber = episode.IndexNumber.Value; + var seasonNumber = episode.ParentIndexNumber.Value; + episodeTvdbId = await _tvDbClientManager.GetEpisodeTvdbId( + Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb)), episodeNumber, seasonNumber, + cancellationToken); + if (string.IsNullOrEmpty(episodeTvdbId)) + { + _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber}found for series {SeriesTvdbId}", + seasonNumber, episodeNumber); + return imageResult; + } + } + var episodeResult = - await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(tvdbId), cancellationToken); + await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), cancellationToken); var image = GetImageInfo(episodeResult.Data); if (image != null) @@ -69,7 +84,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB } catch (TvDbServerException e) { - _logger.LogError(e, "Failed to retrieve episode images for {TvDbId}", tvdbId); + _logger.LogError(e, "Failed to retrieve episode images for {TvDbId}", episodeTvdbId); } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index 68125d50a8..d7fc9b5635 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -20,7 +20,6 @@ namespace MediaBrowser.Providers.TV /// class TvdbEpisodeProvider : IRemoteMetadataProvider, IHasOrder { - internal static TvdbEpisodeProvider Current; private readonly IHttpClient _httpClient; private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; @@ -29,7 +28,6 @@ namespace MediaBrowser.Providers.TV { _httpClient = httpClient; _logger = logger; - Current = this; _tvDbClientManager = TvDbClientManager.Instance; } @@ -48,8 +46,18 @@ namespace MediaBrowser.Providers.TV { try { - var episodeResult = - await _tvDbClientManager.GetEpisodesAsync((int)searchInfo.IndexNumber, cancellationToken); + var episodeTvdbId = searchInfo.GetProviderId(MetadataProviders.Tvdb); + if (string.IsNullOrEmpty(episodeTvdbId)) + { + episodeTvdbId = await _tvDbClientManager.GetEpisodeTvdbId(searchInfo, cancellationToken); + if (string.IsNullOrEmpty(episodeTvdbId)) + { + _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} found for series {SeriesTvdbId}", + searchInfo.ParentIndexNumber, searchInfo.IndexNumber); + return list; + } + } + var episodeResult = await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), cancellationToken); var metadataResult = MapEpisodeToResult(searchInfo, episodeResult.Data); if (metadataResult.HasMetadata) @@ -93,6 +101,17 @@ namespace MediaBrowser.Providers.TV var tvdbId = searchInfo.GetProviderId(MetadataProviders.Tvdb); try { + if (string.IsNullOrEmpty(tvdbId)) + { + tvdbId = await _tvDbClientManager.GetEpisodeTvdbId(searchInfo, cancellationToken); + if (string.IsNullOrEmpty(tvdbId)) + { + _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber}found for series {SeriesTvdbId}", + searchInfo.ParentIndexNumber, searchInfo.IndexNumber, tvdbId); + return result; + } + } + var episodeResult = await _tvDbClientManager.GetEpisodesAsync( Convert.ToInt32(tvdbId), cancellationToken); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index 42c7bc5c38..932b2ff638 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -81,9 +81,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB var imageResults = await _tvDbClientManager.GetImagesAsync(tvdbId, imageQuery, cancellationToken); remoteImages.AddRange(GetImages(imageResults.Data, language)); } - catch (TvDbServerException e) + catch (TvDbServerException) { - _logger.LogInformation(e, "No images of type {KeyType} found for series {TvdbId}", keyType, tvdbId); + _logger.LogDebug("No images of type {KeyType} found for series {TvdbId}", keyType, tvdbId); } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index b9caf0b3a9..38251a5c0b 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -75,9 +75,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB remoteImages.AddRange(GetImages(imageResults.Data, language)); } - catch (TvDbServerException e) + catch (TvDbServerException) { - _logger.LogError(e, "Failed to retrieve images of type {KeyType} for series {TvDbId}", keyType, + _logger.LogDebug("No images of type {KeyType} exist for series {TvDbId}", keyType, tvdbId); } } From da2c7db0df47d12d18c0c58539f51ca090e14e5b Mon Sep 17 00:00:00 2001 From: Vasily Date: Mon, 11 Feb 2019 18:47:40 +0100 Subject: [PATCH 18/39] Apply suggestions from code review Co-Authored-By: cvium --- .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 2 +- MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs | 6 +++--- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 8f02b13772..3a559727d7 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -67,7 +67,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB cancellationToken); if (string.IsNullOrEmpty(episodeTvdbId)) { - _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber}found for series {SeriesTvdbId}", + _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", seasonNumber, episodeNumber); return imageResult; } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index d7fc9b5635..b1144a2854 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.TV var list = new List(); // The search query must either provide an episode number or date - // TODO premieredate functionality is ded, could grab all episodes and search for it + // TODO premieredate functionality is dead, could grab all episodes and search for it if (!searchInfo.IndexNumber.HasValue || !searchInfo.PremiereDate.HasValue) { return list; @@ -52,7 +52,7 @@ namespace MediaBrowser.Providers.TV episodeTvdbId = await _tvDbClientManager.GetEpisodeTvdbId(searchInfo, cancellationToken); if (string.IsNullOrEmpty(episodeTvdbId)) { - _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} found for series {SeriesTvdbId}", + _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", searchInfo.ParentIndexNumber, searchInfo.IndexNumber); return list; } @@ -106,7 +106,7 @@ namespace MediaBrowser.Providers.TV tvdbId = await _tvDbClientManager.GetEpisodeTvdbId(searchInfo, cancellationToken); if (string.IsNullOrEmpty(tvdbId)) { - _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber}found for series {SeriesTvdbId}", + _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", searchInfo.ParentIndexNumber, searchInfo.IndexNumber, tvdbId); return result; } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index 932b2ff638..9218e9c089 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -103,7 +103,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB VoteCount = image.RatingsInfo.Count, Url = TVUtils.BannerUrl + image.FileName, ProviderName = ProviderName, - Language = languages.FirstOrDefault(l => l.Id == image.LanguageId)?.Abbreviation, + Language = languages.FirstOrDefault(lang => lang.Id == image.LanguageId)?.Abbreviation, ThumbnailUrl = TVUtils.BannerUrl + image.Thumbnail }; From 935c7231ebb4d4c8b84746d32c056f8a71f38782 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Mon, 11 Feb 2019 19:07:53 +0100 Subject: [PATCH 19/39] Review comments --- .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 2 +- MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index 38251a5c0b..b4b950be1e 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -98,7 +98,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB VoteCount = image.RatingsInfo.Count, Url = TVUtils.BannerUrl + image.FileName, ProviderName = Name, - Language = languages.FirstOrDefault(l => l.Id == image.LanguageId)?.Abbreviation, + Language = languages.FirstOrDefault(lang => lang.Id == image.LanguageId)?.Abbreviation, ThumbnailUrl = TVUtils.BannerUrl + image.Thumbnail }; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 6b124c96d9..342591da0f 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -279,7 +279,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB /// /// The spacers /// - const string spacers = "/,.:;\\(){}[]+-_=–*"; // (there are not actually two - in the they are different char codes) + const string spacers = "/,.:;\\(){}[]+-_=–*"; // (there are two types of dashes, short and long) /// /// Gets the name of the comparable. @@ -293,7 +293,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB var sb = new StringBuilder(); foreach (var c in name) { - if ((int)c >= 0x2B0 && (int)c <= 0x0333) + if (c >= 0x2B0 && c <= 0x0333) { // skip char modifier and diacritics } @@ -398,7 +398,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB Type = PersonType.Actor, Name = (actor.Name ?? string.Empty).Trim(), Role = actor.Role, - ImageUrl = actor.Image, + ImageUrl = TVUtils.BannerUrl + actor.Image, SortOrder = actor.SortOrder }; From 18231fedef3da98d303f9d2a979f3440306893b2 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Mon, 11 Feb 2019 19:14:24 +0100 Subject: [PATCH 20/39] Add braces to if's and other minor style changes --- .../TV/MissingEpisodeProvider.cs | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index c54a3fa4a3..4952608480 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -129,8 +129,7 @@ namespace MediaBrowser.Providers.TV IReadOnlyCollection<(int SeasonNumber, int Episodenumber, DateTime FirstAired)> episodeLookup, CancellationToken cancellationToken) { - var existingEpisodes = allItems.OfType() - .ToList(); + var existingEpisodes = allItems.OfType().ToList(); var seasonCounts = episodeLookup.GroupBy(e => e.SeasonNumber).ToDictionary(g => g.Key, g => g.Count()); @@ -280,15 +279,7 @@ namespace MediaBrowser.Providers.TV } // If the season no longer exists in the remote lookup, delete it, but only if an existing episode doesn't require it - if (episodeLookup.All(e => e.SeasonNumber != seasonNumber)) - { - if (allEpisodes.All(s => s.ParentIndexNumber != seasonNumber || s.IsInSeasonFolder)) - { - return true; - } - } - - return false; + return episodeLookup.All(e => e.SeasonNumber != seasonNumber) && allEpisodes.All(s => s.ParentIndexNumber != seasonNumber || s.IsInSeasonFolder); } // Season does not have a number @@ -354,9 +345,9 @@ namespace MediaBrowser.Providers.TV /// /// The existing episodes. /// - /// The tuple. + /// /// Episode. - private Episode GetExistingEpisode(IList existingEpisodes, Dictionary seasonCounts, (int SeasonNumber, int EpisodeNumber, DateTime FirstAired) episodeTuple) + private Episode GetExistingEpisode(IList existingEpisodes, IReadOnlyDictionary seasonCounts, (int SeasonNumber, int EpisodeNumber, DateTime FirstAired) episodeTuple) { var seasonNumber = episodeTuple.SeasonNumber; var episodeNumber = episodeTuple.EpisodeNumber; @@ -365,14 +356,20 @@ namespace MediaBrowser.Providers.TV { var episode = GetExistingEpisode(existingEpisodes, seasonNumber, episodeNumber); if (episode != null) + { return episode; + } seasonNumber--; if (seasonCounts.ContainsKey(seasonNumber)) + { episodeNumber += seasonCounts[seasonNumber]; + } else + { break; + } } return null; From e8189cd0f621abca473383423b91dd87c857a494 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Mon, 11 Feb 2019 19:19:26 +0100 Subject: [PATCH 21/39] Fix namespaces --- MediaBrowser.Providers/People/TvdbPersonImageProvider.cs | 1 + MediaBrowser.Providers/TV/MissingEpisodeProvider.cs | 1 - MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index 0f978d71fb..b3ffa3ca9a 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -12,6 +12,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Providers.TV; +using MediaBrowser.Providers.TV.TheTVDB; using Microsoft.Extensions.Logging; using TvDbSharper; diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 4952608480..81220db413 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -15,7 +15,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using MediaBrowser.Providers.TV.TheTVDB; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index b1144a2854..f4dc22a809 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging; using TvDbSharper; using TvDbSharper.Dto; -namespace MediaBrowser.Providers.TV +namespace MediaBrowser.Providers.TV.TheTVDB { /// From f5bda652c49819730bc76c71a453b47c00e0ef53 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Mon, 11 Feb 2019 20:33:44 +0100 Subject: [PATCH 22/39] Add support for searching for episodes by premiere date and fixed timezones --- .../TV/TheTVDB/TvDbClientManager.cs | 28 +++++++++++-------- .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 14 ++++++---- .../TV/TheTVDB/TvdbEpisodeProvider.cs | 4 +-- .../TV/TheTVDB/TvdbSeriesProvider.cs | 3 +- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 1bf4f6e03d..6d17d4b133 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -165,21 +165,27 @@ namespace MediaBrowser.Providers.TV { searchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var seriesTvdbId); - var episodeNumber = searchInfo.IndexNumber.Value; - var seasonNumber = searchInfo.ParentIndexNumber.Value; - return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeNumber, seasonNumber, cancellationToken); + var episodeQuery = new EpisodeQuery(); + + // Prefer SxE over premiere date as it is more robust + if (searchInfo.IndexNumber.HasValue && searchInfo.ParentIndexNumber.HasValue) + { + episodeQuery.AiredEpisode = searchInfo.IndexNumber.Value; + episodeQuery.AiredSeason = searchInfo.ParentIndexNumber.Value; + } + else if (searchInfo.PremiereDate.HasValue) + { + // tvdb expects yyyy-mm-dd format + episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd"); + } + + return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeQuery, cancellationToken); } - public async Task GetEpisodeTvdbId(int seriesTvdbId, int episodeNumber, int seasonNumber, CancellationToken cancellationToken) + public async Task GetEpisodeTvdbId(int seriesTvdbId, EpisodeQuery episodeQuery, CancellationToken cancellationToken) { - var episodeQuery = new EpisodeQuery - { - AiredSeason = seasonNumber, - AiredEpisode = episodeNumber - }; - var episodePage = await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), - episodeQuery, cancellationToken); + var episodePage = await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), episodeQuery, cancellationToken); return episodePage.Data.FirstOrDefault()?.Id.ToString(); } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 3a559727d7..49468531d5 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -60,15 +60,17 @@ namespace MediaBrowser.Providers.TV.TheTVDB { if (string.IsNullOrEmpty(episodeTvdbId)) { - var episodeNumber = episode.IndexNumber.Value; - var seasonNumber = episode.ParentIndexNumber.Value; - episodeTvdbId = await _tvDbClientManager.GetEpisodeTvdbId( - Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb)), episodeNumber, seasonNumber, - cancellationToken); + var episodeInfo = new EpisodeInfo + { + IndexNumber = episode.IndexNumber.Value, + ParentIndexNumber = episode.ParentIndexNumber.Value, + SeriesProviderIds = series.ProviderIds + }; + episodeTvdbId = await _tvDbClientManager.GetEpisodeTvdbId(episodeInfo, cancellationToken); if (string.IsNullOrEmpty(episodeTvdbId)) { _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", - seasonNumber, episodeNumber); + episodeInfo.ParentIndexNumber, episodeInfo.IndexNumber); return imageResult; } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index f4dc22a809..2a52b76ebd 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -36,7 +36,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB var list = new List(); // The search query must either provide an episode number or date - // TODO premieredate functionality is dead, could grab all episodes and search for it if (!searchInfo.IndexNumber.HasValue || !searchInfo.PremiereDate.HasValue) { return list; @@ -172,8 +171,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB if (DateTime.TryParse(episode.FirstAired, out var date)) { - date = date.ToUniversalTime(); - + // dates from tvdb are UTC but without offset or Z item.PremiereDate = date; item.ProductionYear = date.Year; } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 342591da0f..8e5616d15e 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -349,8 +349,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB if (DateTime.TryParse(tvdbSeries.FirstAired, out var date)) { - date = date.ToUniversalTime(); - + // dates from tvdb are UTC but without offset or Z series.PremiereDate = date; series.ProductionYear = date.Year; } From 350e7956402105fe2301c417f2e26f0a6abc0a80 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Mon, 11 Feb 2019 20:50:23 +0100 Subject: [PATCH 23/39] Fix namespace in tvdbclientmanager --- MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 6d17d4b133..44491e5113 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -10,7 +10,7 @@ using Microsoft.Extensions.Caching.Memory; using TvDbSharper; using TvDbSharper.Dto; -namespace MediaBrowser.Providers.TV +namespace MediaBrowser.Providers.TV.TheTVDB { // TODO add to DI once Bond's PR is merged public sealed class TvDbClientManager From e970d7a6aa87f007df77588f2749062717531bc9 Mon Sep 17 00:00:00 2001 From: Vasily Date: Tue, 12 Feb 2019 08:34:12 +0100 Subject: [PATCH 24/39] Update MediaBrowser.Providers/TV/MissingEpisodeProvider.cs Co-Authored-By: cvium --- MediaBrowser.Providers/TV/MissingEpisodeProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 81220db413..561cedb03c 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -151,7 +151,7 @@ namespace MediaBrowser.Providers.TV var airDate = tuple.FirstAired; - var now = DateTime.UtcNow.AddDays(0 - UnairedEpisodeThresholdDays); + var now = DateTime.UtcNow.AddDays(-UnairedEpisodeThresholdDays); if (airDate < now && addMissingEpisodes || airDate > now) { From 8029cd3ebb022817657e870e773ac4f1225e3b24 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Tue, 12 Feb 2019 19:50:19 +0100 Subject: [PATCH 25/39] Add better language support --- MediaBrowser.Controller/Library/TVUtils.cs | 10 ++ .../People/TvdbPersonImageProvider.cs | 2 +- .../TV/MissingEpisodeProvider.cs | 2 +- .../TV/TheTVDB/TvDbClientManager.cs | 108 +++++++++++++----- .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 6 +- .../TV/TheTVDB/TvdbEpisodeProvider.cs | 8 +- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 3 +- .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 3 +- .../TV/TheTVDB/TvdbSeriesProvider.cs | 34 ++---- 9 files changed, 108 insertions(+), 68 deletions(-) diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs index 0089f65d0d..355e97db15 100644 --- a/MediaBrowser.Controller/Library/TVUtils.cs +++ b/MediaBrowser.Controller/Library/TVUtils.cs @@ -17,6 +17,16 @@ namespace MediaBrowser.Controller.Library /// public static readonly string BannerUrl = TvdbBaseUrl + "banners/"; + public static string NormalizeLanguage(string language) + { + if (string.IsNullOrWhiteSpace(language)) + { + return null; + } + + // pt-br is just pt to tvdb + return language.Split('-')[0].ToLowerInvariant(); + } /// /// Gets the air days. /// diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index b3ffa3ca9a..86719fe830 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.People try { - var actorsResult = await _tvDbClientManager.GetActorsAsync(tvdbId, cancellationToken); + var actorsResult = await _tvDbClientManager.GetActorsAsync(tvdbId, series.GetPreferredMetadataLanguage(), cancellationToken); var actor = actorsResult.Data.FirstOrDefault(a => string.Equals(a.Name, personName, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(a.Image)); diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 561cedb03c..f05b0d6b83 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.TV return false; } - var episodes = await TvDbClientManager.Instance.GetAllEpisodesAsync(Convert.ToInt32(tvdbId), cancellationToken); + var episodes = await TvDbClientManager.Instance.GetAllEpisodesAsync(Convert.ToInt32(tvdbId), series.GetPreferredMetadataLanguage(), cancellationToken); var episodeLookup = episodes .Select(i => diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 44491e5113..9d63b83731 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; @@ -22,6 +23,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private static readonly object syncRoot = new object(); private static TvDbClient tvDbClient; private static DateTime tokenCreatedAt; + private const string DefaultLanguage = "en"; private TvDbClientManager() { @@ -70,32 +72,35 @@ namespace MediaBrowser.Providers.TV.TheTVDB tokenCreatedAt = DateTime.Now; } - // Default to English - tvDbClient.AcceptedLanguage = "en"; + return tvDbClient; } } - public Task> GetSeriesByNameAsync(string name, CancellationToken cancellationToken) + public Task> GetSeriesByNameAsync(string name, string language, + CancellationToken cancellationToken) { - return TryGetValue("series" + name,() => TvDbClient.Search.SearchSeriesByNameAsync(name, cancellationToken)); + return TryGetValue("series" + name, language,() => TvDbClient.Search.SearchSeriesByNameAsync(name, cancellationToken)); } - public Task> GetSeriesByIdAsync(int tvdbId, CancellationToken cancellationToken) + public Task> GetSeriesByIdAsync(int tvdbId, string language, + CancellationToken cancellationToken) { - return TryGetValue("series" + tvdbId,() => TvDbClient.Series.GetAsync(tvdbId, cancellationToken)); + return TryGetValue("series" + tvdbId, language,() => TvDbClient.Series.GetAsync(tvdbId, cancellationToken)); } - public Task> GetEpisodesAsync(int episodeTvdbId, CancellationToken cancellationToken) + public Task> GetEpisodesAsync(int episodeTvdbId, string language, + CancellationToken cancellationToken) { - return TryGetValue("episode" + episodeTvdbId,() => TvDbClient.Episodes.GetAsync(episodeTvdbId, cancellationToken)); + return TryGetValue("episode" + episodeTvdbId, language,() => TvDbClient.Episodes.GetAsync(episodeTvdbId, cancellationToken)); } - public async Task> GetAllEpisodesAsync(int tvdbId, CancellationToken cancellationToken) + public async Task> GetAllEpisodesAsync(int tvdbId, string language, + CancellationToken cancellationToken) { // Traverse all episode pages and join them together var episodes = new List(); - var episodePage = await GetEpisodesPageAsync(tvdbId, new EpisodeQuery(), cancellationToken); + var episodePage = await GetEpisodesPageAsync(tvdbId, new EpisodeQuery(), language, cancellationToken); episodes.AddRange(episodePage.Data); if (!episodePage.Links.Next.HasValue || !episodePage.Links.Last.HasValue) { @@ -107,45 +112,51 @@ namespace MediaBrowser.Providers.TV.TheTVDB for (var page = next; page <= last; ++page) { - episodePage = await GetEpisodesPageAsync(tvdbId, page, new EpisodeQuery(), cancellationToken); + episodePage = await GetEpisodesPageAsync(tvdbId, page, new EpisodeQuery(), language, cancellationToken); episodes.AddRange(episodePage.Data); } return episodes; } - public Task> GetSeriesByImdbIdAsync(string imdbId, CancellationToken cancellationToken) + public Task> GetSeriesByImdbIdAsync(string imdbId, string language, + CancellationToken cancellationToken) { - return TryGetValue("series" + imdbId,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(imdbId, cancellationToken)); + return TryGetValue("series" + imdbId, language,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(imdbId, cancellationToken)); } - public Task> GetSeriesByZap2ItIdAsync(string zap2ItId, CancellationToken cancellationToken) + public Task> GetSeriesByZap2ItIdAsync(string zap2ItId, string language, + CancellationToken cancellationToken) { - return TryGetValue("series" + zap2ItId,() => TvDbClient.Search.SearchSeriesByZap2ItIdAsync(zap2ItId, cancellationToken)); + return TryGetValue("series" + zap2ItId, language,() => TvDbClient.Search.SearchSeriesByZap2ItIdAsync(zap2ItId, cancellationToken)); } - public Task> GetActorsAsync(int tvdbId, CancellationToken cancellationToken) + public Task> GetActorsAsync(int tvdbId, string language, + CancellationToken cancellationToken) { - return TryGetValue("actors" + tvdbId,() => TvDbClient.Series.GetActorsAsync(tvdbId, cancellationToken)); + return TryGetValue("actors" + tvdbId, language,() => TvDbClient.Series.GetActorsAsync(tvdbId, cancellationToken)); } - public Task> GetImagesAsync(int tvdbId, ImagesQuery imageQuery, CancellationToken cancellationToken) + public Task> GetImagesAsync(int tvdbId, ImagesQuery imageQuery, string language, + CancellationToken cancellationToken) { var cacheKey = "images" + tvdbId + "keytype" + imageQuery.KeyType + "subkey" + imageQuery.SubKey; - return TryGetValue(cacheKey,() => TvDbClient.Series.GetImagesAsync(tvdbId, imageQuery, cancellationToken)); + return TryGetValue(cacheKey, language,() => TvDbClient.Series.GetImagesAsync(tvdbId, imageQuery, cancellationToken)); } public Task> GetLanguagesAsync(CancellationToken cancellationToken) { - return TryGetValue("languages",() => TvDbClient.Languages.GetAllAsync(cancellationToken)); + return TryGetValue("languages", null,() => TvDbClient.Languages.GetAllAsync(cancellationToken)); } - public Task> GetSeriesEpisodeSummaryAsync(int tvdbId, CancellationToken cancellationToken) + public Task> GetSeriesEpisodeSummaryAsync(int tvdbId, string language, + CancellationToken cancellationToken) { - return TryGetValue("seriesepisodesummary" + tvdbId, + return TryGetValue("seriesepisodesummary" + tvdbId, language, () => TvDbClient.Series.GetEpisodesSummaryAsync(tvdbId, cancellationToken)); } - public Task> GetEpisodesPageAsync(int tvdbId, int page, EpisodeQuery episodeQuery, CancellationToken cancellationToken) + public Task> GetEpisodesPageAsync(int tvdbId, int page, EpisodeQuery episodeQuery, + string language, CancellationToken cancellationToken) { // Not quite as dynamic as it could be var cacheKey = "episodespage" + tvdbId + "page" + page; @@ -157,11 +168,13 @@ namespace MediaBrowser.Providers.TV.TheTVDB { cacheKey += "airedepisode" + episodeQuery.AiredEpisode.Value; } - return TryGetValue(cacheKey, + + return TryGetValue(cacheKey, language, () => TvDbClient.Series.GetEpisodesAsync(tvdbId, page, episodeQuery, cancellationToken)); } - public Task GetEpisodeTvdbId(EpisodeInfo searchInfo, CancellationToken cancellationToken) + public Task GetEpisodeTvdbId(EpisodeInfo searchInfo, string language, + CancellationToken cancellationToken) { searchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var seriesTvdbId); @@ -180,21 +193,23 @@ namespace MediaBrowser.Providers.TV.TheTVDB episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd"); } - return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeQuery, cancellationToken); + return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeQuery, cancellationToken, language); } - public async Task GetEpisodeTvdbId(int seriesTvdbId, EpisodeQuery episodeQuery, CancellationToken cancellationToken) + public async Task GetEpisodeTvdbId(int seriesTvdbId, EpisodeQuery episodeQuery, + CancellationToken cancellationToken, string language) { - var episodePage = await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), episodeQuery, cancellationToken); + var episodePage = await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken); return episodePage.Data.FirstOrDefault()?.Id.ToString(); } - public Task> GetEpisodesPageAsync(int tvdbId, EpisodeQuery episodeQuery, CancellationToken cancellationToken) + public Task> GetEpisodesPageAsync(int tvdbId, EpisodeQuery episodeQuery, + string language, CancellationToken cancellationToken) { - return GetEpisodesPageAsync(tvdbId, 1, episodeQuery, cancellationToken); + return GetEpisodesPageAsync(tvdbId, 1, episodeQuery, language, cancellationToken); } - private async Task TryGetValue(object key, Func> resultFactory) + private async Task TryGetValue(string key, string language, Func> resultFactory) { if (_cache.TryGetValue(key, out T cachedValue)) { @@ -209,6 +224,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB return cachedValue; } + tvDbClient.AcceptedLanguage = TVUtils.NormalizeLanguage(language) ?? DefaultLanguage; var result = await resultFactory.Invoke(); _cache.Set(key, result, TimeSpan.FromHours(1)); return result; @@ -218,5 +234,35 @@ namespace MediaBrowser.Providers.TV.TheTVDB _cacheWriteLock.Release(); } } + + private static string GenerateKey(object[] objects) + { + var key = string.Empty; + + foreach (var obj in objects) + { + key += nameof(obj); + var objType = obj.GetType(); + if (objType.IsPrimitive || objType == typeof(string)) + { + key += obj.ToString(); + } + else + { + foreach (PropertyInfo propertyInfo in objType.GetProperties()) + { + var currentValue = propertyInfo.GetValue(obj, null); + if (currentValue == null) + { + continue; + } + + key += propertyInfo.Name + currentValue; + } + } + } + + return key; + } } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 49468531d5..0935b584a2 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB var episode = (Episode)item; var series = episode.Series; var imageResult = new List(); - + var language = item.GetPreferredMetadataLanguage(); if (series != null && TvdbSeriesProvider.IsValidSeries(series.ProviderIds)) { var episodeTvdbId = episode.GetProviderId(MetadataProviders.Tvdb); @@ -66,7 +66,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB ParentIndexNumber = episode.ParentIndexNumber.Value, SeriesProviderIds = series.ProviderIds }; - episodeTvdbId = await _tvDbClientManager.GetEpisodeTvdbId(episodeInfo, cancellationToken); + episodeTvdbId = await _tvDbClientManager.GetEpisodeTvdbId(episodeInfo, language, cancellationToken); if (string.IsNullOrEmpty(episodeTvdbId)) { _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", @@ -76,7 +76,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB } var episodeResult = - await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), cancellationToken); + await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), language, cancellationToken); var image = GetImageInfo(episodeResult.Data); if (image != null) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index 2a52b76ebd..10665135fa 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB var episodeTvdbId = searchInfo.GetProviderId(MetadataProviders.Tvdb); if (string.IsNullOrEmpty(episodeTvdbId)) { - episodeTvdbId = await _tvDbClientManager.GetEpisodeTvdbId(searchInfo, cancellationToken); + episodeTvdbId = await _tvDbClientManager.GetEpisodeTvdbId(searchInfo, searchInfo.MetadataLanguage, cancellationToken); if (string.IsNullOrEmpty(episodeTvdbId)) { _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", @@ -56,7 +56,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB return list; } } - var episodeResult = await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), cancellationToken); + var episodeResult = await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), searchInfo.MetadataLanguage, cancellationToken); var metadataResult = MapEpisodeToResult(searchInfo, episodeResult.Data); if (metadataResult.HasMetadata) @@ -102,7 +102,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB { if (string.IsNullOrEmpty(tvdbId)) { - tvdbId = await _tvDbClientManager.GetEpisodeTvdbId(searchInfo, cancellationToken); + tvdbId = await _tvDbClientManager.GetEpisodeTvdbId(searchInfo, searchInfo.MetadataLanguage, cancellationToken); if (string.IsNullOrEmpty(tvdbId)) { _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", @@ -112,7 +112,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB } var episodeResult = await _tvDbClientManager.GetEpisodesAsync( - Convert.ToInt32(tvdbId), + Convert.ToInt32(tvdbId), searchInfo.MetadataLanguage, cancellationToken); result = MapEpisodeToResult(searchInfo, episodeResult.Data); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index 9218e9c089..fdd7adc461 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -65,7 +65,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb)); var seasonNumber = season.IndexNumber.Value; var language = item.GetPreferredMetadataLanguage(); - _tvDbClientManager.TvDbClient.AcceptedLanguage = language; var remoteImages = new List(); var keyTypes = new[] {KeyType.Season, KeyType.Seasonwide, KeyType.Fanart}; @@ -78,7 +77,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB }; try { - var imageResults = await _tvDbClientManager.GetImagesAsync(tvdbId, imageQuery, cancellationToken); + var imageResults = await _tvDbClientManager.GetImagesAsync(tvdbId, imageQuery, language, cancellationToken); remoteImages.AddRange(GetImages(imageResults.Data, language)); } catch (TvDbServerException) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index b4b950be1e..66596f88db 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -58,7 +58,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB } var language = item.GetPreferredMetadataLanguage(); - _tvDbClientManager.TvDbClient.AcceptedLanguage = language; var remoteImages = new List(); var keyTypes = new[] {KeyType.Poster, KeyType.Series, KeyType.Fanart}; var tvdbId = Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb)); @@ -71,7 +70,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB try { var imageResults = - await _tvDbClientManager.GetImagesAsync(tvdbId, imageQuery, cancellationToken); + await _tvDbClientManager.GetImagesAsync(tvdbId, imageQuery, language, cancellationToken); remoteImages.AddRange(GetImages(imageResults.Data, language)); } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 8e5616d15e..4053cc488b 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -39,17 +39,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB _tvDbClientManager = TvDbClientManager.Instance; } - private string NormalizeLanguage(string language) - { - if (string.IsNullOrWhiteSpace(language)) - { - return language; - } - - // pt-br is just pt to tvdb - return language.Split('-')[0].ToLowerInvariant(); - } - public async Task> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken) { if (IsValidSeries(searchInfo.ProviderIds)) @@ -103,7 +92,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB private async Task FetchSeriesData(MetadataResult result, string metadataLanguage, Dictionary seriesProviderIds, CancellationToken cancellationToken) { - _tvDbClientManager.TvDbClient.AcceptedLanguage = NormalizeLanguage(metadataLanguage); var series = result.Item; if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(tvdbId)) @@ -126,8 +114,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB try { var seriesResult = - await _tvDbClientManager.GetSeriesByIdAsync(Convert.ToInt32(tvdbId), cancellationToken); - MapSeriesToResult(result, seriesResult.Data); + await _tvDbClientManager.GetSeriesByIdAsync(Convert.ToInt32(tvdbId), metadataLanguage, cancellationToken); + MapSeriesToResult(result, seriesResult.Data, metadataLanguage); } catch (TvDbServerException e) { @@ -141,7 +129,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB try { - var actorsResult = await _tvDbClientManager.GetActorsAsync(Convert.ToInt32(tvdbId), cancellationToken); + var actorsResult = await _tvDbClientManager.GetActorsAsync(Convert.ToInt32(tvdbId), metadataLanguage, cancellationToken); MapActorsToResult(result, actorsResult.Data); } catch (TvDbServerException e) @@ -152,18 +140,17 @@ namespace MediaBrowser.Providers.TV.TheTVDB private async Task GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken) { - _tvDbClientManager.TvDbClient.AcceptedLanguage = NormalizeLanguage(language); TvDbResponse result = null; try { if (string.Equals(idType, MetadataProviders.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase)) { - result = await _tvDbClientManager.GetSeriesByZap2ItIdAsync(id, cancellationToken); + result = await _tvDbClientManager.GetSeriesByZap2ItIdAsync(id, language, cancellationToken); } else { - result = await _tvDbClientManager.GetSeriesByImdbIdAsync(id, cancellationToken); + result = await _tvDbClientManager.GetSeriesByImdbIdAsync(id, language, cancellationToken); } } catch (TvDbServerException e) @@ -218,13 +205,12 @@ namespace MediaBrowser.Providers.TV.TheTVDB private async Task> FindSeriesInternal(string name, string language, CancellationToken cancellationToken) { - _tvDbClientManager.TvDbClient.AcceptedLanguage = NormalizeLanguage(language); var comparableName = GetComparableName(name); var list = new List, RemoteSearchResult>>(); TvDbResponse result; try { - result = await _tvDbClientManager.GetSeriesByNameAsync(comparableName, cancellationToken); + result = await _tvDbClientManager.GetSeriesByNameAsync(comparableName, language, cancellationToken); } catch (TvDbServerException e) { @@ -252,7 +238,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB try { var seriesSesult = - await _tvDbClientManager.GetSeriesByIdAsync(seriesSearchResult.Id, cancellationToken); + await _tvDbClientManager.GetSeriesByIdAsync(seriesSearchResult.Id, language, cancellationToken); remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSesult.Data.ImdbId); remoteSearchResult.SetProviderId(MetadataProviders.Zap2It, seriesSesult.Data.Zap2itId); } @@ -329,7 +315,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB return name.Trim(); } - private void MapSeriesToResult(MetadataResult result, TvDbSharper.Dto.Series tvdbSeries) + private void MapSeriesToResult(MetadataResult result, TvDbSharper.Dto.Series tvdbSeries, string metadataLanguage) { Series series = result.Item; series.SetProviderId(MetadataProviders.Tvdb, tvdbSeries.Id.ToString()); @@ -367,14 +353,14 @@ namespace MediaBrowser.Providers.TV.TheTVDB try { var episodeSummary = _tvDbClientManager - .GetSeriesEpisodeSummaryAsync(tvdbSeries.Id, CancellationToken.None).Result.Data; + .GetSeriesEpisodeSummaryAsync(tvdbSeries.Id, metadataLanguage, CancellationToken.None).Result.Data; var maxSeasonNumber = episodeSummary.AiredSeasons.Select(s => Convert.ToInt32(s)).Max(); var episodeQuery = new EpisodeQuery { AiredSeason = maxSeasonNumber }; var episodesPage = - _tvDbClientManager.GetEpisodesPageAsync(tvdbSeries.Id, episodeQuery, CancellationToken.None).Result.Data; + _tvDbClientManager.GetEpisodesPageAsync(tvdbSeries.Id, episodeQuery, metadataLanguage, CancellationToken.None).Result.Data; result.Item.EndDate = episodesPage.Select(e => { DateTime.TryParse(e.FirstAired, out var firstAired); From dfbf5fc9fa3209b0a6e76b3fcb60e13d15390c26 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Tue, 12 Feb 2019 21:04:45 +0100 Subject: [PATCH 26/39] Add generic key generation function --- .../TV/TheTVDB/TvDbClientManager.cs | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 9d63b83731..a3979bf422 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -80,19 +80,22 @@ namespace MediaBrowser.Providers.TV.TheTVDB public Task> GetSeriesByNameAsync(string name, string language, CancellationToken cancellationToken) { - return TryGetValue("series" + name, language,() => TvDbClient.Search.SearchSeriesByNameAsync(name, cancellationToken)); + var cacheKey = GenerateKey("series", name, language); + return TryGetValue(cacheKey, language,() => TvDbClient.Search.SearchSeriesByNameAsync(name, cancellationToken)); } public Task> GetSeriesByIdAsync(int tvdbId, string language, CancellationToken cancellationToken) { - return TryGetValue("series" + tvdbId, language,() => TvDbClient.Series.GetAsync(tvdbId, cancellationToken)); + var cacheKey = GenerateKey("series", tvdbId, language); + return TryGetValue(cacheKey, language,() => TvDbClient.Series.GetAsync(tvdbId, cancellationToken)); } public Task> GetEpisodesAsync(int episodeTvdbId, string language, CancellationToken cancellationToken) { - return TryGetValue("episode" + episodeTvdbId, language,() => TvDbClient.Episodes.GetAsync(episodeTvdbId, cancellationToken)); + var cacheKey = GenerateKey("episode", episodeTvdbId, language); + return TryGetValue(cacheKey, language,() => TvDbClient.Episodes.GetAsync(episodeTvdbId, cancellationToken)); } public async Task> GetAllEpisodesAsync(int tvdbId, string language, @@ -122,24 +125,27 @@ namespace MediaBrowser.Providers.TV.TheTVDB public Task> GetSeriesByImdbIdAsync(string imdbId, string language, CancellationToken cancellationToken) { - return TryGetValue("series" + imdbId, language,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(imdbId, cancellationToken)); + var cacheKey = GenerateKey("series", imdbId, language); + return TryGetValue(cacheKey, language,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(imdbId, cancellationToken)); } public Task> GetSeriesByZap2ItIdAsync(string zap2ItId, string language, CancellationToken cancellationToken) { - return TryGetValue("series" + zap2ItId, language,() => TvDbClient.Search.SearchSeriesByZap2ItIdAsync(zap2ItId, cancellationToken)); + var cacheKey = GenerateKey("series", zap2ItId, language); + return TryGetValue( cacheKey, language,() => TvDbClient.Search.SearchSeriesByZap2ItIdAsync(zap2ItId, cancellationToken)); } public Task> GetActorsAsync(int tvdbId, string language, CancellationToken cancellationToken) { - return TryGetValue("actors" + tvdbId, language,() => TvDbClient.Series.GetActorsAsync(tvdbId, cancellationToken)); + var cacheKey = GenerateKey("actors", tvdbId, language); + return TryGetValue(cacheKey, language,() => TvDbClient.Series.GetActorsAsync(tvdbId, cancellationToken)); } public Task> GetImagesAsync(int tvdbId, ImagesQuery imageQuery, string language, CancellationToken cancellationToken) { - var cacheKey = "images" + tvdbId + "keytype" + imageQuery.KeyType + "subkey" + imageQuery.SubKey; + var cacheKey = GenerateKey("images", tvdbId, language, imageQuery); return TryGetValue(cacheKey, language,() => TvDbClient.Series.GetImagesAsync(tvdbId, imageQuery, cancellationToken)); } @@ -151,23 +157,15 @@ namespace MediaBrowser.Providers.TV.TheTVDB public Task> GetSeriesEpisodeSummaryAsync(int tvdbId, string language, CancellationToken cancellationToken) { - return TryGetValue("seriesepisodesummary" + tvdbId, language, + var cacheKey = GenerateKey("seriesepisodesummary", tvdbId, language); + return TryGetValue(cacheKey, language, () => TvDbClient.Series.GetEpisodesSummaryAsync(tvdbId, cancellationToken)); } public Task> GetEpisodesPageAsync(int tvdbId, int page, EpisodeQuery episodeQuery, string language, CancellationToken cancellationToken) { - // Not quite as dynamic as it could be - var cacheKey = "episodespage" + tvdbId + "page" + page; - if (episodeQuery.AiredSeason.HasValue) - { - cacheKey += "airedseason" + episodeQuery.AiredSeason.Value; - } - if (episodeQuery.AiredEpisode.HasValue) - { - cacheKey += "airedepisode" + episodeQuery.AiredEpisode.Value; - } + var cacheKey = GenerateKey(language, tvdbId, episodeQuery); return TryGetValue(cacheKey, language, () => TvDbClient.Series.GetEpisodesAsync(tvdbId, page, episodeQuery, cancellationToken)); @@ -235,13 +233,12 @@ namespace MediaBrowser.Providers.TV.TheTVDB } } - private static string GenerateKey(object[] objects) + private static string GenerateKey(params object[] objects) { var key = string.Empty; foreach (var obj in objects) { - key += nameof(obj); var objType = obj.GetType(); if (objType.IsPrimitive || objType == typeof(string)) { From 828434058f47507216a11d296b9df4378ec0bd6b Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Tue, 12 Feb 2019 21:06:31 +0100 Subject: [PATCH 27/39] Use language supplied as argument instead of reading from TvDbClient --- MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 4053cc488b..e88093e42e 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -321,7 +321,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB series.SetProviderId(MetadataProviders.Tvdb, tvdbSeries.Id.ToString()); series.Name = tvdbSeries.SeriesName; series.Overview = (tvdbSeries.Overview ?? string.Empty).Trim(); - result.ResultLanguage = TvDbClientManager.Instance.TvDbClient.AcceptedLanguage; + result.ResultLanguage = metadataLanguage; series.AirDays = TVUtils.GetAirDays(tvdbSeries.AirsDayOfWeek); series.AirTime = tvdbSeries.AirsTime; From 5e2e190f3ed3ff108f906204ff9867235f37ef8e Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Tue, 12 Feb 2019 21:30:52 +0100 Subject: [PATCH 28/39] Add key delimiter to avoid potential clashes --- MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index a3979bf422..66f692bd00 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -242,7 +242,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB var objType = obj.GetType(); if (objType.IsPrimitive || objType == typeof(string)) { - key += obj.ToString(); + key += obj + ";"; } else { @@ -254,7 +254,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB continue; } - key += propertyInfo.Name + currentValue; + key += propertyInfo.Name + currentValue + ";"; } } } From 1aaa8de1f944817916847dbf8dc19a86cbd64cbd Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Tue, 12 Feb 2019 21:44:10 +0100 Subject: [PATCH 29/39] Use Task.WhenAll properly --- MediaBrowser.Providers/People/TvdbPersonImageProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index 86719fe830..1efd5005b0 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -65,9 +65,9 @@ namespace MediaBrowser.Providers.People .Where(i => TvdbSeriesProvider.IsValidSeries(i.ProviderIds)) .ToList(); - var infos = await Task.WhenAll(seriesWithPerson.Select(async i => await GetImageFromSeriesData(i, item.Name, cancellationToken)) + var infos = (await Task.WhenAll(seriesWithPerson.Select(async i => await GetImageFromSeriesData(i, item.Name, cancellationToken)))) .Where(i => i != null) - .Take(1)); + .Take(1); return infos; } From b9efcace7918d520576ad73e8aab4107091519b9 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 13 Feb 2019 11:21:56 +0100 Subject: [PATCH 30/39] Extract imagetype-to-keytype statements into a utility function and move tvdb specific utils to separate class --- MediaBrowser.Controller/Library/TVUtils.cs | 20 -------- .../People/TvdbPersonImageProvider.cs | 3 +- .../TV/TheTVDB/TvDbClientManager.cs | 6 +-- .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 5 +- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 19 ++------ .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 21 ++------- .../TV/TheTVDB/TvdbSeriesProvider.cs | 4 +- .../TV/TheTVDB/TvdbUtils.cs | 47 +++++++++++++++++++ 8 files changed, 60 insertions(+), 65 deletions(-) create mode 100644 MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs index 355e97db15..fd5fb6748f 100644 --- a/MediaBrowser.Controller/Library/TVUtils.cs +++ b/MediaBrowser.Controller/Library/TVUtils.cs @@ -7,26 +7,6 @@ namespace MediaBrowser.Controller.Library /// public static class TVUtils { - /// - /// The TVDB API key - /// - public static readonly string TvdbApiKey = "OG4V3YJ3FAP7FP2K"; - public static readonly string TvdbBaseUrl = "https://www.thetvdb.com/"; - /// - /// The banner URL - /// - public static readonly string BannerUrl = TvdbBaseUrl + "banners/"; - - public static string NormalizeLanguage(string language) - { - if (string.IsNullOrWhiteSpace(language)) - { - return null; - } - - // pt-br is just pt to tvdb - return language.Split('-')[0].ToLowerInvariant(); - } /// /// Gets the air days. /// diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index 1efd5005b0..7e13f7b30b 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -11,7 +11,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.TV; using MediaBrowser.Providers.TV.TheTVDB; using Microsoft.Extensions.Logging; using TvDbSharper; @@ -89,7 +88,7 @@ namespace MediaBrowser.Providers.People return new RemoteImageInfo { - Url = TVUtils.BannerUrl + actor.Image, + Url = TvdbUtils.BannerUrl + actor.Image, Type = ImageType.Primary, ProviderName = Name }; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 66f692bd00..b0843dcdcf 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private TvDbClientManager() { tvDbClient = new TvDbClient(); - tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey); + tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey); tokenCreatedAt = DateTime.Now; } @@ -67,7 +67,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB } catch { - tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey); + tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey); } tokenCreatedAt = DateTime.Now; @@ -222,7 +222,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB return cachedValue; } - tvDbClient.AcceptedLanguage = TVUtils.NormalizeLanguage(language) ?? DefaultLanguage; + tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage; var result = await resultFactory.Invoke(); _cache.Set(key, result, TimeSpan.FromHours(1)); return result; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 0935b584a2..93c52955b2 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -1,13 +1,10 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; @@ -105,7 +102,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB Width = Convert.ToInt32(episode.ThumbWidth), Height = Convert.ToInt32(episode.ThumbHeight), ProviderName = Name, - Url = TVUtils.BannerUrl + episode.Filename, + Url = TvdbUtils.BannerUrl + episode.Filename, Type = ImageType.Primary }; } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index fdd7adc461..1f5458e5cb 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; @@ -100,10 +99,10 @@ namespace MediaBrowser.Providers.TV.TheTVDB RatingType = RatingType.Score, CommunityRating = (double?)image.RatingsInfo.Average, VoteCount = image.RatingsInfo.Count, - Url = TVUtils.BannerUrl + image.FileName, + Url = TvdbUtils.BannerUrl + image.FileName, ProviderName = ProviderName, Language = languages.FirstOrDefault(lang => lang.Id == image.LanguageId)?.Abbreviation, - ThumbnailUrl = TVUtils.BannerUrl + image.Thumbnail + ThumbnailUrl = TvdbUtils.BannerUrl + image.Thumbnail }; var resolution = image.Resolution.Split('x'); @@ -113,19 +112,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB imageInfo.Height = Convert.ToInt32(resolution[1]); } - if (string.Equals(image.KeyType, "season", StringComparison.OrdinalIgnoreCase)) - { - imageInfo.Type = ImageType.Primary; - } - else if (string.Equals(image.KeyType, "seasonwide", StringComparison.OrdinalIgnoreCase)) - { - imageInfo.Type = ImageType.Banner; - } - else if (string.Equals(image.KeyType, "fanart", StringComparison.OrdinalIgnoreCase)) - { - imageInfo.Type = ImageType.Backdrop; - } - + imageInfo.Type = TvdbUtils.GetImageTypeFromKeyType(image.KeyType); list.Add(imageInfo); } var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index 66596f88db..a106d7cd3d 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; @@ -95,10 +93,10 @@ namespace MediaBrowser.Providers.TV.TheTVDB RatingType = RatingType.Score, CommunityRating = (double?)image.RatingsInfo.Average, VoteCount = image.RatingsInfo.Count, - Url = TVUtils.BannerUrl + image.FileName, + Url = TvdbUtils.BannerUrl + image.FileName, ProviderName = Name, Language = languages.FirstOrDefault(lang => lang.Id == image.LanguageId)?.Abbreviation, - ThumbnailUrl = TVUtils.BannerUrl + image.Thumbnail + ThumbnailUrl = TvdbUtils.BannerUrl + image.Thumbnail }; var resolution = image.Resolution.Split('x'); @@ -108,20 +106,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB imageInfo.Height = Convert.ToInt32(resolution[1]); } - - if (string.Equals(image.KeyType, "poster", StringComparison.OrdinalIgnoreCase)) - { - imageInfo.Type = ImageType.Primary; - } - else if (string.Equals(image.KeyType, "series", StringComparison.OrdinalIgnoreCase)) - { - imageInfo.Type = ImageType.Banner; - } - else if (string.Equals(image.KeyType, "fanart", StringComparison.OrdinalIgnoreCase)) - { - imageInfo.Type = ImageType.Backdrop; - } - + imageInfo.Type = TvdbUtils.GetImageTypeFromKeyType(image.KeyType); list.Add(imageInfo); } var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index e88093e42e..d51fbfb47b 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -232,7 +232,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB Name = tvdbTitles.FirstOrDefault(), ProductionYear = firstAired.Year, SearchProviderName = Name, - ImageUrl = TVUtils.BannerUrl + seriesSearchResult.Banner + ImageUrl = TvdbUtils.BannerUrl + seriesSearchResult.Banner }; try @@ -383,7 +383,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB Type = PersonType.Actor, Name = (actor.Name ?? string.Empty).Trim(), Role = actor.Role, - ImageUrl = TVUtils.BannerUrl + actor.Image, + ImageUrl = TvdbUtils.BannerUrl + actor.Image, SortOrder = actor.SortOrder }; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs new file mode 100644 index 0000000000..d253b76a46 --- /dev/null +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs @@ -0,0 +1,47 @@ +using System; +using System.ComponentModel; +using MediaBrowser.Model.Entities; +using TvDbSharper.Dto; + +namespace MediaBrowser.Providers.TV.TheTVDB +{ + /// + /// Class TVUtils + /// + public static class TvdbUtils + { + /// + /// The TVDB API key + /// + public static readonly string TvdbApiKey = "OG4V3YJ3FAP7FP2K"; + public static readonly string TvdbBaseUrl = "https://www.thetvdb.com/"; + /// + /// The banner URL + /// + public static readonly string BannerUrl = TvdbBaseUrl + "banners/"; + + public static ImageType GetImageTypeFromKeyType(string keyType) + { + switch (keyType.ToLowerInvariant()) + { + case "poster": + case "season": return ImageType.Primary; + case "series": + case "seasonwide": return ImageType.Banner; + case "fanart": return ImageType.Backdrop; + default: throw new ArgumentException($"Invalid or unknown keytype: {keyType}", nameof(keyType)); + } + } + + public static string NormalizeLanguage(string language) + { + if (string.IsNullOrWhiteSpace(language)) + { + return null; + } + + // pt-br is just pt to tvdb + return language.Split('-')[0].ToLowerInvariant(); + } + } +} From 98f003f71a0ef5c34247299bdc19b0257db580f4 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 13 Feb 2019 11:23:54 +0100 Subject: [PATCH 31/39] Review comments --- MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index b0843dcdcf..2d6038f53e 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -191,11 +191,12 @@ namespace MediaBrowser.Providers.TV.TheTVDB episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd"); } - return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeQuery, cancellationToken, language); + return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken); } public async Task GetEpisodeTvdbId(int seriesTvdbId, EpisodeQuery episodeQuery, - CancellationToken cancellationToken, string language) + string language, + CancellationToken cancellationToken) { var episodePage = await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken); return episodePage.Data.FirstOrDefault()?.Id.ToString(); @@ -254,7 +255,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB continue; } - key += propertyInfo.Name + currentValue + ";"; + key += propertyInfo.Name + "=" + currentValue + ";"; } } } From 5a054e5150f18ba6e23f463f024c305acb39585e Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 13 Feb 2019 12:20:13 +0100 Subject: [PATCH 32/39] Remove useless doc --- MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs index d253b76a46..8610908bdb 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs @@ -1,23 +1,12 @@ using System; using System.ComponentModel; using MediaBrowser.Model.Entities; -using TvDbSharper.Dto; - namespace MediaBrowser.Providers.TV.TheTVDB { - /// - /// Class TVUtils - /// public static class TvdbUtils { - /// - /// The TVDB API key - /// public static readonly string TvdbApiKey = "OG4V3YJ3FAP7FP2K"; public static readonly string TvdbBaseUrl = "https://www.thetvdb.com/"; - /// - /// The banner URL - /// public static readonly string BannerUrl = TvdbBaseUrl + "banners/"; public static ImageType GetImageTypeFromKeyType(string keyType) From ce51025e7cd27cab307dedd108eab6a1886757a3 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Fri, 15 Feb 2019 20:11:27 +0100 Subject: [PATCH 33/39] Use DI for TvDbClientManager --- .../ApplicationHost.cs | 6 ++- .../People/TvdbPersonImageProvider.cs | 4 +- .../TV/MissingEpisodeProvider.cs | 12 +++++- .../TV/SeriesMetadataService.cs | 20 +++++++++- .../TV/TheTVDB/TvDbClientManager.cs | 38 +++---------------- .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 4 +- .../TV/TheTVDB/TvdbEpisodeProvider.cs | 4 +- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 8 ++-- .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 6 +-- .../TV/TheTVDB/TvdbSeriesProvider.cs | 4 +- 10 files changed, 54 insertions(+), 52 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 784486d524..e5e38bbf36 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -102,8 +102,10 @@ using MediaBrowser.Model.Xml; using MediaBrowser.Providers.Chapters; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Subtitles; +using MediaBrowser.Providers.TV.TheTVDB; using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; using ServiceStack; @@ -625,12 +627,13 @@ namespace Emby.Server.Implementations /// protected async Task RegisterResources(IServiceCollection serviceCollection) { + serviceCollection.AddMemoryCache(options => options.SizeLimit = 1024); + serviceCollection.AddSingleton(ConfigurationManager); serviceCollection.AddSingleton(this); serviceCollection.AddSingleton(ApplicationPaths); - serviceCollection.AddSingleton(JsonSerializer); serviceCollection.AddSingleton(LoggerFactory); @@ -640,6 +643,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(EnvironmentInfo); serviceCollection.AddSingleton(FileSystemManager); + serviceCollection.AddSingleton(); HttpClient = CreateHttpClient(); serviceCollection.AddSingleton(HttpClient); diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index 7e13f7b30b..c6d349addc 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -24,12 +24,12 @@ namespace MediaBrowser.Providers.People private readonly ILibraryManager _libraryManager; private readonly TvDbClientManager _tvDbClientManager; - public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClient httpClient, ILogger logger) + public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) { _libraryManager = libraryManager; _httpClient = httpClient; _logger = logger; - _tvDbClientManager = TvDbClientManager.Instance; + _tvDbClientManager = tvDbClientManager; } public string Name => ProviderName; diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index f05b0d6b83..d24a7bbc71 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -27,17 +27,25 @@ namespace MediaBrowser.Providers.TV private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; private readonly IFileSystem _fileSystem; + private readonly TvDbClientManager _tvDbClientManager; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private const double UnairedEpisodeThresholdDays = 2; - public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem) + public MissingEpisodeProvider( + ILogger logger, + IServerConfigurationManager config, + ILibraryManager libraryManager, + ILocalizationManager localization, + IFileSystem fileSystem, + TvDbClientManager tvDbClientManager) { _logger = logger; _config = config; _libraryManager = libraryManager; _localization = localization; _fileSystem = fileSystem; + _tvDbClientManager = tvDbClientManager; } public async Task Run(Series series, bool addNewItems, CancellationToken cancellationToken) @@ -48,7 +56,7 @@ namespace MediaBrowser.Providers.TV return false; } - var episodes = await TvDbClientManager.Instance.GetAllEpisodesAsync(Convert.ToInt32(tvdbId), series.GetPreferredMetadataLanguage(), cancellationToken); + var episodes = await _tvDbClientManager.GetAllEpisodesAsync(Convert.ToInt32(tvdbId), series.GetPreferredMetadataLanguage(), cancellationToken); var episodeLookup = episodes .Select(i => diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index b0abd7c7cb..afbd838e4b 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -10,6 +10,7 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Xml; using MediaBrowser.Providers.Manager; +using MediaBrowser.Providers.TV.TheTVDB; using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.TV @@ -18,11 +19,24 @@ namespace MediaBrowser.Providers.TV { private readonly ILocalizationManager _localization; private readonly IXmlReaderSettingsFactory _xmlSettings; + private readonly TvDbClientManager _tvDbClientManager; - public SeriesMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager, ILocalizationManager localization, IXmlReaderSettingsFactory xmlSettings) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) + public SeriesMetadataService( + IServerConfigurationManager serverConfigurationManager, + ILogger logger, + IProviderManager providerManager, + IFileSystem fileSystem, + IUserDataManager userDataManager, + ILibraryManager libraryManager, + ILocalizationManager localization, + IXmlReaderSettingsFactory xmlSettings, + TvDbClientManager tvDbClientManager + ) + : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) { _localization = localization; _xmlSettings = xmlSettings; + _tvDbClientManager = tvDbClientManager; } protected override async Task AfterMetadataRefresh(Series item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) @@ -32,11 +46,13 @@ namespace MediaBrowser.Providers.TV var seasonProvider = new DummySeasonProvider(ServerConfigurationManager, Logger, _localization, LibraryManager, FileSystem); await seasonProvider.Run(item, cancellationToken).ConfigureAwait(false); + // TODO why does it not register this itself omg var provider = new MissingEpisodeProvider(Logger, ServerConfigurationManager, LibraryManager, _localization, - FileSystem); + FileSystem, + _tvDbClientManager); try { diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 2d6038f53e..21f09d8d11 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using Microsoft.Extensions.Caching.Memory; @@ -13,47 +12,22 @@ using TvDbSharper.Dto; namespace MediaBrowser.Providers.TV.TheTVDB { - // TODO add to DI once Bond's PR is merged - public sealed class TvDbClientManager + public class TvDbClientManager { - private static volatile TvDbClientManager instance; - // TODO add to DI once Bond's PR is merged private readonly SemaphoreSlim _cacheWriteLock = new SemaphoreSlim(1, 1); - private static MemoryCache _cache; - private static readonly object syncRoot = new object(); - private static TvDbClient tvDbClient; - private static DateTime tokenCreatedAt; + private readonly IMemoryCache _cache; + private readonly TvDbClient tvDbClient; + private DateTime tokenCreatedAt; private const string DefaultLanguage = "en"; - private TvDbClientManager() + public TvDbClientManager(IMemoryCache memoryCache) { + _cache = memoryCache; tvDbClient = new TvDbClient(); tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey); tokenCreatedAt = DateTime.Now; } - public static TvDbClientManager Instance - { - get - { - if (instance != null) - { - return instance; - } - - lock (syncRoot) - { - if (instance == null) - { - instance = new TvDbClientManager(); - _cache = new MemoryCache(new MemoryCacheOptions()); - } - } - - return instance; - } - } - public TvDbClient TvDbClient { get diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 93c52955b2..640cd4d089 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -20,11 +20,11 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; - public TvdbEpisodeImageProvider(IHttpClient httpClient, ILogger logger) + public TvdbEpisodeImageProvider(IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) { _httpClient = httpClient; _logger = logger; - _tvDbClientManager = TvDbClientManager.Instance; + _tvDbClientManager = tvDbClientManager; } public string Name => "TheTVDB"; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index 10665135fa..65fb52c4ac 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -24,11 +24,11 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; - public TvdbEpisodeProvider(IHttpClient httpClient, ILogger logger) + public TvdbEpisodeProvider(IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) { _httpClient = httpClient; _logger = logger; - _tvDbClientManager = TvDbClientManager.Instance; + _tvDbClientManager = tvDbClientManager; } public async Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index 1f5458e5cb..ee0388a703 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -25,11 +25,11 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; - public TvdbSeasonImageProvider(IHttpClient httpClient, ILogger logger) + public TvdbSeasonImageProvider(IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) { _httpClient = httpClient; _logger = logger; - _tvDbClientManager = TvDbClientManager.Instance; + _tvDbClientManager = tvDbClientManager; } public string Name => ProviderName; @@ -88,10 +88,10 @@ namespace MediaBrowser.Providers.TV.TheTVDB return remoteImages; } - private static IEnumerable GetImages(Image[] images, string preferredLanguage) + private IEnumerable GetImages(Image[] images, string preferredLanguage) { var list = new List(); - var languages = TvDbClientManager.Instance.GetLanguagesAsync(CancellationToken.None).Result.Data; + var languages = _tvDbClientManager.GetLanguagesAsync(CancellationToken.None).Result.Data; foreach (Image image in images) { var imageInfo = new RemoteImageInfo diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index a106d7cd3d..dc9f61756f 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -22,11 +22,11 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; - public TvdbSeriesImageProvider(IHttpClient httpClient, ILogger logger) + public TvdbSeriesImageProvider(IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) { _httpClient = httpClient; _logger = logger; - _tvDbClientManager = TvDbClientManager.Instance; + _tvDbClientManager = tvDbClientManager; } public string Name => ProviderName; @@ -84,7 +84,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private IEnumerable GetImages(Image[] images, string preferredLanguage) { var list = new List(); - var languages = TvDbClientManager.Instance.GetLanguagesAsync(CancellationToken.None).Result.Data; + var languages = _tvDbClientManager.GetLanguagesAsync(CancellationToken.None).Result.Data; foreach (Image image in images) { diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index d51fbfb47b..9210c8b536 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -29,14 +29,14 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILocalizationManager _localizationManager; private readonly TvDbClientManager _tvDbClientManager; - public TvdbSeriesProvider(IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, ILocalizationManager localizationManager) + public TvdbSeriesProvider(IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, ILocalizationManager localizationManager, TvDbClientManager tvDbClientManager) { _httpClient = httpClient; _logger = logger; _libraryManager = libraryManager; _localizationManager = localizationManager; Current = this; - _tvDbClientManager = TvDbClientManager.Instance; + _tvDbClientManager = tvDbClientManager; } public async Task> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken) From cb6d2cbd2dffa7854f4a151672be6f6187a95154 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Fri, 15 Feb 2019 22:02:17 +0100 Subject: [PATCH 34/39] Remove SizeLimit --- Emby.Server.Implementations/ApplicationHost.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index e5e38bbf36..432cd0beae 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -105,7 +105,6 @@ using MediaBrowser.Providers.Subtitles; using MediaBrowser.Providers.TV.TheTVDB; using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; -using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; using ServiceStack; @@ -627,7 +626,7 @@ namespace Emby.Server.Implementations /// protected async Task RegisterResources(IServiceCollection serviceCollection) { - serviceCollection.AddMemoryCache(options => options.SizeLimit = 1024); + serviceCollection.AddMemoryCache(); serviceCollection.AddSingleton(ConfigurationManager); serviceCollection.AddSingleton(this); From 1f30a50f4a361be303c9221d9d3e5c4d8db2b364 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 20 Feb 2019 11:03:04 +0100 Subject: [PATCH 35/39] Address review comments --- .../TV/MissingEpisodeProvider.cs | 34 +++++++++---------- .../TV/TheTVDB/TvDbClientManager.cs | 22 ++++++------ .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 5 +-- .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 2 +- 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index d24a7bbc71..0a2975e0f9 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -64,11 +64,11 @@ namespace MediaBrowser.Providers.TV DateTime.TryParse(i.FirstAired, out var firstAired); var seasonNumber = i.AiredSeason.GetValueOrDefault(-1); var episodeNumber = i.AiredEpisodeNumber.GetValueOrDefault(-1); - return (SeasonNumber: seasonNumber, EpisodeNumber: episodeNumber, firstAired: firstAired); + return (seasonNumber: seasonNumber, episodeNumber: episodeNumber, firstAired: firstAired); }) - .Where(i => i.SeasonNumber != -1 && i.EpisodeNumber != -1) - .OrderBy(i => i.SeasonNumber) - .ThenBy(i => i.EpisodeNumber) + .Where(i => i.seasonNumber != -1 && i.episodeNumber != -1) + .OrderBy(i => i.seasonNumber) + .ThenBy(i => i.episodeNumber) .ToList(); var allRecursiveChildren = series.GetRecursiveChildren(); @@ -133,18 +133,18 @@ namespace MediaBrowser.Providers.TV Series series, IEnumerable allItems, bool addMissingEpisodes, - IReadOnlyCollection<(int SeasonNumber, int Episodenumber, DateTime FirstAired)> episodeLookup, + IReadOnlyCollection<(int seasonNumber, int episodenumber, DateTime firstAired)> episodeLookup, CancellationToken cancellationToken) { var existingEpisodes = allItems.OfType().ToList(); - var seasonCounts = episodeLookup.GroupBy(e => e.SeasonNumber).ToDictionary(g => g.Key, g => g.Count()); + var seasonCounts = episodeLookup.GroupBy(e => e.seasonNumber).ToDictionary(g => g.Key, g => g.Count()); var hasChanges = false; foreach (var tuple in episodeLookup) { - if (tuple.SeasonNumber <= 0 || tuple.Episodenumber <= 0) + if (tuple.seasonNumber <= 0 || tuple.episodenumber <= 0) { // Ignore episode/season zeros continue; @@ -157,15 +157,15 @@ namespace MediaBrowser.Providers.TV continue; } - var airDate = tuple.FirstAired; + var airDate = tuple.firstAired; var now = DateTime.UtcNow.AddDays(-UnairedEpisodeThresholdDays); if (airDate < now && addMissingEpisodes || airDate > now) { // tvdb has a lot of nearly blank episodes - _logger.LogInformation("Creating virtual missing/unaired episode {0} {1}x{2}", series.Name, tuple.SeasonNumber, tuple.Episodenumber); - await AddEpisode(series, tuple.SeasonNumber, tuple.Episodenumber, cancellationToken).ConfigureAwait(false); + _logger.LogInformation("Creating virtual missing/unaired episode {0} {1}x{2}", series.Name, tuple.seasonNumber, tuple.episodenumber); + await AddEpisode(series, tuple.seasonNumber, tuple.episodenumber, cancellationToken).ConfigureAwait(false); hasChanges = true; } @@ -179,7 +179,7 @@ namespace MediaBrowser.Providers.TV /// private bool RemoveObsoleteOrMissingEpisodes( IEnumerable allRecursiveChildren, - IEnumerable<(int SeasonNumber, int EpisodeNumber, DateTime FirstAired)> episodeLookup, + IEnumerable<(int seasonNumber, int episodeNumber, DateTime firstAired)> episodeLookup, bool allowMissingEpisodes) { var existingEpisodes = allRecursiveChildren.OfType(); @@ -218,7 +218,7 @@ namespace MediaBrowser.Providers.TV } // If the episode no longer exists in the remote lookup, delete it - if (!episodeLookup.Any(e => e.SeasonNumber == seasonNumber && e.EpisodeNumber == episodeNumber)) + if (!episodeLookup.Any(e => e.seasonNumber == seasonNumber && e.episodeNumber == episodeNumber)) { return true; } @@ -252,7 +252,7 @@ namespace MediaBrowser.Providers.TV /// The episode lookup. /// Task{System.Boolean}. private bool RemoveObsoleteOrMissingSeasons(IList allRecursiveChildren, - IEnumerable<(int SeasonNumber, int EpisodeNumber, DateTime FirstAired)> episodeLookup) + IEnumerable<(int seasonNumber, int episodeNumber, DateTime firstAired)> episodeLookup) { var existingSeasons = allRecursiveChildren.OfType().ToList(); @@ -286,7 +286,7 @@ namespace MediaBrowser.Providers.TV } // If the season no longer exists in the remote lookup, delete it, but only if an existing episode doesn't require it - return episodeLookup.All(e => e.SeasonNumber != seasonNumber) && allEpisodes.All(s => s.ParentIndexNumber != seasonNumber || s.IsInSeasonFolder); + return episodeLookup.All(e => e.seasonNumber != seasonNumber) && allEpisodes.All(s => s.ParentIndexNumber != seasonNumber || s.IsInSeasonFolder); } // Season does not have a number @@ -354,10 +354,10 @@ namespace MediaBrowser.Providers.TV /// /// /// Episode. - private Episode GetExistingEpisode(IList existingEpisodes, IReadOnlyDictionary seasonCounts, (int SeasonNumber, int EpisodeNumber, DateTime FirstAired) episodeTuple) + private Episode GetExistingEpisode(IList existingEpisodes, IReadOnlyDictionary seasonCounts, (int seasonNumber, int episodeNumber, DateTime firstAired) episodeTuple) { - var seasonNumber = episodeTuple.SeasonNumber; - var episodeNumber = episodeTuple.EpisodeNumber; + var seasonNumber = episodeTuple.seasonNumber; + var episodeNumber = episodeTuple.episodeNumber; while (true) { diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 21f09d8d11..7e9c7a76e4 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -16,16 +16,16 @@ namespace MediaBrowser.Providers.TV.TheTVDB { private readonly SemaphoreSlim _cacheWriteLock = new SemaphoreSlim(1, 1); private readonly IMemoryCache _cache; - private readonly TvDbClient tvDbClient; - private DateTime tokenCreatedAt; + private readonly TvDbClient _tvDbClient; + private DateTime _tokenCreatedAt; private const string DefaultLanguage = "en"; public TvDbClientManager(IMemoryCache memoryCache) { _cache = memoryCache; - tvDbClient = new TvDbClient(); - tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey); - tokenCreatedAt = DateTime.Now; + _tvDbClient = new TvDbClient(); + _tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey); + _tokenCreatedAt = DateTime.Now; } public TvDbClient TvDbClient @@ -33,21 +33,21 @@ namespace MediaBrowser.Providers.TV.TheTVDB get { // Refresh if necessary - if (tokenCreatedAt > DateTime.Now.Subtract(TimeSpan.FromHours(20))) + if (_tokenCreatedAt > DateTime.Now.Subtract(TimeSpan.FromHours(20))) { try { - tvDbClient.Authentication.RefreshTokenAsync(); + _tvDbClient.Authentication.RefreshTokenAsync(); } catch { - tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey); + _tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey); } - tokenCreatedAt = DateTime.Now; + _tokenCreatedAt = DateTime.Now; } - return tvDbClient; + return _tvDbClient; } } @@ -197,7 +197,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB return cachedValue; } - tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage; + _tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage; var result = await resultFactory.Invoke(); _cache.Set(key, result, TimeSpan.FromHours(1)); return result; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index ee0388a703..fec0e1177c 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -19,8 +18,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB { public class TvdbSeasonImageProvider : IRemoteImageProvider, IHasOrder { - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - private readonly IHttpClient _httpClient; private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; @@ -66,7 +63,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB var language = item.GetPreferredMetadataLanguage(); var remoteImages = new List(); - var keyTypes = new[] {KeyType.Season, KeyType.Seasonwide, KeyType.Fanart}; + var keyTypes = new[] { KeyType.Season, KeyType.Seasonwide, KeyType.Fanart }; foreach (var keyType in keyTypes) { var imageQuery = new ImagesQuery diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index dc9f61756f..5137b4c2d5 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB var language = item.GetPreferredMetadataLanguage(); var remoteImages = new List(); - var keyTypes = new[] {KeyType.Poster, KeyType.Series, KeyType.Fanart}; + var keyTypes = new[] { KeyType.Poster, KeyType.Series, KeyType.Fanart }; var tvdbId = Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb)); foreach (KeyType keyType in keyTypes) { From 4a5c526ccc4038a8d8a63a2bf8eabf074427e37e Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 20 Feb 2019 19:08:47 +0100 Subject: [PATCH 36/39] Fix logging args and add ilogger with categoryname --- MediaBrowser.Providers/People/TvdbPersonImageProvider.cs | 2 +- .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 4 ++-- MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs | 8 +++++--- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 2 +- .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 2 +- MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index c6d349addc..061d5e5e9d 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Providers.People private readonly ILibraryManager _libraryManager; private readonly TvDbClientManager _tvDbClientManager; - public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) + public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) { _libraryManager = libraryManager; _httpClient = httpClient; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 640cd4d089..5b4084372c 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; - public TvdbEpisodeImageProvider(IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) + public TvdbEpisodeImageProvider(IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) { _httpClient = httpClient; _logger = logger; @@ -67,7 +67,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB if (string.IsNullOrEmpty(episodeTvdbId)) { _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", - episodeInfo.ParentIndexNumber, episodeInfo.IndexNumber); + episodeInfo.ParentIndexNumber, episodeInfo.IndexNumber, series.GetProviderId(MetadataProviders.Tvdb)); return imageResult; } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index 65fb52c4ac..5664204477 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; - public TvdbEpisodeProvider(IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) + public TvdbEpisodeProvider(IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) { _httpClient = httpClient; _logger = logger; @@ -48,11 +48,13 @@ namespace MediaBrowser.Providers.TV.TheTVDB var episodeTvdbId = searchInfo.GetProviderId(MetadataProviders.Tvdb); if (string.IsNullOrEmpty(episodeTvdbId)) { + searchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), + out var seriesTvdbId); episodeTvdbId = await _tvDbClientManager.GetEpisodeTvdbId(searchInfo, searchInfo.MetadataLanguage, cancellationToken); if (string.IsNullOrEmpty(episodeTvdbId)) { _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", - searchInfo.ParentIndexNumber, searchInfo.IndexNumber); + searchInfo.ParentIndexNumber, searchInfo.IndexNumber, seriesTvdbId); return list; } } @@ -124,7 +126,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB } else { - _logger.LogDebug("No series identity found for {0}", searchInfo.Name); + _logger.LogDebug("No series identity found for {EpisodeName}", searchInfo.Name); } return result; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index fec0e1177c..bb0fd51daa 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; - public TvdbSeasonImageProvider(IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) + public TvdbSeasonImageProvider(IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) { _httpClient = httpClient; _logger = logger; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index 5137b4c2d5..09cbb4d510 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILogger _logger; private readonly TvDbClientManager _tvDbClientManager; - public TvdbSeriesImageProvider(IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) + public TvdbSeriesImageProvider(IHttpClient httpClient, ILogger logger, TvDbClientManager tvDbClientManager) { _httpClient = httpClient; _logger = logger; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 9210c8b536..48b67a540f 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private readonly ILocalizationManager _localizationManager; private readonly TvDbClientManager _tvDbClientManager; - public TvdbSeriesProvider(IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, ILocalizationManager localizationManager, TvDbClientManager tvDbClientManager) + public TvdbSeriesProvider(IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, ILocalizationManager localizationManager, TvDbClientManager tvDbClientManager) { _httpClient = httpClient; _logger = logger; From 8af4417f8fd1d03892f8ed7b5d70f487b87f5fc7 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 20 Feb 2019 19:35:47 +0100 Subject: [PATCH 37/39] Add ConfigureAwait to awaited calls --- .../People/TvdbPersonImageProvider.cs | 8 +++-- .../TV/TheTVDB/TvDbClientManager.cs | 12 ++++--- .../TV/TheTVDB/TvdbEpisodeImageProvider.cs | 7 +++-- .../TV/TheTVDB/TvdbEpisodeProvider.cs | 14 ++++++--- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 3 +- .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 3 +- .../TV/TheTVDB/TvdbSeriesProvider.cs | 31 +++++++++++++------ 7 files changed, 54 insertions(+), 24 deletions(-) diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index 061d5e5e9d..8c8b99e89e 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -64,7 +64,9 @@ namespace MediaBrowser.Providers.People .Where(i => TvdbSeriesProvider.IsValidSeries(i.ProviderIds)) .ToList(); - var infos = (await Task.WhenAll(seriesWithPerson.Select(async i => await GetImageFromSeriesData(i, item.Name, cancellationToken)))) + var infos = (await Task.WhenAll(seriesWithPerson.Select(async i => + await GetImageFromSeriesData(i, item.Name, cancellationToken).ConfigureAwait(false))) + .ConfigureAwait(false)) .Where(i => i != null) .Take(1); @@ -77,7 +79,9 @@ namespace MediaBrowser.Providers.People try { - var actorsResult = await _tvDbClientManager.GetActorsAsync(tvdbId, series.GetPreferredMetadataLanguage(), cancellationToken); + var actorsResult = await _tvDbClientManager + .GetActorsAsync(tvdbId, series.GetPreferredMetadataLanguage(), cancellationToken) + .ConfigureAwait(false); var actor = actorsResult.Data.FirstOrDefault(a => string.Equals(a.Name, personName, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(a.Image)); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 7e9c7a76e4..efb8a0fe8f 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -77,7 +77,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB { // Traverse all episode pages and join them together var episodes = new List(); - var episodePage = await GetEpisodesPageAsync(tvdbId, new EpisodeQuery(), language, cancellationToken); + var episodePage = await GetEpisodesPageAsync(tvdbId, new EpisodeQuery(), language, cancellationToken) + .ConfigureAwait(false); episodes.AddRange(episodePage.Data); if (!episodePage.Links.Next.HasValue || !episodePage.Links.Last.HasValue) { @@ -89,7 +90,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB for (var page = next; page <= last; ++page) { - episodePage = await GetEpisodesPageAsync(tvdbId, page, new EpisodeQuery(), language, cancellationToken); + episodePage = await GetEpisodesPageAsync(tvdbId, page, new EpisodeQuery(), language, cancellationToken) + .ConfigureAwait(false); episodes.AddRange(episodePage.Data); } @@ -172,7 +174,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB string language, CancellationToken cancellationToken) { - var episodePage = await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken); + var episodePage = + await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken) + .ConfigureAwait(false); return episodePage.Data.FirstOrDefault()?.Id.ToString(); } @@ -198,7 +202,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB } _tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage; - var result = await resultFactory.Invoke(); + var result = await resultFactory.Invoke().ConfigureAwait(false); _cache.Set(key, result, TimeSpan.FromHours(1)); return result; } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 5b4084372c..c04e98e640 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -63,7 +63,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB ParentIndexNumber = episode.ParentIndexNumber.Value, SeriesProviderIds = series.ProviderIds }; - episodeTvdbId = await _tvDbClientManager.GetEpisodeTvdbId(episodeInfo, language, cancellationToken); + episodeTvdbId = await _tvDbClientManager + .GetEpisodeTvdbId(episodeInfo, language, cancellationToken).ConfigureAwait(false); if (string.IsNullOrEmpty(episodeTvdbId)) { _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", @@ -73,7 +74,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB } var episodeResult = - await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), language, cancellationToken); + await _tvDbClientManager + .GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), language, cancellationToken) + .ConfigureAwait(false); var image = GetImageInfo(episodeResult.Data); if (image != null) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index 5664204477..b256f26675 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -50,7 +50,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB { searchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var seriesTvdbId); - episodeTvdbId = await _tvDbClientManager.GetEpisodeTvdbId(searchInfo, searchInfo.MetadataLanguage, cancellationToken); + episodeTvdbId = await _tvDbClientManager + .GetEpisodeTvdbId(searchInfo, searchInfo.MetadataLanguage, cancellationToken) + .ConfigureAwait(false); if (string.IsNullOrEmpty(episodeTvdbId)) { _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", @@ -58,7 +60,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB return list; } } - var episodeResult = await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), searchInfo.MetadataLanguage, cancellationToken); + + var episodeResult = await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), + searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false); var metadataResult = MapEpisodeToResult(searchInfo, episodeResult.Data); if (metadataResult.HasMetadata) @@ -104,7 +108,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB { if (string.IsNullOrEmpty(tvdbId)) { - tvdbId = await _tvDbClientManager.GetEpisodeTvdbId(searchInfo, searchInfo.MetadataLanguage, cancellationToken); + tvdbId = await _tvDbClientManager + .GetEpisodeTvdbId(searchInfo, searchInfo.MetadataLanguage, cancellationToken) + .ConfigureAwait(false); if (string.IsNullOrEmpty(tvdbId)) { _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", @@ -115,7 +121,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB var episodeResult = await _tvDbClientManager.GetEpisodesAsync( Convert.ToInt32(tvdbId), searchInfo.MetadataLanguage, - cancellationToken); + cancellationToken).ConfigureAwait(false); result = MapEpisodeToResult(searchInfo, episodeResult.Data); } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index bb0fd51daa..94ca603f20 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -73,7 +73,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB }; try { - var imageResults = await _tvDbClientManager.GetImagesAsync(tvdbId, imageQuery, language, cancellationToken); + var imageResults = await _tvDbClientManager + .GetImagesAsync(tvdbId, imageQuery, language, cancellationToken).ConfigureAwait(false); remoteImages.AddRange(GetImages(imageResults.Data, language)); } catch (TvDbServerException) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index 09cbb4d510..a38440dac0 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -68,7 +68,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB try { var imageResults = - await _tvDbClientManager.GetImagesAsync(tvdbId, imageQuery, language, cancellationToken); + await _tvDbClientManager.GetImagesAsync(tvdbId, imageQuery, language, cancellationToken) + .ConfigureAwait(false); remoteImages.AddRange(GetImages(imageResults.Data, language)); } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 48b67a540f..9c24e4c987 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -84,7 +84,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB result.Item = new Series(); result.HasMetadata = true; - await FetchSeriesData(result, itemId.MetadataLanguage, itemId.ProviderIds, cancellationToken); + await FetchSeriesData(result, itemId.MetadataLanguage, itemId.ProviderIds, cancellationToken) + .ConfigureAwait(false); } return result; @@ -102,19 +103,23 @@ namespace MediaBrowser.Providers.TV.TheTVDB if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out var imdbId) && !string.IsNullOrEmpty(imdbId)) { series.SetProviderId(MetadataProviders.Imdb, imdbId); - tvdbId = await GetSeriesByRemoteId(imdbId, MetadataProviders.Imdb.ToString(), metadataLanguage, cancellationToken); + tvdbId = await GetSeriesByRemoteId(imdbId, MetadataProviders.Imdb.ToString(), metadataLanguage, + cancellationToken).ConfigureAwait(false); } if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out var zap2It) && !string.IsNullOrEmpty(zap2It)) { series.SetProviderId(MetadataProviders.Zap2It, zap2It); - tvdbId = await GetSeriesByRemoteId(zap2It, MetadataProviders.Zap2It.ToString(), metadataLanguage, cancellationToken); + tvdbId = await GetSeriesByRemoteId(zap2It, MetadataProviders.Zap2It.ToString(), metadataLanguage, + cancellationToken).ConfigureAwait(false); } try { var seriesResult = - await _tvDbClientManager.GetSeriesByIdAsync(Convert.ToInt32(tvdbId), metadataLanguage, cancellationToken); + await _tvDbClientManager + .GetSeriesByIdAsync(Convert.ToInt32(tvdbId), metadataLanguage, cancellationToken) + .ConfigureAwait(false); MapSeriesToResult(result, seriesResult.Data, metadataLanguage); } catch (TvDbServerException e) @@ -129,7 +134,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB try { - var actorsResult = await _tvDbClientManager.GetActorsAsync(Convert.ToInt32(tvdbId), metadataLanguage, cancellationToken); + var actorsResult = await _tvDbClientManager + .GetActorsAsync(Convert.ToInt32(tvdbId), metadataLanguage, cancellationToken).ConfigureAwait(false); MapActorsToResult(result, actorsResult.Data); } catch (TvDbServerException e) @@ -146,11 +152,13 @@ namespace MediaBrowser.Providers.TV.TheTVDB { if (string.Equals(idType, MetadataProviders.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase)) { - result = await _tvDbClientManager.GetSeriesByZap2ItIdAsync(id, language, cancellationToken); + result = await _tvDbClientManager.GetSeriesByZap2ItIdAsync(id, language, cancellationToken) + .ConfigureAwait(false); } else { - result = await _tvDbClientManager.GetSeriesByImdbIdAsync(id, language, cancellationToken); + result = await _tvDbClientManager.GetSeriesByImdbIdAsync(id, language, cancellationToken) + .ConfigureAwait(false); } } catch (TvDbServerException e) @@ -210,7 +218,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB TvDbResponse result; try { - result = await _tvDbClientManager.GetSeriesByNameAsync(comparableName, language, cancellationToken); + result = await _tvDbClientManager.GetSeriesByNameAsync(comparableName, language, cancellationToken) + .ConfigureAwait(false); } catch (TvDbServerException e) { @@ -238,7 +247,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB try { var seriesSesult = - await _tvDbClientManager.GetSeriesByIdAsync(seriesSearchResult.Id, language, cancellationToken); + await _tvDbClientManager.GetSeriesByIdAsync(seriesSearchResult.Id, language, cancellationToken) + .ConfigureAwait(false); remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSesult.Data.ImdbId); remoteSearchResult.SetProviderId(MetadataProviders.Zap2It, seriesSesult.Data.Zap2itId); } @@ -403,7 +413,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB return; } - var srch = await FindSeries(info.Name, info.Year, info.MetadataLanguage, CancellationToken.None).ConfigureAwait(false); + var srch = await FindSeries(info.Name, info.Year, info.MetadataLanguage, CancellationToken.None) + .ConfigureAwait(false); var entry = srch.FirstOrDefault(); From 3965f90236f8add9bca8d507aeba92675948b7f8 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 20 Feb 2019 19:36:33 +0100 Subject: [PATCH 38/39] Update MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs Co-Authored-By: cvium --- MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index a38440dac0..365f49fb75 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -52,7 +52,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB { if (!TvdbSeriesProvider.IsValidSeries(item.ProviderIds)) { - return new RemoteImageInfo[] { }; + return Array.Empty(); } var language = item.GetPreferredMetadataLanguage(); From c597f0de35f64f5886ebe717742897aaf63e9560 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 20 Feb 2019 19:52:50 +0100 Subject: [PATCH 39/39] Make Tvdb strings const --- MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs | 6 +++--- MediaBrowser.Providers/TV/TvExternalIds.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs index 8610908bdb..112cbf800a 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs @@ -5,9 +5,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB { public static class TvdbUtils { - public static readonly string TvdbApiKey = "OG4V3YJ3FAP7FP2K"; - public static readonly string TvdbBaseUrl = "https://www.thetvdb.com/"; - public static readonly string BannerUrl = TvdbBaseUrl + "banners/"; + public const string TvdbApiKey = "OG4V3YJ3FAP7FP2K"; + public const string TvdbBaseUrl = "https://www.thetvdb.com/"; + public const string BannerUrl = TvdbBaseUrl + "banners/"; public static ImageType GetImageTypeFromKeyType(string keyType) { diff --git a/MediaBrowser.Providers/TV/TvExternalIds.cs b/MediaBrowser.Providers/TV/TvExternalIds.cs index f50ebc1afd..3f889fbbec 100644 --- a/MediaBrowser.Providers/TV/TvExternalIds.cs +++ b/MediaBrowser.Providers/TV/TvExternalIds.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.TV public string Key => MetadataProviders.Tvdb.ToString(); - public string UrlFormatString => "https://thetvdb.com/?tab=series&id={0}"; + public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=series&id={0}"; public bool Supports(IHasProviderIds item) { @@ -53,7 +53,7 @@ namespace MediaBrowser.Providers.TV public string Key => MetadataProviders.Tvdb.ToString(); - public string UrlFormatString => "https://thetvdb.com/?tab=episode&id={0}"; + public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=episode&id={0}"; public bool Supports(IHasProviderIds item) {