diff options
Diffstat (limited to 'Emby.Server.Implementations/IO')
| -rw-r--r-- | Emby.Server.Implementations/IO/FileRefresher.cs | 31 | ||||
| -rw-r--r-- | Emby.Server.Implementations/IO/LibraryMonitor.cs | 48 | ||||
| -rw-r--r-- | Emby.Server.Implementations/IO/ManagedFileSystem.cs | 71 | ||||
| -rw-r--r-- | Emby.Server.Implementations/IO/StreamHelper.cs | 20 |
4 files changed, 98 insertions, 72 deletions
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs index 47a83d77c..6326208f7 100644 --- a/Emby.Server.Implementations/IO/FileRefresher.cs +++ b/Emby.Server.Implementations/IO/FileRefresher.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; @@ -14,7 +12,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.IO { - public class FileRefresher : IDisposable + public sealed class FileRefresher : IDisposable { private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; @@ -22,7 +20,7 @@ namespace Emby.Server.Implementations.IO private readonly List<string> _affectedPaths = new List<string>(); private readonly object _timerLock = new object(); - private Timer _timer; + private Timer? _timer; private bool _disposed; public FileRefresher(string path, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ILogger logger) @@ -36,7 +34,7 @@ namespace Emby.Server.Implementations.IO AddPath(path); } - public event EventHandler<EventArgs> Completed; + public event EventHandler<EventArgs>? Completed; public string Path { get; private set; } @@ -111,7 +109,7 @@ namespace Emby.Server.Implementations.IO RestartTimer(); } - private void OnTimerCallback(object state) + private void OnTimerCallback(object? state) { List<string> paths; @@ -127,7 +125,7 @@ namespace Emby.Server.Implementations.IO try { - ProcessPathChanges(paths.ToList()); + ProcessPathChanges(paths); } catch (Exception ex) { @@ -137,12 +135,12 @@ namespace Emby.Server.Implementations.IO private void ProcessPathChanges(List<string> paths) { - var itemsToRefresh = paths + IEnumerable<BaseItem> itemsToRefresh = paths .Distinct(StringComparer.OrdinalIgnoreCase) .Select(GetAffectedBaseItem) .Where(item => item != null) - .GroupBy(x => x.Id) - .Select(x => x.First()); + .GroupBy(x => x!.Id) // Removed null values in the previous .Where() + .Select(x => x.First())!; foreach (var item in itemsToRefresh) { @@ -176,15 +174,15 @@ namespace Emby.Server.Implementations.IO /// </summary> /// <param name="path">The path.</param> /// <returns>BaseItem.</returns> - private BaseItem GetAffectedBaseItem(string path) + private BaseItem? GetAffectedBaseItem(string path) { - BaseItem item = null; + BaseItem? item = null; while (item == null && !string.IsNullOrEmpty(path)) { item = _libraryManager.FindByPath(path, null); - path = System.IO.Path.GetDirectoryName(path); + path = System.IO.Path.GetDirectoryName(path) ?? string.Empty; } if (item != null) @@ -219,8 +217,13 @@ namespace Emby.Server.Implementations.IO /// <inheritdoc /> public void Dispose() { - _disposed = true; + if (_disposed) + { + return; + } + DisposeTimer(); + _disposed = true; GC.SuppressFinalize(this); } } diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index aa80bccd7..657daac3f 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -42,6 +42,25 @@ namespace Emby.Server.Implementations.IO private bool _disposed = false; /// <summary> + /// Initializes a new instance of the <see cref="LibraryMonitor" /> class. + /// </summary> + /// <param name="logger">The logger.</param> + /// <param name="libraryManager">The library manager.</param> + /// <param name="configurationManager">The configuration manager.</param> + /// <param name="fileSystem">The filesystem.</param> + public LibraryMonitor( + ILogger<LibraryMonitor> logger, + ILibraryManager libraryManager, + IServerConfigurationManager configurationManager, + IFileSystem fileSystem) + { + _libraryManager = libraryManager; + _logger = logger; + _configurationManager = configurationManager; + _fileSystem = fileSystem; + } + + /// <summary> /// Add the path to our temporary ignore list. Use when writing to a path within our listening scope. /// </summary> /// <param name="path">The path.</param> @@ -80,7 +99,7 @@ namespace Emby.Server.Implementations.IO // But if we make this delay too high, we risk missing legitimate changes, such as user adding a new file, or hand-editing metadata await Task.Delay(45000).ConfigureAwait(false); - _tempIgnoredPaths.TryRemove(path, out var val); + _tempIgnoredPaths.TryRemove(path, out _); if (refreshPath) { @@ -95,21 +114,6 @@ namespace Emby.Server.Implementations.IO } } - /// <summary> - /// Initializes a new instance of the <see cref="LibraryMonitor" /> class. - /// </summary> - public LibraryMonitor( - ILogger<LibraryMonitor> logger, - ILibraryManager libraryManager, - IServerConfigurationManager configurationManager, - IFileSystem fileSystem) - { - _libraryManager = libraryManager; - _logger = logger; - _configurationManager = configurationManager; - _fileSystem = fileSystem; - } - private bool IsLibraryMonitorEnabled(BaseItem item) { if (item is BasePluginFolder) @@ -199,7 +203,7 @@ namespace Emby.Server.Implementations.IO /// <param name="lst">The LST.</param> /// <param name="path">The path.</param> /// <returns><c>true</c> if [contains parent folder] [the specified LST]; otherwise, <c>false</c>.</returns> - /// <exception cref="ArgumentNullException">path</exception> + /// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c>.</exception> private static bool ContainsParentFolder(IEnumerable<string> lst, string path) { if (string.IsNullOrEmpty(path)) @@ -263,7 +267,7 @@ namespace Emby.Server.Implementations.IO if (_fileSystemWatchers.TryAdd(path, newWatcher)) { newWatcher.EnableRaisingEvents = true; - _logger.LogInformation("Watching directory " + path); + _logger.LogInformation("Watching directory {Path}", path); } else { @@ -272,7 +276,7 @@ namespace Emby.Server.Implementations.IO } catch (Exception ex) { - _logger.LogError(ex, "Error watching path: {path}", path); + _logger.LogError(ex, "Error watching path: {Path}", path); } }); } @@ -445,12 +449,12 @@ namespace Emby.Server.Implementations.IO } var newRefresher = new FileRefresher(path, _configurationManager, _libraryManager, _logger); - newRefresher.Completed += NewRefresher_Completed; + newRefresher.Completed += OnNewRefresherCompleted; _activeRefreshers.Add(newRefresher); } } - private void NewRefresher_Completed(object sender, EventArgs e) + private void OnNewRefresherCompleted(object sender, EventArgs e) { var refresher = (FileRefresher)sender; DisposeRefresher(refresher); @@ -477,6 +481,7 @@ namespace Emby.Server.Implementations.IO { lock (_activeRefreshers) { + refresher.Completed -= OnNewRefresherCompleted; refresher.Dispose(); _activeRefreshers.Remove(refresher); } @@ -488,6 +493,7 @@ namespace Emby.Server.Implementations.IO { foreach (var refresher in _activeRefreshers.ToList()) { + refresher.Completed -= OnNewRefresherCompleted; refresher.Dispose(); } diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 77da46cd6..5c86dbbb7 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -17,20 +15,26 @@ namespace Emby.Server.Implementations.IO /// </summary> public class ManagedFileSystem : IFileSystem { - protected ILogger<ManagedFileSystem> Logger; + private readonly ILogger<ManagedFileSystem> _logger; private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>(); private readonly string _tempPath; private static readonly bool _isEnvironmentCaseInsensitive = OperatingSystem.IsWindows(); + /// <summary> + /// Initializes a new instance of the <see cref="ManagedFileSystem"/> class. + /// </summary> + /// <param name="logger">The <see cref="ILogger"/> instance to use.</param> + /// <param name="applicationPaths">The <see cref="IApplicationPaths"/> instance to use.</param> public ManagedFileSystem( ILogger<ManagedFileSystem> logger, IApplicationPaths applicationPaths) { - Logger = logger; + _logger = logger; _tempPath = applicationPaths.TempDirectory; } + /// <inheritdoc /> public virtual void AddShortcutHandler(IShortcutHandler handler) { _shortcutHandlers.Add(handler); @@ -41,7 +45,7 @@ namespace Emby.Server.Implementations.IO /// </summary> /// <param name="filename">The filename.</param> /// <returns><c>true</c> if the specified filename is shortcut; otherwise, <c>false</c>.</returns> - /// <exception cref="ArgumentNullException">filename</exception> + /// <exception cref="ArgumentNullException"><paramref name="filename"/> is <c>null</c>.</exception> public virtual bool IsShortcut(string filename) { if (string.IsNullOrEmpty(filename)) @@ -58,7 +62,7 @@ namespace Emby.Server.Implementations.IO /// </summary> /// <param name="filename">The filename.</param> /// <returns>System.String.</returns> - /// <exception cref="ArgumentNullException">filename</exception> + /// <exception cref="ArgumentNullException"><paramref name="filename"/> is <c>null</c>.</exception> public virtual string? ResolveShortcut(string filename) { if (string.IsNullOrEmpty(filename)) @@ -72,6 +76,7 @@ namespace Emby.Server.Implementations.IO return handler?.Resolve(filename); } + /// <inheritdoc /> public virtual string MakeAbsolutePath(string folderPath, string filePath) { // path is actually a stream @@ -233,9 +238,9 @@ namespace Emby.Server.Implementations.IO result.IsDirectory = info is DirectoryInfo || (info.Attributes & FileAttributes.Directory) == FileAttributes.Directory; // if (!result.IsDirectory) - //{ + // { // result.IsHidden = (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden; - //} + // } if (info is FileInfo fileInfo) { @@ -254,7 +259,7 @@ namespace Emby.Server.Implementations.IO catch (FileNotFoundException ex) { // Dangling symlinks cannot be detected before opening the file unfortunately... - Logger.LogError(ex, "Reading the file size of the symlink at {Path} failed. Marking the file as not existing.", fileInfo.FullName); + _logger.LogError(ex, "Reading the file size of the symlink at {Path} failed. Marking the file as not existing.", fileInfo.FullName); result.Exists = false; } } @@ -343,7 +348,7 @@ namespace Emby.Server.Implementations.IO } catch (Exception ex) { - Logger.LogError(ex, "Error determining CreationTimeUtc for {FullName}", info.FullName); + _logger.LogError(ex, "Error determining CreationTimeUtc for {FullName}", info.FullName); return DateTime.MinValue; } } @@ -358,11 +363,13 @@ namespace Emby.Server.Implementations.IO return GetCreationTimeUtc(GetFileSystemInfo(path)); } + /// <inheritdoc /> public virtual DateTime GetCreationTimeUtc(FileSystemMetadata info) { return info.CreationTimeUtc; } + /// <inheritdoc /> public virtual DateTime GetLastWriteTimeUtc(FileSystemMetadata info) { return info.LastWriteTimeUtc; @@ -382,7 +389,7 @@ namespace Emby.Server.Implementations.IO } catch (Exception ex) { - Logger.LogError(ex, "Error determining LastAccessTimeUtc for {FullName}", info.FullName); + _logger.LogError(ex, "Error determining LastAccessTimeUtc for {FullName}", info.FullName); return DateTime.MinValue; } } @@ -397,6 +404,7 @@ namespace Emby.Server.Implementations.IO return GetLastWriteTimeUtc(GetFileSystemInfo(path)); } + /// <inheritdoc /> public virtual void SetHidden(string path, bool isHidden) { if (!OperatingSystem.IsWindows()) @@ -421,6 +429,7 @@ namespace Emby.Server.Implementations.IO } } + /// <inheritdoc /> public virtual void SetAttributes(string path, bool isHidden, bool readOnly) { if (!OperatingSystem.IsWindows()) @@ -444,7 +453,7 @@ namespace Emby.Server.Implementations.IO if (readOnly) { - attributes = attributes | FileAttributes.ReadOnly; + attributes |= FileAttributes.ReadOnly; } else { @@ -453,7 +462,7 @@ namespace Emby.Server.Implementations.IO if (isHidden) { - attributes = attributes | FileAttributes.Hidden; + attributes |= FileAttributes.Hidden; } else { @@ -498,6 +507,7 @@ namespace Emby.Server.Implementations.IO File.Copy(temp1, file2, true); } + /// <inheritdoc /> public virtual bool ContainsSubPath(string parentPath, string path) { if (string.IsNullOrEmpty(parentPath)) @@ -515,6 +525,7 @@ namespace Emby.Server.Implementations.IO _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } + /// <inheritdoc /> public virtual string NormalizePath(string path) { if (string.IsNullOrEmpty(path)) @@ -530,24 +541,16 @@ namespace Emby.Server.Implementations.IO return Path.TrimEndingDirectorySeparator(path); } + /// <inheritdoc /> public virtual bool AreEqual(string path1, string path2) { - if (path1 == null && path2 == null) - { - return true; - } - - if (path1 == null || path2 == null) - { - return false; - } - return string.Equals( NormalizePath(path1), NormalizePath(path2), _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } + /// <inheritdoc /> public virtual string GetFileNameWithoutExtension(FileSystemMetadata info) { if (info.IsDirectory) @@ -558,11 +561,11 @@ namespace Emby.Server.Implementations.IO return Path.GetFileNameWithoutExtension(info.FullName); } + /// <inheritdoc /> public virtual bool IsPathFile(string path) { - // Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\ - if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) != -1 && - !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase)) + if (path.Contains("://", StringComparison.OrdinalIgnoreCase) + && !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase)) { return false; } @@ -570,17 +573,23 @@ namespace Emby.Server.Implementations.IO return true; } + /// <inheritdoc /> public virtual void DeleteFile(string path) { SetAttributes(path, false, false); File.Delete(path); } + /// <inheritdoc /> public virtual List<FileSystemMetadata> GetDrives() { // check for ready state to avoid waiting for drives to timeout // some drives on linux have no actual size or are used for other purposes - return DriveInfo.GetDrives().Where(d => d.IsReady && d.TotalSize != 0 && d.DriveType != DriveType.Ram) + return DriveInfo.GetDrives() + .Where( + d => (d.DriveType == DriveType.Fixed || d.DriveType == DriveType.Network || d.DriveType == DriveType.Removable) + && d.IsReady + && d.TotalSize != 0) .Select(d => new FileSystemMetadata { Name = d.Name, @@ -589,16 +598,19 @@ namespace Emby.Server.Implementations.IO }).ToList(); } + /// <inheritdoc /> public virtual IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false) { return ToMetadata(new DirectoryInfo(path).EnumerateDirectories("*", GetEnumerationOptions(recursive))); } + /// <inheritdoc /> public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false) { return GetFiles(path, null, false, recursive); } + /// <inheritdoc /> public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, IReadOnlyList<string>? extensions, bool enableCaseSensitiveExtensions, bool recursive = false) { var enumerationOptions = GetEnumerationOptions(recursive); @@ -629,6 +641,7 @@ namespace Emby.Server.Implementations.IO return ToMetadata(files); } + /// <inheritdoc /> public virtual IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false) { var directoryInfo = new DirectoryInfo(path); @@ -642,16 +655,19 @@ namespace Emby.Server.Implementations.IO return infos.Select(GetFileSystemMetadata); } + /// <inheritdoc /> public virtual IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false) { return Directory.EnumerateDirectories(path, "*", GetEnumerationOptions(recursive)); } + /// <inheritdoc /> public virtual IEnumerable<string> GetFilePaths(string path, bool recursive = false) { return GetFilePaths(path, null, false, recursive); } + /// <inheritdoc /> public virtual IEnumerable<string> GetFilePaths(string path, string[]? extensions, bool enableCaseSensitiveExtensions, bool recursive = false) { var enumerationOptions = GetEnumerationOptions(recursive); @@ -682,6 +698,7 @@ namespace Emby.Server.Implementations.IO return files; } + /// <inheritdoc /> public virtual IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false) { return Directory.EnumerateFileSystemEntries(path, "*", GetEnumerationOptions(recursive)); diff --git a/Emby.Server.Implementations/IO/StreamHelper.cs b/Emby.Server.Implementations/IO/StreamHelper.cs index e4f5f4cf0..f55c16d6d 100644 --- a/Emby.Server.Implementations/IO/StreamHelper.cs +++ b/Emby.Server.Implementations/IO/StreamHelper.cs @@ -17,11 +17,11 @@ namespace Emby.Server.Implementations.IO try { int read; - while ((read = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + while ((read = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0) { cancellationToken.ThrowIfCancellationRequested(); - await destination.WriteAsync(buffer, 0, read, cancellationToken).ConfigureAwait(false); + await destination.WriteAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false); if (onStarted != null) { @@ -44,11 +44,11 @@ namespace Emby.Server.Implementations.IO if (emptyReadLimit <= 0) { int read; - while ((read = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + while ((read = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0) { cancellationToken.ThrowIfCancellationRequested(); - await destination.WriteAsync(buffer, 0, read, cancellationToken).ConfigureAwait(false); + await destination.WriteAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false); } return; @@ -60,7 +60,7 @@ namespace Emby.Server.Implementations.IO { cancellationToken.ThrowIfCancellationRequested(); - var bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + var bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); if (bytesRead == 0) { @@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.IO { eofCount = 0; - await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); + await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false); } } } @@ -88,13 +88,13 @@ namespace Emby.Server.Implementations.IO { int bytesRead; - while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0) { var bytesToWrite = Math.Min(bytesRead, copyLength); if (bytesToWrite > 0) { - await destination.WriteAsync(buffer, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + await destination.WriteAsync(buffer.AsMemory(0, Convert.ToInt32(bytesToWrite)), cancellationToken).ConfigureAwait(false); } copyLength -= bytesToWrite; @@ -137,9 +137,9 @@ namespace Emby.Server.Implementations.IO int bytesRead; int totalBytesRead = 0; - while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0) { - await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); + await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false); totalBytesRead += bytesRead; } |
