feat: Deprecate `names` list for custom formats in config YAML

gui
Robert Dailey 2 years ago
parent 008ca5dd10
commit 154ec7cf7e

@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- JSON Schema added to the config template YAML file.
- `names` list under `custom_formats` in config YAML is now deprecated. Use `trash_ids` to list your
custom formats instead.
## [2.2.1] - 2022-06-18

@ -127,7 +127,7 @@
"items": {
"type": "object",
"additionalProperties": false,
"description": "A list of one or more sets of custom formats (by name and/or trash_id), each with an optional set of quality profiles names that identify which quality profiles to assign the scores for those custom formats to.",
"description": "A list of one or more sets of custom formats each with an optional set of quality profiles names that identify which quality profiles to assign the scores for those custom formats to.",
"anyOf": [
{"required": ["trash_ids"]},
{"required": ["names"]}

@ -11,7 +11,7 @@
# Configuration specific to Sonarr
sonarr:
# Set the URL/API Key to your actual instance
# Set the URL/API Key to your actual instance
- base_url: http://localhost:8989
api_key: f7e74ba6c80046e39e076a27af5a8444
@ -46,19 +46,17 @@ radarr:
delete_old_custom_formats: false
custom_formats:
# A list of custom formats to sync to Radarr. Must match the "name" in the importable JSON.
# Do NOT use the heading names here, those do not work! These are case-insensitive.
- names:
- BR-DISK
- EVO (no WEBDL)
- LQ
- x265 (720/1080p)
- 3D
# A list of custom formats to sync to Radarr. Must match the "trash_id" in the guide JSON.
- trash_ids:
- ed38b889b31be83fda192888e2286d83 # BR-DISK
- 90cedc1fea7ea5d11298bebd3d1d3223 # EVO (no WEBDL)
- 90a6f9a284dff5103f6346090e6280c8 # LQ
- dc98083864ea246d05a42df0d05f81cc # x265 (720/1080p)
- b8cd450cbfa689c0259a01d9e29ba3d6 # 3D
# Uncomment the below properties to specify one or more quality profiles that should be
# updated with scores from the guide for each custom format. Without this, custom formats
# are synced to Radarr but no scores are set in any quality profiles.
# quality_profiles:
# - name: Quality Profile 1
# - name: Quality Profile 2

@ -4,6 +4,7 @@ using FluentAssertions;
using Newtonsoft.Json.Linq;
using NSubstitute;
using NUnit.Framework;
using Serilog;
using TestLibrary.FluentAssertions;
using TrashLib.Radarr.Config;
using TrashLib.Radarr.CustomFormat.Guide;
@ -21,7 +22,7 @@ public class GuideProcessorTest
private class TestGuideProcessorSteps : IGuideProcessorSteps
{
public ICustomFormatStep CustomFormat { get; } = new CustomFormatStep();
public IConfigStep Config { get; } = new ConfigStep();
public IConfigStep Config { get; } = new ConfigStep(Substitute.For<ILogger>());
public IQualityProfileStep QualityProfile { get; } = new QualityProfileStep();
}

@ -1,6 +1,7 @@
using FluentAssertions;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using TestLibrary.AutoFixture;
using TrashLib.Radarr.Config;
using TrashLib.Radarr.CustomFormat.Models;
using TrashLib.Radarr.CustomFormat.Models.Cache;
@ -13,8 +14,8 @@ namespace TrashLib.Tests.Radarr.CustomFormat.Processors.GuideSteps;
[Parallelizable(ParallelScope.All)]
public class ConfigStepTest
{
[Test]
public void Cache_names_are_used_instead_of_name_in_json_data()
[Test, AutoMockData]
public void Cache_names_are_used_instead_of_name_in_json_data(ConfigStep processor)
{
var testProcessedCfs = new List<ProcessedCustomFormatData>
{
@ -30,7 +31,6 @@ public class ConfigStepTest
}
};
var processor = new ConfigStep();
processor.Process(testProcessedCfs, testConfig);
processor.CustomFormatsNotInGuide.Should().BeEmpty();
@ -46,8 +46,8 @@ public class ConfigStepTest
.WhenTypeIs<JToken>());
}
[Test]
public void Custom_formats_missing_from_config_are_skipped()
[Test, AutoMockData]
public void Custom_formats_missing_from_config_are_skipped(ConfigStep processor)
{
var testProcessedCfs = new List<ProcessedCustomFormatData>
{
@ -63,7 +63,6 @@ public class ConfigStepTest
}
};
var processor = new ConfigStep();
processor.Process(testProcessedCfs, testConfig);
processor.CustomFormatsNotInGuide.Should().BeEmpty();
@ -81,8 +80,8 @@ public class ConfigStepTest
.WhenTypeIs<JToken>());
}
[Test]
public void Custom_formats_missing_from_guide_are_added_to_not_in_guide_list()
[Test, AutoMockData]
public void Custom_formats_missing_from_guide_are_added_to_not_in_guide_list(ConfigStep processor)
{
var testProcessedCfs = new List<ProcessedCustomFormatData>
{
@ -98,7 +97,6 @@ public class ConfigStepTest
}
};
var processor = new ConfigStep();
processor.Process(testProcessedCfs, testConfig);
processor.CustomFormatsNotInGuide.Should().BeEquivalentTo(new List<string> {"name3"}, op => op
@ -118,8 +116,8 @@ public class ConfigStepTest
.WhenTypeIs<JToken>());
}
[Test]
public void Duplicate_config_name_and_id_are_ignored()
[Test, AutoMockData]
public void Duplicate_config_name_and_id_are_ignored(ConfigStep processor)
{
var testProcessedCfs = new List<ProcessedCustomFormatData>
{
@ -135,7 +133,6 @@ public class ConfigStepTest
}
};
var processor = new ConfigStep();
processor.Process(testProcessedCfs, testConfig);
processor.CustomFormatsNotInGuide.Should().BeEmpty();
@ -148,8 +145,8 @@ public class ConfigStepTest
});
}
[Test]
public void Duplicate_config_names_are_ignored()
[Test, AutoMockData]
public void Duplicate_config_names_are_ignored(ConfigStep processor)
{
var testProcessedCfs = new List<ProcessedCustomFormatData>
{
@ -161,7 +158,6 @@ public class ConfigStepTest
new() {Names = new List<string> {"name1", "name1"}}
};
var processor = new ConfigStep();
processor.Process(testProcessedCfs, testConfig);
processor.CustomFormatsNotInGuide.Should().BeEmpty();
@ -174,8 +170,8 @@ public class ConfigStepTest
});
}
[Test]
public void Find_custom_formats_by_name_and_trash_id()
[Test, AutoMockData]
public void Find_custom_formats_by_name_and_trash_id(ConfigStep processor)
{
var testProcessedCfs = new List<ProcessedCustomFormatData>
{
@ -197,7 +193,6 @@ public class ConfigStepTest
}
};
var processor = new ConfigStep();
processor.Process(testProcessedCfs, testConfig);
processor.CustomFormatsNotInGuide.Should().BeEmpty();

@ -2,6 +2,7 @@ using System.Collections.ObjectModel;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using TestLibrary.AutoFixture;
using TestLibrary.FluentAssertions;
using TrashLib.Radarr.Config;
using TrashLib.Radarr.CustomFormat.Models;
@ -61,8 +62,8 @@ public class CustomFormatStepTest
});
}
[Test]
public void Cache_entry_is_not_set_when_id_is_different()
[Test, AutoMockData]
public void Cache_entry_is_not_set_when_id_is_different(CustomFormatStep processor)
{
var guideData = new List<CustomFormatData>
{
@ -82,7 +83,6 @@ public class CustomFormatStepTest
}
};
var processor = new CustomFormatStep();
processor.Process(guideData, testConfig, testCache);
processor.DuplicatedCustomFormats.Should().BeEmpty();
@ -95,8 +95,8 @@ public class CustomFormatStepTest
});
}
[Test]
public void Cfs_not_in_config_are_skipped()
[Test, AutoMockData]
public void Cfs_not_in_config_are_skipped(CustomFormatStep processor)
{
var ctx = new Context();
var testConfig = new List<CustomFormatConfig>
@ -104,7 +104,6 @@ public class CustomFormatStepTest
new() {Names = new List<string> {"name1", "name3"}}
};
var processor = new CustomFormatStep();
processor.Process(ctx.TestGuideData, testConfig, new CustomFormatCache());
processor.DuplicatedCustomFormats.Should().BeEmpty();
@ -118,8 +117,8 @@ public class CustomFormatStepTest
});
}
[Test]
public void Config_cfs_in_different_sections_are_processed()
[Test, AutoMockData]
public void Config_cfs_in_different_sections_are_processed(CustomFormatStep processor)
{
var ctx = new Context();
var testConfig = new List<CustomFormatConfig>
@ -128,7 +127,6 @@ public class CustomFormatStepTest
new() {Names = new List<string> {"name2"}}
};
var processor = new CustomFormatStep();
processor.Process(ctx.TestGuideData, testConfig, new CustomFormatCache());
processor.DuplicatedCustomFormats.Should().BeEmpty();
@ -143,8 +141,8 @@ public class CustomFormatStepTest
op => op.Using(new JsonEquivalencyStep()));
}
[Test]
public void Custom_format_is_deleted_if_in_config_and_cache_but_not_in_guide()
[Test, AutoMockData]
public void Custom_format_is_deleted_if_in_config_and_cache_but_not_in_guide(CustomFormatStep processor)
{
var guideData = new List<CustomFormatData>
{
@ -161,7 +159,6 @@ public class CustomFormatStepTest
TrashIdMappings = new Collection<TrashIdMapping> {new("id1000", "name1")}
};
var processor = new CustomFormatStep();
processor.Process(guideData, testConfig, testCache);
processor.DuplicatedCustomFormats.Should().BeEmpty();
@ -174,8 +171,8 @@ public class CustomFormatStepTest
});
}
[Test]
public void Custom_format_is_deleted_if_not_in_config_but_in_cache_and_in_guide()
[Test, AutoMockData]
public void Custom_format_is_deleted_if_not_in_config_but_in_cache_and_in_guide(CustomFormatStep processor)
{
var cache = new CustomFormatCache
{
@ -187,7 +184,6 @@ public class CustomFormatStepTest
new("3D", "id1", null, new JObject())
};
var processor = new CustomFormatStep();
processor.Process(guideCfs, Array.Empty<CustomFormatConfig>(), cache);
processor.DuplicatedCustomFormats.Should().BeEmpty();
@ -196,8 +192,8 @@ public class CustomFormatStepTest
processor.ProcessedCustomFormats.Should().BeEmpty();
}
[Test]
public void Custom_format_name_in_cache_is_updated_if_renamed_in_guide_and_config()
[Test, AutoMockData]
public void Custom_format_name_in_cache_is_updated_if_renamed_in_guide_and_config(CustomFormatStep processor)
{
var guideData = new List<CustomFormatData>
{
@ -214,7 +210,6 @@ public class CustomFormatStepTest
TrashIdMappings = new Collection<TrashIdMapping> {new("id1", "name1")}
};
var processor = new CustomFormatStep();
processor.Process(guideData, testConfig, testCache);
processor.DuplicatedCustomFormats.Should().BeEmpty();
@ -225,8 +220,8 @@ public class CustomFormatStepTest
.BeEquivalentTo(new TrashIdMapping("id1", "name2"));
}
[Test]
public void Duplicates_are_recorded_and_removed_from_processed_custom_formats_list()
[Test, AutoMockData]
public void Duplicates_are_recorded_and_removed_from_processed_custom_formats_list(CustomFormatStep processor)
{
var guideData = new List<CustomFormatData>
{
@ -239,7 +234,6 @@ public class CustomFormatStepTest
new() {Names = new List<string> {"name1"}}
};
var processor = new CustomFormatStep();
processor.Process(guideData, testConfig, null);
//Dictionary<string, List<ProcessedCustomFormatData>>
@ -255,8 +249,8 @@ public class CustomFormatStepTest
processor.ProcessedCustomFormats.Should().BeEmpty();
}
[Test]
public void Match_cf_names_regardless_of_case_in_config()
[Test, AutoMockData]
public void Match_cf_names_regardless_of_case_in_config(CustomFormatStep processor)
{
var ctx = new Context();
var testConfig = new List<CustomFormatConfig>
@ -264,7 +258,6 @@ public class CustomFormatStepTest
new() {Names = new List<string> {"name1", "NAME1"}}
};
var processor = new CustomFormatStep();
processor.Process(ctx.TestGuideData, testConfig, new CustomFormatCache());
processor.DuplicatedCustomFormats.Should().BeEmpty();
@ -277,8 +270,8 @@ public class CustomFormatStepTest
op => op.Using(new JsonEquivalencyStep()));
}
[Test]
public void Match_custom_format_using_trash_id()
[Test, AutoMockData]
public void Match_custom_format_using_trash_id(CustomFormatStep processor)
{
var guideData = new List<CustomFormatData>
{
@ -291,7 +284,6 @@ public class CustomFormatStepTest
new() {TrashIds = new List<string> {"id2"}}
};
var processor = new CustomFormatStep();
processor.Process(guideData, testConfig, null);
processor.DuplicatedCustomFormats.Should().BeEmpty();
@ -304,8 +296,8 @@ public class CustomFormatStepTest
});
}
[Test]
public void Non_existent_cfs_in_config_are_skipped()
[Test, AutoMockData]
public void Non_existent_cfs_in_config_are_skipped(CustomFormatStep processor)
{
var ctx = new Context();
var testConfig = new List<CustomFormatConfig>
@ -313,7 +305,6 @@ public class CustomFormatStepTest
new() {Names = new List<string> {"doesnt_exist"}}
};
var processor = new CustomFormatStep();
processor.Process(ctx.TestGuideData, testConfig, new CustomFormatCache());
processor.DuplicatedCustomFormats.Should().BeEmpty();
@ -322,8 +313,8 @@ public class CustomFormatStepTest
processor.ProcessedCustomFormats.Should().BeEmpty();
}
[Test]
public void Score_from_json_takes_precedence_over_score_from_guide()
[Test, AutoMockData]
public void Score_from_json_takes_precedence_over_score_from_guide(CustomFormatStep processor)
{
var guideData = new List<CustomFormatData>
{
@ -342,7 +333,6 @@ public class CustomFormatStepTest
}
};
var processor = new CustomFormatStep();
processor.Process(guideData, testConfig, null);
processor.DuplicatedCustomFormats.Should().BeEmpty();

@ -1,20 +1,35 @@
using Common.Extensions;
using Serilog;
using TrashLib.Radarr.Config;
using TrashLib.Radarr.CustomFormat.Models;
namespace TrashLib.Radarr.CustomFormat.Processors.GuideSteps;
internal class ConfigStep : IConfigStep
public class ConfigStep : IConfigStep
{
private readonly ILogger _log;
private readonly List<ProcessedConfigData> _configData = new();
private readonly List<string> _customFormatsNotInGuide = new();
public IReadOnlyCollection<string> CustomFormatsNotInGuide => _customFormatsNotInGuide;
public IReadOnlyCollection<ProcessedConfigData> ConfigData => _configData;
public void Process(IReadOnlyCollection<ProcessedCustomFormatData> processedCfs,
IEnumerable<CustomFormatConfig> config)
public ConfigStep(ILogger log)
{
_log = log;
}
public void Process(
IReadOnlyCollection<ProcessedCustomFormatData> processedCfs,
IReadOnlyCollection<CustomFormatConfig> config)
{
if (config.SelectMany(x => x.Names).Any())
{
_log.Warning(
"`names` list for `custom_formats` is deprecated and will be removed in the future; use " +
"`trash_ids` instead");
}
foreach (var singleConfig in config)
{
var validCfs = new List<ProcessedCustomFormatData>();

@ -5,7 +5,7 @@ using TrashLib.Radarr.CustomFormat.Models.Cache;
namespace TrashLib.Radarr.CustomFormat.Processors.GuideSteps;
internal class CustomFormatStep : ICustomFormatStep
public class CustomFormatStep : ICustomFormatStep
{
private readonly List<(string, string)> _customFormatsWithOutdatedNames = new();
private readonly List<ProcessedCustomFormatData> _processedCustomFormats = new();

@ -9,5 +9,5 @@ public interface IConfigStep
IReadOnlyCollection<ProcessedConfigData> ConfigData { get; }
void Process(IReadOnlyCollection<ProcessedCustomFormatData> processedCfs,
IEnumerable<CustomFormatConfig> config);
IReadOnlyCollection<CustomFormatConfig> config);
}

@ -210,35 +210,9 @@ Synchronization]] page.
Radarr **will not be deleted** if you enable this setting.
- `custom_formats` (Optional; *Default: No custom formats are synced*)<br>
A list of one or more sets of custom formats (by name and/or trash_id), each with an optional set
of quality profiles names that identify which quality profiles to assign the scores for those
custom formats to. The child properties documented below apply to each element of this list.
> **Note:** Even though `names` and `trash_ids` below are marked *optional*, at least one of them
> is required. For example, if `names` is empty you must use `trash_ids` and vice versa. You can
> also use both together if you want.
>
> **Advice:** When would you use `names` or `trash_ids`? Rule of thumb: Stick to `trash_ids`. It's
> more robust and immune to breaking than using names. Names can change, Trash IDs never change.
- `names` (Optional; *`trash_ids` is required if not used*)<br>
A list of one or more custom format names to synchronize to Radarr. The names *must* be taken
from the JSON itself in the guide, for example:
```json
{
"trash_id": "496f355514737f7d83bf7aa4d24f8169",
"name": "TrueHD ATMOS",
"includeCustomFormatWhenRenaming": false
}
```
You take the value of `"name"` above and add it to the list of names like so:
```yml
names:
- TrueHD ATMOS
```
A list of one or more sets of custom formats each with an optional set of quality profiles names
that identify which quality profiles to assign the scores for those custom formats to. The child
properties documented below apply to each element of this list.
- `trash_ids` (Optional; *`names` is required if not used*)<br>
A list of one or more Trash IDs of custom formats to synchronize to Radarr. The IDs *must* be
@ -264,7 +238,7 @@ Synchronization]] page.
>
> - If `delete_old_custom_formats` is set to true, custom formats are **deleted** in Radarr if
> you remove them from this list.
> - It's OK for the same custom format to exist in multiple lists of `names`. Recyclarr will
> - It's OK for the same custom format to exist in multiple lists of `trash_ids`. Recyclarr will
> only ever synchronize it once. Allowing it to be specified multiple times allows you to
> assign it to different profiles with different scores.

Loading…
Cancel
Save