aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Eisele <Ghost_of_Stone@web.de>2025-04-26 17:35:57 +0200
committerGitHub <noreply@github.com>2025-04-26 09:35:57 -0600
commita0b3b7335fccf028701e94af31028184bfd4e6cb (patch)
tree68ad53085b4d49f3f230ef7659b66d0e559ef3a1
parent5d65cfcd994d44665cc7804b3a8e9049d4915788 (diff)
Add .gitignore style ignoring (#13906)
-rw-r--r--Directory.Packages.props3
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj4
-rw-r--r--Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs4
-rw-r--r--Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs77
-rw-r--r--Emby.Server.Implementations/Library/IgnorePatterns.cs1
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs24
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs30
7 files changed, 123 insertions, 20 deletions
diff --git a/Directory.Packages.props b/Directory.Packages.props
index cdce608d9..a24b081dd 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -21,6 +21,7 @@
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="7.3.0.3" />
<PackageVersion Include="ICU4N.Transliterator" Version="60.1.0-alpha.356" />
<PackageVersion Include="IDisposableAnalyzers" Version="4.0.8" />
+ <PackageVersion Include="Ignore" Version="0.2.1" />
<PackageVersion Include="Jellyfin.XmlTv" Version="10.8.0" />
<PackageVersion Include="libse" Version="4.0.12" />
<PackageVersion Include="LrcParser" Version="2025.228.1" />
@@ -88,4 +89,4 @@
<PackageVersion Include="Xunit.SkippableFact" Version="1.5.23" />
<PackageVersion Include="xunit" Version="2.9.3" />
</ItemGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index d99923b4f..15843730e 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -65,6 +65,10 @@
</ItemGroup>
<ItemGroup>
+ <PackageReference Include="Ignore" />
+ </ItemGroup>
+
+ <ItemGroup>
<EmbeddedResource Include="Localization\iso6392.txt" />
<EmbeddedResource Include="Localization\countries.json" />
<EmbeddedResource Include="Localization\Core\*.json" />
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index b01fd93a7..f29a0b3ad 100644
--- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.Library
{
if (parent is not null)
{
- // Ignore extras folders but allow it at the collection level
+ // Ignore extras for unsupported types
if (_namingOptions.AllExtrasTypesFolderNames.ContainsKey(filename)
&& parent is not AggregateFolder
&& parent is not UserRootFolder)
@@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.Library
{
if (parent is not null)
{
- // Don't resolve these into audio files
+ // Don't resolve theme songs
if (Path.GetFileNameWithoutExtension(filename.AsSpan()).Equals(BaseItem.ThemeSongFileName, StringComparison.Ordinal)
&& AudioFileParser.IsAudioFile(filename, _namingOptions))
{
diff --git a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
new file mode 100644
index 000000000..2c186c917
--- /dev/null
+++ b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
@@ -0,0 +1,77 @@
+using System;
+using System.IO;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Resolvers;
+using MediaBrowser.Model.IO;
+
+namespace Emby.Server.Implementations.Library;
+
+/// <summary>
+/// Resolver rule class for ignoring files via .ignore.
+/// </summary>
+public class DotIgnoreIgnoreRule : IResolverIgnoreRule
+{
+ private static FileInfo? FindIgnoreFile(DirectoryInfo directory)
+ {
+ var ignoreFile = new FileInfo(Path.Join(directory.FullName, ".ignore"));
+ if (ignoreFile.Exists)
+ {
+ return ignoreFile;
+ }
+
+ var parentDir = directory.Parent;
+ if (parentDir == null || parentDir.FullName == directory.FullName)
+ {
+ return null;
+ }
+
+ return FindIgnoreFile(parentDir);
+ }
+
+ /// <inheritdoc />
+ public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent)
+ {
+ return IsIgnored(fileInfo, parent);
+ }
+
+ /// <summary>
+ /// Checks whether or not the file is ignored.
+ /// </summary>
+ /// <param name="fileInfo">The file information.</param>
+ /// <param name="parent">The parent BaseItem.</param>
+ /// <returns>True if the file should be ignored.</returns>
+ public static bool IsIgnored(FileSystemMetadata fileInfo, BaseItem? parent)
+ {
+ var parentDirPath = Path.GetDirectoryName(fileInfo.FullName);
+ if (string.IsNullOrEmpty(parentDirPath))
+ {
+ return false;
+ }
+
+ var folder = new DirectoryInfo(parentDirPath);
+ var ignoreFile = FindIgnoreFile(folder);
+ if (ignoreFile is null)
+ {
+ return false;
+ }
+
+ string ignoreFileString;
+ using (var reader = ignoreFile.OpenText())
+ {
+ ignoreFileString = reader.ReadToEnd();
+ }
+
+ if (string.IsNullOrEmpty(ignoreFileString))
+ {
+ // Ignore directory if we just have the file
+ return true;
+ }
+
+ // If file has content, base ignoring off the content .gitignore-style rules
+ var ignoreRules = ignoreFileString.Split('\n', StringSplitOptions.RemoveEmptyEntries);
+ var ignore = new Ignore.Ignore();
+ ignore.Add(ignoreRules);
+
+ return ignore.IsIgnored(fileInfo.FullName);
+ }
+}
diff --git a/Emby.Server.Implementations/Library/IgnorePatterns.cs b/Emby.Server.Implementations/Library/IgnorePatterns.cs
index bb45dd87e..25ddade82 100644
--- a/Emby.Server.Implementations/Library/IgnorePatterns.cs
+++ b/Emby.Server.Implementations/Library/IgnorePatterns.cs
@@ -1,5 +1,4 @@
using System;
-using System.Linq;
using DotNet.Globbing;
namespace Emby.Server.Implementations.Library
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index a6eddbbc3..21c953fb2 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -649,10 +649,11 @@ namespace Emby.Server.Implementations.Library
args.FileSystemChildren = files;
}
- // Check to see if we should resolve based on our contents
- if (args.IsDirectory && !ShouldResolvePathContents(args))
+ // Filter content based on ignore rules
+ if (args.IsDirectory)
{
- return null;
+ var filtered = args.GetActualFileSystemChildren().ToArray();
+ args.FileSystemChildren = filtered ?? [];
}
return ResolveItem(args, resolvers);
@@ -683,17 +684,6 @@ namespace Emby.Server.Implementations.Library
return newList;
}
- /// <summary>
- /// Determines whether a path should be ignored based on its contents - called after the contents have been read.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- private static bool ShouldResolvePathContents(ItemResolveArgs args)
- {
- // Ignore any folders containing a file called .ignore
- return !args.ContainsFileSystemEntryByName(".ignore");
- }
-
public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, CollectionType? collectionType = null)
{
return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers);
@@ -2724,16 +2714,18 @@ namespace Emby.Server.Implementations.Library
public IEnumerable<BaseItem> FindExtras(BaseItem owner, IReadOnlyList<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
+ // Apply .ignore rules
+ var filtered = fileSystemChildren.Where(c => !DotIgnoreIgnoreRule.IsIgnored(c, owner)).ToList();
var ownerVideoInfo = VideoResolver.Resolve(owner.Path, owner.IsFolder, _namingOptions, libraryRoot: owner.ContainingFolderPath);
if (ownerVideoInfo is null)
{
yield break;
}
- var count = fileSystemChildren.Count;
+ var count = filtered.Count;
for (var i = 0; i < count; i++)
{
- var current = fileSystemChildren[i];
+ var current = filtered[i];
if (current.IsDirectory && _namingOptions.AllExtrasTypesFolderNames.ContainsKey(current.Name))
{
var filesInSubFolder = _fileSystem.GetFiles(current.FullName, null, false, false);
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs
new file mode 100644
index 000000000..d677c9f09
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs
@@ -0,0 +1,30 @@
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.Library;
+
+public class DotIgnoreIgnoreRuleTest
+{
+ [Fact]
+ public void Test()
+ {
+ 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"));
+ }
+
+ [Fact]
+ public void TestNegatePattern()
+ {
+ 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"));
+ }
+}