diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index b79d18abd8..0ab721b77d 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -402,8 +402,6 @@ namespace MediaBrowser.Model.Configuration /// public bool RequireHttps { get; set; } = false; - public bool EnableNewOmdbSupport { get; set; } = true; - /// /// Gets or sets the filter for remote IP connectivity. Used in conjuntion with . /// diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs index 4102170986..d8b33a799f 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -17,24 +16,17 @@ namespace MediaBrowser.Providers.Plugins.Omdb { public class OmdbEpisodeProvider : IRemoteMetadataProvider, IHasOrder { - private readonly IHttpClientFactory _httpClientFactory; private readonly OmdbItemProvider _itemProvider; - private readonly IFileSystem _fileSystem; - private readonly IServerConfigurationManager _configurationManager; - private readonly IApplicationHost _appHost; + private readonly OmdbProvider _omdbProvider; public OmdbEpisodeProvider( - IApplicationHost appHost, IHttpClientFactory httpClientFactory, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { - _httpClientFactory = httpClientFactory; - _fileSystem = fileSystem; - _configurationManager = configurationManager; - _appHost = appHost; - _itemProvider = new OmdbItemProvider(_appHost, httpClientFactory, libraryManager, fileSystem, configurationManager); + _itemProvider = new OmdbItemProvider(httpClientFactory, libraryManager, fileSystem, configurationManager); + _omdbProvider = new OmdbProvider(httpClientFactory, fileSystem, configurationManager); } // After TheTvDb @@ -44,12 +36,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb public Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) { - return _itemProvider.GetSearchResults(searchInfo, "episode", cancellationToken); + return _itemProvider.GetSearchResults(searchInfo, cancellationToken); } public async Task> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken) { - var result = new MetadataResult() + var result = new MetadataResult { Item = new Episode(), QueriedById = true @@ -61,13 +53,20 @@ namespace MediaBrowser.Providers.Plugins.Omdb return result; } - if (info.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string? seriesImdbId) && !string.IsNullOrEmpty(seriesImdbId)) + if (info.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string? seriesImdbId) + && !string.IsNullOrEmpty(seriesImdbId) + && info.IndexNumber.HasValue + && info.ParentIndexNumber.HasValue) { - if (info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue) - { - result.HasMetadata = await new OmdbProvider(_httpClientFactory, _fileSystem, _configurationManager) - .FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProvider.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); - } + result.HasMetadata = await _omdbProvider.FetchEpisodeData( + result, + info.IndexNumber.Value, + info.ParentIndexNumber.Value, + info.GetProviderId(MetadataProvider.Imdb), + seriesImdbId, + info.MetadataLanguage, + info.MetadataCountryCode, + cancellationToken).ConfigureAwait(false); } return result; diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs index 0a72083497..4c3fc23b2b 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs @@ -2,8 +2,9 @@ #pragma warning disable CS1591 +using System; using System.Collections.Generic; -using System.Globalization; +using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -22,14 +23,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb public class OmdbImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClientFactory _httpClientFactory; - private readonly IFileSystem _fileSystem; - private readonly IServerConfigurationManager _configurationManager; + private readonly OmdbProvider _omdbProvider; public OmdbImageProvider(IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { _httpClientFactory = httpClientFactory; - _fileSystem = fileSystem; - _configurationManager = configurationManager; + _omdbProvider = new OmdbProvider(_httpClientFactory, fileSystem, configurationManager); } public string Name => "The Open Movie Database"; @@ -49,38 +48,27 @@ namespace MediaBrowser.Providers.Plugins.Omdb public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { var imdbId = item.GetProviderId(MetadataProvider.Imdb); + if (string.IsNullOrWhiteSpace(imdbId)) + { + return Enumerable.Empty(); + } - var list = new List(); - - var provider = new OmdbProvider(_httpClientFactory, _fileSystem, _configurationManager); + var rootObject = await _omdbProvider.GetRootObject(imdbId, cancellationToken).ConfigureAwait(false); - if (!string.IsNullOrWhiteSpace(imdbId)) + if (string.IsNullOrEmpty(rootObject.Poster)) { - var rootObject = await provider.GetRootObject(imdbId, cancellationToken).ConfigureAwait(false); + return Enumerable.Empty(); + } - if (!string.IsNullOrEmpty(rootObject.Poster)) + // the poster url is sometimes higher quality than the poster api + return new[] + { + new RemoteImageInfo { - if (item is Episode) - { - // img.omdbapi.com is returning 404's - list.Add(new RemoteImageInfo - { - ProviderName = Name, - Url = rootObject.Poster - }); - } - else - { - list.Add(new RemoteImageInfo - { - ProviderName = Name, - Url = string.Format(CultureInfo.InvariantCulture, "https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId) - }); - } + ProviderName = Name, + Url = rootObject.Poster } - } - - return list; + }; } public Task GetImageResponse(string url, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs index 35bc3ce6be..e5753b2b59 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs @@ -8,11 +8,11 @@ using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; +using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Jellyfin.Extensions.Json; -using MediaBrowser.Common; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -31,13 +31,10 @@ namespace MediaBrowser.Providers.Plugins.Omdb { private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; - private readonly IFileSystem _fileSystem; - private readonly IServerConfigurationManager _configurationManager; - private readonly IApplicationHost _appHost; private readonly JsonSerializerOptions _jsonOptions; + private readonly OmdbProvider _omdbProvider; public OmdbItemProvider( - IApplicationHost appHost, IHttpClientFactory httpClientFactory, ILibraryManager libraryManager, IFileSystem fileSystem, @@ -45,9 +42,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb { _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; - _fileSystem = fileSystem; - _configurationManager = configurationManager; - _appHost = appHost; + _omdbProvider = new OmdbProvider(_httpClientFactory, fileSystem, configurationManager); _jsonOptions = new JsonSerializerOptions(JsonDefaults.Options); _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStringConverter()); @@ -59,185 +54,166 @@ namespace MediaBrowser.Providers.Plugins.Omdb // After primary option public int Order => 2; + public Task> GetSearchResults(TrailerInfo searchInfo, CancellationToken cancellationToken) + { + return GetSearchResultsInternal(searchInfo, true, cancellationToken); + } + public Task> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken) { - return GetSearchResults(searchInfo, "series", cancellationToken); + return GetSearchResultsInternal(searchInfo, true, cancellationToken); } public Task> GetSearchResults(MovieInfo searchInfo, CancellationToken cancellationToken) { - return GetSearchResults(searchInfo, "movie", cancellationToken); + return GetSearchResultsInternal(searchInfo, true, cancellationToken); } - public Task> GetSearchResults(ItemLookupInfo searchInfo, string type, CancellationToken cancellationToken) + public Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) { - return GetSearchResultsInternal(searchInfo, type, true, cancellationToken); + return GetSearchResultsInternal(searchInfo, true, cancellationToken); } - private async Task> GetSearchResultsInternal(ItemLookupInfo searchInfo, string type, bool isSearch, CancellationToken cancellationToken) + private async Task> GetSearchResultsInternal(ItemLookupInfo searchInfo, bool isSearch, CancellationToken cancellationToken) { + var type = searchInfo switch + { + EpisodeInfo => "episode", + SeriesInfo => "series", + _ => "movie" + }; + + // This is a bit hacky? var episodeSearchInfo = searchInfo as EpisodeInfo; + var indexNumberEnd = episodeSearchInfo?.IndexNumberEnd; var imdbId = searchInfo.GetProviderId(MetadataProvider.Imdb); - var urlQuery = "plot=full&r=json"; - if (type == "episode" && episodeSearchInfo != null) + var urlQuery = new StringBuilder("plot=full&r=json"); + if (episodeSearchInfo != null) { episodeSearchInfo.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out imdbId); - } - - var name = searchInfo.Name; - var year = searchInfo.Year; + if (searchInfo.IndexNumber.HasValue) + { + urlQuery.Append("&Episode=").Append(searchInfo.IndexNumber.Value); + } - if (!string.IsNullOrWhiteSpace(name)) - { - var parsedName = _libraryManager.ParseName(name); - var yearInName = parsedName.Year; - name = parsedName.Name; - year ??= yearInName; + if (searchInfo.ParentIndexNumber.HasValue) + { + urlQuery.Append("&Season=").Append(searchInfo.ParentIndexNumber.Value); + } } if (string.IsNullOrWhiteSpace(imdbId)) { - if (year.HasValue) + var name = searchInfo.Name; + var year = searchInfo.Year; + if (!string.IsNullOrWhiteSpace(name)) { - urlQuery += "&y=" + year.Value.ToString(CultureInfo.InvariantCulture); + var parsedName = _libraryManager.ParseName(name); + var yearInName = parsedName.Year; + name = parsedName.Name; + year ??= yearInName; } - // &s means search and returns a list of results as opposed to t - if (isSearch) - { - urlQuery += "&s=" + WebUtility.UrlEncode(name); - } - else + if (year.HasValue) { - urlQuery += "&t=" + WebUtility.UrlEncode(name); + urlQuery.Append("&y=").Append(year); } - urlQuery += "&type=" + type; + // &s means search and returns a list of results as opposed to t + urlQuery.Append(isSearch ? "&s=" : "&t="); + urlQuery.Append(WebUtility.UrlEncode(name)); + urlQuery.Append("&type=") + .Append(type); } else { - urlQuery += "&i=" + imdbId; + urlQuery.Append("&i=") + .Append(imdbId); isSearch = false; } - if (type == "episode") - { - if (searchInfo.IndexNumber.HasValue) - { - urlQuery += string.Format(CultureInfo.InvariantCulture, "&Episode={0}", searchInfo.IndexNumber); - } - - if (searchInfo.ParentIndexNumber.HasValue) - { - urlQuery += string.Format(CultureInfo.InvariantCulture, "&Season={0}", searchInfo.ParentIndexNumber); - } - } - - var url = OmdbProvider.GetOmdbUrl(urlQuery); + var url = OmdbProvider.GetOmdbUrl(urlQuery.ToString()); - using var response = await OmdbProvider.GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false); + using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - var resultList = new List(); if (isSearch) { var searchResultList = await JsonSerializer.DeserializeAsync(stream, _jsonOptions, cancellationToken).ConfigureAwait(false); - if (searchResultList != null && searchResultList.Search != null) + if (searchResultList?.Search != null) { - resultList.AddRange(searchResultList.Search); + var resultCount = searchResultList.Search.Count; + var result = new RemoteSearchResult[resultCount]; + for (var i = 0; i < resultCount; i++) + { + result[i] = ResultToMetadataResult(searchResultList.Search[i], searchInfo, indexNumberEnd); + } + + return result; } } else { var result = await JsonSerializer.DeserializeAsync(stream, _jsonOptions, cancellationToken).ConfigureAwait(false); - if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(result?.Response, "true", StringComparison.OrdinalIgnoreCase)) { - resultList.Add(result); + return new[] { ResultToMetadataResult(result, searchInfo, indexNumberEnd) }; } } - return resultList.Select(result => - { - var item = new RemoteSearchResult - { - IndexNumber = searchInfo.IndexNumber, - Name = result.Title, - ParentIndexNumber = searchInfo.ParentIndexNumber, - SearchProviderName = Name - }; - - if (episodeSearchInfo != null && episodeSearchInfo.IndexNumberEnd.HasValue) - { - item.IndexNumberEnd = episodeSearchInfo.IndexNumberEnd.Value; - } - - item.SetProviderId(MetadataProvider.Imdb, result.imdbID); - - if (result.Year.Length > 0 - && int.TryParse(result.Year.AsSpan().Slice(0, Math.Min(result.Year.Length, 4)), NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedYear)) - { - item.ProductionYear = parsedYear; - } - - if (!string.IsNullOrEmpty(result.Released) - && DateTime.TryParse(result.Released, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var released)) - { - item.PremiereDate = released; - } - - if (!string.IsNullOrWhiteSpace(result.Poster) && !string.Equals(result.Poster, "N/A", StringComparison.OrdinalIgnoreCase)) - { - item.ImageUrl = result.Poster; - } - - return item; - }); + return Enumerable.Empty(); } public Task> GetMetadata(TrailerInfo info, CancellationToken cancellationToken) { - return GetMovieResult(info, cancellationToken); + return GetResult(info, cancellationToken); } - public Task> GetSearchResults(TrailerInfo searchInfo, CancellationToken cancellationToken) + public Task> GetMetadata(SeriesInfo info, CancellationToken cancellationToken) { - return GetSearchResults(searchInfo, "movie", cancellationToken); + return GetResult(info, cancellationToken); } - public async Task> GetMetadata(SeriesInfo info, CancellationToken cancellationToken) + public Task> GetMetadata(MovieInfo info, CancellationToken cancellationToken) { - var result = new MetadataResult + return GetResult(info, cancellationToken); + } + + private RemoteSearchResult ResultToMetadataResult(SearchResult result, ItemLookupInfo searchInfo, int? indexNumberEnd) + { + var item = new RemoteSearchResult { - Item = new Series(), - QueriedById = true + IndexNumber = searchInfo.IndexNumber, + Name = result.Title, + ParentIndexNumber = searchInfo.ParentIndexNumber, + SearchProviderName = Name, + IndexNumberEnd = indexNumberEnd }; - var imdbId = info.GetProviderId(MetadataProvider.Imdb); - if (string.IsNullOrWhiteSpace(imdbId)) + item.SetProviderId(MetadataProvider.Imdb, result.imdbID); + + if (OmdbProvider.TryParseYear(result.Year, out var parsedYear)) { - imdbId = await GetSeriesImdbId(info, cancellationToken).ConfigureAwait(false); - result.QueriedById = false; + item.ProductionYear = parsedYear; } - if (!string.IsNullOrEmpty(imdbId)) + if (!string.IsNullOrEmpty(result.Released) + && DateTime.TryParse(result.Released, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var released)) { - result.Item.SetProviderId(MetadataProvider.Imdb, imdbId); - result.HasMetadata = true; - - await new OmdbProvider(_httpClientFactory, _fileSystem, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + item.PremiereDate = released; } - return result; - } + if (!string.IsNullOrWhiteSpace(result.Poster)) + { + item.ImageUrl = result.Poster; + } - public Task> GetMetadata(MovieInfo info, CancellationToken cancellationToken) - { - return GetMovieResult(info, cancellationToken); + return item; } - private async Task> GetMovieResult(ItemLookupInfo info, CancellationToken cancellationToken) + private async Task> GetResult(ItemLookupInfo info, CancellationToken cancellationToken) where T : BaseItem, new() { var result = new MetadataResult @@ -249,7 +225,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var imdbId = info.GetProviderId(MetadataProvider.Imdb); if (string.IsNullOrWhiteSpace(imdbId)) { - imdbId = await GetMovieImdbId(info, cancellationToken).ConfigureAwait(false); + imdbId = await GetImdbId(info, cancellationToken).ConfigureAwait(false); result.QueriedById = false; } @@ -258,22 +234,15 @@ namespace MediaBrowser.Providers.Plugins.Omdb result.Item.SetProviderId(MetadataProvider.Imdb, imdbId); result.HasMetadata = true; - await new OmdbProvider(_httpClientFactory, _fileSystem, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + await _omdbProvider.Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } return result; } - private async Task GetMovieImdbId(ItemLookupInfo info, CancellationToken cancellationToken) - { - var results = await GetSearchResultsInternal(info, "movie", false, cancellationToken).ConfigureAwait(false); - var first = results.FirstOrDefault(); - return first?.GetProviderId(MetadataProvider.Imdb); - } - - private async Task GetSeriesImdbId(SeriesInfo info, CancellationToken cancellationToken) + private async Task GetImdbId(ItemLookupInfo info, CancellationToken cancellationToken) { - var results = await GetSearchResultsInternal(info, "series", false, cancellationToken).ConfigureAwait(false); + var results = await GetSearchResultsInternal(info, false, cancellationToken).ConfigureAwait(false); var first = results.FirstOrDefault(); return first?.GetProviderId(MetadataProvider.Imdb); } diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 7fe9fac4fd..12ea2d55b9 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -4,10 +4,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; +using System.Net.Http.Json; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -40,8 +42,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb _configurationManager = configurationManager; _jsonOptions = new JsonSerializerOptions(JsonDefaults.Options); - _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStringConverter()); - _jsonOptions.Converters.Add(new JsonOmdbNotAvailableInt32Converter()); + // These converters need to take priority + _jsonOptions.Converters.Insert(0, new JsonOmdbNotAvailableStringConverter()); + _jsonOptions.Converters.Insert(0, new JsonOmdbNotAvailableInt32Converter()); } /// Fetches data from OMDB service. @@ -64,8 +67,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb var result = await GetRootObject(imdbId, cancellationToken).ConfigureAwait(false); + var isEnglishRequested = IsConfiguredForEnglish(item, language); // Only take the name and rating if the user's language is set to English, since Omdb has no localization - if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase) || _configurationManager.Configuration.EnableNewOmdbSupport) + if (isEnglishRequested) { item.Name = result.Title; @@ -75,9 +79,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb } } - if (!string.IsNullOrEmpty(result.Year) && result.Year.Length >= 4 - && int.TryParse(result.Year.AsSpan().Slice(0, 4), NumberStyles.Number, CultureInfo.InvariantCulture, out var year) - && year >= 0) + if (TryParseYear(result.Year, out var year)) { item.ProductionYear = year; } @@ -113,7 +115,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb item.SetProviderId(MetadataProvider.Imdb, result.imdbID); } - ParseAdditionalMetadata(itemResult, result); + ParseAdditionalMetadata(itemResult, result, isEnglishRequested); } /// Gets data about an episode. @@ -176,8 +178,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb return false; } + var isEnglishRequested = IsConfiguredForEnglish(item, language); // Only take the name and rating if the user's language is set to English, since Omdb has no localization - if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase) || _configurationManager.Configuration.EnableNewOmdbSupport) + if (isEnglishRequested) { item.Name = result.Title; @@ -187,9 +190,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb } } - if (!string.IsNullOrEmpty(result.Year) && result.Year.Length >= 4 - && int.TryParse(result.Year.AsSpan().Slice(0, 4), NumberStyles.Number, CultureInfo.InvariantCulture, out var year) - && year >= 0) + if (TryParseYear(result.Year, out var year)) { item.ProductionYear = year; } @@ -225,7 +226,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb item.SetProviderId(MetadataProvider.Imdb, result.imdbID); } - ParseAdditionalMetadata(itemResult, result); + ParseAdditionalMetadata(itemResult, result, isEnglishRequested); return true; } @@ -259,6 +260,30 @@ namespace MediaBrowser.Providers.Plugins.Omdb return Url + "&" + query; } + /// + /// Extract the year from a string. + /// + /// The input string. + /// The year. + /// A value indicating whether the input could successfully be parsed as a year. + public static bool TryParseYear(string input, [NotNullWhen(true)] out int? year) + { + if (string.IsNullOrEmpty(input)) + { + year = 0; + return false; + } + + if (int.TryParse(input.AsSpan(0, 4), NumberStyles.Number, CultureInfo.InvariantCulture, out var result)) + { + year = result; + return true; + } + + year = 0; + return false; + } + private async Task EnsureItemInfo(string imdbId, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(imdbId)) @@ -291,7 +316,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb "i={0}&plot=short&tomatoes=true&r=json", imdbParam)); - var rootObject = await GetDeserializedOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false); + var rootObject = await _httpClientFactory.CreateClient(NamedClient.Default).GetFromJsonAsync(url, _jsonOptions, cancellationToken).ConfigureAwait(false); await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false); @@ -331,37 +356,13 @@ namespace MediaBrowser.Providers.Plugins.Omdb imdbParam, seasonId)); - var rootObject = await GetDeserializedOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false); + var rootObject = await _httpClientFactory.CreateClient(NamedClient.Default).GetFromJsonAsync(url, _jsonOptions, cancellationToken).ConfigureAwait(false); await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false); return path; } - /// Gets response from OMDB service as type T. - /// HttpClient instance to use for service call. - /// Http URL to use for service call. - /// CancellationToken to use for service call. - /// The first generic type parameter. - /// OMDB service response as type T. - public async Task GetDeserializedOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken) - { - using var response = await GetOmdbResponse(httpClient, url, cancellationToken).ConfigureAwait(false); - await using Stream content = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - - return await JsonSerializer.DeserializeAsync(content, _jsonOptions, cancellationToken).ConfigureAwait(false); - } - - /// Gets response from OMDB service. - /// HttpClient instance to use for service call. - /// Http URL to use for service call. - /// CancellationToken to use for service call. - /// OMDB service response as HttpResponseMessage. - public static Task GetOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken) - { - return httpClient.GetAsync(url, cancellationToken); - } - internal string GetDataFilePath(string imdbId) { if (string.IsNullOrEmpty(imdbId)) @@ -390,31 +391,25 @@ namespace MediaBrowser.Providers.Plugins.Omdb return Path.Combine(dataPath, filename); } - private void ParseAdditionalMetadata(MetadataResult itemResult, RootObject result) + private static void ParseAdditionalMetadata(MetadataResult itemResult, RootObject result, bool isEnglishRequested) where T : BaseItem { var item = itemResult.Item; - var isConfiguredForEnglish = IsConfiguredForEnglish(item) || _configurationManager.Configuration.EnableNewOmdbSupport; - // Grab series genres because IMDb data is better than TVDB. Leave movies alone // But only do it if English is the preferred language because this data will not be localized - if (isConfiguredForEnglish && !string.IsNullOrWhiteSpace(result.Genre)) + if (isEnglishRequested && !string.IsNullOrWhiteSpace(result.Genre)) { item.Genres = Array.Empty(); - foreach (var genre in result.Genre - .Split(',', StringSplitOptions.RemoveEmptyEntries) - .Select(i => i.Trim()) - .Where(i => !string.IsNullOrWhiteSpace(i))) + foreach (var genre in result.Genre.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) { item.AddGenre(genre); } } - if (isConfiguredForEnglish) + if (isEnglishRequested) { - // Omdb is currently English only, so for other languages skip this and let secondary providers fill it in item.Overview = result.Plot; } @@ -427,7 +422,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb { var person = new PersonInfo { - Name = result.Director.Trim(), + Name = result.Director, Type = PersonType.Director }; @@ -438,7 +433,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb { var person = new PersonInfo { - Name = result.Writer.Trim(), + Name = result.Writer, Type = PersonType.Writer }; @@ -447,29 +442,34 @@ namespace MediaBrowser.Providers.Plugins.Omdb if (!string.IsNullOrWhiteSpace(result.Actors)) { - var actorList = result.Actors.Split(','); + var actorList = result.Actors.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); foreach (var actor in actorList) { - if (!string.IsNullOrWhiteSpace(actor)) + if (string.IsNullOrWhiteSpace(actor)) { - var person = new PersonInfo - { - Name = actor.Trim(), - Type = PersonType.Actor - }; - - itemResult.AddPerson(person); + continue; } + + var person = new PersonInfo + { + Name = actor, + Type = PersonType.Actor + }; + + itemResult.AddPerson(person); } } } - private bool IsConfiguredForEnglish(BaseItem item) + private static bool IsConfiguredForEnglish(BaseItem item, string language) { - var lang = item.GetPreferredMetadataLanguage(); + if (string.IsNullOrEmpty(language)) + { + language = item.GetPreferredMetadataLanguage(); + } // The data isn't localized and so can only be used for English users - return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase); + return string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); } internal class SeasonRootObject @@ -546,7 +546,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb if (Ratings != null) { var rating = Ratings.FirstOrDefault(i => string.Equals(i.Source, "Rotten Tomatoes", StringComparison.OrdinalIgnoreCase)); - if (rating != null && rating.Value != null) + if (rating?.Value != null) { var value = rating.Value.TrimEnd('%'); if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var score))