From 4f08eb5c189761c22eec8a6156f4c939dd37c103 Mon Sep 17 00:00:00 2001 From: Vicente Date: Mon, 13 Jul 2026 08:45:30 +0200 Subject: [PATCH 01/15] Update dependencies --- src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj | 4 ++-- src/NzbDrone.Common/Radarr.Common.csproj | 6 +++--- .../Radarr.Integration.Test.csproj | 2 +- src/Radarr.Api.V3/Radarr.Api.V3.csproj | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj b/src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj index 023342c7128..11fa0082f25 100644 --- a/src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj +++ b/src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj @@ -3,8 +3,8 @@ net10.0 - - + + diff --git a/src/NzbDrone.Common/Radarr.Common.csproj b/src/NzbDrone.Common/Radarr.Common.csproj index 6bc1cfa8379..8184c8122b3 100644 --- a/src/NzbDrone.Common/Radarr.Common.csproj +++ b/src/NzbDrone.Common/Radarr.Common.csproj @@ -5,8 +5,8 @@ - - + + @@ -15,7 +15,7 @@ - + diff --git a/src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj b/src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj index 7bd9b9c3e79..9895c4f44b6 100644 --- a/src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj +++ b/src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj @@ -4,7 +4,7 @@ Library - + diff --git a/src/Radarr.Api.V3/Radarr.Api.V3.csproj b/src/Radarr.Api.V3/Radarr.Api.V3.csproj index 6a702ef0a82..28fbe0b9b22 100644 --- a/src/Radarr.Api.V3/Radarr.Api.V3.csproj +++ b/src/Radarr.Api.V3/Radarr.Api.V3.csproj @@ -3,7 +3,7 @@ net10.0 - + From 01c55253eb1fbc191b40b4eac42e8c1e8c1e8719 Mon Sep 17 00:00:00 2001 From: Vicente Date: Mon, 13 Jul 2026 08:51:17 +0200 Subject: [PATCH 02/15] Update dependencies --- src/NzbDrone.Common/Radarr.Common.csproj | 2 +- src/NzbDrone.Core/Radarr.Core.csproj | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NzbDrone.Common/Radarr.Common.csproj b/src/NzbDrone.Common/Radarr.Common.csproj index 8184c8122b3..6c8a441f41a 100644 --- a/src/NzbDrone.Common/Radarr.Common.csproj +++ b/src/NzbDrone.Common/Radarr.Common.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/NzbDrone.Core/Radarr.Core.csproj b/src/NzbDrone.Core/Radarr.Core.csproj index 392c3a3b5af..266f32dbef0 100644 --- a/src/NzbDrone.Core/Radarr.Core.csproj +++ b/src/NzbDrone.Core/Radarr.Core.csproj @@ -7,12 +7,12 @@ - - + + - - + + From ec1e57d05c87cd5d85117f8e1a2eeeb2a768c2fb Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sun, 5 Jul 2026 07:12:18 +0300 Subject: [PATCH 03/15] Fixed: Validate source, modifier and resolution specifications (cherry picked from commit f9e18a7c4475345f325237670d7e71ceac97038b) --- .../Specifications/IndexerFlagSpecification.cs | 6 +++--- .../Specifications/QualityModifierSpecification.cs | 9 ++++----- .../Specifications/ResolutionSpecification.cs | 11 +++++++++-- .../Specifications/SourceSpecification.cs | 11 +++++++++-- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/IndexerFlagSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/IndexerFlagSpecification.cs index 56d8926b5ed..7132ba4d788 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/IndexerFlagSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/IndexerFlagSpecification.cs @@ -11,11 +11,11 @@ public class IndexerFlagSpecificationValidator : AbstractValidator c.Value).NotEmpty(); - RuleFor(c => c.Value).Custom((qualityValue, context) => + RuleFor(c => c.Value).Custom((flag, context) => { - if (!Enum.IsDefined(typeof(IndexerFlags), qualityValue)) + if (!Enum.IsDefined(typeof(IndexerFlags), flag)) { - context.AddFailure($"Invalid indexer flag condition value: {qualityValue}"); + context.AddFailure($"Invalid indexer flag condition value: {flag}"); } }); } diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/QualityModifierSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/QualityModifierSpecification.cs index d5a1cf7872a..6268f55a9b9 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/QualityModifierSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/QualityModifierSpecification.cs @@ -10,12 +10,11 @@ public class QualityModifierSpecificationValidator : AbstractValidator c.Value).NotEmpty(); - RuleFor(c => c.Value).Custom((qualityValue, context) => + RuleFor(c => c.Value).Custom((value, context) => { - if (!Enum.IsDefined(typeof(Modifier), qualityValue)) + if (!Enum.IsDefined(typeof(Modifier), value)) { - context.AddFailure(string.Format("Invalid quality modifier condition value: {0}", qualityValue)); + context.AddFailure($"Invalid quality modifier condition value: {value}"); } }); } @@ -23,7 +22,7 @@ public QualityModifierSpecificationValidator() public class QualityModifierSpecification : CustomFormatSpecificationBase { - private static readonly QualityModifierSpecificationValidator Validator = new QualityModifierSpecificationValidator(); + private static readonly QualityModifierSpecificationValidator Validator = new(); public override int Order => 7; public override string ImplementationName => "Quality Modifier"; diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/ResolutionSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/ResolutionSpecification.cs index a7866d0a026..1cd93efa085 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/ResolutionSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/ResolutionSpecification.cs @@ -1,3 +1,4 @@ +using System; using FluentValidation; using NzbDrone.Core.Annotations; using NzbDrone.Core.Parser; @@ -9,13 +10,19 @@ public class ResolutionSpecificationValidator : AbstractValidator c.Value).NotEmpty(); + RuleFor(c => c.Value).Custom((value, context) => + { + if (!Enum.IsDefined(typeof(Resolution), value)) + { + context.AddFailure($"Invalid resolution condition value: {value}"); + } + }); } } public class ResolutionSpecification : CustomFormatSpecificationBase { - private static readonly ResolutionSpecificationValidator Validator = new ResolutionSpecificationValidator(); + private static readonly ResolutionSpecificationValidator Validator = new(); public override int Order => 6; public override string ImplementationName => "Resolution"; diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/SourceSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/SourceSpecification.cs index b5d3566996d..7bd8bbe9604 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/SourceSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/SourceSpecification.cs @@ -1,3 +1,4 @@ +using System; using FluentValidation; using NzbDrone.Core.Annotations; using NzbDrone.Core.Qualities; @@ -9,13 +10,19 @@ public class SourceSpecificationValidator : AbstractValidator c.Value).NotEmpty(); + RuleFor(c => c.Value).Custom((value, context) => + { + if (!Enum.IsDefined(typeof(QualitySource), value)) + { + context.AddFailure($"Invalid source condition value: {value}"); + } + }); } } public class SourceSpecification : CustomFormatSpecificationBase { - private static readonly SourceSpecificationValidator Validator = new SourceSpecificationValidator(); + private static readonly SourceSpecificationValidator Validator = new(); public override int Order => 5; public override string ImplementationName => "Source"; From 2d1ff13c1b16b3625e91dfa445c63b8f98b3959a Mon Sep 17 00:00:00 2001 From: Bogdan Date: Mon, 6 Jul 2026 11:29:28 +0300 Subject: [PATCH 04/15] Fixed: Error when fetching non-existing tags in import script (cherry picked from commit 9a94807a1cf932ae3e3e6e55cf8f5ad924d2fdd8) --- src/NzbDrone.Core/MediaFiles/ScriptImportDecider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NzbDrone.Core/MediaFiles/ScriptImportDecider.cs b/src/NzbDrone.Core/MediaFiles/ScriptImportDecider.cs index 56888ffb09d..08f66bd1fe3 100644 --- a/src/NzbDrone.Core/MediaFiles/ScriptImportDecider.cs +++ b/src/NzbDrone.Core/MediaFiles/ScriptImportDecider.cs @@ -139,7 +139,7 @@ public ScriptImportDecision TryImport(string sourcePath, string destinationFileP environmentVariables.Add("Radarr_Movie_ImdbId", movie.MovieMetadata.Value.ImdbId ?? string.Empty); environmentVariables.Add("Radarr_Movie_OriginalLanguage", IsoLanguages.Get(movie.MovieMetadata.Value.OriginalLanguage).ThreeLetterCode); environmentVariables.Add("Radarr_Movie_Genres", string.Join("|", movie.MovieMetadata.Value.Genres)); - environmentVariables.Add("Radarr_Movie_Tags", string.Join("|", movie.Tags.Select(t => _tagRepository.Get(t).Label))); + environmentVariables.Add("Radarr_Movie_Tags", string.Join("|", _tagRepository.GetTags(movie.Tags).Select(t => t.Label))); environmentVariables.Add("Radarr_Movie_In_Cinemas_Date", movie.MovieMetadata.Value.InCinemas.ToString() ?? string.Empty); environmentVariables.Add("Radarr_Movie_Physical_Release_Date", movie.MovieMetadata.Value.PhysicalRelease.ToString() ?? string.Empty); From 5e8e532cd3cecdd3645a4c44148a88a3c26713b1 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Wed, 15 Jul 2026 12:52:48 +0300 Subject: [PATCH 05/15] Fix malformed cookie test --- src/NzbDrone.Common.Test/Http/HttpClientFixture.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs index e2fc4cd00c8..6395fd94268 100644 --- a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs +++ b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs @@ -796,8 +796,8 @@ public async Task should_parse_malformed_cloudflare_cookie(string culture) Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); try { - // the date is bad in the below - should be 13-Jul-2026 - var malformedCookie = @"__cfduid=d29e686a9d65800021c66faca0a29b4261436890790; expires=Mon, 13-Jul-26 16:19:50 GMT; path=/; HttpOnly"; + // the date is bad in the below - should be 16-Jul-2046 + var malformedCookie = @"__cfduid=d29e686a9d65800021c66faca0a29b4261436890790; expires=Mon, 16-Jul-46 16:19:50 GMT; path=/; HttpOnly"; var requestSet = new HttpRequestBuilder($"https://{_httpBinHost}/response-headers") .AddQueryParam("Set-Cookie", malformedCookie) .Build(); @@ -805,7 +805,7 @@ public async Task should_parse_malformed_cloudflare_cookie(string culture) requestSet.AllowAutoRedirect = false; requestSet.StoreResponseCookie = true; - var responseSet = await Subject.GetAsync(requestSet); + await Subject.GetAsync(requestSet); var request = new HttpRequest($"https://{_httpBinHost}/get"); From de4811318ef1eb6560a5e480cc4bba2afc008ca9 Mon Sep 17 00:00:00 2001 From: Auggie <773150+augustuen@users.noreply.github.com> Date: Sat, 18 Jul 2026 22:52:59 +0200 Subject: [PATCH 06/15] Bump to 6.4.0 --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3bb988ad374..671b00f9903 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -9,7 +9,7 @@ variables: testsFolder: './_tests' yarnCacheFolder: $(Pipeline.Workspace)/.yarn nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages - majorVersion: '6.3.0' + majorVersion: '6.4.0' minorVersion: $[counter('minorVersion', 2000)] radarrVersion: '$(majorVersion).$(minorVersion)' buildName: '$(Build.SourceBranchName).$(radarrVersion)' From 69f8ceaeb59021be314d7790a5eb1f38b99f10da Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sun, 19 Jul 2026 16:25:46 +0300 Subject: [PATCH 07/15] Fixed: Improve cache busting for covers using a hash instead of file modification time --- .../MediaCoverServiceFixture.cs | 22 +++---- .../MediaCover/MediaCoverService.cs | 61 ++++--------------- src/Radarr.Api.V3/Movies/MovieController.cs | 18 ++---- 3 files changed, 25 insertions(+), 76 deletions(-) diff --git a/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs b/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs index 120fd5ab7c0..d951c08b84a 100644 --- a/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs @@ -26,37 +26,29 @@ public void Setup() _movie = Builder.CreateNew() .With(v => v.Id = 2) - .With(v => v.MovieMetadata.Value.Images = new List { new MediaCover.MediaCover(MediaCoverTypes.Poster, "") }) + .With(v => v.MovieMetadata.Value.Images = new List { new(MediaCoverTypes.Poster, "") }) .Build(); - - Mocker.GetMock().Setup(m => m.GetMovie(It.Is(id => id == _movie.Id))).Returns(_movie); } [Test] public void should_convert_cover_urls_to_local() { var covers = new List - { - new MediaCover.MediaCover { CoverType = MediaCoverTypes.Banner } - }; - - Mocker.GetMock().Setup(c => c.FileGetLastWrite(It.IsAny())) - .Returns(new DateTime(1234)); - - Mocker.GetMock().Setup(c => c.FileExists(It.IsAny())) - .Returns(true); + { + new() { CoverType = MediaCoverTypes.Banner, RemoteUrl = "https://artworks.examples.com/banners/1.jpg" } + }; Subject.ConvertToLocalUrls(12, covers); - covers.Single().Url.Should().Be("/MediaCover/12/banner.jpg?lastWrite=1234"); + covers.Single().Url.Should().Be("/MediaCover/12/banner.jpg?h=a6210a45e2b93963ad9e"); } [Test] - public void should_convert_media_urls_to_local_without_time_if_file_doesnt_exist() + public void should_convert_media_urls_to_local_without_hash_if_remote_url_is_empty() { var covers = new List { - new MediaCover.MediaCover { CoverType = MediaCoverTypes.Banner } + new() { CoverType = MediaCoverTypes.Banner } }; Subject.ConvertToLocalUrls(12, covers); diff --git a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs index 8a2a32a39c1..40f43abac71 100644 --- a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs +++ b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Threading; using NLog; -using NzbDrone.Common; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; @@ -19,9 +17,7 @@ namespace NzbDrone.Core.MediaCover { public interface IMapCoversToLocal { - Dictionary GetCoverFileInfos(); - void ConvertToLocalUrls(int movieId, IEnumerable covers, Dictionary fileInfos = null); - void ConvertToLocalUrls(IEnumerable>> items, Dictionary coverFileInfos); + void ConvertToLocalUrls(int movieId, IEnumerable covers); string GetCoverPath(int movieId, MediaCoverTypes coverType, int? height = null); } @@ -43,7 +39,7 @@ public class MediaCoverService : // ImageSharp is slow on ARM (no hardware acceleration on mono yet) // So limit the number of concurrent resizing tasks - private static SemaphoreSlim _semaphore = new SemaphoreSlim((int)Math.Ceiling(Environment.ProcessorCount / 2.0)); + private static readonly SemaphoreSlim Semaphore = new((int)Math.Ceiling(Environment.ProcessorCount / 2.0)); public MediaCoverService(IMediaCoverProxy mediaCoverProxy, IImageResizer resizer, @@ -69,28 +65,16 @@ public MediaCoverService(IMediaCoverProxy mediaCoverProxy, public string GetCoverPath(int movieId, MediaCoverTypes coverType, int? height = null) { - var heightSuffix = height.HasValue ? "-" + height.ToString() : ""; + var heightSuffix = height.HasValue ? $"-{height}" : ""; - return Path.Combine(GetMovieCoverPath(movieId), coverType.ToString().ToLower() + heightSuffix + GetExtension(coverType)); + return Path.Combine(GetMovieCoverPath(movieId), coverType.ToString().ToLowerInvariant() + heightSuffix + GetExtension(coverType)); } - public Dictionary GetCoverFileInfos() - { - if (!_diskProvider.FolderExists(_coverRootFolder)) - { - return new Dictionary(); - } - - return _diskProvider - .GetFileInfos(_coverRootFolder, true) - .ToDictionary(x => x.FullName, PathEqualityComparer.Instance); - } - - public void ConvertToLocalUrls(int movieId, IEnumerable covers, Dictionary fileInfos = null) + public void ConvertToLocalUrls(int movieId, IEnumerable covers) { if (movieId == 0) { - // Movie isn't in Radarr yet, map via a proxy to circument referrer issues + // Movie isn't in Radarr yet, map via a proxy to circumvent referrer issues foreach (var mediaCover in covers) { mediaCover.Url = _mediaCoverProxy.RegisterUrl(mediaCover.RemoteUrl); @@ -105,37 +89,16 @@ public void ConvertToLocalUrls(int movieId, IEnumerable covers, Dict continue; } - var filePath = GetCoverPath(movieId, mediaCover.CoverType); - - mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/" + movieId + "/" + mediaCover.CoverType.ToString().ToLower() + GetExtension(mediaCover.CoverType); - - DateTime? lastWrite = null; - - if (fileInfos != null && fileInfos.TryGetValue(filePath, out var file)) - { - lastWrite = file.LastWriteTimeUtc; - } - else if (_diskProvider.FileExists(filePath)) - { - lastWrite = _diskProvider.FileGetLastWrite(filePath); - } + mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/" + movieId + "/" + mediaCover.CoverType.ToString().ToLowerInvariant() + GetExtension(mediaCover.CoverType); - if (lastWrite.HasValue) + if (mediaCover.RemoteUrl.IsNotNullOrWhiteSpace()) { - mediaCover.Url += "?lastWrite=" + lastWrite.Value.Ticks; + mediaCover.Url += "?h=" + mediaCover.RemoteUrl.SHA256Hash()[..20]; } } } } - public void ConvertToLocalUrls(IEnumerable>> items, Dictionary coverFileInfos) - { - foreach (var movie in items) - { - ConvertToLocalUrls(movie.Item1, movie.Item2, coverFileInfos); - } - } - private string GetMovieCoverPath(int movieId) { return Path.Combine(_coverRootFolder, movieId.ToString()); @@ -184,7 +147,7 @@ private bool EnsureCovers(Movie movie) try { - _semaphore.Wait(); + Semaphore.Wait(); foreach (var tuple in toResize) { @@ -193,7 +156,7 @@ private bool EnsureCovers(Movie movie) } finally { - _semaphore.Release(); + Semaphore.Release(); } return updated; @@ -252,7 +215,7 @@ private void EnsureResizedCovers(Movie movie, MediaCover cover, bool forceResize } } - private string GetExtension(MediaCoverTypes coverType) + private static string GetExtension(MediaCoverTypes coverType) { return coverType switch { diff --git a/src/Radarr.Api.V3/Movies/MovieController.cs b/src/Radarr.Api.V3/Movies/MovieController.cs index 42d85925478..d92f75dc3c0 100644 --- a/src/Radarr.Api.V3/Movies/MovieController.cs +++ b/src/Radarr.Api.V3/Movies/MovieController.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading.Tasks; using FluentValidation; @@ -156,9 +154,7 @@ public List AllMovie(int? tmdbId, bool excludeLocalCovers = false if (!excludeLocalCovers) { - var coverFileInfos = _coverMapper.GetCoverFileInfos(); - - MapCoversToLocal(moviesResources, coverFileInfos); + MapCoversToLocal(moviesResources.ToArray()); } LinkMovieStatistics(moviesResources, sdict); @@ -283,14 +279,12 @@ public void DeleteMovie(int id, bool deleteFiles = false, bool addImportExclusio _moviesService.DeleteMovie(id, deleteFiles, addImportExclusion); } - private void MapCoversToLocal(MovieResource movie) - { - _coverMapper.ConvertToLocalUrls(movie.Id, movie.Images); - } - - private void MapCoversToLocal(IEnumerable movies, Dictionary coverFileInfos) + private void MapCoversToLocal(params MovieResource[] movies) { - _coverMapper.ConvertToLocalUrls(movies.Select(x => Tuple.Create(x.Id, x.Images.AsEnumerable())), coverFileInfos); + foreach (var movieResource in movies) + { + _coverMapper.ConvertToLocalUrls(movieResource.Id, movieResource.Images); + } } private void FetchAndLinkMovieStatistics(MovieResource resource) From 9d814137c12dae035e95ee14e3a2c079ce7d20db Mon Sep 17 00:00:00 2001 From: Colin Mackie Date: Fri, 24 Apr 2026 18:48:49 -0700 Subject: [PATCH 08/15] Wrap bulk UpdateMany/SetFields in a transaction (cherry picked from commit d8b16d79263668d26f3f35d019cf742ab6177e61) --- src/NzbDrone.Core/Datastore/BasicRepository.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/NzbDrone.Core/Datastore/BasicRepository.cs b/src/NzbDrone.Core/Datastore/BasicRepository.cs index 618999992a6..cb5dca20a3a 100644 --- a/src/NzbDrone.Core/Datastore/BasicRepository.cs +++ b/src/NzbDrone.Core/Datastore/BasicRepository.cs @@ -266,8 +266,10 @@ public void UpdateMany(IList models) } using (var conn = _database.OpenConnection()) + using (var tran = conn.BeginTransaction(IsolationLevel.ReadCommitted)) { - UpdateFields(conn, null, models, _properties); + UpdateFields(conn, tran, models, _properties); + tran.Commit(); } } @@ -371,8 +373,10 @@ public void SetFields(IList models, params Expression x.GetMemberName()).ToList(); using (var conn = _database.OpenConnection()) + using (var tran = conn.BeginTransaction(IsolationLevel.ReadCommitted)) { - UpdateFields(conn, null, models, propertiesToUpdate); + UpdateFields(conn, tran, models, propertiesToUpdate); + tran.Commit(); } foreach (var model in models) From 2089d4a33f2ffe0151cbab5ebb3b7ec48af3c34c Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sat, 13 Jun 2026 22:42:07 +0300 Subject: [PATCH 09/15] Avoid suppresing search for movies errors --- src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index 6afb7326980..bad93937472 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -516,17 +516,12 @@ public List SearchForNewMovie(string title) var searchTerm = parserTitle.Replace("_", "+").Replace(" ", "+").Replace(".", "+"); - var firstChar = searchTerm.First(); - var request = _radarrMetadata.Create() .SetSegment("route", "search") .AddQueryParam("q", searchTerm) .AddQueryParam("year", yearTerm) .Build(); - request.AllowAutoRedirect = true; - request.SuppressHttpError = true; - var httpResponse = _httpClient.Get>(request); return httpResponse.Resource.SelectList(MapSearchResult); From f3bb6321cdee98cf8f22c80acc26d847bd4ecba8 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Wed, 31 Dec 2025 20:39:46 +0200 Subject: [PATCH 10/15] Ignore case for rating type enums --- .../MetadataSource/SkyHook/SkyHookProxy.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index bad93937472..76bfce6dc23 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -646,7 +646,7 @@ private static Ratings MapRatings(RatingResource ratings) { mappedRatings.Tmdb = new RatingChild { - Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.Tmdb.Type), + Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.Tmdb.Type, true), Value = ratings.Tmdb.Value, Votes = ratings.Tmdb.Count }; @@ -656,7 +656,7 @@ private static Ratings MapRatings(RatingResource ratings) { mappedRatings.Imdb = new RatingChild { - Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.Imdb.Type), + Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.Imdb.Type, true), Value = ratings.Imdb.Value, Votes = ratings.Imdb.Count }; @@ -666,7 +666,7 @@ private static Ratings MapRatings(RatingResource ratings) { mappedRatings.Metacritic = new RatingChild { - Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.Metacritic.Type), + Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.Metacritic.Type, true), Value = ratings.Metacritic.Value, Votes = ratings.Metacritic.Count }; @@ -676,7 +676,7 @@ private static Ratings MapRatings(RatingResource ratings) { mappedRatings.RottenTomatoes = new RatingChild { - Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.RottenTomatoes.Type), + Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.RottenTomatoes.Type, true), Value = ratings.RottenTomatoes.Value, Votes = ratings.RottenTomatoes.Count }; @@ -686,7 +686,7 @@ private static Ratings MapRatings(RatingResource ratings) { mappedRatings.Trakt = new RatingChild { - Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.Trakt.Type), + Type = (RatingType)Enum.Parse(typeof(RatingType), ratings.Trakt.Type, true), Value = ratings.Trakt.Value, Votes = ratings.Trakt.Count }; From 1737a7166976e077dc288081c3b47a85ac4ee79a Mon Sep 17 00:00:00 2001 From: Bogdan Date: Wed, 10 Jun 2026 16:18:55 +0300 Subject: [PATCH 11/15] Fix log message for MinCustomFormatScore rejection (cherry picked from commit 56931254b140a235b5ae27418c3149ef2bbdaf94) --- .../DecisionEngine/Specifications/UpgradableSpecification.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradableSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradableSpecification.cs index 5fc5152f159..248bd89dba4 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradableSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradableSpecification.cs @@ -104,7 +104,7 @@ public UpgradeableRejectReason IsUpgradable(QualityProfile qualityProfile, Quali if (newFormatScore < currentFormatScore + qualityProfile.MinUpgradeFormatScore) { - _logger.Debug("New item's custom formats [{0}] ({1}) do not meet minimum custom format score increment of {3} required for upgrade, skipping. Existing: [{4}] ({5}).", + _logger.Debug("New item's custom formats [{0}] ({1}) do not meet minimum custom format score increment of {2} required for upgrade, skipping. Existing: [{3}] ({4}).", newCustomFormats.ConcatToString(), newFormatScore, qualityProfile.MinUpgradeFormatScore, From 750247fbe7dbcaa06b3b95ef52286b7c314d46f8 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sun, 3 May 2026 16:59:49 +0300 Subject: [PATCH 12/15] Defer deserialize JSON response resource with Lazy (cherry picked from commit 036aeadd37b863de59c8b02ac7e733b341597d2d) --- src/NzbDrone.Common/Http/HttpResponse.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/NzbDrone.Common/Http/HttpResponse.cs b/src/NzbDrone.Common/Http/HttpResponse.cs index 8a8266c662f..db2c6a06aef 100644 --- a/src/NzbDrone.Common/Http/HttpResponse.cs +++ b/src/NzbDrone.Common/Http/HttpResponse.cs @@ -101,12 +101,14 @@ public override string ToString() public class HttpResponse : HttpResponse where T : new() { + private readonly Lazy _resource; + public HttpResponse(HttpResponse response) : base(response.Request, response.Headers, response.ResponseData, response.StatusCode, response.Version) { - Resource = Json.Deserialize(response.Content); + _resource = new Lazy(() => Json.Deserialize(response.Content)); } - public T Resource { get; private set; } + public T Resource => _resource.Value; } } From 0c78421d553f8395fee14171892da60650123c97 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sat, 3 Jan 2026 20:08:59 +0200 Subject: [PATCH 13/15] Fix disposing of the HttpRequestMessage (cherry picked from commit e747ec8f5c3496cd0fd5c223925d9b4e2bdbf9f3) --- .../Http/Dispatchers/ManagedHttpDispatcher.cs | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs b/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs index 2847789197e..929e1bf67a6 100644 --- a/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs +++ b/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs @@ -53,11 +53,9 @@ public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, public async Task GetResponseAsync(HttpRequest request, CookieContainer cookies) { - var requestMessage = new HttpRequestMessage(request.Method, (Uri)request.Url) - { - Version = HttpVersion.Version20, - VersionPolicy = HttpVersionPolicy.RequestVersionOrLower - }; + using var requestMessage = new HttpRequestMessage(request.Method, (Uri)request.Url); + requestMessage.Version = HttpVersion.Version20; + requestMessage.VersionPolicy = HttpVersionPolicy.RequestVersionOrLower; requestMessage.Headers.UserAgent.ParseAdd(_userAgentBuilder.GetUserAgent(request.UseSimplifiedUserAgent)); requestMessage.Headers.ConnectionClose = !request.ConnectionKeepAlive; @@ -113,31 +111,30 @@ public async Task GetResponseAsync(HttpRequest request, CookieCont try { using var responseMessage = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cts.Token); - { - byte[] data = null; - try + byte[] data = null; + + try + { + if (request.ResponseStream != null && responseMessage.StatusCode == HttpStatusCode.OK) { - if (request.ResponseStream != null && responseMessage.StatusCode == HttpStatusCode.OK) - { - await responseMessage.Content.CopyToAsync(request.ResponseStream, null, cts.Token); - } - else - { - data = await responseMessage.Content.ReadAsByteArrayAsync(cts.Token); - } + await responseMessage.Content.CopyToAsync(request.ResponseStream, null, cts.Token); } - catch (Exception ex) + else { - throw new WebException("Failed to read complete http response", ex, WebExceptionStatus.ReceiveFailure, null); + data = await responseMessage.Content.ReadAsByteArrayAsync(cts.Token); } + } + catch (Exception ex) + { + throw new WebException("Failed to read complete http response", ex, WebExceptionStatus.ReceiveFailure, null); + } - var headers = responseMessage.Headers.ToNameValueCollection(); + var headers = responseMessage.Headers.ToNameValueCollection(); - headers.Add(responseMessage.Content.Headers.ToNameValueCollection()); + headers.Add(responseMessage.Content.Headers.ToNameValueCollection()); - return new HttpResponse(request, new HttpHeader(headers), data, responseMessage.StatusCode, responseMessage.Version); - } + return new HttpResponse(request, new HttpHeader(headers), data, responseMessage.StatusCode, responseMessage.Version); } catch (OperationCanceledException ex) when (cts.IsCancellationRequested) { From bb1043040f04e7beb8d7e8631feb2c485dd80830 Mon Sep 17 00:00:00 2001 From: Vicente Date: Sun, 26 Jul 2026 09:43:50 +0200 Subject: [PATCH 14/15] Upgrade libs --- src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj | 2 +- src/NzbDrone.Common/Radarr.Common.csproj | 6 +++--- src/NzbDrone.Core/Radarr.Core.csproj | 6 +++--- .../Radarr.Integration.Test.csproj | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj b/src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj index 11fa0082f25..ec7a03dbde7 100644 --- a/src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj +++ b/src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj @@ -4,7 +4,7 @@ - + diff --git a/src/NzbDrone.Common/Radarr.Common.csproj b/src/NzbDrone.Common/Radarr.Common.csproj index 6c8a441f41a..9c091bf80a1 100644 --- a/src/NzbDrone.Common/Radarr.Common.csproj +++ b/src/NzbDrone.Common/Radarr.Common.csproj @@ -5,8 +5,8 @@ - - + + @@ -15,7 +15,7 @@ - + diff --git a/src/NzbDrone.Core/Radarr.Core.csproj b/src/NzbDrone.Core/Radarr.Core.csproj index 266f32dbef0..7fd51a0b203 100644 --- a/src/NzbDrone.Core/Radarr.Core.csproj +++ b/src/NzbDrone.Core/Radarr.Core.csproj @@ -7,12 +7,12 @@ - + - - + + diff --git a/src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj b/src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj index 9895c4f44b6..22cd44cb89f 100644 --- a/src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj +++ b/src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj @@ -4,7 +4,7 @@ Library - + From 23df39ad6f57bc1b882f5744f223603303c1351b Mon Sep 17 00:00:00 2001 From: Vicente Date: Sun, 26 Jul 2026 09:48:43 +0200 Subject: [PATCH 15/15] update libs --- src/Directory.Build.props | 4 ++-- src/NzbDrone.Api.Test/Radarr.Api.Test.csproj | 2 +- src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj | 2 +- src/NzbDrone.Common.Test/Radarr.Common.Test.csproj | 2 +- src/NzbDrone.Core.Test/Radarr.Core.Test.csproj | 2 +- src/NzbDrone.Host.Test/Radarr.Host.Test.csproj | 2 +- src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj | 2 +- src/NzbDrone.Libraries.Test/Radarr.Libraries.Test.csproj | 2 +- src/NzbDrone.Mono.Test/Radarr.Mono.Test.csproj | 2 +- src/NzbDrone.Update.Test/Radarr.Update.Test.csproj | 2 +- src/NzbDrone.Windows.Test/Radarr.Windows.Test.csproj | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 23a7de4d426..366057f38aa 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -101,8 +101,8 @@ - - + + diff --git a/src/NzbDrone.Api.Test/Radarr.Api.Test.csproj b/src/NzbDrone.Api.Test/Radarr.Api.Test.csproj index ede6b5975c7..e78ea891ec4 100644 --- a/src/NzbDrone.Api.Test/Radarr.Api.Test.csproj +++ b/src/NzbDrone.Api.Test/Radarr.Api.Test.csproj @@ -12,7 +12,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj b/src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj index ec7a03dbde7..155c9084620 100644 --- a/src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj +++ b/src/NzbDrone.Automation.Test/Radarr.Automation.Test.csproj @@ -10,7 +10,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/NzbDrone.Common.Test/Radarr.Common.Test.csproj b/src/NzbDrone.Common.Test/Radarr.Common.Test.csproj index fd5c35a501a..483d5ee8ee3 100644 --- a/src/NzbDrone.Common.Test/Radarr.Common.Test.csproj +++ b/src/NzbDrone.Common.Test/Radarr.Common.Test.csproj @@ -8,7 +8,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj b/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj index 6f858c1430a..3b5fe0279ba 100644 --- a/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj @@ -24,7 +24,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/NzbDrone.Host.Test/Radarr.Host.Test.csproj b/src/NzbDrone.Host.Test/Radarr.Host.Test.csproj index 8ba6cadba8f..1dcd878b498 100644 --- a/src/NzbDrone.Host.Test/Radarr.Host.Test.csproj +++ b/src/NzbDrone.Host.Test/Radarr.Host.Test.csproj @@ -7,7 +7,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj b/src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj index 22cd44cb89f..f5e7d35c44b 100644 --- a/src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj +++ b/src/NzbDrone.Integration.Test/Radarr.Integration.Test.csproj @@ -11,7 +11,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/NzbDrone.Libraries.Test/Radarr.Libraries.Test.csproj b/src/NzbDrone.Libraries.Test/Radarr.Libraries.Test.csproj index 0277bd08227..f836c4b3753 100644 --- a/src/NzbDrone.Libraries.Test/Radarr.Libraries.Test.csproj +++ b/src/NzbDrone.Libraries.Test/Radarr.Libraries.Test.csproj @@ -6,7 +6,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/NzbDrone.Mono.Test/Radarr.Mono.Test.csproj b/src/NzbDrone.Mono.Test/Radarr.Mono.Test.csproj index 6c695c02220..9b6ecb4870c 100644 --- a/src/NzbDrone.Mono.Test/Radarr.Mono.Test.csproj +++ b/src/NzbDrone.Mono.Test/Radarr.Mono.Test.csproj @@ -20,7 +20,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/NzbDrone.Update.Test/Radarr.Update.Test.csproj b/src/NzbDrone.Update.Test/Radarr.Update.Test.csproj index f93e85645c7..c0f7caa29be 100644 --- a/src/NzbDrone.Update.Test/Radarr.Update.Test.csproj +++ b/src/NzbDrone.Update.Test/Radarr.Update.Test.csproj @@ -7,7 +7,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/NzbDrone.Windows.Test/Radarr.Windows.Test.csproj b/src/NzbDrone.Windows.Test/Radarr.Windows.Test.csproj index 2a72e9f546f..748164c6900 100644 --- a/src/NzbDrone.Windows.Test/Radarr.Windows.Test.csproj +++ b/src/NzbDrone.Windows.Test/Radarr.Windows.Test.csproj @@ -8,7 +8,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive