mirror of
https://github.com/Sonarr/Sonarr
synced 2025-10-05 23:52:45 +02:00
New: Excluded Tags on Release Profile
This commit is contained in:
@@ -17,6 +17,7 @@ export interface TagDetail extends ModelBase {
|
||||
indexerIds: number[];
|
||||
notificationIds: number[];
|
||||
restrictionIds: number[];
|
||||
excludedReleaseProfileIds: number[];
|
||||
seriesIds: number[];
|
||||
}
|
||||
|
||||
|
@@ -5,14 +5,18 @@ import { addTag } from 'Store/Actions/tagActions';
|
||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import sortByProp from 'Utilities/Array/sortByProp';
|
||||
import TagInput, { TagBase } from './TagInput';
|
||||
import TagInput, { TagBase, TagInputProps } from './TagInput';
|
||||
|
||||
interface SeriesTag extends TagBase {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface SeriesTagInputProps<V> {
|
||||
export interface SeriesTagInputProps<V>
|
||||
extends Omit<
|
||||
TagInputProps<SeriesTag>,
|
||||
'tags' | 'tagList' | 'onTagAdd' | 'onTagDelete' | 'onChange'
|
||||
> {
|
||||
name: string;
|
||||
value: V;
|
||||
onChange: (change: InputChanged<V>) => void;
|
||||
@@ -63,6 +67,7 @@ export default function SeriesTagInput<V extends number | number[]>({
|
||||
name,
|
||||
value,
|
||||
onChange,
|
||||
...otherProps
|
||||
}: SeriesTagInputProps<V>) {
|
||||
const dispatch = useDispatch();
|
||||
const isArray = Array.isArray(value);
|
||||
@@ -135,6 +140,7 @@ export default function SeriesTagInput<V extends number | number[]>({
|
||||
|
||||
return (
|
||||
<TagInput
|
||||
{...otherProps}
|
||||
name={name}
|
||||
tags={tags}
|
||||
tagList={tagList}
|
||||
|
@@ -1,16 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Tag } from 'App/State/TagsAppState';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import { Kind } from 'Helpers/Props/kinds';
|
||||
import sortByProp from 'Utilities/Array/sortByProp';
|
||||
import Label from './Label';
|
||||
import Label, { LabelProps } from './Label';
|
||||
import styles from './TagList.css';
|
||||
|
||||
interface TagListProps {
|
||||
tags: number[];
|
||||
tagList: Tag[];
|
||||
kind?: Extract<Kind, LabelProps['kind']>;
|
||||
}
|
||||
|
||||
function TagList({ tags, tagList }: TagListProps) {
|
||||
export default function TagList({
|
||||
tags,
|
||||
tagList,
|
||||
kind = kinds.INFO,
|
||||
}: TagListProps) {
|
||||
const sortedTags = tags
|
||||
.map((tagId) => tagList.find((tag) => tag.id === tagId))
|
||||
.filter((tag) => !!tag)
|
||||
@@ -20,7 +26,7 @@ function TagList({ tags, tagList }: TagListProps) {
|
||||
<div className={styles.tags}>
|
||||
{sortedTags.map((tag) => {
|
||||
return (
|
||||
<Label key={tag.id} kind={kinds.INFO}>
|
||||
<Label key={tag.id} kind={kind}>
|
||||
{tag.label}
|
||||
</Label>
|
||||
);
|
||||
@@ -28,5 +34,3 @@ function TagList({ tags, tagList }: TagListProps) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TagList;
|
||||
|
@@ -33,6 +33,7 @@ const newReleaseProfile: ReleaseProfile = {
|
||||
required: [],
|
||||
ignored: [],
|
||||
tags: [],
|
||||
excludedTags: [],
|
||||
indexerId: 0,
|
||||
};
|
||||
|
||||
@@ -76,7 +77,8 @@ function EditReleaseProfileModalContent({
|
||||
const { item, isFetching, isSaving, error, saveError, ...otherProps } =
|
||||
useSelector(createReleaseProfileSelector(id));
|
||||
|
||||
const { name, enabled, required, ignored, tags, indexerId } = item;
|
||||
const { name, enabled, required, ignored, tags, excludedTags, indexerId } =
|
||||
item;
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const previousIsSaving = usePrevious(isSaving);
|
||||
@@ -202,6 +204,19 @@ function EditReleaseProfileModalContent({
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('ExcludedTags')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
name="excludedTags"
|
||||
helpText={translate('ReleaseProfileExcludedTagSeriesHelpText')}
|
||||
kind={kinds.DANGER}
|
||||
{...excludedTags}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
|
@@ -28,6 +28,7 @@ function ReleaseProfileItem(props: ReleaseProfileProps) {
|
||||
required = [],
|
||||
ignored = [],
|
||||
tags,
|
||||
excludedTags,
|
||||
indexerId = 0,
|
||||
tagList,
|
||||
indexerList,
|
||||
@@ -92,6 +93,8 @@ function ReleaseProfileItem(props: ReleaseProfileProps) {
|
||||
|
||||
<TagList tags={tags} tagList={tagList} />
|
||||
|
||||
<TagList tags={excludedTags} tagList={tagList} kind={kinds.DANGER} />
|
||||
|
||||
<div>
|
||||
{enabled ? null : (
|
||||
<Label kind={kinds.DISABLED} outline={true}>
|
||||
|
@@ -61,7 +61,7 @@ export interface TagDetailsModalContentProps {
|
||||
delayProfileIds: number[];
|
||||
importListIds: number[];
|
||||
notificationIds: number[];
|
||||
restrictionIds: number[];
|
||||
releaseProfileIds: number[];
|
||||
indexerIds: number[];
|
||||
downloadClientIds: number[];
|
||||
autoTagIds: number[];
|
||||
@@ -76,7 +76,7 @@ function TagDetailsModalContent({
|
||||
delayProfileIds = [],
|
||||
importListIds = [],
|
||||
notificationIds = [],
|
||||
restrictionIds = [],
|
||||
releaseProfileIds = [],
|
||||
indexerIds = [],
|
||||
downloadClientIds = [],
|
||||
autoTagIds = [],
|
||||
@@ -109,7 +109,7 @@ function TagDetailsModalContent({
|
||||
|
||||
const releaseProfiles = useSelector(
|
||||
createMatchingItemSelector(
|
||||
restrictionIds,
|
||||
releaseProfileIds,
|
||||
(state: AppState) => state.settings.releaseProfiles.items
|
||||
)
|
||||
);
|
||||
|
@@ -22,6 +22,7 @@ function Tag({ id, label }: TagProps) {
|
||||
importListIds = [],
|
||||
notificationIds = [],
|
||||
restrictionIds = [],
|
||||
excludedReleaseProfileIds = [],
|
||||
indexerIds = [],
|
||||
downloadClientIds = [],
|
||||
autoTagIds = [],
|
||||
@@ -35,12 +36,17 @@ function Tag({ id, label }: TagProps) {
|
||||
importListIds.length ||
|
||||
notificationIds.length ||
|
||||
restrictionIds.length ||
|
||||
excludedReleaseProfileIds.length ||
|
||||
indexerIds.length ||
|
||||
downloadClientIds.length ||
|
||||
autoTagIds.length ||
|
||||
seriesIds.length
|
||||
);
|
||||
|
||||
const mergedReleaseProfileIds = Array.from(
|
||||
new Set([...restrictionIds, ...excludedReleaseProfileIds]).values()
|
||||
);
|
||||
|
||||
const handleShowDetailsPress = useCallback(() => {
|
||||
setIsDetailsModalOpen(true);
|
||||
}, []);
|
||||
@@ -95,7 +101,7 @@ function Tag({ id, label }: TagProps) {
|
||||
<TagInUse
|
||||
label={translate('ReleaseProfile')}
|
||||
labelPlural={translate('ReleaseProfiles')}
|
||||
count={restrictionIds.length}
|
||||
count={mergedReleaseProfileIds.length}
|
||||
/>
|
||||
|
||||
<TagInUse
|
||||
@@ -126,7 +132,7 @@ function Tag({ id, label }: TagProps) {
|
||||
delayProfileIds={delayProfileIds}
|
||||
importListIds={importListIds}
|
||||
notificationIds={notificationIds}
|
||||
restrictionIds={restrictionIds}
|
||||
releaseProfileIds={mergedReleaseProfileIds}
|
||||
indexerIds={indexerIds}
|
||||
downloadClientIds={downloadClientIds}
|
||||
autoTagIds={autoTagIds}
|
||||
|
@@ -7,6 +7,7 @@ interface ReleaseProfile extends ModelBase {
|
||||
ignored: string[];
|
||||
indexerId: number;
|
||||
tags: number[];
|
||||
excludedTags: number[];
|
||||
}
|
||||
|
||||
export default ReleaseProfile;
|
||||
|
@@ -0,0 +1,116 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Profiles.Releases;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Profiles
|
||||
{
|
||||
[TestFixture]
|
||||
public class ReleaseProfileServiceFixture : CoreTest<ReleaseProfileService>
|
||||
{
|
||||
private List<ReleaseProfile> _releaseProfiles;
|
||||
private ReleaseProfile _defaultReleaseProfile;
|
||||
private ReleaseProfile _includedReleaseProfile;
|
||||
private ReleaseProfile _excludedReleaseProfile;
|
||||
private ReleaseProfile _includedAndExcludedReleaseProfile;
|
||||
private int _providedTag;
|
||||
private int _providedTagToExclude;
|
||||
private int _notUsedTag;
|
||||
private List<ReleaseProfile> _releaseProfilesWithoutTags;
|
||||
private List<ReleaseProfile> _releaseProfilesWithProvidedTag;
|
||||
private List<ReleaseProfile> _releaseProfilesWithProvidedTagOrWithoutTags;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_providedTag = 1;
|
||||
_providedTagToExclude = 2;
|
||||
_notUsedTag = 3;
|
||||
|
||||
_releaseProfiles = Builder<ReleaseProfile>.CreateListOfSize(5)
|
||||
.TheFirst(1)
|
||||
.With(r => r.Required = ["required_one"])
|
||||
.TheNext(1)
|
||||
.With(r => r.Required = ["required_two"])
|
||||
.With(r => r.Tags = [_providedTag])
|
||||
.TheNext(1)
|
||||
.With(r => r.Required = ["required_three"])
|
||||
.With(r => r.ExcludedTags = [_providedTagToExclude])
|
||||
.TheNext(1)
|
||||
.With(r => r.Required = ["required_four"])
|
||||
.With(r => r.Tags = [_providedTag])
|
||||
.With(r => r.ExcludedTags = [_providedTagToExclude])
|
||||
.TheNext(1)
|
||||
.With(r => r.Required = ["required_five"])
|
||||
.With(r => r.Tags = [_notUsedTag])
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
_defaultReleaseProfile = _releaseProfiles[0];
|
||||
_includedReleaseProfile = _releaseProfiles[1];
|
||||
_excludedReleaseProfile = _releaseProfiles[2];
|
||||
_includedAndExcludedReleaseProfile = _releaseProfiles[3];
|
||||
|
||||
_releaseProfilesWithoutTags = [_defaultReleaseProfile, _excludedReleaseProfile];
|
||||
_releaseProfilesWithProvidedTag = [_includedReleaseProfile, _includedAndExcludedReleaseProfile];
|
||||
_releaseProfilesWithProvidedTagOrWithoutTags = [_defaultReleaseProfile, _includedReleaseProfile, _excludedReleaseProfile, _includedAndExcludedReleaseProfile];
|
||||
|
||||
Mocker.GetMock<IRestrictionRepository>()
|
||||
.Setup(s => s.All())
|
||||
.Returns(_releaseProfiles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void all_for_tags_should_return_release_profiles_without_tags_by_default()
|
||||
{
|
||||
var releaseProfiles = Subject.AllForTags([]);
|
||||
releaseProfiles.Should().Equal(_releaseProfilesWithoutTags);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void all_for_tags_should_return_release_profiles_with_provided_tag_or_without_tags()
|
||||
{
|
||||
var releaseProfiles = Subject.AllForTags([_providedTag]);
|
||||
releaseProfiles.Should().Equal(_releaseProfilesWithProvidedTagOrWithoutTags);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void all_for_tags_should_not_return_release_profiles_with_provided_tag_excluded()
|
||||
{
|
||||
var releaseProfiles = Subject.AllForTags([_providedTagToExclude]);
|
||||
releaseProfiles.Should().NotContain(_excludedReleaseProfile);
|
||||
releaseProfiles.Should().NotContain(_includedAndExcludedReleaseProfile);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void all_for_tag_should_return_release_profiles_with_provided_tag()
|
||||
{
|
||||
var releaseProfiles = Subject.AllForTag(_providedTag);
|
||||
releaseProfiles.Should().Equal(_releaseProfilesWithProvidedTag);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void all_should_return_all_release_profiles()
|
||||
{
|
||||
var releaseProfiles = Subject.All();
|
||||
releaseProfiles.Should().Equal(_releaseProfiles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void all_for_tags_should_not_return_release_profiles_with_a_provided_tag_both_included_and_excluded()
|
||||
{
|
||||
var releaseProfiles = Subject.AllForTags([_providedTag, _providedTagToExclude]);
|
||||
releaseProfiles.Should().Equal([_defaultReleaseProfile, _includedReleaseProfile]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void all_for_tags_should_return_matching_tags_that_are_not_excluded_tags()
|
||||
{
|
||||
var releaseProfiles = Subject.AllForTags([_providedTag]);
|
||||
releaseProfiles.Should().Equal([_defaultReleaseProfile, _includedReleaseProfile, _excludedReleaseProfile, _includedAndExcludedReleaseProfile]);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(221)]
|
||||
public class add_exclusion_tags_to_release_profiles : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("ReleaseProfiles").AddColumn("ExcludedTags").AsString().NotNullable().WithDefaultValue("[]");
|
||||
}
|
||||
}
|
||||
}
|
@@ -690,6 +690,9 @@
|
||||
"Events": "Events",
|
||||
"Example": "Example",
|
||||
"Exception": "Exception",
|
||||
"ExcludedTags": "Excluded Tags",
|
||||
"ExcludedReleaseProfile": "Excluded Release Profile",
|
||||
"ExcludedReleaseProfiles": "Excluded Release Profiles",
|
||||
"Existing": "Existing",
|
||||
"ExistingSeries": "Existing Series",
|
||||
"ExistingTag": "Existing tag",
|
||||
@@ -1699,6 +1702,7 @@
|
||||
"ReleaseGroups": "Release Groups",
|
||||
"ReleaseHash": "Release Hash",
|
||||
"ReleaseProfile": "Release Profile",
|
||||
"ReleaseProfileExcludedTagSeriesHelpText": "Release profiles will not apply to series with at least one matching tag.",
|
||||
"ReleaseProfileIndexerHelpText": "Specify what indexer the profile applies to",
|
||||
"ReleaseProfileIndexerHelpTextWarning": "Setting a specific indexer on a release profile will cause this profile to only apply to releases from that indexer.",
|
||||
"ReleaseProfileTagSeriesHelpText": "Release profiles will apply to series with at least one matching tag. Leave blank to apply to all series",
|
||||
|
@@ -11,6 +11,7 @@ namespace NzbDrone.Core.Profiles.Releases
|
||||
public List<string> Ignored { get; set; }
|
||||
public int IndexerId { get; set; }
|
||||
public HashSet<int> Tags { get; set; }
|
||||
public HashSet<int> ExcludedTags { get; set; }
|
||||
|
||||
public ReleaseProfile()
|
||||
{
|
||||
@@ -18,6 +19,7 @@ namespace NzbDrone.Core.Profiles.Releases
|
||||
Required = new List<string>();
|
||||
Ignored = new List<string>();
|
||||
Tags = new HashSet<int>();
|
||||
ExcludedTags = new HashSet<int>();
|
||||
IndexerId = 0;
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ namespace NzbDrone.Core.Profiles.Releases
|
||||
public interface IReleaseProfileService
|
||||
{
|
||||
List<ReleaseProfile> All();
|
||||
List<ReleaseProfile> AllExcludedForTag(int tagId);
|
||||
List<ReleaseProfile> AllForTag(int tagId);
|
||||
List<ReleaseProfile> AllForTags(HashSet<int> tagIds);
|
||||
List<ReleaseProfile> EnabledForTags(HashSet<int> tagIds, int indexerId);
|
||||
@@ -35,6 +36,11 @@ namespace NzbDrone.Core.Profiles.Releases
|
||||
return all;
|
||||
}
|
||||
|
||||
public List<ReleaseProfile> AllExcludedForTag(int tagId)
|
||||
{
|
||||
return _repo.All().Where(r => r.ExcludedTags.Contains(tagId)).ToList();
|
||||
}
|
||||
|
||||
public List<ReleaseProfile> AllForTag(int tagId)
|
||||
{
|
||||
return _repo.All().Where(r => r.Tags.Contains(tagId)).ToList();
|
||||
@@ -42,7 +48,7 @@ namespace NzbDrone.Core.Profiles.Releases
|
||||
|
||||
public List<ReleaseProfile> AllForTags(HashSet<int> tagIds)
|
||||
{
|
||||
return _repo.All().Where(r => r.Tags.Intersect(tagIds).Any() || r.Tags.Empty()).ToList();
|
||||
return _repo.All().Where(r => (r.Tags.Intersect(tagIds).Any() || r.Tags.Empty()) && !r.ExcludedTags.Intersect(tagIds).Any()).ToList();
|
||||
}
|
||||
|
||||
public List<ReleaseProfile> EnabledForTags(HashSet<int> tagIds, int indexerId)
|
||||
|
@@ -10,6 +10,7 @@ namespace NzbDrone.Core.Tags
|
||||
public List<int> SeriesIds { get; set; }
|
||||
public List<int> NotificationIds { get; set; }
|
||||
public List<int> RestrictionIds { get; set; }
|
||||
public List<int> ExcludedReleaseProfileIds { get; set; }
|
||||
public List<int> DelayProfileIds { get; set; }
|
||||
public List<int> ImportListIds { get; set; }
|
||||
public List<int> IndexerIds { get; set; }
|
||||
@@ -19,6 +20,7 @@ namespace NzbDrone.Core.Tags
|
||||
public bool InUse => SeriesIds.Any() ||
|
||||
NotificationIds.Any() ||
|
||||
RestrictionIds.Any() ||
|
||||
ExcludedReleaseProfileIds.Any() ||
|
||||
DelayProfileIds.Any() ||
|
||||
ImportListIds.Any() ||
|
||||
IndexerIds.Any() ||
|
||||
|
@@ -91,7 +91,8 @@ namespace NzbDrone.Core.Tags
|
||||
var delayProfiles = _delayProfileService.AllForTag(tagId);
|
||||
var importLists = _importListFactory.AllForTag(tagId);
|
||||
var notifications = _notificationFactory.AllForTag(tagId);
|
||||
var restrictions = _releaseProfileService.AllForTag(tagId);
|
||||
var releaseProfiles = _releaseProfileService.AllForTag(tagId);
|
||||
var excludedReleaseProfiles = _releaseProfileService.AllExcludedForTag(tagId);
|
||||
var series = _seriesService.AllForTag(tagId);
|
||||
var indexers = _indexerService.AllForTag(tagId);
|
||||
var autoTags = _autoTaggingService.AllForTag(tagId);
|
||||
@@ -104,7 +105,8 @@ namespace NzbDrone.Core.Tags
|
||||
DelayProfileIds = delayProfiles.Select(c => c.Id).ToList(),
|
||||
ImportListIds = importLists.Select(c => c.Id).ToList(),
|
||||
NotificationIds = notifications.Select(c => c.Id).ToList(),
|
||||
RestrictionIds = restrictions.Select(c => c.Id).ToList(),
|
||||
RestrictionIds = releaseProfiles.Select(c => c.Id).ToList(),
|
||||
ExcludedReleaseProfileIds = excludedReleaseProfiles.Select(c => c.Id).ToList(),
|
||||
SeriesIds = series.Select(c => c.Id).ToList(),
|
||||
IndexerIds = indexers.Select(c => c.Id).ToList(),
|
||||
AutoTagIds = autoTags.Select(c => c.Id).ToList(),
|
||||
@@ -118,7 +120,8 @@ namespace NzbDrone.Core.Tags
|
||||
var delayProfiles = _delayProfileService.All();
|
||||
var importLists = _importListFactory.All();
|
||||
var notifications = _notificationFactory.All();
|
||||
var restrictions = _releaseProfileService.All();
|
||||
var releaseProfiles = _releaseProfileService.All();
|
||||
var excludedReleaseProfiles = _releaseProfileService.All();
|
||||
var series = _seriesService.GetAllSeriesTags();
|
||||
var indexers = _indexerService.All();
|
||||
var autoTags = _autoTaggingService.All();
|
||||
@@ -135,7 +138,8 @@ namespace NzbDrone.Core.Tags
|
||||
DelayProfileIds = delayProfiles.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
|
||||
ImportListIds = importLists.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
|
||||
NotificationIds = notifications.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
|
||||
RestrictionIds = restrictions.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
|
||||
RestrictionIds = releaseProfiles.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
|
||||
ExcludedReleaseProfileIds = excludedReleaseProfiles.Where(c => c.ExcludedTags.Contains(tag.Id)).Select(c => c.Id).ToList(),
|
||||
SeriesIds = series.Where(c => c.Value.Contains(tag.Id)).Select(c => c.Key).ToList(),
|
||||
IndexerIds = indexers.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
|
||||
AutoTagIds = GetAutoTagIds(tag, autoTags),
|
||||
|
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Profiles.Releases;
|
||||
using NzbDrone.Core.Tags;
|
||||
using Sonarr.Http;
|
||||
using Sonarr.Http.REST;
|
||||
using Sonarr.Http.REST.Attributes;
|
||||
@@ -16,11 +17,13 @@ namespace Sonarr.Api.V3.Profiles.Release
|
||||
{
|
||||
private readonly IReleaseProfileService _profileService;
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
private readonly ITagService _tagService;
|
||||
|
||||
public ReleaseProfileController(IReleaseProfileService profileService, IIndexerFactory indexerFactory)
|
||||
public ReleaseProfileController(IReleaseProfileService profileService, IIndexerFactory indexerFactory, ITagService tagService)
|
||||
{
|
||||
_profileService = profileService;
|
||||
_indexerFactory = indexerFactory;
|
||||
_tagService = tagService;
|
||||
|
||||
SharedValidator.RuleFor(d => d).Custom((restriction, context) =>
|
||||
{
|
||||
@@ -44,6 +47,11 @@ namespace Sonarr.Api.V3.Profiles.Release
|
||||
context.AddFailure(nameof(ReleaseProfile.IndexerId), "Indexer does not exist");
|
||||
}
|
||||
});
|
||||
|
||||
SharedValidator.RuleFor(d => d.Tags.Intersect(d.ExcludedTags))
|
||||
.Empty()
|
||||
.WithName("ExcludedTags")
|
||||
.WithMessage(d => $"'{string.Join(", ", _tagService.GetTags(d.Tags.Intersect(d.ExcludedTags)).Select(t => t.Label))}' cannot be in both 'Tags' and 'Excluded Tags'");
|
||||
}
|
||||
|
||||
[RestPostById]
|
||||
|
@@ -17,10 +17,12 @@ namespace Sonarr.Api.V3.Profiles.Release
|
||||
public object Ignored { get; set; }
|
||||
public int IndexerId { get; set; }
|
||||
public HashSet<int> Tags { get; set; }
|
||||
public HashSet<int> ExcludedTags { get; set; }
|
||||
|
||||
public ReleaseProfileResource()
|
||||
{
|
||||
Tags = new HashSet<int>();
|
||||
ExcludedTags = new HashSet<int>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +43,8 @@ namespace Sonarr.Api.V3.Profiles.Release
|
||||
Required = model.Required ?? new List<string>(),
|
||||
Ignored = model.Ignored ?? new List<string>(),
|
||||
IndexerId = model.IndexerId,
|
||||
Tags = new HashSet<int>(model.Tags)
|
||||
Tags = new HashSet<int>(model.Tags),
|
||||
ExcludedTags = new HashSet<int>(model.ExcludedTags)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -60,7 +63,8 @@ namespace Sonarr.Api.V3.Profiles.Release
|
||||
Required = resource.MapRequired(),
|
||||
Ignored = resource.MapIgnored(),
|
||||
IndexerId = resource.IndexerId,
|
||||
Tags = new HashSet<int>(resource.Tags)
|
||||
Tags = new HashSet<int>(resource.Tags),
|
||||
ExcludedTags = new HashSet<int>(resource.ExcludedTags)
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,7 @@ namespace Sonarr.Api.V3.Tags
|
||||
public List<int> ImportListIds { get; set; }
|
||||
public List<int> NotificationIds { get; set; }
|
||||
public List<int> RestrictionIds { get; set; }
|
||||
public List<int> ExcludedReleaseProfileIds { get; set; }
|
||||
public List<int> IndexerIds { get; set; }
|
||||
public List<int> DownloadClientIds { get; set; }
|
||||
public List<int> AutoTagIds { get; set; }
|
||||
@@ -35,6 +36,7 @@ namespace Sonarr.Api.V3.Tags
|
||||
ImportListIds = model.ImportListIds,
|
||||
NotificationIds = model.NotificationIds,
|
||||
RestrictionIds = model.RestrictionIds,
|
||||
ExcludedReleaseProfileIds = model.ExcludedReleaseProfileIds,
|
||||
IndexerIds = model.IndexerIds,
|
||||
DownloadClientIds = model.DownloadClientIds,
|
||||
AutoTagIds = model.AutoTagIds,
|
||||
|
Reference in New Issue
Block a user