Minor LibraryMonitor improvements

* Enable nullable
* Add a fast return to ReportFileSystemChanged when path should be ignored
* Use Span overloads of Path.* functions where possible
* IFileSystem: remove NormalizePath as Path.TrimEndingDirectorySeparator already checks if it's a root path
pull/10218/head
Bond_009 9 months ago
parent 3f19befc59
commit 767a42fbdb

@ -157,7 +157,7 @@ namespace Emby.Server.Implementations
_fileSystemManager = new ManagedFileSystem(LoggerFactory.CreateLogger<ManagedFileSystem>(), applicationPaths); _fileSystemManager = new ManagedFileSystem(LoggerFactory.CreateLogger<ManagedFileSystem>(), applicationPaths);
Logger = LoggerFactory.CreateLogger<ApplicationHost>(); Logger = LoggerFactory.CreateLogger<ApplicationHost>();
_fileSystemManager.AddShortcutHandler(new MbLinkShortcutHandler(_fileSystemManager)); _fileSystemManager.AddShortcutHandler(new MbLinkShortcutHandler());
_deviceId = new DeviceId(ApplicationPaths, LoggerFactory); _deviceId = new DeviceId(ApplicationPaths, LoggerFactory);
ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version; ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version;

@ -85,7 +85,7 @@ namespace Emby.Server.Implementations.IO
} }
} }
public void ResetPath(string path, string affectedFile) public void ResetPath(string path, string? affectedFile)
{ {
lock (_timerLock) lock (_timerLock)
{ {
@ -148,13 +148,6 @@ namespace Emby.Server.Implementations.IO
{ {
item.ChangedExternally(); item.ChangedExternally();
} }
catch (IOException ex)
{
// For now swallow and log.
// Research item: If an IOException occurs, the item may be in a disconnected state (media unavailable)
// Should we remove it from it's parent?
_logger.LogError(ex, "Error refreshing {Name}", item.Name);
}
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Error refreshing {Name}", item.Name); _logger.LogError(ex, "Error refreshing {Name}", item.Name);

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
@ -160,7 +158,7 @@ namespace Emby.Server.Implementations.IO
/// </summary> /// </summary>
/// <param name="sender">The source of the event.</param> /// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param> /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
private void OnLibraryManagerItemRemoved(object sender, ItemChangeEventArgs e) private void OnLibraryManagerItemRemoved(object? sender, ItemChangeEventArgs e)
{ {
if (e.Parent is AggregateFolder) if (e.Parent is AggregateFolder)
{ {
@ -173,7 +171,7 @@ namespace Emby.Server.Implementations.IO
/// </summary> /// </summary>
/// <param name="sender">The source of the event.</param> /// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param> /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
private void OnLibraryManagerItemAdded(object sender, ItemChangeEventArgs e) private void OnLibraryManagerItemAdded(object? sender, ItemChangeEventArgs e)
{ {
if (e.Parent is AggregateFolder) if (e.Parent is AggregateFolder)
{ {
@ -189,19 +187,28 @@ namespace Emby.Server.Implementations.IO
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
/// <returns><c>true</c> if [contains parent folder] [the specified LST]; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if [contains parent folder] [the specified LST]; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c>.</exception>
private static bool ContainsParentFolder(IEnumerable<string> lst, string path) private static bool ContainsParentFolder(IReadOnlyList<string> lst, ReadOnlySpan<char> path)
{ {
ArgumentException.ThrowIfNullOrEmpty(path); if (path.IsEmpty)
{
throw new ArgumentException("Path can't be empty", nameof(path));
}
path = path.TrimEnd(Path.DirectorySeparatorChar); path = path.TrimEnd(Path.DirectorySeparatorChar);
return lst.Any(str => foreach (var str in lst)
{ {
// this should be a little quicker than examining each actual parent folder... // this should be a little quicker than examining each actual parent folder...
var compare = str.TrimEnd(Path.DirectorySeparatorChar); var compare = str.AsSpan().TrimEnd(Path.DirectorySeparatorChar);
return path.Equals(compare, StringComparison.OrdinalIgnoreCase) || (path.StartsWith(compare, StringComparison.OrdinalIgnoreCase) && path[compare.Length] == Path.DirectorySeparatorChar); if (path.Equals(compare, StringComparison.OrdinalIgnoreCase)
}); || (path.StartsWith(compare, StringComparison.OrdinalIgnoreCase) && path[compare.Length] == Path.DirectorySeparatorChar))
{
return true;
}
}
return false;
} }
/// <summary> /// <summary>
@ -349,21 +356,19 @@ namespace Emby.Server.Implementations.IO
{ {
ArgumentException.ThrowIfNullOrEmpty(path); ArgumentException.ThrowIfNullOrEmpty(path);
var monitorPath = !IgnorePatterns.ShouldIgnore(path); if (IgnorePatterns.ShouldIgnore(path))
{
return;
}
// Ignore certain files, If the parent of an ignored path has a change event, ignore that too // Ignore certain files, If the parent of an ignored path has a change event, ignore that too
if (_tempIgnoredPaths.Keys.Any(i => foreach (var i in _tempIgnoredPaths.Keys)
{ {
if (_fileSystem.AreEqual(i, path)) if (_fileSystem.AreEqual(i, path)
{ || _fileSystem.ContainsSubPath(i, path))
_logger.LogDebug("Ignoring change to {Path}", path);
return true;
}
if (_fileSystem.ContainsSubPath(i, path))
{ {
_logger.LogDebug("Ignoring change to {Path}", path); _logger.LogDebug("Ignoring change to {Path}", path);
return true; return;
} }
// Go up a level // Go up a level
@ -371,20 +376,12 @@ namespace Emby.Server.Implementations.IO
if (!string.IsNullOrEmpty(parent) && _fileSystem.AreEqual(parent, path)) if (!string.IsNullOrEmpty(parent) && _fileSystem.AreEqual(parent, path))
{ {
_logger.LogDebug("Ignoring change to {Path}", path); _logger.LogDebug("Ignoring change to {Path}", path);
return true; return;
} }
return false;
}))
{
monitorPath = false;
} }
if (monitorPath) // Avoid implicitly captured closure
{ CreateRefresher(path);
// Avoid implicitly captured closure
CreateRefresher(path);
}
} }
private void CreateRefresher(string path) private void CreateRefresher(string path)
@ -417,7 +414,7 @@ namespace Emby.Server.Implementations.IO
} }
// They are siblings. Rebase the refresher to the parent folder. // They are siblings. Rebase the refresher to the parent folder.
if (string.Equals(parentPath, Path.GetDirectoryName(refresher.Path), StringComparison.Ordinal)) if (parentPath is not null && string.Equals(parentPath, Path.GetDirectoryName(refresher.Path), StringComparison.Ordinal))
{ {
refresher.ResetPath(parentPath, path); refresher.ResetPath(parentPath, path);
return; return;
@ -430,8 +427,13 @@ namespace Emby.Server.Implementations.IO
} }
} }
private void OnNewRefresherCompleted(object sender, EventArgs e) private void OnNewRefresherCompleted(object? sender, EventArgs e)
{ {
if (sender is null)
{
return;
}
var refresher = (FileRefresher)sender; var refresher = (FileRefresher)sender;
DisposeRefresher(refresher); DisposeRefresher(refresher);
} }

@ -485,25 +485,11 @@ namespace Emby.Server.Implementations.IO
_isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
} }
/// <inheritdoc />
public virtual string NormalizePath(string path)
{
ArgumentException.ThrowIfNullOrEmpty(path);
if (path.EndsWith(":\\", StringComparison.OrdinalIgnoreCase))
{
return path;
}
return Path.TrimEndingDirectorySeparator(path);
}
/// <inheritdoc /> /// <inheritdoc />
public virtual bool AreEqual(string path1, string path2) public virtual bool AreEqual(string path1, string path2)
{ {
return string.Equals( return Path.TrimEndingDirectorySeparator(path1).Equals(
NormalizePath(path1), Path.TrimEndingDirectorySeparator(path2),
NormalizePath(path2),
_isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
} }

@ -8,24 +8,17 @@ namespace Emby.Server.Implementations.IO
{ {
public class MbLinkShortcutHandler : IShortcutHandler public class MbLinkShortcutHandler : IShortcutHandler
{ {
private readonly IFileSystem _fileSystem;
public MbLinkShortcutHandler(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
public string Extension => ".mblink"; public string Extension => ".mblink";
public string? Resolve(string shortcutPath) public string? Resolve(string shortcutPath)
{ {
ArgumentException.ThrowIfNullOrEmpty(shortcutPath); ArgumentException.ThrowIfNullOrEmpty(shortcutPath);
if (string.Equals(Path.GetExtension(shortcutPath), ".mblink", StringComparison.OrdinalIgnoreCase)) if (Path.GetExtension(shortcutPath.AsSpan()).Equals(".mblink", StringComparison.OrdinalIgnoreCase))
{ {
var path = File.ReadAllText(shortcutPath); var path = File.ReadAllText(shortcutPath);
return _fileSystem.NormalizePath(path); return Path.TrimEndingDirectorySeparator(path);
} }
return null; return null;

@ -608,7 +608,7 @@ namespace Emby.Server.Implementations.Library
var originalList = paths.ToList(); var originalList = paths.ToList();
var list = originalList.Where(i => i.IsDirectory) var list = originalList.Where(i => i.IsDirectory)
.Select(i => _fileSystem.NormalizePath(i.FullName)) .Select(i => Path.TrimEndingDirectorySeparator(i.FullName))
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.ToList(); .ToList();

@ -116,13 +116,6 @@ namespace MediaBrowser.Model.IO
/// <returns><c>true</c> if [contains sub path] [the specified parent path]; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if [contains sub path] [the specified parent path]; otherwise, <c>false</c>.</returns>
bool ContainsSubPath(string parentPath, string path); bool ContainsSubPath(string parentPath, string path);
/// <summary>
/// Normalizes the path.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>System.String.</returns>
string NormalizePath(string path);
/// <summary> /// <summary>
/// Gets the file name without extension. /// Gets the file name without extension.
/// </summary> /// </summary>

@ -52,6 +52,8 @@
<Rule Id="SA1204" Action="None" /> <Rule Id="SA1204" Action="None" />
<!-- disable warning SA1309: Fields must not begin with an underscore --> <!-- disable warning SA1309: Fields must not begin with an underscore -->
<Rule Id="SA1309" Action="None" /> <Rule Id="SA1309" Action="None" />
<!-- disable warning SA1311: Static readonly fields should begin with upper-case letter -->
<Rule Id="SA1311" Action="None" />
<!-- disable warning SA1413: Use trailing comma in multi-line initializers --> <!-- disable warning SA1413: Use trailing comma in multi-line initializers -->
<Rule Id="SA1413" Action="None" /> <Rule Id="SA1413" Action="None" />
<!-- disable warning SA1512: Single-line comments must not be followed by blank line --> <!-- disable warning SA1512: Single-line comments must not be followed by blank line -->

Loading…
Cancel
Save