From 2998442db658a050e49a1ef2ebfb3d3c733a7019 Mon Sep 17 00:00:00 2001 From: Flaminel Date: Fri, 25 Sep 2026 17:37:39 +0300 Subject: [PATCH] Fix search command tracking for Lidarr, Readarr and Whisparr v3 (#814) --- .../Features/Arr/ArrClientTests.cs | 4 +- .../Features/Arr/LidarrClientTests.cs | 96 ++++++++++++++++++- .../Features/Arr/RadarrClientTests.cs | 34 +++++++ .../Features/Arr/ReadarrClientTests.cs | 96 ++++++++++++++++++- .../Features/Arr/WhisparrV3ClientTests.cs | 75 ++++++++++++++- .../Features/Arr/ArrClient.cs | 5 +- .../Features/Arr/LidarrClient.cs | 25 +++-- .../Features/Arr/RadarrClient.cs | 7 +- .../Features/Arr/ReadarrClient.cs | 28 +++--- .../Features/Arr/SonarrClient.cs | 3 +- .../Features/Arr/WhisparrV3Client.cs | 20 ++-- 11 files changed, 348 insertions(+), 45 deletions(-) diff --git a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/ArrClientTests.cs b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/ArrClientTests.cs index f6864b57..e2bd5e25 100644 --- a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/ArrClientTests.cs +++ b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/ArrClientTests.cs @@ -358,7 +358,7 @@ public class ArrClientTests status.Status.ShouldBe(ArrCommandState.Completed); status.Message.ShouldBe("ok"); var request = _httpMessageHandler.CapturedRequests.ShouldHaveSingleItem(); - request.RequestUri!.AbsolutePath.ShouldBe("/api/v3/command/42"); + request.RequestUri!.AbsolutePath.ShouldBe("/api/v1/command/42"); } [Fact] @@ -406,7 +406,7 @@ public class ArrClientTests HttpRequestMessage request = _httpMessageHandler.CapturedRequests.ShouldHaveSingleItem(); request.Method.ShouldBe(HttpMethod.Get); - request.RequestUri!.AbsolutePath.ShouldBe("/api/v3/command"); + request.RequestUri!.AbsolutePath.ShouldBe("/api/v1/command"); request.Headers.GetValues("x-api-key").ShouldHaveSingleItem().ShouldBe("secret-key"); } diff --git a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/LidarrClientTests.cs b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/LidarrClientTests.cs index 11ba12db..839a78fc 100644 --- a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/LidarrClientTests.cs +++ b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/LidarrClientTests.cs @@ -101,6 +101,40 @@ public class LidarrClientTests #endregion + #region GetCommandStatusAsync / GetCommandsAsync + + [Fact] + public async Task GetCommandStatusAsync_UsesV1CommandPath() + { + // Arrange + _httpMessageHandler.SetupResponse((_, _) => Task.FromResult(JsonResponse( + new ArrCommandStatus(42, ArrCommandState.Completed, null)))); + + // Act + await _client.GetCommandStatusAsync(_arrInstance, 42); + + // Assert + var request = _httpMessageHandler.CapturedRequests.ShouldHaveSingleItem(); + request.RequestUri!.AbsolutePath.ShouldBe("/api/v1/command/42"); + } + + [Fact] + public async Task GetCommandsAsync_UsesV1CommandPath() + { + // Arrange + _httpMessageHandler.SetupResponse((_, _) => Task.FromResult(JsonResponse( + new List { new(1, ArrCommandState.Started, null) }))); + + // Act + await _client.GetCommandsAsync(_arrInstance); + + // Assert + var request = _httpMessageHandler.CapturedRequests.ShouldHaveSingleItem(); + request.RequestUri!.AbsolutePath.ShouldBe("/api/v1/command"); + } + + #endregion + #region HasContentId [Fact] @@ -151,9 +185,9 @@ public class LidarrClientTests } [Fact] - public async Task SearchItemsAsync_PostsAlbumSearchCommandWithAllIds_AndReturnsEmpty() + public async Task SearchItemsAsync_PostsAlbumSearchCommandWithAllIds_AndReturnsCommandId() { - // Arrange — Lidarr's SearchItemsAsync always returns [] regardless of HTTP response + // Arrange RouteResponses(commandIdForPost: 11); var items = new HashSet { @@ -165,7 +199,7 @@ public class LidarrClientTests var ids = await _client.SearchItemsAsync(_arrInstance, items); // Assert - ids.ShouldBeEmpty(); + ids.ShouldBe(new long[] { 11 }); var post = _httpMessageHandler.CapturedRequests.Single(r => r.Method == HttpMethod.Post); post.RequestUri!.AbsolutePath.ShouldBe("/api/v1/command"); var body = _httpMessageHandler.CapturedRequestBodies[_httpMessageHandler.CapturedRequests.IndexOf(post)]; @@ -174,6 +208,62 @@ public class LidarrClientTests body!.ShouldContain("\"albumIds\":[10,20]", Case.Insensitive); } + [Fact] + public async Task SearchItemsAsync_NoCommandIdInResponse_ReturnsEmpty() + { + // Arrange — POST returns 200 with body that has no id (treated as null id) + _httpMessageHandler.SetupResponse((req, _) => + { + if (req.Method == HttpMethod.Post) + { + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent("{}", Encoding.UTF8, "application/json"), + }); + } + return Task.FromResult(JsonNullResponse()); + }); + + var items = new HashSet { new() { Id = 1 } }; + + // Act + var ids = await _client.SearchItemsAsync(_arrInstance, items); + + // Assert + ids.ShouldBeEmpty(); + } + + [Fact] + public async Task SearchItemsAsync_NullCommandResponseFromDryRun_ReturnsEmpty() + { + // Arrange — interceptor returns null on dry-run + _dryRunInterceptor + .InterceptAsync(Arg.Any>>(), Arg.Any()) + .Returns((HttpResponseMessage?)null); + _httpMessageHandler.SetupResponse((_, _) => Task.FromResult(JsonNullResponse())); + + var items = new HashSet { new() { Id = 5 } }; + + // Act + var ids = await _client.SearchItemsAsync(_arrInstance, items); + + // Assert + ids.ShouldBeEmpty(); + } + + [Fact] + public async Task SearchItemAsync_ReturnsCommandIdInsteadOfThrowing() + { + // Arrange + RouteResponses(commandIdForPost: 77); + + // Act + var id = await _client.SearchItemAsync(_arrInstance, new SearchItem { Id = 1 }); + + // Assert + id.ShouldBe(77); + } + [Fact] public async Task SearchItemsAsync_ServerError_Throws() { diff --git a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/RadarrClientTests.cs b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/RadarrClientTests.cs index b97a6df4..24fb54f9 100644 --- a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/RadarrClientTests.cs +++ b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/RadarrClientTests.cs @@ -254,6 +254,40 @@ public class RadarrClientTests #endregion + #region GetCommandStatusAsync / GetCommandsAsync + + [Fact] + public async Task GetCommandStatusAsync_UsesV3CommandPath() + { + // Arrange + _httpMessageHandler.SetupResponse((_, _) => Task.FromResult(JsonResponse( + new ArrCommandStatus(42, ArrCommandState.Completed, null)))); + + // Act + await _client.GetCommandStatusAsync(_arrInstance, 42); + + // Assert + var request = _httpMessageHandler.CapturedRequests.ShouldHaveSingleItem(); + request.RequestUri!.AbsolutePath.ShouldBe("/api/v3/command/42"); + } + + [Fact] + public async Task GetCommandsAsync_UsesV3CommandPath() + { + // Arrange + _httpMessageHandler.SetupResponse((_, _) => Task.FromResult(JsonResponse( + new List { new(1, ArrCommandState.Started, null) }))); + + // Act + await _client.GetCommandsAsync(_arrInstance); + + // Assert + var request = _httpMessageHandler.CapturedRequests.ShouldHaveSingleItem(); + request.RequestUri!.AbsolutePath.ShouldBe("/api/v3/command"); + } + + #endregion + #region StreamAllMoviesAsync / GetAllTagsAsync / GetQualityProfilesAsync / GetMovieFileScoresAsync [Fact] diff --git a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/ReadarrClientTests.cs b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/ReadarrClientTests.cs index cfdb047d..305e1f35 100644 --- a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/ReadarrClientTests.cs +++ b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/ReadarrClientTests.cs @@ -101,6 +101,40 @@ public class ReadarrClientTests #endregion + #region GetCommandStatusAsync / GetCommandsAsync + + [Fact] + public async Task GetCommandStatusAsync_UsesV1CommandPath() + { + // Arrange + _httpMessageHandler.SetupResponse((_, _) => Task.FromResult(JsonResponse( + new ArrCommandStatus(42, ArrCommandState.Completed, null)))); + + // Act + await _client.GetCommandStatusAsync(_arrInstance, 42); + + // Assert + var request = _httpMessageHandler.CapturedRequests.ShouldHaveSingleItem(); + request.RequestUri!.AbsolutePath.ShouldBe("/api/v1/command/42"); + } + + [Fact] + public async Task GetCommandsAsync_UsesV1CommandPath() + { + // Arrange + _httpMessageHandler.SetupResponse((_, _) => Task.FromResult(JsonResponse( + new List { new(1, ArrCommandState.Started, null) }))); + + // Act + await _client.GetCommandsAsync(_arrInstance); + + // Assert + var request = _httpMessageHandler.CapturedRequests.ShouldHaveSingleItem(); + request.RequestUri!.AbsolutePath.ShouldBe("/api/v1/command"); + } + + #endregion + #region HasContentId [Fact] @@ -151,9 +185,9 @@ public class ReadarrClientTests } [Fact] - public async Task SearchItemsAsync_PostsBookSearchCommandWithAllIds_AndReturnsEmpty() + public async Task SearchItemsAsync_PostsBookSearchCommandWithAllIds_AndReturnsCommandId() { - // Arrange — Readarr's SearchItemsAsync always returns [] regardless of HTTP response + // Arrange RouteResponses(commandIdForPost: 22); var items = new HashSet { @@ -165,7 +199,7 @@ public class ReadarrClientTests var ids = await _client.SearchItemsAsync(_arrInstance, items); // Assert - ids.ShouldBeEmpty(); + ids.ShouldBe(new long[] { 22 }); var post = _httpMessageHandler.CapturedRequests.Single(r => r.Method == HttpMethod.Post); post.RequestUri!.AbsolutePath.ShouldBe("/api/v1/command"); var body = _httpMessageHandler.CapturedRequestBodies[_httpMessageHandler.CapturedRequests.IndexOf(post)]; @@ -174,6 +208,62 @@ public class ReadarrClientTests body!.ShouldContain("\"BookIds\":[100,200]", Case.Insensitive); } + [Fact] + public async Task SearchItemsAsync_NoCommandIdInResponse_ReturnsEmpty() + { + // Arrange — POST returns 200 with body that has no id (treated as null id) + _httpMessageHandler.SetupResponse((req, _) => + { + if (req.Method == HttpMethod.Post) + { + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent("{}", Encoding.UTF8, "application/json"), + }); + } + return Task.FromResult(JsonNullResponse()); + }); + + var items = new HashSet { new() { Id = 1 } }; + + // Act + var ids = await _client.SearchItemsAsync(_arrInstance, items); + + // Assert + ids.ShouldBeEmpty(); + } + + [Fact] + public async Task SearchItemsAsync_NullCommandResponseFromDryRun_ReturnsEmpty() + { + // Arrange — interceptor returns null on dry-run + _dryRunInterceptor + .InterceptAsync(Arg.Any>>(), Arg.Any()) + .Returns((HttpResponseMessage?)null); + _httpMessageHandler.SetupResponse((_, _) => Task.FromResult(JsonNullResponse())); + + var items = new HashSet { new() { Id = 1 } }; + + // Act + var ids = await _client.SearchItemsAsync(_arrInstance, items); + + // Assert + ids.ShouldBeEmpty(); + } + + [Fact] + public async Task SearchItemAsync_ReturnsCommandIdInsteadOfThrowing() + { + // Arrange + RouteResponses(commandIdForPost: 33); + + // Act + var id = await _client.SearchItemAsync(_arrInstance, new SearchItem { Id = 1 }); + + // Assert + id.ShouldBe(33); + } + [Fact] public async Task SearchItemsAsync_ServerError_Throws() { diff --git a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/WhisparrV3ClientTests.cs b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/WhisparrV3ClientTests.cs index 19462e1e..df006700 100644 --- a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/WhisparrV3ClientTests.cs +++ b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/WhisparrV3ClientTests.cs @@ -137,8 +137,8 @@ public class WhisparrV3ClientTests [Fact] public async Task SearchItemsAsync_PostsMoviesSearchCommandWithAllIds() { - // Arrange: GET /movie/{id} calls build the log context, return null so it bails out - _httpMessageHandler.SetupResponse((req, _) => Task.FromResult(JsonNullResponse())); + // Arrange + RouteResponses(commandIdForPost: 22); HashSet items = new() { new SearchItem { Id = 10 }, @@ -149,7 +149,7 @@ public class WhisparrV3ClientTests List ids = await _client.SearchItemsAsync(_arrInstance, items); // Assert - ids.ShouldBeEmpty(); + ids.ShouldBe(new long[] { 22 }); HttpRequestMessage post = _httpMessageHandler.CapturedRequests.Single(r => r.Method == HttpMethod.Post); post.RequestUri!.AbsolutePath.ShouldBe("/api/v3/command"); string? body = _httpMessageHandler.CapturedRequestBodies[_httpMessageHandler.CapturedRequests.IndexOf(post)]; @@ -181,6 +181,62 @@ public class WhisparrV3ClientTests get.RequestUri!.AbsolutePath.ShouldBe("/api/v3/movie/10"); } + [Fact] + public async Task SearchItemsAsync_NoCommandIdInResponse_ReturnsEmpty() + { + // Arrange — POST returns 200 with body that has no id (treated as null id) + _httpMessageHandler.SetupResponse((req, _) => + { + if (req.Method == HttpMethod.Post) + { + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent("{}", Encoding.UTF8, "application/json"), + }); + } + return Task.FromResult(JsonNullResponse()); + }); + + HashSet items = new() { new SearchItem { Id = 1 } }; + + // Act + List ids = await _client.SearchItemsAsync(_arrInstance, items); + + // Assert + ids.ShouldBeEmpty(); + } + + [Fact] + public async Task SearchItemsAsync_NullCommandResponseFromDryRun_ReturnsEmpty() + { + // Arrange — interceptor returns null on dry-run + _dryRunInterceptor + .InterceptAsync(Arg.Any>>(), Arg.Any()) + .Returns((HttpResponseMessage?)null); + _httpMessageHandler.SetupResponse((_, _) => Task.FromResult(JsonNullResponse())); + + HashSet items = new() { new SearchItem { Id = 1 } }; + + // Act + List ids = await _client.SearchItemsAsync(_arrInstance, items); + + // Assert + ids.ShouldBeEmpty(); + } + + [Fact] + public async Task SearchItemAsync_ReturnsCommandIdInsteadOfThrowing() + { + // Arrange + RouteResponses(commandIdForPost: 33); + + // Act + long id = await _client.SearchItemAsync(_arrInstance, new SearchItem { Id = 1 }); + + // Assert + id.ShouldBe(33); + } + #endregion [Fact] @@ -251,6 +307,19 @@ public class WhisparrV3ClientTests #region Helpers + private void RouteResponses(long commandIdForPost) + { + _httpMessageHandler.SetupResponse((req, _) => + { + if (req.Method == HttpMethod.Post && req.RequestUri!.AbsolutePath.EndsWith("/command")) + { + return Task.FromResult(JsonResponse(new { id = commandIdForPost })); + } + + return Task.FromResult(JsonNullResponse()); + }); + } + private static QueueRecord BuildRecord(long id) => new() { Id = id, diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Arr/ArrClient.cs b/code/backend/Cleanuparr.Infrastructure/Features/Arr/ArrClient.cs index 4cb24ef7..2ffa0aa7 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Arr/ArrClient.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Arr/ArrClient.cs @@ -32,10 +32,9 @@ public abstract class ArrClient : IArrClient protected const int QueuePageSize = 200; /// - /// Command API path. - /// Every client uses v3 here, whatever its ApiVersion. + /// Command API path for this app's API version. /// - protected const string CommandUrlPath = "/api/v3/command"; + protected string CommandUrlPath => $"/api/{ApiVersion}/command"; /// /// REST API version segment for this app, e.g. "v3". diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Arr/LidarrClient.cs b/code/backend/Cleanuparr.Infrastructure/Features/Arr/LidarrClient.cs index fd4dff60..a39a6df6 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Arr/LidarrClient.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Arr/LidarrClient.cs @@ -52,8 +52,10 @@ public class LidarrClient : ArrClient, ILidarrClient return []; } + List commandIds = []; + UriBuilder uriBuilder = new(arrInstance.Url); - uriBuilder.Path = $"{uriBuilder.Path.TrimEnd('/')}/api/{ApiVersion}/command"; + uriBuilder.Path = $"{uriBuilder.Path.TrimEnd('/')}{CommandUrlPath}"; foreach (var command in GetSearchCommands(items)) { @@ -69,19 +71,28 @@ public class LidarrClient : ArrClient, ILidarrClient try { - HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync(() => SendRequestAsync(request)); - response?.Dispose(); - - _logger.LogInformation("{log}", GetSearchLog(arrInstance.Url, command, true, logContext)); + using HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync(() => SendRequestAsync(request)); + + if (response is not null) + { + long? commandId = await ReadCommandIdAsync(response); + + if (commandId.HasValue) + { + commandIds.Add(commandId.Value); + } + } + + _logger.LogInformation("{Log}", GetSearchLog(arrInstance.Url, command, true, logContext)); } catch { - _logger.LogError("{log}", GetSearchLog(arrInstance.Url, command, false, logContext)); + _logger.LogError("{Log}", GetSearchLog(arrInstance.Url, command, false, logContext)); throw; } } - return []; + return commandIds; } public override bool HasContentId(QueueRecord record) => record.ArtistId is not 0 && record.AlbumId is not 0; diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Arr/RadarrClient.cs b/code/backend/Cleanuparr.Infrastructure/Features/Arr/RadarrClient.cs index 1b018b3d..b056dbfb 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Arr/RadarrClient.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Arr/RadarrClient.cs @@ -77,7 +77,7 @@ public class RadarrClient : ArrClient, IRadarrClient try { - HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync(() => SendRequestAsync(request)); + using HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync(() => SendRequestAsync(request)); if (response is null) { @@ -85,15 +85,14 @@ public class RadarrClient : ArrClient, IRadarrClient } long? commandId = await ReadCommandIdAsync(response); - response.Dispose(); - _logger.LogInformation("{log}", GetSearchLog(arrInstance.Url, command, true, logContext)); + _logger.LogInformation("{Log}", GetSearchLog(arrInstance.Url, command, true, logContext)); return commandId.HasValue ? [commandId.Value] : []; } catch { - _logger.LogError("{log}", GetSearchLog(arrInstance.Url, command, false, logContext)); + _logger.LogError("{Log}", GetSearchLog(arrInstance.Url, command, false, logContext)); throw; } } diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Arr/ReadarrClient.cs b/code/backend/Cleanuparr.Infrastructure/Features/Arr/ReadarrClient.cs index 9f9f5416..4e65739b 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Arr/ReadarrClient.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Arr/ReadarrClient.cs @@ -53,16 +53,16 @@ public class ReadarrClient : ArrClient, IReadarrClient } List ids = items.Select(item => item.Id).ToList(); - + UriBuilder uriBuilder = new(arrInstance.Url); - uriBuilder.Path = $"{uriBuilder.Path.TrimEnd('/')}/api/{ApiVersion}/command"; - + uriBuilder.Path = $"{uriBuilder.Path.TrimEnd('/')}{CommandUrlPath}"; + ReadarrCommand command = new() { Name = "BookSearch", BookIds = ids, }; - + using HttpRequestMessage request = new(HttpMethod.Post, uriBuilder.Uri); request.Content = new StringContent( JsonSerializer.Serialize(command, CleanuparrJsonOptions.Outbound), @@ -75,18 +75,24 @@ public class ReadarrClient : ArrClient, IReadarrClient try { - HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync(() => SendRequestAsync(request)); - response?.Dispose(); - - _logger.LogInformation("{log}", GetSearchLog(arrInstance.Url, command, true, logContext)); + using HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync(() => SendRequestAsync(request)); + + long? commandId = null; + + if (response is not null) + { + commandId = await ReadCommandIdAsync(response); + } + + _logger.LogInformation("{Log}", GetSearchLog(arrInstance.Url, command, true, logContext)); + + return commandId.HasValue ? [commandId.Value] : []; } catch { - _logger.LogError("{log}", GetSearchLog(arrInstance.Url, command, false, logContext)); + _logger.LogError("{Log}", GetSearchLog(arrInstance.Url, command, false, logContext)); throw; } - - return []; } public override bool HasContentId(QueueRecord record) => record.AuthorId is not 0 && record.BookId is not 0; diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Arr/SonarrClient.cs b/code/backend/Cleanuparr.Infrastructure/Features/Arr/SonarrClient.cs index 10550802..09a04432 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Arr/SonarrClient.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Arr/SonarrClient.cs @@ -75,12 +75,11 @@ public class SonarrClient : ArrClient, ISonarrClient try { - HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync(() => SendRequestAsync(request)); + using HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync(() => SendRequestAsync(request)); if (response is not null) { long? commandId = await ReadCommandIdAsync(response); - response.Dispose(); if (commandId.HasValue) { diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Arr/WhisparrV3Client.cs b/code/backend/Cleanuparr.Infrastructure/Features/Arr/WhisparrV3Client.cs index c60431d7..2d3b1d2f 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Arr/WhisparrV3Client.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Arr/WhisparrV3Client.cs @@ -77,18 +77,24 @@ public class WhisparrV3Client : ArrClient, IWhisparrV3Client try { - HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync(() => SendRequestAsync(request)); - response?.Dispose(); - - _logger.LogInformation("{log}", GetSearchLog(arrInstance.Url, command, true, logContext)); + using HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync(() => SendRequestAsync(request)); + + long? commandId = null; + + if (response is not null) + { + commandId = await ReadCommandIdAsync(response); + } + + _logger.LogInformation("{Log}", GetSearchLog(arrInstance.Url, command, true, logContext)); + + return commandId.HasValue ? [commandId.Value] : []; } catch { - _logger.LogError("{log}", GetSearchLog(arrInstance.Url, command, false, logContext)); + _logger.LogError("{Log}", GetSearchLog(arrInstance.Url, command, false, logContext)); throw; } - - return []; } public override bool HasContentId(QueueRecord record) => record.MovieId is not 0;