1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-16 21:15:33 -04:00

Compare commits

..

7 Commits

Author SHA1 Message Date
Qstick
5fac348613 Bump SonarCloud azure extension to 3.X
(cherry picked from commit 7b8e352d876cd8f8e5b6296f0c3938bed4db8bb8)
2025-01-11 17:44:00 -06:00
Bogdan
7ba9603449 Fixed: Sending Discord notifications with images without absolute links 2025-01-06 13:27:14 +02:00
Bogdan
e36de8ab8d New: Auto tag based on movie status 2025-01-06 04:24:29 +02:00
Stevie Robinson
f8704a1655 Translate backend: Autotagging + CF specs
Signed-off-by: Stevie Robinson <stevie.robinson@gmail.com>
(cherry picked from commit de1cc25c903924fecbca79fedb458d729ae584fd)

Towards #9647
2025-01-06 04:06:34 +02:00
Stevie Robinson
f507d5154e Fixed: Listening on all IPv4 Addresses
(cherry picked from commit 035c474f10c257331a5f47e863d24af82537e335)
2025-01-05 13:53:29 +02:00
Stevie Robinson
5f03e7142a Fixed: qBittorrent Ratio Limit Check
(cherry picked from commit 4dcc015fb19ceb57d2e8f4985c5137e765829d1c)
2025-01-05 13:53:17 +02:00
Bogdan
c0ebbee7c9 Bump version to 5.18.0 2025-01-05 13:52:57 +02:00
21 changed files with 142 additions and 34 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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));
}
}
}

View File

@@ -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)

View File

@@ -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)]

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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",

View File

@@ -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
};
}

View File

@@ -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");
}
}
}

View File

@@ -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();

View File

@@ -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})"
};