mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-16 21:15:33 -04:00
Compare commits
7 Commits
v5.17.2.95
...
v5.18.0.95
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fac348613 | ||
|
|
7ba9603449 | ||
|
|
e36de8ab8d | ||
|
|
f8704a1655 | ||
|
|
f507d5154e | ||
|
|
5f03e7142a | ||
|
|
c0ebbee7c9 |
@@ -9,7 +9,7 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '5.17.2'
|
||||
majorVersion: '5.18.0'
|
||||
minorVersion: $[counter('minorVersion', 2000)]
|
||||
radarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
||||
@@ -1205,12 +1205,12 @@ stages:
|
||||
submodules: true
|
||||
- powershell: Set-Service SCardSvr -StartupType Manual
|
||||
displayName: Enable Windows Test Service
|
||||
- task: SonarCloudPrepare@2
|
||||
- task: SonarCloudPrepare@3
|
||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||
inputs:
|
||||
SonarCloud: 'SonarCloud'
|
||||
organization: 'radarr'
|
||||
scannerMode: 'MSBuild'
|
||||
scannerMode: 'dotnet'
|
||||
projectKey: 'Radarr_Radarr'
|
||||
projectName: 'Radarr'
|
||||
projectVersion: '$(radarrVersion)'
|
||||
@@ -1223,7 +1223,7 @@ stages:
|
||||
./build.sh --backend -f net6.0 -r win-x64
|
||||
TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage
|
||||
displayName: Coverage Unit Tests
|
||||
- task: SonarCloudAnalyze@2
|
||||
- task: SonarCloudAnalyze@3
|
||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||
displayName: Publish SonarCloud Results
|
||||
- task: reportgenerator@5.3.11
|
||||
|
||||
@@ -711,6 +711,30 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
item.CanMoveFiles.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase("pausedUP")]
|
||||
[TestCase("stoppedUP")]
|
||||
public void should_be_removable_and_should_allow_move_files_if_max_ratio_reached_after_rounding_and_paused(string state)
|
||||
{
|
||||
GivenGlobalSeedLimits(1.0f);
|
||||
GivenCompletedTorrent(state, ratio: 1.1006066990976857f);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeTrue();
|
||||
item.CanMoveFiles.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase("pausedUP")]
|
||||
[TestCase("stoppedUP")]
|
||||
public void should_be_removable_and_should_allow_move_files_if_just_under_max_ratio_reached_after_rounding_and_paused(string state)
|
||||
{
|
||||
GivenGlobalSeedLimits(1.0f);
|
||||
GivenCompletedTorrent(state, ratio: 0.9999f);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeTrue();
|
||||
item.CanMoveFiles.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase("pausedUP")]
|
||||
[TestCase("stoppedUP")]
|
||||
public void should_be_removable_and_should_allow_move_files_if_overridden_max_ratio_reached_and_paused(string state)
|
||||
@@ -723,6 +747,30 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
item.CanMoveFiles.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase("pausedUP")]
|
||||
[TestCase("stoppedUP")]
|
||||
public void should_be_removable_and_should_allow_move_files_if_overridden_max_ratio_reached_after_rounding_and_paused(string state)
|
||||
{
|
||||
GivenGlobalSeedLimits(2.0f);
|
||||
GivenCompletedTorrent(state, ratio: 1.1006066990976857f, ratioLimit: 1.1f);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeTrue();
|
||||
item.CanMoveFiles.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase("pausedUP")]
|
||||
[TestCase("stoppedUP")]
|
||||
public void should_be_removable_and_should_allow_move_files_if_just_under_overridden_max_ratio_reached_after_rounding_and_paused(string state)
|
||||
{
|
||||
GivenGlobalSeedLimits(2.0f);
|
||||
GivenCompletedTorrent(state, ratio: 0.9999f, ratioLimit: 1.0f);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeTrue();
|
||||
item.CanMoveFiles.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase("pausedUP")]
|
||||
[TestCase("stoppedUP")]
|
||||
public void should_not_be_removable_if_overridden_max_ratio_not_reached_and_paused(string state)
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Genre";
|
||||
|
||||
[FieldDefinition(1, Label = "Genre(s)", Type = FieldType.Tag)]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationGenre", Type = FieldType.Tag)]
|
||||
public IEnumerable<string> Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Movie movie)
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Original Language";
|
||||
|
||||
[FieldDefinition(1, Label = "Language", Type = FieldType.Select, SelectOptions = typeof(OriginalLanguageFieldConverter))]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationOriginalLanguage", Type = FieldType.Select, SelectOptions = typeof(OriginalLanguageFieldConverter))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Movie movie)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Quality Profile";
|
||||
|
||||
[FieldDefinition(1, Label = "Quality Profile", Type = FieldType.QualityProfile)]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationQualityProfile", Type = FieldType.QualityProfile)]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Movie movie)
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Root Folder";
|
||||
|
||||
[FieldDefinition(1, Label = "Root Folder", Type = FieldType.RootFolder)]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationRootFolder", Type = FieldType.RootFolder)]
|
||||
public string Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Movie movie)
|
||||
|
||||
@@ -24,10 +24,10 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Runtime";
|
||||
|
||||
[FieldDefinition(1, Label = "Minimum Runtime", Type = FieldType.Number)]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationMinimumRuntime", Type = FieldType.Number, Unit = "minutes")]
|
||||
public int Min { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Maximum Runtime", Type = FieldType.Number)]
|
||||
[FieldDefinition(2, Label = "AutoTaggingSpecificationMaximumRuntime", Type = FieldType.Number, Unit = "minutes")]
|
||||
public int Max { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Movie movie)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
{
|
||||
public class StatusSpecificationValidator : AbstractValidator<StatusSpecification>
|
||||
{
|
||||
public StatusSpecificationValidator()
|
||||
{
|
||||
RuleFor(c => c.Status).Custom((statusType, context) =>
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(MovieStatusType), statusType))
|
||||
{
|
||||
context.AddFailure($"Invalid status type condition value: {statusType}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class StatusSpecification : AutoTaggingSpecificationBase
|
||||
{
|
||||
private static readonly StatusSpecificationValidator Validator = new ();
|
||||
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Status";
|
||||
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationStatus", Type = FieldType.Select, SelectOptions = typeof(MovieStatusType))]
|
||||
public int Status { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Movie movie)
|
||||
{
|
||||
return movie?.MovieMetadata?.Value?.Status == (MovieStatusType)Status;
|
||||
}
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,10 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Year";
|
||||
|
||||
[FieldDefinition(1, Label = "Minimum Year", Type = FieldType.Number)]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationMinimumYear", Type = FieldType.Number)]
|
||||
public int Min { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Maximum Year", Type = FieldType.Number)]
|
||||
[FieldDefinition(2, Label = "AutoTaggingSpecificationMaximumYear", Type = FieldType.Number)]
|
||||
public int Max { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Movie movie)
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public override int Order => 3;
|
||||
public override string ImplementationName => "Language";
|
||||
|
||||
[FieldDefinition(1, Label = "Language", Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter))]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationLanguage", Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter))]
|
||||
public int Value { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationExceptLanguage", HelpText = "CustomFormatsSpecificationExceptLanguageHelpText", Type = FieldType.Checkbox)]
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public override int Order => 7;
|
||||
public override string ImplementationName => "Quality Modifier";
|
||||
|
||||
[FieldDefinition(1, Label = "Quality Modifier", Type = FieldType.Select, SelectOptions = typeof(Modifier))]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationQualityModifier", Type = FieldType.Select, SelectOptions = typeof(Modifier))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public override int Order => 6;
|
||||
public override string ImplementationName => "Resolution";
|
||||
|
||||
[FieldDefinition(1, Label = "Resolution", Type = FieldType.Select, SelectOptions = typeof(Resolution))]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationResolution", Type = FieldType.Select, SelectOptions = typeof(Resolution))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
|
||||
@@ -21,10 +21,10 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public override int Order => 8;
|
||||
public override string ImplementationName => "Size";
|
||||
|
||||
[FieldDefinition(1, Label = "Minimum Size", HelpText = "Release must be greater than this size", Unit = "GB", Type = FieldType.Number)]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationMinimumSize", HelpText = "CustomFormatsSpecificationMinimumSizeHelpText", Unit = "GB", Type = FieldType.Number)]
|
||||
public double Min { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Maximum Size", HelpText = "Release must be less than or equal to this size", Unit = "GB", Type = FieldType.Number)]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationMaximumSize", HelpText = "CustomFormatsSpecificationMaximumSizeHelpText", Unit = "GB", Type = FieldType.Number)]
|
||||
public double Max { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public override int Order => 5;
|
||||
public override string ImplementationName => "Source";
|
||||
|
||||
[FieldDefinition(1, Label = "Source", Type = FieldType.Select, SelectOptions = typeof(QualitySource))]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationSource", Type = FieldType.Select, SelectOptions = typeof(QualitySource))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
|
||||
@@ -20,10 +20,10 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public override int Order => 10;
|
||||
public override string ImplementationName => "Year";
|
||||
|
||||
[FieldDefinition(1, Label = "Minimum Year", Type = FieldType.Number)]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationMinimumYear", Type = FieldType.Number)]
|
||||
public int Min { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Maximum Year", Type = FieldType.Number)]
|
||||
[FieldDefinition(2, Label = "CustomFormatsSpecificationMaximumYear", Type = FieldType.Number)]
|
||||
public int Max { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
|
||||
@@ -630,14 +630,14 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
{
|
||||
if (torrent.RatioLimit >= 0)
|
||||
{
|
||||
if (torrent.Ratio >= torrent.RatioLimit)
|
||||
if (torrent.RatioLimit - torrent.Ratio <= 0.001f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (torrent.RatioLimit == -2 && config.MaxRatioEnabled)
|
||||
{
|
||||
if (Math.Round(torrent.Ratio, 2) >= config.MaxRatio)
|
||||
if (config.MaxRatio - torrent.Ratio <= 0.001f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -116,6 +116,15 @@
|
||||
"AutoTaggingLoadError": "Unable to load auto tagging",
|
||||
"AutoTaggingNegateHelpText": "If checked, the auto tagging rule will not apply if this {implementationName} condition matches.",
|
||||
"AutoTaggingRequiredHelpText": "This {implementationName} condition must match for the auto tagging rule to apply. Otherwise a single {implementationName} match is sufficient.",
|
||||
"AutoTaggingSpecificationGenre": "Genre(s)",
|
||||
"AutoTaggingSpecificationMaximumRuntime": "Maximum Runtime",
|
||||
"AutoTaggingSpecificationMaximumYear": "Maximum Year",
|
||||
"AutoTaggingSpecificationMinimumRuntime": "Minimum Runtime",
|
||||
"AutoTaggingSpecificationMinimumYear": "Minimum Year",
|
||||
"AutoTaggingSpecificationOriginalLanguage": "Language",
|
||||
"AutoTaggingSpecificationQualityProfile": "Quality Profile",
|
||||
"AutoTaggingSpecificationRootFolder": "Root Folder",
|
||||
"AutoTaggingSpecificationStatus": "Status",
|
||||
"AutoTaggingSpecificationTag": "Tag",
|
||||
"AutoUnmonitorPreviouslyDownloadedMoviesHelpText": "Movies deleted from the disk are automatically unmonitored in {appName}",
|
||||
"Automatic": "Automatic",
|
||||
@@ -270,8 +279,18 @@
|
||||
"CustomFormatsSpecificationExceptLanguage": "Except Language",
|
||||
"CustomFormatsSpecificationExceptLanguageHelpText": "Matches if any language other than the selected language is present",
|
||||
"CustomFormatsSpecificationFlag": "Flag",
|
||||
"CustomFormatsSpecificationLanguage": "Language",
|
||||
"CustomFormatsSpecificationMaximumSize": "Maximum Size",
|
||||
"CustomFormatsSpecificationMaximumSizeHelpText": "Release must be less than or equal to this size",
|
||||
"CustomFormatsSpecificationMaximumYear": "Maximum Year",
|
||||
"CustomFormatsSpecificationMinimumSize": "Minimum Size",
|
||||
"CustomFormatsSpecificationMinimumSizeHelpText": "Release must be greater than this size",
|
||||
"CustomFormatsSpecificationMinimumYear": "Minimum Year",
|
||||
"CustomFormatsSpecificationQualityModifier": "Quality Modifier",
|
||||
"CustomFormatsSpecificationRegularExpression": "Regular Expression",
|
||||
"CustomFormatsSpecificationRegularExpressionHelpText": "Custom Format RegEx is Case Insensitive",
|
||||
"CustomFormatsSpecificationResolution": "Resolution",
|
||||
"CustomFormatsSpecificationSource": "Source",
|
||||
"Cutoff": "Cutoff",
|
||||
"CutoffNotMet": "Cutoff Not Met",
|
||||
"CutoffUnmet": "Cutoff Unmet",
|
||||
|
||||
@@ -270,7 +270,7 @@ namespace NzbDrone.Core.Notifications.Discord
|
||||
{
|
||||
embed.Thumbnail = new DiscordImage
|
||||
{
|
||||
Url = movie.MovieMetadata.Value.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Poster)?.Url
|
||||
Url = movie.MovieMetadata.Value.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Poster)?.RemoteUrl
|
||||
};
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ namespace NzbDrone.Core.Notifications.Discord
|
||||
{
|
||||
embed.Image = new DiscordImage
|
||||
{
|
||||
Url = movie.MovieMetadata.Value.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Fanart)?.Url
|
||||
Url = movie.MovieMetadata.Value.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Fanart)?.RemoteUrl
|
||||
};
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ namespace NzbDrone.Core.Notifications.Discord
|
||||
{
|
||||
embed.Thumbnail = new DiscordImage
|
||||
{
|
||||
Url = movie.MovieMetadata.Value.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Poster)?.Url
|
||||
Url = movie.MovieMetadata.Value.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Poster)?.RemoteUrl
|
||||
};
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ namespace NzbDrone.Core.Notifications.Discord
|
||||
{
|
||||
embed.Image = new DiscordImage
|
||||
{
|
||||
Url = movie.MovieMetadata.Value.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Fanart)?.Url
|
||||
Url = movie.MovieMetadata.Value.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Fanart)?.RemoteUrl
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation.Validators;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.Validation
|
||||
@@ -10,10 +9,5 @@ namespace NzbDrone.Core.Validation
|
||||
{
|
||||
return ruleBuilder.Must(x => x.IsValidIpAddress()).WithMessage("Must contain wildcard (*) or a valid IP Address");
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, string> NotListenAllIp4Address<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
return ruleBuilder.SetValidator(new RegularExpressionValidator(@"^(?!0\.0\.0\.0)")).WithMessage("Use * instead of 0.0.0.0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace Radarr.Api.V3.Config
|
||||
|
||||
SharedValidator.RuleFor(c => c.BindAddress)
|
||||
.ValidIpAddress()
|
||||
.NotListenAllIp4Address()
|
||||
.When(c => c.BindAddress != "*" && c.BindAddress != "localhost");
|
||||
|
||||
SharedValidator.RuleFor(c => c.Port).ValidPort();
|
||||
|
||||
@@ -228,10 +228,15 @@ namespace Radarr.Http.ClientSchema
|
||||
|
||||
if (attrib != null)
|
||||
{
|
||||
var label = attrib.Label.IsNotNullOrWhiteSpace()
|
||||
? _localizationService.GetLocalizedString(attrib.Label,
|
||||
GetTokens(selectOptions, attrib.Label, TokenField.Label))
|
||||
: attrib.Label;
|
||||
|
||||
return new SelectOption
|
||||
{
|
||||
Value = value,
|
||||
Name = attrib.Label ?? name,
|
||||
Name = label ?? name,
|
||||
Order = attrib.Order,
|
||||
Hint = attrib.Hint ?? $"({value})"
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user