From d90ee3ae11bb54f79a00fc94ecfedae37d13e056 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Fri, 18 Oct 2024 00:49:00 +0300 Subject: [PATCH] Fixed: Release Year mandatory to generate valid file formats --- .../Organizer/FileNameBuilder.cs | 4 +- .../Organizer/FileNameValidation.cs | 53 ++++++++++++++++--- .../Organizer/FileNameValidationService.cs | 2 +- .../ApiTests/NamingConfigFixture.cs | 2 +- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index c600e2c69e..b499da390d 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -41,7 +41,9 @@ public class FileNameBuilder : IBuildFileNames private static readonly Regex TitleRegex = new Regex(@"(?\{(?:imdb-|edition-))?\{(?[- ._\[(]*)(?(?:[a-z0-9]+)(?:(?[- ._]+)(?:[a-z0-9]+))?)(?::(?[ ,a-z0-9|+-]+(?[-} ._)\]]*)\}", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); - public static readonly Regex MovieTitleRegex = new Regex(@"(?\{(?:(?:Movie)(?[- ._])(?:Clean)?(?:OriginalTitle|Title(?:The)?)(?::(?[a-z0-9|-]+))?|Original[- ._](?:Title|Filename))\})", + public static readonly Regex ReleaseYearRegex = new Regex(@"\{Release[- ._]Year\}", RegexOptions.Compiled | RegexOptions.IgnoreCase); + + public static readonly Regex MovieTitleRegex = new Regex(@"(?\{(?:Movie)(?[- ._])(?:Clean)?(?:OriginalTitle|Title(?:The)?)(?::(?[a-z0-9|-]+))?\})", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex FileNameCleanupRegex = new Regex(@"([- ._])(\1)+", RegexOptions.Compiled); diff --git a/src/NzbDrone.Core/Organizer/FileNameValidation.cs b/src/NzbDrone.Core/Organizer/FileNameValidation.cs index b7682407bc..fe49d9f317 100644 --- a/src/NzbDrone.Core/Organizer/FileNameValidation.cs +++ b/src/NzbDrone.Core/Organizer/FileNameValidation.cs @@ -1,5 +1,6 @@ using System.IO; using System.Linq; +using System.Text.RegularExpressions; using FluentValidation; using FluentValidation.Validators; using NzbDrone.Common.Extensions; @@ -8,20 +9,56 @@ namespace NzbDrone.Core.Organizer { public static class FileNameValidation { - public static IRuleBuilderOptions ValidMovieFolderFormat(this IRuleBuilder ruleBuilder) - { - ruleBuilder.SetValidator(new NotEmptyValidator(null)); - ruleBuilder.SetValidator(new IllegalCharactersValidator()); - - return ruleBuilder.SetValidator(new RegularExpressionValidator(FileNameBuilder.MovieTitleRegex)).WithMessage("Must contain movie title"); - } + internal static readonly Regex OriginalTokenRegex = new (@"(\{Original[- ._](?:Title|Filename)\})", + RegexOptions.Compiled | RegexOptions.IgnoreCase); public static IRuleBuilderOptions ValidMovieFormat(this IRuleBuilder ruleBuilder) { ruleBuilder.SetValidator(new NotEmptyValidator(null)); ruleBuilder.SetValidator(new IllegalCharactersValidator()); - return ruleBuilder.SetValidator(new RegularExpressionValidator(FileNameBuilder.MovieTitleRegex)).WithMessage("Must contain movie title"); + return ruleBuilder.SetValidator(new ValidMovieFormatValidator()); + } + + public static IRuleBuilderOptions ValidMovieFolderFormat(this IRuleBuilder ruleBuilder) + { + ruleBuilder.SetValidator(new NotEmptyValidator(null)); + ruleBuilder.SetValidator(new IllegalCharactersValidator()); + + return ruleBuilder.SetValidator(new ValidMovieFolderFormatValidator()); + } + } + + public class ValidMovieFormatValidator : PropertyValidator + { + protected override string GetDefaultMessageTemplate() => "Must contain movie title and release year OR Original Title"; + + protected override bool IsValid(PropertyValidatorContext context) + { + if (context.PropertyValue is not string value) + { + return false; + } + + return (FileNameBuilder.MovieTitleRegex.IsMatch(value) && FileNameBuilder.ReleaseYearRegex.IsMatch(value)) || + FileNameValidation.OriginalTokenRegex.IsMatch(value); + } + } + + public class ValidMovieFolderFormatValidator : PropertyValidator + { + protected override string GetDefaultMessageTemplate() => "Must contain movie title"; + + protected override bool IsValid(PropertyValidatorContext context) + { + if (context.PropertyValue is not string value) + { + return false; + } + + // TODO: Deprecate OriginalTokenRegex use for Movie Folder Format + return FileNameBuilder.MovieTitleRegex.IsMatch(value) || + FileNameValidation.OriginalTokenRegex.IsMatch(value); } } diff --git a/src/NzbDrone.Core/Organizer/FileNameValidationService.cs b/src/NzbDrone.Core/Organizer/FileNameValidationService.cs index c0ab89ad72..2829d99784 100644 --- a/src/NzbDrone.Core/Organizer/FileNameValidationService.cs +++ b/src/NzbDrone.Core/Organizer/FileNameValidationService.cs @@ -13,7 +13,7 @@ public class FileNameValidationService : IFilenameValidationService public ValidationFailure ValidateMovieFilename(SampleResult sampleResult) { - var validationFailure = new ValidationFailure("MovieFormat", ERROR_MESSAGE); + var validationFailure = new ValidationFailure("StandardMovieFormat", ERROR_MESSAGE); var parsedMovieInfo = Parser.Parser.ParseMovieTitle(sampleResult.FileName); if (parsedMovieInfo == null) diff --git a/src/NzbDrone.Integration.Test/ApiTests/NamingConfigFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/NamingConfigFixture.cs index 361e1e00f5..47c0e82035 100644 --- a/src/NzbDrone.Integration.Test/ApiTests/NamingConfigFixture.cs +++ b/src/NzbDrone.Integration.Test/ApiTests/NamingConfigFixture.cs @@ -25,7 +25,7 @@ public void should_be_able_to_update() { var config = NamingConfig.GetSingle(); config.RenameMovies = false; - config.StandardMovieFormat = "{Movie Title}"; + config.StandardMovieFormat = "{Movie Title} {Release Year}"; var result = NamingConfig.Put(config); result.RenameMovies.Should().BeFalse();