From 43d5abc1fe980590dbbd07bda1822d83427d4bfb Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 12 Jan 2026 20:04:01 -0800 Subject: [PATCH] Validate Special Folder Format doesn't match excluded folder Closes #8339 --- .../MediaFiles/DiskScanService.cs | 22 +++++++++++++ .../Organizer/FileNameValidation.cs | 32 ++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/NzbDrone.Core/MediaFiles/DiskScanService.cs b/src/NzbDrone.Core/MediaFiles/DiskScanService.cs index 396a5b955..f1da95742 100644 --- a/src/NzbDrone.Core/MediaFiles/DiskScanService.cs +++ b/src/NzbDrone.Core/MediaFiles/DiskScanService.cs @@ -245,6 +245,28 @@ public List FilterPaths(string basePath, IEnumerable paths, bool return filteredPaths; } + public static List FilteredSubFolderMatches(string subfolder) + { + var matches = new List(); + + foreach (var match in ExcludedSubFoldersRegex.Matches(subfolder)) + { + matches.Add(match.ToString()); + } + + foreach (var match in ExcludedExtrasSubFolderRegex.Matches(subfolder)) + { + matches.Add(match.ToString()); + } + + foreach (var match in ExcludedExtraFilesRegex.Matches(subfolder)) + { + matches.Add(match.ToString()); + } + + return matches; + } + private void SetPermissions(string path) { if (!_configService.SetPermissionsLinux) diff --git a/src/NzbDrone.Core/Organizer/FileNameValidation.cs b/src/NzbDrone.Core/Organizer/FileNameValidation.cs index b36f9426c..810be5e00 100644 --- a/src/NzbDrone.Core/Organizer/FileNameValidation.cs +++ b/src/NzbDrone.Core/Organizer/FileNameValidation.cs @@ -5,6 +5,7 @@ using FluentValidation; using FluentValidation.Validators; using NzbDrone.Common.Extensions; +using NzbDrone.Core.MediaFiles; namespace NzbDrone.Core.Organizer { @@ -59,8 +60,9 @@ public static IRuleBuilderOptions ValidSeasonFolderFormat(this IRu public static IRuleBuilderOptions ValidSpecialsFolderFormat(this IRuleBuilder ruleBuilder) { ruleBuilder.SetValidator(new NotEmptyValidator(null)); + ruleBuilder.SetValidator(new IllegalCharactersValidator()); - return ruleBuilder.SetValidator(new IllegalCharactersValidator()); + return ruleBuilder.SetValidator(new FilteredSubfolderValidator()); } public static IRuleBuilderOptions ValidCustomColonReplacement(this IRuleBuilder ruleBuilder) @@ -177,4 +179,32 @@ protected override bool IsValid(PropertyValidatorContext context) return true; } } + + public class FilteredSubfolderValidator : PropertyValidator + { + private static readonly char[] InvalidPathChars = Path.GetInvalidPathChars(); + + protected override string GetDefaultMessageTemplate() => "Matches excluded subfolder pattern: {FilteredSubfolders}"; + + protected override bool IsValid(PropertyValidatorContext context) + { + var value = context.PropertyValue as string; + + if (value.IsNullOrWhiteSpace()) + { + return true; + } + + var subfolder = value + Path.DirectorySeparatorChar; + var matches = DiskScanService.FilteredSubFolderMatches(subfolder); + + if (matches.Any()) + { + context.MessageFormatter.AppendArgument("FilteredSubfolders", string.Join("", matches.Select(m => m.TrimEnd(Path.DirectorySeparatorChar)))); + return false; + } + + return true; + } + } }