From eb03bcd0ff0c69717fc80bff08d10c5a34687c33 Mon Sep 17 00:00:00 2001 From: Flaminel Date: Wed, 21 Oct 2026 21:30:14 +0300 Subject: [PATCH] fixed missing episodes also listed as upgrades disabled --- .../Seeker/SeekerUnitEvaluatorTests.cs | 37 ++++++++++++++++++- .../Features/Seeker/SeekerUnitEvaluator.cs | 10 ++++- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Seeker/SeekerUnitEvaluatorTests.cs b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Seeker/SeekerUnitEvaluatorTests.cs index 02d744fb..0d619f3f 100644 --- a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Seeker/SeekerUnitEvaluatorTests.cs +++ b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Seeker/SeekerUnitEvaluatorTests.cs @@ -100,7 +100,8 @@ public class SeekerUnitEvaluatorTests : IDisposable long id, string status = "continuing", bool monitored = true, - List? tags = null) + List? tags = null, + int qualityProfileId = 0) { return new SearchableSeries { @@ -110,6 +111,7 @@ public class SeekerUnitEvaluatorTests : IDisposable Monitored = monitored, Tags = tags ?? [], SeriesType = "standard", + QualityProfileId = qualityProfileId, }; } @@ -600,7 +602,38 @@ public class SeekerUnitEvaluatorTests : IDisposable ReasonOf(evaluation, "Series 1 S01").ShouldBe(SeekerExclusionReason.SearchedThisCycle); } - #endregion + [Fact] + public async Task EvaluateAsync_Sonarr_MissingEpisodeWithStaleCustomFormatRowAndUpgradesDisabled_IsNotExcluded() + { + // Arrange + SetupSonarr([Series(1, qualityProfileId: 4)], [Episode(20, 1)]); + _sonarrClient.GetQualityProfilesAsync(Arg.Any()) + .Returns([new ArrQualityProfile { Id = 4, Name = "Locked", UpgradeAllowed = false }]); + _eventsContext.CustomFormatScoreEntries.Add(new CustomFormatScoreEntry + { + ArrInstanceId = _sonarr.Id, + ExternalItemId = 1, + EpisodeId = 20, + Title = "Series 1 S01E01", + ItemType = InstanceType.Sonarr, + SeasonNumber = 1, + AirDateUtc = _timeProvider.GetUtcNow().AddDays(-30), + IsMonitored = true, + CurrentScore = 5, + CutoffScore = 10, + }); + await _eventsContext.SaveChangesAsync(); + + // Act + SeekerEvaluation evaluation = await EvaluateSonarrAsync(InstanceConfig(_sonarr, c => c.UseCustomFormatScore = true)); + + // Assert + SeekerSearchUnit unit = evaluation.Units.ShouldHaveSingleItem(); + unit.Reasons.ShouldBe([SeekerSearchReason.Missing]); + evaluation.Excluded.ShouldNotContain(e => e.Reason == SeekerExclusionReason.UpgradesDisabled); + } + +#endregion #region Stage counts diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Seeker/SeekerUnitEvaluator.cs b/code/backend/Cleanuparr.Infrastructure/Features/Seeker/SeekerUnitEvaluator.cs index b89582b7..2b9ca007 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Seeker/SeekerUnitEvaluator.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Seeker/SeekerUnitEvaluator.cs @@ -439,6 +439,8 @@ public sealed class SeekerUnitEvaluator && (!instanceConfig.MonitoredOnly || e.IsMonitored)) .ToListAsync(cancellationToken); + HashSet wantedEpisodeIds = wanted.Select(w => w.EpisodeId).ToHashSet(); + foreach (CustomFormatScoreEntry entry in belowCutoff) { if (!seriesById.TryGetValue(entry.ExternalItemId, out SearchableSeries? series)) @@ -448,8 +450,12 @@ public sealed class SeekerUnitEvaluator if (upgradesDisabledProfileIds.Contains(series.QualityProfileId)) { - _logger.LogDebug("skip | upgrades disabled in profile | {Title} | {InstanceName}", entry.Title, arrInstance.Name); - ExcludeEpisode(entry.ExternalItemId, entry.EpisodeId, entry.Title, SeekerExclusionReason.UpgradesDisabled); + if (!wantedEpisodeIds.Contains(entry.EpisodeId)) + { + _logger.LogDebug("skip | upgrades disabled in profile | {Title} | {InstanceName}", entry.Title, arrInstance.Name); + ExcludeEpisode(entry.ExternalItemId, entry.EpisodeId, entry.Title, SeekerExclusionReason.UpgradesDisabled); + } + continue; }