mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-05-24 02:02:29 -04:00
Add .gitignore style ignoring (#13906)
This commit is contained in:
parent
5d65cfcd99
commit
a0b3b7335f
@ -21,6 +21,7 @@
|
|||||||
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="7.3.0.3" />
|
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="7.3.0.3" />
|
||||||
<PackageVersion Include="ICU4N.Transliterator" Version="60.1.0-alpha.356" />
|
<PackageVersion Include="ICU4N.Transliterator" Version="60.1.0-alpha.356" />
|
||||||
<PackageVersion Include="IDisposableAnalyzers" Version="4.0.8" />
|
<PackageVersion Include="IDisposableAnalyzers" Version="4.0.8" />
|
||||||
|
<PackageVersion Include="Ignore" Version="0.2.1" />
|
||||||
<PackageVersion Include="Jellyfin.XmlTv" Version="10.8.0" />
|
<PackageVersion Include="Jellyfin.XmlTv" Version="10.8.0" />
|
||||||
<PackageVersion Include="libse" Version="4.0.12" />
|
<PackageVersion Include="libse" Version="4.0.12" />
|
||||||
<PackageVersion Include="LrcParser" Version="2025.228.1" />
|
<PackageVersion Include="LrcParser" Version="2025.228.1" />
|
||||||
@ -88,4 +89,4 @@
|
|||||||
<PackageVersion Include="Xunit.SkippableFact" Version="1.5.23" />
|
<PackageVersion Include="Xunit.SkippableFact" Version="1.5.23" />
|
||||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -64,6 +64,10 @@
|
|||||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Ignore" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Localization\iso6392.txt" />
|
<EmbeddedResource Include="Localization\iso6392.txt" />
|
||||||
<EmbeddedResource Include="Localization\countries.json" />
|
<EmbeddedResource Include="Localization\countries.json" />
|
||||||
|
@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
{
|
{
|
||||||
if (parent is not null)
|
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)
|
if (_namingOptions.AllExtrasTypesFolderNames.ContainsKey(filename)
|
||||||
&& parent is not AggregateFolder
|
&& parent is not AggregateFolder
|
||||||
&& parent is not UserRootFolder)
|
&& parent is not UserRootFolder)
|
||||||
@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
{
|
{
|
||||||
if (parent is not null)
|
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)
|
if (Path.GetFileNameWithoutExtension(filename.AsSpan()).Equals(BaseItem.ThemeSongFileName, StringComparison.Ordinal)
|
||||||
&& AudioFileParser.IsAudioFile(filename, _namingOptions))
|
&& AudioFileParser.IsAudioFile(filename, _namingOptions))
|
||||||
{
|
{
|
||||||
|
77
Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
Normal file
77
Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using DotNet.Globbing;
|
using DotNet.Globbing;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Library
|
namespace Emby.Server.Implementations.Library
|
||||||
|
@ -649,10 +649,11 @@ namespace Emby.Server.Implementations.Library
|
|||||||
args.FileSystemChildren = files;
|
args.FileSystemChildren = files;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if we should resolve based on our contents
|
// Filter content based on ignore rules
|
||||||
if (args.IsDirectory && !ShouldResolvePathContents(args))
|
if (args.IsDirectory)
|
||||||
{
|
{
|
||||||
return null;
|
var filtered = args.GetActualFileSystemChildren().ToArray();
|
||||||
|
args.FileSystemChildren = filtered ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResolveItem(args, resolvers);
|
return ResolveItem(args, resolvers);
|
||||||
@ -683,17 +684,6 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return newList;
|
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)
|
public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, CollectionType? collectionType = null)
|
||||||
{
|
{
|
||||||
return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers);
|
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)
|
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);
|
var ownerVideoInfo = VideoResolver.Resolve(owner.Path, owner.IsFolder, _namingOptions, libraryRoot: owner.ContainingFolderPath);
|
||||||
if (ownerVideoInfo is null)
|
if (ownerVideoInfo is null)
|
||||||
{
|
{
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var count = fileSystemChildren.Count;
|
var count = filtered.Count;
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var current = fileSystemChildren[i];
|
var current = filtered[i];
|
||||||
if (current.IsDirectory && _namingOptions.AllExtrasTypesFolderNames.ContainsKey(current.Name))
|
if (current.IsDirectory && _namingOptions.AllExtrasTypesFolderNames.ContainsKey(current.Name))
|
||||||
{
|
{
|
||||||
var filesInSubFolder = _fileSystem.GetFiles(current.FullName, null, false, false);
|
var filesInSubFolder = _fileSystem.GetFiles(current.FullName, null, false, false);
|
||||||
|
@ -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"));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user