< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.Library.DotIgnoreIgnoreRule
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
Line coverage
65%
Covered lines: 27
Uncovered lines: 14
Coverable lines: 41
Total lines: 140
Line coverage: 65.8%
Branch coverage
50%
Covered branches: 15
Total branches: 30
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 10/1/2025 - 12:09:48 AM Line coverage: 51.5% (17/33) Branch coverage: 44.4% (8/18) Total lines: 10110/28/2025 - 12:11:27 AM Line coverage: 48.5% (17/35) Branch coverage: 36.3% (8/22) Total lines: 10611/18/2025 - 12:11:25 AM Line coverage: 38.7% (12/31) Branch coverage: 30.7% (8/26) Total lines: 9612/29/2025 - 12:13:19 AM Line coverage: 65.8% (27/41) Branch coverage: 50% (15/30) Total lines: 140 10/1/2025 - 12:09:48 AM Line coverage: 51.5% (17/33) Branch coverage: 44.4% (8/18) Total lines: 10110/28/2025 - 12:11:27 AM Line coverage: 48.5% (17/35) Branch coverage: 36.3% (8/22) Total lines: 10611/18/2025 - 12:11:25 AM Line coverage: 38.7% (12/31) Branch coverage: 30.7% (8/26) Total lines: 9612/29/2025 - 12:13:19 AM Line coverage: 65.8% (27/41) Branch coverage: 50% (15/30) Total lines: 140

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
FindIgnoreFile(...)75%4480%
ShouldIgnore(...)100%11100%
IsIgnored(...)35.71%331453.84%
CheckIgnoreRules(...)100%210%
CheckIgnoreRules(...)100%11100%
CheckIgnoreRules(...)87.5%8892.85%
GetFileContent(...)0%2040%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3using System.Text.RegularExpressions;
 4using MediaBrowser.Controller.Entities;
 5using MediaBrowser.Controller.IO;
 6using MediaBrowser.Controller.Resolvers;
 7using MediaBrowser.Model.IO;
 8
 9namespace Emby.Server.Implementations.Library;
 10
 11/// <summary>
 12/// Resolver rule class for ignoring files via .ignore.
 13/// </summary>
 14public class DotIgnoreIgnoreRule : IResolverIgnoreRule
 15{
 116    private static readonly bool IsWindows = OperatingSystem.IsWindows();
 17
 18    private static FileInfo? FindIgnoreFile(DirectoryInfo directory)
 19    {
 182220        for (var current = directory; current is not null; current = current.Parent)
 21        {
 79422            var ignorePath = Path.Join(current.FullName, ".ignore");
 79423            if (File.Exists(ignorePath))
 24            {
 025                return new FileInfo(ignorePath);
 26            }
 27        }
 28
 11729        return null;
 30    }
 31
 32    /// <inheritdoc />
 8333    public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent) => IsIgnored(fileInfo, parent);
 34
 35    /// <summary>
 36    /// Checks whether or not the file is ignored.
 37    /// </summary>
 38    /// <param name="fileInfo">The file information.</param>
 39    /// <param name="parent">The parent BaseItem.</param>
 40    /// <returns>True if the file should be ignored.</returns>
 41    public static bool IsIgnored(FileSystemMetadata fileInfo, BaseItem? parent)
 42    {
 11743        var searchDirectory = fileInfo.IsDirectory
 11744            ? new DirectoryInfo(fileInfo.FullName)
 11745            : new DirectoryInfo(Path.GetDirectoryName(fileInfo.FullName) ?? string.Empty);
 46
 11747        if (string.IsNullOrEmpty(searchDirectory.FullName))
 48        {
 049            return false;
 50        }
 51
 11752        var ignoreFile = FindIgnoreFile(searchDirectory);
 11753        if (ignoreFile is null)
 54        {
 11755            return false;
 56        }
 57
 58        // Fast path in case the ignore files isn't a symlink and is empty
 059        if (ignoreFile.LinkTarget is null && ignoreFile.Length == 0)
 60        {
 61            // Ignore directory if we just have the file
 062            return true;
 63        }
 64
 065        var content = GetFileContent(ignoreFile);
 066        return string.IsNullOrWhiteSpace(content)
 067            || CheckIgnoreRules(fileInfo.FullName, content, fileInfo.IsDirectory);
 68    }
 69
 70    private static bool CheckIgnoreRules(string path, string ignoreFileContent, bool isDirectory)
 71    {
 72        // If file has content, base ignoring off the content .gitignore-style rules
 073        var rules = ignoreFileContent.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries
 074        return CheckIgnoreRules(path, rules, isDirectory);
 75    }
 76
 77    /// <summary>
 78    /// Checks whether a path should be ignored based on an array of ignore rules.
 79    /// </summary>
 80    /// <param name="path">The path to check.</param>
 81    /// <param name="rules">The array of ignore rules.</param>
 82    /// <param name="isDirectory">Whether the path is a directory.</param>
 83    /// <returns>True if the path should be ignored.</returns>
 84    internal static bool CheckIgnoreRules(string path, string[] rules, bool isDirectory)
 1585        => CheckIgnoreRules(path, rules, isDirectory, IsWindows);
 86
 87    /// <summary>
 88    /// Checks whether a path should be ignored based on an array of ignore rules.
 89    /// </summary>
 90    /// <param name="path">The path to check.</param>
 91    /// <param name="rules">The array of ignore rules.</param>
 92    /// <param name="isDirectory">Whether the path is a directory.</param>
 93    /// <param name="normalizePath">Whether to normalize backslashes to forward slashes (for Windows paths).</param>
 94    /// <returns>True if the path should be ignored.</returns>
 95    internal static bool CheckIgnoreRules(string path, string[] rules, bool isDirectory, bool normalizePath)
 96    {
 2597        var ignore = new Ignore.Ignore();
 98
 99        // Add each rule individually to catch and skip invalid patterns
 25100        var validRulesAdded = 0;
 176101        foreach (var rule in rules)
 102        {
 103            try
 104            {
 63105                ignore.Add(rule);
 45106                validRulesAdded++;
 45107            }
 18108            catch (RegexParseException)
 109            {
 110                // Ignore invalid patterns
 18111            }
 112        }
 113
 114        // If no valid rules were added, fall back to ignoring everything (like an empty .ignore file)
 25115        if (validRulesAdded == 0)
 116        {
 2117            return true;
 118        }
 119
 120         // Mitigate the problem of the Ignore library not handling Windows paths correctly.
 121         // See https://github.com/jellyfin/jellyfin/issues/15484
 23122        var pathToCheck = normalizePath ? path.NormalizePath('/') : path;
 123
 124        // Add trailing slash for directories to match "folder/"
 23125        if (isDirectory)
 126        {
 0127            pathToCheck = string.Concat(pathToCheck.AsSpan().TrimEnd('/'), "/");
 128        }
 129
 23130        return ignore.IsIgnored(pathToCheck);
 131    }
 132
 133    private static string GetFileContent(FileInfo ignoreFile)
 134    {
 0135        ignoreFile = FileSystemHelper.ResolveLinkTarget(ignoreFile, returnFinalTarget: true) ?? ignoreFile;
 0136        return ignoreFile.Exists
 0137            ? File.ReadAllText(ignoreFile.FullName)
 0138            : string.Empty;
 139    }
 140}