diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs new file mode 100644 index 0000000000..25782f8c11 --- /dev/null +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -0,0 +1,252 @@ +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Providers; +using ServiceStack; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Api +{ + [Route("/Items/{Id}/ExternalIdInfos", "GET")] + [Api(Description = "Gets external id infos for an item")] + public class GetExternalIdInfos : IReturn> + { + /// + /// Gets or sets the id. + /// + /// The id. + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + [Route("/Items/RemoteSearch/Movie", "POST")] + [Api(Description = "Gets external id infos for an item")] + public class GetMovieRemoteSearchResults : RemoteSearchQuery, IReturn> + { + } + + [Route("/Items/RemoteSearch/Trailer", "POST")] + [Api(Description = "Gets external id infos for an item")] + public class GetTrailerRemoteSearchResults : RemoteSearchQuery, IReturn> + { + } + + [Route("/Items/RemoteSearch/AdultVideo", "POST")] + [Api(Description = "Gets external id infos for an item")] + public class GetAdultVideoRemoteSearchResults : RemoteSearchQuery, IReturn> + { + } + + [Route("/Items/RemoteSearch/Series", "POST")] + [Api(Description = "Gets external id infos for an item")] + public class GetSeriesRemoteSearchResults : RemoteSearchQuery, IReturn> + { + } + + [Route("/Items/RemoteSearch/Game", "POST")] + [Api(Description = "Gets external id infos for an item")] + public class GetGameRemoteSearchResults : RemoteSearchQuery, IReturn> + { + } + + [Route("/Items/RemoteSearch/BoxSet", "POST")] + [Api(Description = "Gets external id infos for an item")] + public class GetBoxSetRemoteSearchResults : RemoteSearchQuery, IReturn> + { + } + + [Route("/Items/RemoteSearch/Person", "POST")] + [Api(Description = "Gets external id infos for an item")] + public class GetPersonRemoteSearchResults : RemoteSearchQuery, IReturn> + { + } + + [Route("/Items/RemoteSearch/Image", "GET")] + [Api(Description = "Gets a remote image")] + public class GetRemoteSearchImage + { + [ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ImageUrl { get; set; } + + [ApiMember(Name = "ProviderName", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ProviderName { get; set; } + } + + public class ItemLookupService : BaseApiService + { + private readonly IDtoService _dtoService; + private readonly IProviderManager _providerManager; + private readonly IServerApplicationPaths _appPaths; + private readonly IFileSystem _fileSystem; + + public ItemLookupService(IDtoService dtoService, IProviderManager providerManager, IServerApplicationPaths appPaths, IFileSystem fileSystem) + { + _dtoService = dtoService; + _providerManager = providerManager; + _appPaths = appPaths; + _fileSystem = fileSystem; + } + + public object Get(GetExternalIdInfos request) + { + var item = _dtoService.GetItemByDtoId(request.Id); + + var infos = _providerManager.GetExternalIdInfos(item).ToList(); + + return ToOptimizedResult(infos); + } + + public object Post(GetMovieRemoteSearchResults request) + { + var result = _providerManager.GetRemoteSearchResults(request, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } + + public object Post(GetAdultVideoRemoteSearchResults request) + { + var result = _providerManager.GetRemoteSearchResults(request, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } + + public object Post(GetSeriesRemoteSearchResults request) + { + var result = _providerManager.GetRemoteSearchResults(request, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } + + public object Post(GetGameRemoteSearchResults request) + { + var result = _providerManager.GetRemoteSearchResults(request, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } + + public object Post(GetBoxSetRemoteSearchResults request) + { + var result = _providerManager.GetRemoteSearchResults(request, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } + + public object Post(GetPersonRemoteSearchResults request) + { + var result = _providerManager.GetRemoteSearchResults(request, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } + + public object Post(GetTrailerRemoteSearchResults request) + { + var result = _providerManager.GetRemoteSearchResults(request, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } + + public object Get(GetRemoteSearchImage request) + { + var result = GetRemoteImage(request).Result; + + return result; + } + + /// + /// Gets the remote image. + /// + /// The request. + /// Task{System.Object}. + private async Task GetRemoteImage(GetRemoteSearchImage request) + { + var urlHash = request.ImageUrl.GetMD5(); + var pointerCachePath = GetFullCachePath(urlHash.ToString()); + + string contentPath; + + try + { + using (var reader = new StreamReader(pointerCachePath)) + { + contentPath = await reader.ReadToEndAsync().ConfigureAwait(false); + } + + if (File.Exists(contentPath)) + { + return ToStaticFileResult(contentPath); + } + } + catch (DirectoryNotFoundException) + { + // Means the file isn't cached yet + } + catch (FileNotFoundException) + { + // Means the file isn't cached yet + } + + await DownloadImage(request.ProviderName, request.ImageUrl, urlHash, pointerCachePath).ConfigureAwait(false); + + // Read the pointer file again + using (var reader = new StreamReader(pointerCachePath)) + { + contentPath = await reader.ReadToEndAsync().ConfigureAwait(false); + } + + return ToStaticFileResult(contentPath); + } + + /// + /// Downloads the image. + /// + /// Name of the provider. + /// The URL. + /// The URL hash. + /// The pointer cache path. + /// Task. + private async Task DownloadImage(string providerName, string url, Guid urlHash, string pointerCachePath) + { + var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false); + + var ext = result.ContentType.Split('/').Last(); + + var fullCachePath = GetFullCachePath(urlHash + "." + ext); + + Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); + using (var stream = result.Content) + { + using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + { + await stream.CopyToAsync(filestream).ConfigureAwait(false); + } + } + + Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); + using (var writer = new StreamWriter(pointerCachePath)) + { + await writer.WriteAsync(fullCachePath).ConfigureAwait(false); + } + } + + /// + /// Gets the full cache path. + /// + /// The filename. + /// System.String. + private string GetFullCachePath(string filename) + { + return Path.Combine(_appPaths.CachePath, "remote-images", filename.Substring(0, 1), filename); + } + + } +} diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 4cb20273f5..c3fdbb9a0c 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -6,10 +6,8 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; using MediaBrowser.Model.Querying; using ServiceStack; using System; @@ -50,18 +48,6 @@ namespace MediaBrowser.Api.Library public int Index { get; set; } } - [Route("/Items/{Id}/ExternalIdInfos", "GET")] - [Api(Description = "Gets external id infos for an item")] - public class GetExternalIdInfos : IReturn> - { - /// - /// Gets or sets the id. - /// - /// The id. - [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - /// /// Class GetCriticReviews /// @@ -256,29 +242,18 @@ namespace MediaBrowser.Api.Library private readonly IUserDataManager _userDataManager; private readonly IDtoService _dtoService; - private readonly IProviderManager _providerManager; /// /// Initializes a new instance of the class. /// public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager, - IDtoService dtoService, IUserDataManager userDataManager, IProviderManager providerManager) + IDtoService dtoService, IUserDataManager userDataManager) { _itemRepo = itemRepo; _libraryManager = libraryManager; _userManager = userManager; _dtoService = dtoService; _userDataManager = userDataManager; - _providerManager = providerManager; - } - - public object Get(GetExternalIdInfos request) - { - var item = _dtoService.GetItemByDtoId(request.Id); - - var infos = _providerManager.GetExternalIdInfos(item).ToList(); - - return ToOptimizedResult(infos); } public object Get(GetMediaFolders request) diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index ee2a7eafcf..bcc487a5d5 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -82,6 +82,7 @@ + diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 83ee6ae83c..94b19498a6 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; @@ -126,5 +127,14 @@ namespace MediaBrowser.Controller.Providers CancellationToken cancellationToken) where TItemType : BaseItem, new() where TLookupType : ItemLookupInfo; + + /// + /// Gets the search image. + /// + /// Name of the provider. + /// The URL. + /// The cancellation token. + /// Task{HttpResponseInfo}. + Task GetSearchImage(string providerName, string url, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs index f00a22a3a4..0ff7ee5a9d 100644 --- a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs @@ -18,11 +18,8 @@ namespace MediaBrowser.Controller.Providers Task> GetMetadata(TLookupInfoType info, CancellationToken cancellationToken); } - public interface IRemoteSearchProvider : IMetadataProvider - where TLookupInfoType : ItemLookupInfo + public interface IRemoteSearchProvider : IMetadataProvider { - Task> GetSearchResults(TLookupInfoType searchInfo, CancellationToken cancellationToken); - /// /// Gets the image response. /// @@ -31,6 +28,12 @@ namespace MediaBrowser.Controller.Providers /// Task{HttpResponseInfo}. Task GetImageResponse(string url, CancellationToken cancellationToken); } + + public interface IRemoteSearchProvider : IRemoteSearchProvider + where TLookupInfoType : ItemLookupInfo + { + Task> GetSearchResults(TLookupInfoType searchInfo, CancellationToken cancellationToken); + } public class RemoteSearchQuery where T : ItemLookupInfo diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs index 09939f2d6d..d698b4ce03 100644 --- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs +++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs @@ -32,20 +32,71 @@ namespace MediaBrowser.Providers.BoxSets private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; private readonly ILocalizationManager _localization; + private readonly IHttpClient _httpClient; - public MovieDbBoxSetProvider(ILogger logger, IJsonSerializer json, IServerConfigurationManager config, IFileSystem fileSystem, ILocalizationManager localization) + public MovieDbBoxSetProvider(ILogger logger, IJsonSerializer json, IServerConfigurationManager config, IFileSystem fileSystem, ILocalizationManager localization, IHttpClient httpClient) { _logger = logger; _json = json; _config = config; _fileSystem = fileSystem; _localization = localization; + _httpClient = httpClient; Current = this; } + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + public async Task> GetSearchResults(BoxSetInfo searchInfo, CancellationToken cancellationToken) { - return new List(); + var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb); + + var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + + var tmdbImageUrl = tmdbSettings.images.base_url + "original"; + + if (!string.IsNullOrEmpty(tmdbId)) + { + await EnsureInfo(tmdbId, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false); + + var dataFilePath = GetDataFilePath(_config.ApplicationPaths, tmdbId, searchInfo.MetadataLanguage); + var info = _json.DeserializeFromFile(dataFilePath); + + var images = (info.images ?? new Images()).posters ?? new List(); + + var result = new RemoteSearchResult + { + Name = info.name, + + SearchProviderName = Name, + + ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].file_path) + }; + + result.SetProviderId(MetadataProviders.Tmdb, info.id.ToString(_usCulture)); + + return new[] { result }; + } + + var results = await new MovieDbSearch(_logger, _json).GetSearchResults(searchInfo, cancellationToken).ConfigureAwait(false); + + return results.Select(i => GetRemoteSearchResult(i, tmdbImageUrl)); + } + + private RemoteSearchResult GetRemoteSearchResult(MovieDbSearch.TmdbMovieSearchResult tmdbResult, string baseImageUrl) + { + var result = new RemoteSearchResult + { + Name = tmdbResult.name, + + SearchProviderName = Name, + + ImageUrl = string.IsNullOrEmpty(tmdbResult.poster_path) ? null : (baseImageUrl + tmdbResult.poster_path) + }; + + result.SetProviderId(MetadataProviders.Tmdb, tmdbResult.id.ToString(_usCulture)); + + return result; } public async Task> GetMetadata(BoxSetInfo id, CancellationToken cancellationToken) @@ -55,7 +106,9 @@ namespace MediaBrowser.Providers.BoxSets // We don't already have an Id, need to fetch it if (string.IsNullOrEmpty(tmdbId)) { - var searchResult = await new MovieDbSearch(_logger, _json).FindCollectionId(id, cancellationToken).ConfigureAwait(false); + var searchResults = await new MovieDbSearch(_logger, _json).GetSearchResults(id, cancellationToken).ConfigureAwait(false); + + var searchResult = searchResults.FirstOrDefault(); if (searchResult != null) { @@ -219,10 +272,15 @@ namespace MediaBrowser.Providers.BoxSets private static string GetDataFilePath(IApplicationPaths appPaths, string tmdbId, string preferredLanguage) { + if (string.IsNullOrWhiteSpace(preferredLanguage)) + { + throw new ArgumentNullException("preferredLanguage"); + } + var path = GetDataPath(appPaths, tmdbId); var filename = string.Format("all-{0}.json", - preferredLanguage ?? string.Empty); + preferredLanguage); return Path.Combine(path, filename); } @@ -291,7 +349,12 @@ namespace MediaBrowser.Providers.BoxSets public Task GetImageResponse(string url, CancellationToken cancellationToken) { - throw new NotImplementedException(); + return _httpClient.GetResponse(new HttpRequestOptions + { + CancellationToken = cancellationToken, + Url = url, + ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + }); } } } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 942f414b6f..b35c4887c3 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -657,6 +657,15 @@ namespace MediaBrowser.Providers.Manager providers = providers.Where(i => string.Equals(i.Name, searchInfo.SearchProviderName, StringComparison.OrdinalIgnoreCase)); } + if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataLanguage)) + { + searchInfo.SearchInfo.MetadataLanguage = ConfigurationManager.Configuration.PreferredMetadataLanguage; + } + if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataCountryCode)) + { + searchInfo.SearchInfo.MetadataCountryCode = ConfigurationManager.Configuration.MetadataCountryCode; + } + foreach (var provider in providers) { var results = await provider.GetSearchResults(searchInfo.SearchInfo, cancellationToken).ConfigureAwait(false); @@ -665,7 +674,7 @@ namespace MediaBrowser.Providers.Manager if (list.Count > 0) { - return list; + return list.Take(10); } } @@ -673,6 +682,18 @@ namespace MediaBrowser.Providers.Manager return new List(); } + public Task GetSearchImage(string providerName, string url, CancellationToken cancellationToken) + { + var provider = _metadataProviders.OfType().FirstOrDefault(i => string.Equals(i.Name, providerName, StringComparison.OrdinalIgnoreCase)); + + if (provider == null) + { + throw new ArgumentException("Search provider not found."); + } + + return provider.GetImageResponse(url, cancellationToken); + } + public IEnumerable GetExternalIds(IHasProviderIds item) { return _externalIds.Where(i => diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs index abd48e37c4..b6dca5b7d8 100644 --- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs +++ b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs @@ -39,7 +39,9 @@ namespace MediaBrowser.Providers.Movies // Don't search for music video id's because it is very easy to misidentify. if (string.IsNullOrEmpty(tmdbId) && string.IsNullOrEmpty(imdbId) && typeof(T) != typeof(MusicVideo)) { - var searchResult = await new MovieDbSearch(_logger, _jsonSerializer).FindMovieId(itemId, cancellationToken).ConfigureAwait(false); + var searchResults = await new MovieDbSearch(_logger, _jsonSerializer).GetMovieSearchResults(itemId, cancellationToken).ConfigureAwait(false); + + var searchResult = searchResults.FirstOrDefault(); if (searchResult != null) { diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index 32a77a8a6c..2c47dbc2f4 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -558,7 +558,12 @@ namespace MediaBrowser.Providers.Movies public Task GetImageResponse(string url, CancellationToken cancellationToken) { - throw new NotImplementedException(); + return _httpClient.GetResponse(new HttpRequestOptions + { + CancellationToken = cancellationToken, + Url = url, + ResourcePool = MovieDbResourcePool + }); } } } diff --git a/MediaBrowser.Providers/Movies/MovieDbSearch.cs b/MediaBrowser.Providers/Movies/MovieDbSearch.cs index 383705e0a8..ad73350095 100644 --- a/MediaBrowser.Providers/Movies/MovieDbSearch.cs +++ b/MediaBrowser.Providers/Movies/MovieDbSearch.cs @@ -29,22 +29,22 @@ namespace MediaBrowser.Providers.Movies _json = json; } - public Task FindSeriesId(ItemLookupInfo idInfo, CancellationToken cancellationToken) + public Task> GetSearchResults(SeriesInfo idInfo, CancellationToken cancellationToken) { - return FindId(idInfo, "tv", cancellationToken); + return GetSearchResults(idInfo, "tv", cancellationToken); } - public Task FindMovieId(ItemLookupInfo idInfo, CancellationToken cancellationToken) + public Task> GetMovieSearchResults(ItemLookupInfo idInfo, CancellationToken cancellationToken) { - return FindId(idInfo, "movie", cancellationToken); + return GetSearchResults(idInfo, "movie", cancellationToken); } - public Task FindCollectionId(ItemLookupInfo idInfo, CancellationToken cancellationToken) + public Task> GetSearchResults(BoxSetInfo idInfo, CancellationToken cancellationToken) { - return FindId(idInfo, "collection", cancellationToken); + return GetSearchResults(idInfo, "collection", cancellationToken); } - private async Task FindId(ItemLookupInfo idInfo, string searchType, CancellationToken cancellationToken) + private async Task> GetSearchResults(ItemLookupInfo idInfo, string searchType, CancellationToken cancellationToken) { var name = idInfo.Name; var year = idInfo.Year; @@ -60,48 +60,49 @@ namespace MediaBrowser.Providers.Movies //nope - search for it //var searchType = item is BoxSet ? "collection" : "movie"; - var id = await AttemptFindId(name, searchType, year, language, cancellationToken).ConfigureAwait(false); + var results = await GetSearchResults(name, searchType, year, language, cancellationToken).ConfigureAwait(false); - if (id == null) + if (results.Count == 0) { //try in english if wasn't before - if (language != "en") + if (!string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) { - id = await AttemptFindId(name, searchType, year, "en", cancellationToken).ConfigureAwait(false); + results = await GetSearchResults(name, searchType, year, "en", cancellationToken).ConfigureAwait(false); } - else + } + + if (results.Count == 0) + { + // try with dot and _ turned to space + var originalName = name; + + name = name.Replace(",", " "); + name = name.Replace(".", " "); + name = name.Replace("_", " "); + name = name.Replace("-", " "); + name = name.Replace("!", " "); + name = name.Replace("?", " "); + + name = name.Trim(); + + // Search again if the new name is different + if (!string.Equals(name, originalName)) { - // try with dot and _ turned to space - var originalName = name; + results = await GetSearchResults(name, searchType, year, language, cancellationToken).ConfigureAwait(false); - name = name.Replace(",", " "); - name = name.Replace(".", " "); - name = name.Replace("_", " "); - name = name.Replace("-", " "); - name = name.Replace("!", " "); - name = name.Replace("?", " "); - - name = name.Trim(); - - // Search again if the new name is different - if (!string.Equals(name, originalName)) + if (results.Count == 0 && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) { - id = await AttemptFindId(name, searchType, year, language, cancellationToken).ConfigureAwait(false); + //one more time, in english + results = await GetSearchResults(name, searchType, year, "en", cancellationToken).ConfigureAwait(false); - if (id == null && language != "en") - { - //one more time, in english - id = await AttemptFindId(name, searchType, year, "en", cancellationToken).ConfigureAwait(false); - - } } } } - return id; + return results; } - private async Task AttemptFindId(string name, string type, int? year, string language, CancellationToken cancellationToken) + private async Task> GetSearchResults(string name, string type, int? year, string language, CancellationToken cancellationToken) { var url3 = string.Format(Search3, WebUtility.UrlEncode(name), ApiKey, language, type); @@ -113,11 +114,36 @@ namespace MediaBrowser.Providers.Movies }).ConfigureAwait(false)) { - var searchResult = _json.DeserializeFromStream(json); - return FindBestResult(searchResult.results, name, year); + var searchResults = _json.DeserializeFromStream(json); + + var results = searchResults.results ?? new List(); + + var index = 0; + var resultTuples = results.Select(result => new Tuple(result, index++)).ToList(); + + return resultTuples.OrderBy(i => GetSearchResultOrder(i.Item1, year)) + .ThenBy(i => i.Item2) + .Select(i => i.Item1) + .ToList(); } } + private int GetSearchResultOrder(TmdbMovieSearchResult result, int? year) + { + if (year.HasValue) + { + DateTime r; + + // These dates are always in this exact format + if (DateTime.TryParseExact(result.release_date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out r)) + { + return Math.Abs(r.Year - year.Value); + } + } + + return 0; + } + private TmdbMovieSearchResult FindBestResult(List results, string name, int? year) { if (year.HasValue) diff --git a/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs b/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs index 8c981e2d86..512fee8f85 100644 --- a/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs @@ -11,6 +11,13 @@ namespace MediaBrowser.Providers.Movies { public class MovieDbTrailerProvider : IRemoteMetadataProvider, IHasOrder { + private readonly IHttpClient _httpClient; + + public MovieDbTrailerProvider(IHttpClient httpClient) + { + _httpClient = httpClient; + } + public Task> GetMetadata(TrailerInfo info, CancellationToken cancellationToken) { return MovieDbProvider.Current.GetItemMetadata(info, cancellationToken); @@ -42,7 +49,12 @@ namespace MediaBrowser.Providers.Movies public Task GetImageResponse(string url, CancellationToken cancellationToken) { - throw new NotImplementedException(); + return _httpClient.GetResponse(new HttpRequestOptions + { + CancellationToken = cancellationToken, + Url = url, + ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + }); } } } diff --git a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs index 01be6bf91e..b5bf445bf8 100644 --- a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs +++ b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs @@ -29,12 +29,14 @@ namespace MediaBrowser.Providers.People private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; + private readonly IHttpClient _httpClient; - public MovieDbPersonProvider(IFileSystem fileSystem, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer) + public MovieDbPersonProvider(IFileSystem fileSystem, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient) { _fileSystem = fileSystem; _configurationManager = configurationManager; _jsonSerializer = jsonSerializer; + _httpClient = httpClient; Current = this; } @@ -64,6 +66,8 @@ namespace MediaBrowser.Providers.People { Name = info.name, + SearchProviderName = Name, + ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].file_path) }; @@ -94,6 +98,8 @@ namespace MediaBrowser.Providers.People { var result = new RemoteSearchResult { + SearchProviderName = Name, + Name = i.Name, ImageUrl = string.IsNullOrEmpty(i.Profile_Path) ? null : (baseImageUrl + i.Profile_Path) @@ -349,7 +355,12 @@ namespace MediaBrowser.Providers.People public Task GetImageResponse(string url, CancellationToken cancellationToken) { - throw new NotImplementedException(); + return _httpClient.GetResponse(new HttpRequestOptions + { + CancellationToken = cancellationToken, + Url = url, + ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + }); } } } diff --git a/MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs b/MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs index b46336e90a..9f1d5f021a 100644 --- a/MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs @@ -33,14 +33,16 @@ namespace MediaBrowser.Providers.TV private readonly IServerConfigurationManager _configurationManager; private readonly ILogger _logger; private readonly ILocalizationManager _localization; + private readonly IHttpClient _httpClient; - public MovieDbSeriesProvider(IJsonSerializer jsonSerializer, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger logger, ILocalizationManager localization) + public MovieDbSeriesProvider(IJsonSerializer jsonSerializer, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger logger, ILocalizationManager localization, IHttpClient httpClient) { _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; _configurationManager = configurationManager; _logger = logger; _localization = localization; + _httpClient = httpClient; Current = this; } @@ -82,7 +84,9 @@ namespace MediaBrowser.Providers.TV if (string.IsNullOrEmpty(tmdbId)) { - var searchResult = await new MovieDbSearch(_logger, _jsonSerializer).FindSeriesId(info, cancellationToken).ConfigureAwait(false); + var searchResults = await new MovieDbSearch(_logger, _jsonSerializer).GetSearchResults(info, cancellationToken).ConfigureAwait(false); + + var searchResult = searchResults.FirstOrDefault(); if (searchResult != null) { @@ -462,7 +466,12 @@ namespace MediaBrowser.Providers.TV public Task GetImageResponse(string url, CancellationToken cancellationToken) { - throw new NotImplementedException(); + return _httpClient.GetResponse(new HttpRequestOptions + { + CancellationToken = cancellationToken, + Url = url, + ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + }); } } } diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index 43037d6ce0..c0d784fccd 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -42,7 +42,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints public void Run() { - NatUtility.Logger = new LogWriter(_logger); + //NatUtility.Logger = new LogWriter(_logger); Reload(); } @@ -64,17 +64,17 @@ namespace MediaBrowser.Server.Implementations.EntryPoints void NatUtility_UnhandledException(object sender, UnhandledExceptionEventArgs e) { - var ex = e.ExceptionObject as Exception; + //var ex = e.ExceptionObject as Exception; - if (ex == null) - { - _logger.Error("Unidentified error reported by Mono.Nat"); - } - else - { - // Seeing some blank exceptions coming through here - _logger.ErrorException("Error reported by Mono.Nat: ", ex); - } + //if (ex == null) + //{ + // _logger.Error("Unidentified error reported by Mono.Nat"); + //} + //else + //{ + // // Seeing some blank exceptions coming through here + // _logger.ErrorException("Error reported by Mono.Nat: ", ex); + //} } void NatUtility_DeviceFound(object sender, DeviceEventArgs e) @@ -88,7 +88,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints } catch (Exception ex) { - _logger.ErrorException("Error creating port forwarding rules", ex); + //_logger.ErrorException("Error creating port forwarding rules", ex); } } @@ -106,7 +106,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints private void CreatePortMap(INatDevice device, int port) { - _logger.Info("Creating port map on port {0}", port); + _logger.Debug("Creating port map on port {0}", port); device.CreatePortMap(new Mapping(Protocol.Tcp, port, port) {