From 5d53b101d84cb925a5e88475fe9c5f59770fbc30 Mon Sep 17 00:00:00 2001 From: Nick Wallace Date: Tue, 17 Mar 2026 14:47:12 -0500 Subject: [PATCH] Adding Clearlogo support for TMDB --- .../MediaCover/MediaCoverService.cs | 6 +- .../MetadataSource/SkyHook/SkyHookProxy.cs | 13 +++ .../MetadataSource/TMDb/ITmdbImagesProxy.cs | 7 ++ .../MetadataSource/TMDb/TmdbImagesProxy.cs | 80 +++++++++++++++++++ .../TMDb/TmdbMovieImagesResource.cs | 25 ++++++ 5 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/NzbDrone.Core/MetadataSource/TMDb/ITmdbImagesProxy.cs create mode 100644 src/NzbDrone.Core/MetadataSource/TMDb/TmdbImagesProxy.cs create mode 100644 src/NzbDrone.Core/MetadataSource/TMDb/TmdbMovieImagesResource.cs diff --git a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs index 8a2a32a39c..91bdcd03e5 100644 --- a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs +++ b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs @@ -203,7 +203,11 @@ private void DownloadCover(Movie movie, MediaCover cover) { var fileName = GetCoverPath(movie.Id, cover.CoverType); - _logger.Info("Downloading {0} for {1} {2}", cover.CoverType, movie, cover.RemoteUrl); + if (cover.CoverType == MediaCoverTypes.Clearlogo) + { + _logger.Debug("Downloading Clearlogo for {0} {1}", movie, cover.RemoteUrl); + } + _httpClient.DownloadFile(cover.RemoteUrl, fileName); } diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index 6afb732698..76a76ad380 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -14,6 +14,7 @@ using NzbDrone.Core.Languages; using NzbDrone.Core.MediaCover; using NzbDrone.Core.MetadataSource.SkyHook.Resource; +using NzbDrone.Core.MetadataSource.TMDb; using NzbDrone.Core.Movies; using NzbDrone.Core.Movies.AlternativeTitles; using NzbDrone.Core.Movies.Collections; @@ -33,6 +34,7 @@ public class SkyHookProxy : IProvideMovieInfo, ISearchForNewMovie private readonly IMovieService _movieService; private readonly IMovieMetadataService _movieMetadataService; private readonly IMovieTranslationService _movieTranslationService; + private readonly ITmdbImagesProxy _tmdbImagesProxy; public SkyHookProxy(IHttpClient httpClient, IRadarrCloudRequestBuilder requestBuilder, @@ -40,6 +42,7 @@ public SkyHookProxy(IHttpClient httpClient, IMovieService movieService, IMovieMetadataService movieMetadataService, IMovieTranslationService movieTranslationService, + ITmdbImagesProxy tmdbImagesProxy, Logger logger) { _httpClient = httpClient; @@ -48,6 +51,7 @@ public SkyHookProxy(IHttpClient httpClient, _movieService = movieService; _movieMetadataService = movieMetadataService; _movieTranslationService = movieTranslationService; + _tmdbImagesProxy = tmdbImagesProxy; _logger = logger; } @@ -276,6 +280,15 @@ public MovieMetadata MapMovie(MovieResource resource) movie.Keywords = resource.Keywords ?? new List(); movie.Images = resource.Images.Select(MapImage).ToList(); + if (!movie.Images.Any(i => i.CoverType == MediaCoverTypes.Clearlogo)) + { + var logo = _tmdbImagesProxy.GetMovieLogo(resource.TmdbId); + if (logo != null) + { + movie.Images.Add(logo); + } + } + movie.Recommendations = resource.Recommendations?.Select(r => r.TmdbId).ToList() ?? new List(); // Workaround due to metadata change until cache cleans up diff --git a/src/NzbDrone.Core/MetadataSource/TMDb/ITmdbImagesProxy.cs b/src/NzbDrone.Core/MetadataSource/TMDb/ITmdbImagesProxy.cs new file mode 100644 index 0000000000..cdcb4f9192 --- /dev/null +++ b/src/NzbDrone.Core/MetadataSource/TMDb/ITmdbImagesProxy.cs @@ -0,0 +1,7 @@ +namespace NzbDrone.Core.MetadataSource.TMDb +{ + public interface ITmdbImagesProxy + { + NzbDrone.Core.MediaCover.MediaCover GetMovieLogo(int tmdbId); + } +} diff --git a/src/NzbDrone.Core/MetadataSource/TMDb/TmdbImagesProxy.cs b/src/NzbDrone.Core/MetadataSource/TMDb/TmdbImagesProxy.cs new file mode 100644 index 0000000000..03d6b9f07a --- /dev/null +++ b/src/NzbDrone.Core/MetadataSource/TMDb/TmdbImagesProxy.cs @@ -0,0 +1,80 @@ +using System; +using System.Linq; +using NLog; +using NzbDrone.Common.Cloud; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; +using NzbDrone.Core.MediaCover; + +namespace NzbDrone.Core.MetadataSource.TMDb +{ + public class TmdbImagesProxy : ITmdbImagesProxy + { + private const string TmdbImageBaseUrl = "https://image.tmdb.org/t/p/original"; + + private readonly IHttpClient _httpClient; + private readonly IRadarrCloudRequestBuilder _requestBuilder; + private readonly Logger _logger; + + public TmdbImagesProxy(IHttpClient httpClient, IRadarrCloudRequestBuilder requestBuilder, Logger logger) + { + _httpClient = httpClient; + _requestBuilder = requestBuilder; + _logger = logger; + } + + public MediaCover.MediaCover GetMovieLogo(int tmdbId) + { + try + { + var request = _requestBuilder.TMDB.Create() + .SetSegment("api", "3") + .SetSegment("route", "movie") + .SetSegment("id", tmdbId.ToString()) + .SetSegment("secondaryRoute", "/images") + .AddQueryParam("include_image_language", "en,null") + .Accept(HttpAccept.Json) + .Build(); + + request.AllowAutoRedirect = true; + request.SuppressHttpError = true; + + var response = _httpClient.Get(request); + + if (response.HasHttpError) + { + return null; + } + + if (response.Resource == null || response.Resource.Logos == null || response.Resource.Logos.Length == 0) + { + return null; + } + + var logo = response.Resource.Logos + .OrderByDescending(l => l.Iso6391 == "en" || l.Iso6391 == null ? 1 : 0) + .ThenByDescending(l => l.VoteAverage) + .ThenByDescending(l => l.VoteCount) + .FirstOrDefault(); + + if (logo == null || logo.FilePath.IsNullOrWhiteSpace()) + { + return null; + } + + var filePath = logo.FilePath; + if (filePath.EndsWith(".svg", StringComparison.OrdinalIgnoreCase)) + { + filePath = string.Concat(filePath.AsSpan(0, filePath.Length - 4), ".png"); + } + + var url = TmdbImageBaseUrl + filePath; + return new MediaCover.MediaCover(MediaCoverTypes.Clearlogo, url); + } + catch (Exception) + { + return null; + } + } + } +} diff --git a/src/NzbDrone.Core/MetadataSource/TMDb/TmdbMovieImagesResource.cs b/src/NzbDrone.Core/MetadataSource/TMDb/TmdbMovieImagesResource.cs new file mode 100644 index 0000000000..4667d4cd43 --- /dev/null +++ b/src/NzbDrone.Core/MetadataSource/TMDb/TmdbMovieImagesResource.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; + +namespace NzbDrone.Core.MetadataSource.TMDb +{ + public class TmdbMovieImagesResource + { + public int Id { get; set; } + public TmdbLogoResource[] Logos { get; set; } + } + + public class TmdbLogoResource + { + [JsonProperty("file_path")] + public string FilePath { get; set; } + + [JsonProperty("vote_average")] + public double VoteAverage { get; set; } + + [JsonProperty("vote_count")] + public int VoteCount { get; set; } + + [JsonProperty("iso_639_1")] + public string Iso6391 { get; set; } + } +}