diff --git a/CHANGELOG.md b/CHANGELOG.md
index e8f0920b..7504d50a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Recyclarr will now continue if `git fetch` fails for any repos, so long as there is an existing,
valid clone to use.
+### Fixed
+
+- Address regression causing `reset_unmatched_scores: false` to not be respected.
+
## [5.0.0] - 2023-06-22
This release contains **BREAKING CHANGES**. See the [v5.0 Upgrade Guide][breaking5] for required
diff --git a/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileApiPersistencePhase.cs b/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileApiPersistencePhase.cs
index 91f8efe6..49cb2b6c 100644
--- a/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileApiPersistencePhase.cs
+++ b/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileApiPersistencePhase.cs
@@ -32,7 +32,12 @@ public class QualityProfileApiPersistencePhase
private void LogQualityProfileUpdates(QualityProfileTransactionData transactions)
{
var updatedScores = transactions.UpdatedProfiles
- .Select(x => (x.UpdatedProfile.Name, x.UpdatedScores))
+ .Select(x => (
+ ProfileName: x.UpdatedProfile.Name,
+ Scores: x.UpdatedScores
+ .Where(y => y.Reason != FormatScoreUpdateReason.New && y.Dto.Score != y.NewScore)
+ .ToList()))
+ .Where(x => x.Scores.Any())
.ToList();
if (updatedScores.Count > 0)
@@ -50,7 +55,7 @@ public class QualityProfileApiPersistencePhase
_log.Information("Updated {ProfileCount} profiles and a total of {ScoreCount} scores",
transactions.UpdatedProfiles.Count,
- updatedScores.Sum(s => s.UpdatedScores.Count));
+ updatedScores.Sum(s => s.Scores.Count));
}
else
{
diff --git a/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfilePreviewPhase.cs b/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfilePreviewPhase.cs
index aea2dbf7..a26fd7eb 100644
--- a/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfilePreviewPhase.cs
+++ b/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfilePreviewPhase.cs
@@ -26,7 +26,7 @@ public class QualityProfilePreviewPhase
.AddColumn("[bold]New[/]")
.AddColumn("[bold]Reason[/]");
- foreach (var updatedScore in updatedScores.Where(x => x.Reason != FormatScoreUpdateReason.NoChange))
+ foreach (var updatedScore in updatedScores.Where(x => x.Dto.Score != x.NewScore))
{
table.AddRow(
updatedScore.Dto.Name,
diff --git a/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileTransactionPhase.cs b/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileTransactionPhase.cs
index d82873ac..a8301dcd 100644
--- a/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileTransactionPhase.cs
+++ b/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileTransactionPhase.cs
@@ -71,28 +71,31 @@ public class QualityProfileTransactionPhase
.FullJoin(profileDto.FormatItems,
x => x.FormatId,
x => x.Format,
+ // Exists in config, but not in service (these are unusual and should be errors)
+ // See `FormatScoreUpdateReason` for reason why we need this (it's preview mode)
l => new UpdatedFormatScore
{
Dto = new ProfileFormatItemDto {Format = l.FormatId, Name = l.CfName},
NewScore = l.Score,
Reason = FormatScoreUpdateReason.New
},
+ // Exists in service, but not in config
r => new UpdatedFormatScore
{
Dto = r,
- NewScore = 0,
+ NewScore = profileData.Profile.ResetUnmatchedScores ? 0 : r.Score,
Reason = FormatScoreUpdateReason.Reset
},
+ // Exists in both service and config
(l, r) => new UpdatedFormatScore
{
Dto = r,
NewScore = l.Score,
Reason = FormatScoreUpdateReason.Updated
})
- .Select(x => x.Dto.Score == x.NewScore ? x with {Reason = FormatScoreUpdateReason.NoChange} : x)
.ToList();
- return scoreMap.Any(x => x.Reason != FormatScoreUpdateReason.NoChange)
+ return scoreMap.Any(x => x.Dto.Score != x.NewScore)
? new UpdatedQualityProfile(profileDto) {UpdatedScores = scoreMap}
: null;
}
diff --git a/src/Recyclarr.Cli/Pipelines/QualityProfile/UpdatedFormatScore.cs b/src/Recyclarr.Cli/Pipelines/QualityProfile/UpdatedFormatScore.cs
index 5106a691..f40a6f20 100644
--- a/src/Recyclarr.Cli/Pipelines/QualityProfile/UpdatedFormatScore.cs
+++ b/src/Recyclarr.Cli/Pipelines/QualityProfile/UpdatedFormatScore.cs
@@ -4,11 +4,6 @@ namespace Recyclarr.Cli.Pipelines.QualityProfile;
public enum FormatScoreUpdateReason
{
- ///
- /// A score who's value did not change.
- ///
- NoChange,
-
///
/// A score that is changed.
///
diff --git a/src/Recyclarr.TrashLib/Config/Services/ServiceConfiguration.cs b/src/Recyclarr.TrashLib/Config/Services/ServiceConfiguration.cs
index 4fe7534a..2c2c6eb5 100644
--- a/src/Recyclarr.TrashLib/Config/Services/ServiceConfiguration.cs
+++ b/src/Recyclarr.TrashLib/Config/Services/ServiceConfiguration.cs
@@ -47,6 +47,6 @@ public record QualityDefinitionConfig
public record QualityProfileConfig
{
- public bool? ResetUnmatchedScores { get; init; }
+ public bool ResetUnmatchedScores { get; init; }
public string Name { get; init; } = "";
}
diff --git a/src/tests/Recyclarr.Cli.TestLibrary/NewQp.cs b/src/tests/Recyclarr.Cli.TestLibrary/NewQp.cs
index f7ffd78b..e9cdbecf 100644
--- a/src/tests/Recyclarr.Cli.TestLibrary/NewQp.cs
+++ b/src/tests/Recyclarr.Cli.TestLibrary/NewQp.cs
@@ -11,12 +11,12 @@ public static class NewQp
string profileName,
params (string TrashId, int FormatId, int Score)[] scores)
{
- return Processed(profileName, null, scores);
+ return Processed(profileName, false, scores);
}
public static ProcessedQualityProfileData Processed(
string profileName,
- bool? resetUnmatchedScores,
+ bool resetUnmatchedScores,
params (string TrashId, int FormatId, int Score)[] scores)
{
return Processed(profileName, resetUnmatchedScores,
@@ -25,7 +25,7 @@ public static class NewQp
public static ProcessedQualityProfileData Processed(
string profileName,
- bool? resetUnmatchedScores,
+ bool resetUnmatchedScores,
params (string CfName, string TrashId, int FormatId, int Score)[] scores)
{
return new ProcessedQualityProfileData(new QualityProfileConfig
diff --git a/src/tests/Recyclarr.Cli.Tests/Pipelines/QualityProfile/PipelinePhases/QualityProfileTransactionPhaseTest.cs b/src/tests/Recyclarr.Cli.Tests/Pipelines/QualityProfile/PipelinePhases/QualityProfileTransactionPhaseTest.cs
index a567ef71..b7d1033a 100644
--- a/src/tests/Recyclarr.Cli.Tests/Pipelines/QualityProfile/PipelinePhases/QualityProfileTransactionPhaseTest.cs
+++ b/src/tests/Recyclarr.Cli.Tests/Pipelines/QualityProfile/PipelinePhases/QualityProfileTransactionPhaseTest.cs
@@ -151,7 +151,7 @@ public class QualityProfileTransactionPhaseTest
}
[Test, AutoMockData]
- public void Reset_scores(
+ public void Reset_scores_with_reset_unmatched_true(
QualityProfileTransactionPhase sut)
{
var guideData = new[]
@@ -194,4 +194,48 @@ public class QualityProfileTransactionPhaseTest
NewQp.UpdatedScore("quality4", 0, 500, FormatScoreUpdateReason.New)
}, o => o.Excluding(x => x.Dto.Format));
}
+
+ [Test, AutoMockData]
+ public void Reset_scores_with_reset_unmatched_false(QualityProfileTransactionPhase sut)
+ {
+ var guideData = new[]
+ {
+ NewQp.Processed("profile1", false, ("quality3", "id3", 3, 100), ("quality4", "id4", 4, 500))
+ };
+
+ var serviceData = new[]
+ {
+ new QualityProfileDto
+ {
+ Name = "profile1",
+ FormatItems = new[]
+ {
+ new ProfileFormatItemDto
+ {
+ Name = "quality1",
+ Format = 1,
+ Score = 200
+ },
+ new ProfileFormatItemDto
+ {
+ Name = "quality2",
+ Format = 2,
+ Score = 300
+ }
+ }
+ }
+ };
+
+ var result = sut.Execute(guideData, serviceData);
+
+ result.UpdatedProfiles.Should()
+ .ContainSingle().Which.UpdatedScores.Should()
+ .BeEquivalentTo(new[]
+ {
+ NewQp.UpdatedScore("quality1", 200, 200, FormatScoreUpdateReason.Reset),
+ NewQp.UpdatedScore("quality2", 300, 300, FormatScoreUpdateReason.Reset),
+ NewQp.UpdatedScore("quality3", 0, 100, FormatScoreUpdateReason.New),
+ NewQp.UpdatedScore("quality4", 0, 500, FormatScoreUpdateReason.New)
+ }, o => o.Excluding(x => x.Dto.Format));
+ }
}