aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShadowghost <Shadowghost@users.noreply.github.com>2025-12-28 07:22:20 -0500
committerBond_009 <bond.009@outlook.com>2025-12-28 07:22:20 -0500
commitb9cf26db2f3d219340a17951c289a230d0ccf31a (patch)
treed6836e1c4c4aed476286c630d3dd177007ce8d8f
parent580585846b618bf308ebfd4e698ff0efe1a2de4d (diff)
Backport pull request #15746 from jellyfin/release-10.11.z
Skip invalid ignore rules Original-merge: 6e60634c9f078cc01e343b07a0a6b2a5c230478c Merged-by: crobibero <cody@robibe.ro> Backported-by: Bond_009 <bond.009@outlook.com>
-rw-r--r--Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs48
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs87
2 files changed, 115 insertions, 20 deletions
diff --git a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
index 473ff8e1d..ef5d24c70 100644
--- a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
+++ b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using System.Text.RegularExpressions;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Resolvers;
@@ -70,12 +71,55 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule
{
// If file has content, base ignoring off the content .gitignore-style rules
var rules = ignoreFileContent.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
+ return CheckIgnoreRules(path, rules, isDirectory);
+ }
+
+ /// <summary>
+ /// Checks whether a path should be ignored based on an array of ignore rules.
+ /// </summary>
+ /// <param name="path">The path to check.</param>
+ /// <param name="rules">The array of ignore rules.</param>
+ /// <param name="isDirectory">Whether the path is a directory.</param>
+ /// <returns>True if the path should be ignored.</returns>
+ internal static bool CheckIgnoreRules(string path, string[] rules, bool isDirectory)
+ => CheckIgnoreRules(path, rules, isDirectory, IsWindows);
+
+ /// <summary>
+ /// Checks whether a path should be ignored based on an array of ignore rules.
+ /// </summary>
+ /// <param name="path">The path to check.</param>
+ /// <param name="rules">The array of ignore rules.</param>
+ /// <param name="isDirectory">Whether the path is a directory.</param>
+ /// <param name="normalizePath">Whether to normalize backslashes to forward slashes (for Windows paths).</param>
+ /// <returns>True if the path should be ignored.</returns>
+ internal static bool CheckIgnoreRules(string path, string[] rules, bool isDirectory, bool normalizePath)
+ {
var ignore = new Ignore.Ignore();
- ignore.Add(rules);
+
+ // Add each rule individually to catch and skip invalid patterns
+ var validRulesAdded = 0;
+ foreach (var rule in rules)
+ {
+ try
+ {
+ ignore.Add(rule);
+ validRulesAdded++;
+ }
+ catch (RegexParseException)
+ {
+ // Ignore invalid patterns
+ }
+ }
+
+ // If no valid rules were added, fall back to ignoring everything (like an empty .ignore file)
+ if (validRulesAdded == 0)
+ {
+ return true;
+ }
// Mitigate the problem of the Ignore library not handling Windows paths correctly.
// See https://github.com/jellyfin/jellyfin/issues/15484
- var pathToCheck = IsWindows ? path.NormalizePath('/') : path;
+ var pathToCheck = normalizePath ? path.NormalizePath('/') : path;
// Add trailing slash for directories to match "folder/"
if (isDirectory)
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs
index d677c9f09..a7bbef7ed 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs
@@ -1,30 +1,81 @@
+using Emby.Server.Implementations.Library;
using Xunit;
namespace Jellyfin.Server.Implementations.Tests.Library;
public class DotIgnoreIgnoreRuleTest
{
- [Fact]
- public void Test()
+ private static readonly string[] _rule1 = ["SPs"];
+ private static readonly string[] _rule2 = ["SPs", "!thebestshot.mkv"];
+ private static readonly string[] _rule3 = ["*.txt", @"{\colortbl;\red255\green255\blue255;}", "videos/", @"\invalid\escape\sequence", "*.mkv"];
+ private static readonly string[] _rule4 = [@"{\colortbl;\red255\green255\blue255;}", @"\invalid\escape\sequence"];
+
+ public static TheoryData<string[], string, bool, bool> CheckIgnoreRulesTestData =>
+ new()
+ {
+ // Basic pattern matching
+ { _rule1, "f:/cd/sps/ffffff.mkv", false, true },
+ { _rule1, "cd/sps/ffffff.mkv", false, true },
+ { _rule1, "/cd/sps/ffffff.mkv", false, true },
+
+ // Negate pattern
+ { _rule2, "f:/cd/sps/ffffff.mkv", false, true },
+ { _rule2, "cd/sps/ffffff.mkv", false, true },
+ { _rule2, "/cd/sps/ffffff.mkv", false, true },
+ { _rule2, "f:/cd/sps/thebestshot.mkv", false, false },
+ { _rule2, "cd/sps/thebestshot.mkv", false, false },
+ { _rule2, "/cd/sps/thebestshot.mkv", false, false },
+
+ // Mixed valid and invalid patterns - skips invalid, applies valid
+ { _rule3, "test.txt", false, true },
+ { _rule3, "videos/movie.mp4", false, true },
+ { _rule3, "movie.mkv", false, true },
+ { _rule3, "test.mp3", false, false },
+
+ // Only invalid patterns - falls back to ignore all
+ { _rule4, "any-file.txt", false, true },
+ { _rule4, "any/path/to/file.mkv", false, true },
+ };
+
+ public static TheoryData<string[], string, bool, bool> WindowsPathNormalizationTestData =>
+ new()
+ {
+ // Windows paths with backslashes - should match when normalizePath is true
+ { _rule1, @"C:\cd\sps\ffffff.mkv", false, true },
+ { _rule1, @"D:\media\sps\movie.mkv", false, true },
+ { _rule1, @"\\server\share\sps\file.mkv", false, true },
+
+ // Negate pattern with Windows paths
+ { _rule2, @"C:\cd\sps\ffffff.mkv", false, true },
+ { _rule2, @"C:\cd\sps\thebestshot.mkv", false, false },
+
+ // Directory matching with Windows paths
+ { _rule3, @"C:\videos\movie.mp4", false, true },
+ { _rule3, @"D:\documents\test.txt", false, true },
+ { _rule3, @"E:\music\song.mp3", false, false },
+ };
+
+ [Theory]
+ [MemberData(nameof(CheckIgnoreRulesTestData))]
+ public void CheckIgnoreRules_ReturnsExpectedResult(string[] rules, string path, bool isDirectory, bool expectedIgnored)
+ {
+ Assert.Equal(expectedIgnored, DotIgnoreIgnoreRule.CheckIgnoreRules(path, rules, isDirectory));
+ }
+
+ [Theory]
+ [MemberData(nameof(WindowsPathNormalizationTestData))]
+ public void CheckIgnoreRules_WithWindowsPaths_NormalizesBackslashes(string[] rules, string path, bool isDirectory, bool expectedIgnored)
{
- var ignore = new Ignore.Ignore();
- ignore.Add("SPs");
- Assert.True(ignore.IsIgnored("f:/cd/sps/ffffff.mkv"));
- Assert.True(ignore.IsIgnored("cd/sps/ffffff.mkv"));
- Assert.True(ignore.IsIgnored("/cd/sps/ffffff.mkv"));
+ // With normalizePath=true, backslashes should be converted to forward slashes
+ Assert.Equal(expectedIgnored, DotIgnoreIgnoreRule.CheckIgnoreRules(path, rules, isDirectory, normalizePath: true));
}
- [Fact]
- public void TestNegatePattern()
+ [Theory]
+ [InlineData(@"C:\cd\sps\ffffff.mkv")]
+ [InlineData(@"D:\media\sps\movie.mkv")]
+ public void CheckIgnoreRules_WithWindowsPaths_WithoutNormalization_DoesNotMatch(string path)
{
- var ignore = new Ignore.Ignore();
- ignore.Add("SPs");
- ignore.Add("!thebestshot.mkv");
- Assert.True(ignore.IsIgnored("f:/cd/sps/ffffff.mkv"));
- Assert.True(ignore.IsIgnored("cd/sps/ffffff.mkv"));
- Assert.True(ignore.IsIgnored("/cd/sps/ffffff.mkv"));
- Assert.True(!ignore.IsIgnored("f:/cd/sps/thebestshot.mkv"));
- Assert.True(!ignore.IsIgnored("cd/sps/thebestshot.mkv"));
- Assert.True(!ignore.IsIgnored("/cd/sps/thebestshot.mkv"));
+ // Without normalization, Windows paths with backslashes won't match patterns expecting forward slashes
+ Assert.False(DotIgnoreIgnoreRule.CheckIgnoreRules(path, _rule1, isDirectory: false, normalizePath: false));
}
}