feat: allow optional terms to be synced

New `filter` YAML config provided to enable this feature using
`include_optional` (true or false).
recyclarr
Robert Dailey 3 years ago
parent fc7c8bce30
commit b1abb2ca85

@ -8,6 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added
- New filter configuration for Sonarr release profiles that (for now) allows you to include optional
terms. Look for `filter` in the [Configuration Reference] for more details.
[Configuration Reference]: https://github.com/rcdailey/trash-updater/wiki/Configuration-Reference
## [1.2.0] - 2021-04-19 ## [1.2.0] - 2021-04-19
### Added ### Added
@ -49,4 +56,4 @@ See the [Python Migration Guide][py-mig] for details on how to update your YAML
[1.2.0]: https://github.com/rcdailey/trash-updater/compare/v1.1.0...v1.2.0 [1.2.0]: https://github.com/rcdailey/trash-updater/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/rcdailey/trash-updater/compare/v1.0.0...v1.1.0 [1.1.0]: https://github.com/rcdailey/trash-updater/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/rcdailey/trash-updater/compare/v0.1.0...v1.0.0 [1.0.0]: https://github.com/rcdailey/trash-updater/compare/v0.1.0...v1.0.0
[0.1.0]: https://github.com/rcdailey/trash-updater/releases/tag/v0.1.0 [0.1.0]: https://github.com/rcdailey/trash-updater/releases/tag/v0.1.0

@ -0,0 +1,92 @@
using System.Collections.Generic;
using FluentAssertions;
using NUnit.Framework;
using Trash.Sonarr;
using Trash.Sonarr.ReleaseProfile;
namespace Trash.Tests.Sonarr.ReleaseProfile
{
[TestFixture]
[Parallelizable(ParallelScope.All)]
public class FilteredProfileDataTest
{
[Test]
public void Filter_IncludeOptional_HasAllOptionalItems()
{
var config = new ReleaseProfileConfig();
config.Filter.IncludeOptional = true;
var profileData = new ProfileData
{
Ignored = new List<string> {"ignored1"},
Required = new List<string> {"required1"},
Preferred = new Dictionary<int, List<string>>
{
{100, new List<string> {"preferred1"}}
},
Optional = new ProfileDataOptional
{
Ignored = new List<string> {"ignored2"},
Required = new List<string> {"required2"},
Preferred = new Dictionary<int, List<string>>
{
{200, new List<string> {"preferred2"}},
{100, new List<string> {"preferred3"}}
}
}
};
var filtered = new FilteredProfileData(profileData, config);
filtered.Should().BeEquivalentTo(new
{
Ignored = new List<string> {"ignored1", "ignored2"},
Required = new List<string> {"required1", "required2"},
Preferred = new Dictionary<int, List<string>>
{
{100, new List<string> {"preferred1", "preferred3"}},
{200, new List<string> {"preferred2"}}
}
});
}
[Test]
public void Filter_ExcludeOptional_HasNoOptionalItems()
{
var config = new ReleaseProfileConfig();
config.Filter.IncludeOptional = false;
var profileData = new ProfileData
{
Ignored = new List<string> {"ignored1"},
Required = new List<string> {"required1"},
Preferred = new Dictionary<int, List<string>>
{
{100, new List<string> {"preferred1"}}
},
Optional = new ProfileDataOptional
{
Ignored = new List<string> {"ignored2"},
Required = new List<string> {"required2"},
Preferred = new Dictionary<int, List<string>>
{
{200, new List<string> {"preferred2"}},
{100, new List<string> {"preferred3"}}
}
}
};
var filtered = new FilteredProfileData(profileData, config);
filtered.Should().BeEquivalentTo(new
{
Ignored = new List<string> {"ignored1"},
Required = new List<string> {"required1"},
Preferred = new Dictionary<int, List<string>>
{
{100, new List<string> {"preferred1"}}
}
});
}
}
}

@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Linq;
namespace Trash.Sonarr.ReleaseProfile
{
public class FilteredProfileData
{
private readonly ReleaseProfileConfig _config;
private readonly ProfileData _profileData;
public FilteredProfileData(ProfileData profileData, ReleaseProfileConfig config)
{
_profileData = profileData;
_config = config;
}
public List<string> Required => _config.Filter.IncludeOptional
? _profileData.Required.Concat(_profileData.Optional.Required).ToList()
: _profileData.Required;
public List<string> Ignored => _config.Filter.IncludeOptional
? _profileData.Ignored.Concat(_profileData.Optional.Ignored).ToList()
: _profileData.Ignored;
public Dictionary<int, List<string>> Preferred => _config.Filter.IncludeOptional
? _profileData.Preferred
.Union(_profileData.Optional.Preferred)
.GroupBy(kvp => kvp.Key)
.ToDictionary(grp => grp.Key, grp => new List<string>(grp.SelectMany(l => l.Value)))
: _profileData.Preferred;
public bool? IncludePreferredWhenRenaming => _profileData.IncludePreferredWhenRenaming;
}
}

@ -56,12 +56,13 @@ namespace Trash.Sonarr.ReleaseProfile
return $"[Trash] {titleType} - {profileName}"; return $"[Trash] {titleType} - {profileName}";
} }
private static SonarrReleaseProfile? GetProfileToUpdate(List<SonarrReleaseProfile> profiles, string profileName) private static SonarrReleaseProfile? GetProfileToUpdate(IEnumerable<SonarrReleaseProfile> profiles,
string profileName)
{ {
return profiles.FirstOrDefault(p => p.Name == profileName); return profiles.FirstOrDefault(p => p.Name == profileName);
} }
private static void SetupProfileRequestObject(SonarrReleaseProfile profileToUpdate, ProfileData profile, private static void SetupProfileRequestObject(SonarrReleaseProfile profileToUpdate, FilteredProfileData profile,
List<int> tagIds) List<int> tagIds)
{ {
profileToUpdate.Preferred = profile.Preferred profileToUpdate.Preferred = profile.Preferred
@ -80,7 +81,7 @@ namespace Trash.Sonarr.ReleaseProfile
profileToUpdate.Tags = tagIds; profileToUpdate.Tags = tagIds;
} }
private async Task UpdateExistingProfile(SonarrReleaseProfile profileToUpdate, ProfileData profile, private async Task UpdateExistingProfile(SonarrReleaseProfile profileToUpdate, FilteredProfileData profile,
List<int> tagIds) List<int> tagIds)
{ {
Log.Debug("Update existing profile with id {ProfileId}", profileToUpdate.Id); Log.Debug("Update existing profile with id {ProfileId}", profileToUpdate.Id);
@ -88,7 +89,7 @@ namespace Trash.Sonarr.ReleaseProfile
await _api.UpdateReleaseProfile(profileToUpdate); await _api.UpdateReleaseProfile(profileToUpdate);
} }
private async Task CreateNewProfile(string title, ProfileData profile, List<int> tagIds) private async Task CreateNewProfile(string title, FilteredProfileData profile, List<int> tagIds)
{ {
var newProfile = new SonarrReleaseProfile var newProfile = new SonarrReleaseProfile
{ {
@ -101,7 +102,7 @@ namespace Trash.Sonarr.ReleaseProfile
} }
private async Task ProcessReleaseProfiles(IDictionary<string, ProfileData> profiles, private async Task ProcessReleaseProfiles(IDictionary<string, ProfileData> profiles,
ReleaseProfileConfig profile) ReleaseProfileConfig config)
{ {
await DoVersionEnforcement(); await DoVersionEnforcement();
@ -109,11 +110,11 @@ namespace Trash.Sonarr.ReleaseProfile
// If tags were provided, ensure they exist. Tags that do not exist are added first, so that we // If tags were provided, ensure they exist. Tags that do not exist are added first, so that we
// may specify them with the release profile request payload. // may specify them with the release profile request payload.
if (profile.Tags.Count > 0) if (config.Tags.Count > 0)
{ {
var sonarrTags = await _api.GetTags(); var sonarrTags = await _api.GetTags();
await CreateMissingTags(sonarrTags, profile.Tags); await CreateMissingTags(sonarrTags, config.Tags);
tagIds = sonarrTags.Where(t => profile.Tags.Any(ct => ct.EqualsIgnoreCase(t.Label))) tagIds = sonarrTags.Where(t => config.Tags.Any(ct => ct.EqualsIgnoreCase(t.Label)))
.Select(t => t.Id) .Select(t => t.Id)
.ToList(); .ToList();
} }
@ -125,17 +126,18 @@ namespace Trash.Sonarr.ReleaseProfile
foreach (var (name, profileData) in profiles) foreach (var (name, profileData) in profiles)
{ {
var title = BuildProfileTitle(profile.Type, name); var filteredProfileData = new FilteredProfileData(profileData, config);
var title = BuildProfileTitle(config.Type, name);
var profileToUpdate = GetProfileToUpdate(existingProfiles, title); var profileToUpdate = GetProfileToUpdate(existingProfiles, title);
if (profileToUpdate != null) if (profileToUpdate != null)
{ {
Log.Information("Update existing profile: {ProfileName}", title); Log.Information("Update existing profile: {ProfileName}", title);
await UpdateExistingProfile(profileToUpdate, profileData, tagIds); await UpdateExistingProfile(profileToUpdate, filteredProfileData, tagIds);
} }
else else
{ {
Log.Information("Create new profile: {ProfileName}", title); Log.Information("Create new profile: {ProfileName}", title);
await CreateNewProfile(title, profileData, tagIds); await CreateNewProfile(title, filteredProfileData, tagIds);
} }
} }
} }
@ -146,9 +148,13 @@ namespace Trash.Sonarr.ReleaseProfile
{ {
Log.Information("Processing Release Profile: {ProfileName}", profile.Type); Log.Information("Processing Release Profile: {ProfileName}", profile.Type);
var markdown = await _parser.GetMarkdownData(profile.Type); var markdown = await _parser.GetMarkdownData(profile.Type);
var profiles = Utils.FilterProfiles(_parser.ParseMarkdown(profile, markdown)); var profiles = Utils.FilterProfiles(_parser.ParseMarkdown(profile, markdown));
if (profile.Filter.IncludeOptional)
{
Log.Information("Configuration is set to allow optional terms to be synchronized");
}
if (args.Preview) if (args.Preview)
{ {
Utils.PrintTermsAndScores(profiles); Utils.PrintTermsAndScores(profiles);

@ -26,6 +26,14 @@ namespace Trash.Sonarr
{ {
public ReleaseProfileType Type { get; init; } public ReleaseProfileType Type { get; init; }
public bool StrictNegativeScores { get; init; } public bool StrictNegativeScores { get; init; }
public SonarrProfileFilterConfig Filter { get; init; } = new();
public List<string> Tags { get; init; } = new(); public List<string> Tags { get; init; } = new();
} }
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
public class SonarrProfileFilterConfig
{
public bool IncludeOptional { get; set; }
// todo: Add Include & Exclude later (list of strings)
}
} }

@ -37,6 +37,8 @@ sonarr:
- anime - anime
- type: series - type: series
strict_negative_scores: false strict_negative_scores: false
filter:
include_optional: true
tags: tags:
- tv - tv
``` ```
@ -81,6 +83,11 @@ sonarr:
release profile (if present) are removed and replaced with only the tags in this list. If no release profile (if present) are removed and replaced with only the tags in this list. If no
tags are specified, no tags will be set on the release profile. tags are specified, no tags will be set on the release profile.
- `filter` (Optional): Defines various ways that release profile terms from the guide are
synchronized with Sonarr. Any combination of the below properties may be specified here:
- `include_optional`: Set to `true` to include terms marked "Optional" in the guide. By default,
optional terms are *not* synchronized to Sonarr. The default is `false`.
[sonarr_quality]: https://trash-guides.info/Sonarr/V3/Sonarr-Quality-Settings-File-Size/ [sonarr_quality]: https://trash-guides.info/Sonarr/V3/Sonarr-Quality-Settings-File-Size/
[sonarr_profile_anime]: https://trash-guides.info/Sonarr/V3/Sonarr-Release-Profile-RegEx-Anime/ [sonarr_profile_anime]: https://trash-guides.info/Sonarr/V3/Sonarr-Release-Profile-RegEx-Anime/
[sonarr_profile_series]: https://trash-guides.info/Sonarr/V3/Sonarr-Release-Profile-RegEx/ [sonarr_profile_series]: https://trash-guides.info/Sonarr/V3/Sonarr-Release-Profile-RegEx/

Loading…
Cancel
Save