瀏覽代碼

Cleanup file related code (#14023)

Bond-009 3 周之前
父節點
當前提交
0c3ba30de2

+ 3 - 5
Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs

@@ -2,6 +2,7 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using Jellyfin.Extensions;
 using MediaBrowser.Common.Configuration;
 
 namespace Emby.Server.Implementations.AppBase
@@ -91,10 +92,7 @@ namespace Emby.Server.Implementations.AppBase
         /// <inheritdoc />
         public void CreateAndCheckMarker(string path, string markerName, bool recursive = false)
         {
-            if (!Directory.Exists(path))
-            {
-                Directory.CreateDirectory(path);
-            }
+            Directory.CreateDirectory(path);
 
             CheckOrCreateMarker(path, $".jellyfin-{markerName}", recursive);
         }
@@ -115,7 +113,7 @@ namespace Emby.Server.Implementations.AppBase
             var markerPath = Path.Combine(path, markerName);
             if (!File.Exists(markerPath))
             {
-                File.Create(markerPath).Dispose();
+                FileHelper.CreateEmpty(markerPath);
             }
         }
     }

+ 4 - 4
Emby.Server.Implementations/IO/ManagedFileSystem.cs

@@ -159,13 +159,13 @@ namespace Emby.Server.Implementations.IO
             catch (IOException)
             {
                 // Cross device move requires a copy
-                Directory.CreateDirectory(destination);
-                foreach (string file in Directory.GetFiles(source))
+                var directory = Directory.CreateDirectory(destination);
+                foreach (var file in directory.EnumerateFiles())
                 {
-                    File.Copy(file, Path.Combine(destination, Path.GetFileName(file)), true);
+                    file.CopyTo(Path.Combine(destination, file.Name), true);
                 }
 
-                Directory.Delete(source, true);
+                directory.Delete(true);
             }
         }
 

+ 1 - 1
Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs

@@ -20,7 +20,7 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule
         }
 
         var parentDir = directory.Parent;
-        if (parentDir == null || parentDir.FullName == directory.FullName)
+        if (parentDir is null)
         {
             return null;
         }

+ 1 - 1
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -2945,7 +2945,7 @@ namespace Emby.Server.Implementations.Library
                 {
                     var path = Path.Combine(virtualFolderPath, collectionType.ToString()!.ToLowerInvariant() + ".collection"); // Can't be null with legal values?
 
-                    await File.WriteAllBytesAsync(path, []).ConfigureAwait(false);
+                    FileHelper.CreateEmpty(path);
                 }
 
                 CollectionFolder.SaveLibraryOptions(virtualFolderPath, options);

+ 6 - 6
Emby.Server.Implementations/Library/MediaSourceManager.cs

@@ -681,17 +681,17 @@ namespace Emby.Server.Implementations.Library
 
                 mediaInfo = await _mediaEncoder.GetMediaInfo(
                     new MediaInfoRequest
-                {
-                    MediaSource = mediaSource,
-                    MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
-                    ExtractChapters = false
-                },
+                    {
+                        MediaSource = mediaSource,
+                        MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
+                        ExtractChapters = false
+                    },
                     cancellationToken).ConfigureAwait(false);
 
                 if (cacheFilePath is not null)
                 {
                     Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
-                    FileStream createStream = File.Create(cacheFilePath);
+                    FileStream createStream = AsyncFile.Create(cacheFilePath);
                     await using (createStream.ConfigureAwait(false))
                     {
                         await JsonSerializer.SerializeAsync(createStream, mediaInfo, _jsonOptions, cancellationToken).ConfigureAwait(false);

+ 1 - 1
Emby.Server.Implementations/Localization/LocalizationManager.cs

@@ -520,7 +520,7 @@ namespace Emby.Server.Implementations.Localization
         public bool TryGetISO6392TFromB(string isoB, [NotNullWhen(true)] out string? isoT)
         {
             // Unlikely case the dictionary is not (yet) initialized properly
-            if (_iso6392BtoT == null)
+            if (_iso6392BtoT is null)
             {
                 isoT = null;
                 return false;

+ 1 - 1
Jellyfin.Api/Controllers/SyncPlayController.cs

@@ -125,7 +125,7 @@ public class SyncPlayController : BaseJellyfinApiController
     {
         var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
         var group = _syncPlayManager.GetGroup(currentSession, id);
-        return group == null ? NotFound() : Ok(group);
+        return group is null ? NotFound() : Ok(group);
     }
 
     /// <summary>

+ 1 - 4
Jellyfin.Server.Implementations/StorageHelpers/StorageHelper.cs

@@ -72,10 +72,7 @@ public static class StorageHelper
     private static void TestDataDirectorySize(string path, ILogger logger, long threshold = -1)
     {
         logger.LogDebug("Check path {TestPath} for storage capacity", path);
-        if (!Directory.Exists(path))
-        {
-            Directory.CreateDirectory(path);
-        }
+        Directory.CreateDirectory(path);
 
         var drive = new DriveInfo(path);
         if (threshold != -1 && drive.AvailableFreeSpace < threshold)

+ 24 - 24
Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs

@@ -97,28 +97,28 @@ public class TrickplayManager : ITrickplayManager
             var existingResolution = resolution.Key;
             var tileWidth = resolution.Value.TileWidth;
             var tileHeight = resolution.Value.TileHeight;
-            var shouldBeSavedWithMedia = libraryOptions is null ? false : libraryOptions.SaveTrickplayWithMedia;
-            var localOutputDir = GetTrickplayDirectory(video, tileWidth, tileHeight, existingResolution, false);
-            var mediaOutputDir = GetTrickplayDirectory(video, tileWidth, tileHeight, existingResolution, true);
-            if (shouldBeSavedWithMedia && Directory.Exists(localOutputDir))
+            var shouldBeSavedWithMedia = libraryOptions is not null && libraryOptions.SaveTrickplayWithMedia;
+            var localOutputDir = new DirectoryInfo(GetTrickplayDirectory(video, tileWidth, tileHeight, existingResolution, false));
+            var mediaOutputDir = new DirectoryInfo(GetTrickplayDirectory(video, tileWidth, tileHeight, existingResolution, true));
+            if (shouldBeSavedWithMedia && localOutputDir.Exists)
             {
-                var localDirFiles = Directory.GetFiles(localOutputDir);
-                var mediaDirExists = Directory.Exists(mediaOutputDir);
-                if (localDirFiles.Length > 0 && ((mediaDirExists && Directory.GetFiles(mediaOutputDir).Length == 0) || !mediaDirExists))
+                var localDirFiles = localOutputDir.EnumerateFiles();
+                var mediaDirExists = mediaOutputDir.Exists;
+                if (localDirFiles.Any() && ((mediaDirExists && mediaOutputDir.EnumerateFiles().Any()) || !mediaDirExists))
                 {
                     // Move images from local dir to media dir
-                    MoveContent(localOutputDir, mediaOutputDir);
+                    MoveContent(localOutputDir.FullName, mediaOutputDir.FullName);
                     _logger.LogInformation("Moved trickplay images for {ItemName} to {Location}", video.Name, mediaOutputDir);
                 }
             }
-            else if (!shouldBeSavedWithMedia && Directory.Exists(mediaOutputDir))
+            else if (!shouldBeSavedWithMedia && mediaOutputDir.Exists)
             {
-                var mediaDirFiles = Directory.GetFiles(mediaOutputDir);
-                var localDirExists = Directory.Exists(localOutputDir);
-                if (mediaDirFiles.Length > 0 && ((localDirExists && Directory.GetFiles(localOutputDir).Length == 0) || !localDirExists))
+                var mediaDirFiles = mediaOutputDir.EnumerateFiles();
+                var localDirExists = localOutputDir.Exists;
+                if (mediaDirFiles.Any() && ((localDirExists && localOutputDir.EnumerateFiles().Any()) || !localDirExists))
                 {
                     // Move images from media dir to local dir
-                    MoveContent(mediaOutputDir, localOutputDir);
+                    MoveContent(mediaOutputDir.FullName, localOutputDir.FullName);
                     _logger.LogInformation("Moved trickplay images for {ItemName} to {Location}", video.Name, localOutputDir);
                 }
             }
@@ -131,10 +131,10 @@ public class TrickplayManager : ITrickplayManager
         var parent = Directory.GetParent(sourceFolder);
         if (parent is not null)
         {
-            var parentContent = Directory.GetDirectories(parent.FullName);
-            if (parentContent.Length == 0)
+            var parentContent = parent.EnumerateDirectories();
+            if (!parentContent.Any())
             {
-                Directory.Delete(parent.FullName);
+                parent.Delete();
             }
         }
     }
@@ -220,13 +220,13 @@ public class TrickplayManager : ITrickplayManager
 
                 var tileWidth = options.TileWidth;
                 var tileHeight = options.TileHeight;
-                var saveWithMedia = libraryOptions is null ? false : libraryOptions.SaveTrickplayWithMedia;
-                var outputDir = GetTrickplayDirectory(video, tileWidth, tileHeight, actualWidth, saveWithMedia);
+                var saveWithMedia = libraryOptions is not null && libraryOptions.SaveTrickplayWithMedia;
+                var outputDir = new DirectoryInfo(GetTrickplayDirectory(video, tileWidth, tileHeight, actualWidth, saveWithMedia));
 
                 // Import existing trickplay tiles
-                if (!replace && Directory.Exists(outputDir))
+                if (!replace && outputDir.Exists)
                 {
-                    var existingFiles = Directory.GetFiles(outputDir);
+                    var existingFiles = outputDir.GetFiles();
                     if (existingFiles.Length > 0)
                     {
                         var hasTrickplayResolution = await HasTrickplayResolutionAsync(video.Id, actualWidth).ConfigureAwait(false);
@@ -251,9 +251,9 @@ public class TrickplayManager : ITrickplayManager
 
                         foreach (var tile in existingFiles)
                         {
-                            var image = _imageEncoder.GetImageSize(tile);
+                            var image = _imageEncoder.GetImageSize(tile.FullName);
                             localTrickplayInfo.Height = Math.Max(localTrickplayInfo.Height, (int)Math.Ceiling((double)image.Height / localTrickplayInfo.TileHeight));
-                            var bitrate = (int)Math.Ceiling((decimal)new FileInfo(tile).Length * 8 / localTrickplayInfo.TileWidth / localTrickplayInfo.TileHeight / (localTrickplayInfo.Interval / 1000));
+                            var bitrate = (int)Math.Ceiling((decimal)tile.Length * 8 / localTrickplayInfo.TileWidth / localTrickplayInfo.TileHeight / (localTrickplayInfo.Interval / 1000));
                             localTrickplayInfo.Bandwidth = Math.Max(localTrickplayInfo.Bandwidth, bitrate);
                         }
 
@@ -296,7 +296,7 @@ public class TrickplayManager : ITrickplayManager
                     .ToList();
 
                 // Create tiles
-                var trickplayInfo = CreateTiles(images, actualWidth, options, outputDir);
+                var trickplayInfo = CreateTiles(images, actualWidth, options, outputDir.FullName);
 
                 // Save tiles info
                 try
@@ -319,7 +319,7 @@ public class TrickplayManager : ITrickplayManager
 
                     // Make sure no files stay in metadata folders on failure
                     // if tiles info wasn't saved.
-                    Directory.Delete(outputDir, true);
+                    outputDir.Delete(true);
                 }
             }
             catch (Exception ex)

+ 1 - 1
Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs

@@ -215,7 +215,7 @@ namespace Jellyfin.Server.Extensions
                 });
 
                 // Add all xml doc files to swagger generator.
-                var xmlFiles = Directory.GetFiles(
+                var xmlFiles = Directory.EnumerateFiles(
                     AppContext.BaseDirectory,
                     "*.xml",
                     SearchOption.TopDirectoryOnly);

+ 3 - 3
MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs

@@ -133,9 +133,9 @@ namespace MediaBrowser.MediaEncoding.Attachments
             var outputFolder = _pathManager.GetAttachmentFolderPath(mediaSource.Id);
             using (await _semaphoreLocks.LockAsync(outputFolder, cancellationToken).ConfigureAwait(false))
             {
-                Directory.CreateDirectory(outputFolder);
-                var fileNames = Directory.GetFiles(outputFolder, "*", SearchOption.TopDirectoryOnly).Select(f => Path.GetFileName(f));
-                var missingFiles = mediaSource.MediaAttachments.Where(a => !fileNames.Contains(a.FileName) && !string.Equals(a.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase));
+                var directory = Directory.CreateDirectory(outputFolder);
+                var fileNames = directory.GetFiles("*", SearchOption.TopDirectoryOnly).Select(f => f.Name).ToHashSet();
+                var missingFiles = mediaSource.MediaAttachments.Where(a => a.FileName is not null && !fileNames.Contains(a.FileName) && !string.Equals(a.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase));
                 if (!missingFiles.Any())
                 {
                     // Skip extraction if all files already exist

+ 8 - 0
MediaBrowser.Model/IO/AsyncFile.cs

@@ -26,6 +26,14 @@ namespace MediaBrowser.Model.IO
             Options = FileOptions.Asynchronous
         };
 
+        /// <summary>
+        /// Creates, or truncates and overwrites, a file in the specified path.
+        /// </summary>
+        /// <param name="path">The path and name of the file to create.</param>
+        /// <returns>A <see cref="FileStream" /> that provides read/write access to the file specified in path.</returns>
+        public static FileStream Create(string path)
+            => new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
+
         /// <summary>
         /// Opens an existing file for reading.
         /// </summary>

+ 20 - 0
src/Jellyfin.Extensions/FileHelper.cs

@@ -0,0 +1,20 @@
+using System.IO;
+
+namespace Jellyfin.Extensions;
+
+/// <summary>
+/// Provides helper functions for <see cref="File" />.
+/// </summary>
+public static class FileHelper
+{
+    /// <summary>
+    /// Creates, or truncates a file in the specified path.
+    /// </summary>
+    /// <param name="path">The path and name of the file to create.</param>
+    public static void CreateEmpty(string path)
+    {
+        using (File.OpenHandle(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
+        {
+        }
+    }
+}

+ 2 - 2
src/Jellyfin.LiveTv/Channels/ChannelManager.cs

@@ -363,7 +363,7 @@ namespace Jellyfin.LiveTv.Channels
 
             Directory.CreateDirectory(Path.GetDirectoryName(path));
 
-            FileStream createStream = File.Create(path);
+            FileStream createStream = AsyncFile.Create(path);
             await using (createStream.ConfigureAwait(false))
             {
                 await JsonSerializer.SerializeAsync(createStream, mediaSources, _jsonOptions).ConfigureAwait(false);
@@ -866,7 +866,7 @@ namespace Jellyfin.LiveTv.Channels
             {
                 Directory.CreateDirectory(Path.GetDirectoryName(path));
 
-                var createStream = File.Create(path);
+                var createStream = AsyncFile.Create(path);
                 await using (createStream.ConfigureAwait(false))
                 {
                     await JsonSerializer.SerializeAsync(createStream, result, _jsonOptions).ConfigureAwait(false);

+ 23 - 0
tests/Jellyfin.Extensions.Tests/FileHelperTests.cs

@@ -0,0 +1,23 @@
+using System.IO;
+using Xunit;
+
+namespace Jellyfin.Extensions.Tests;
+
+public static class FileHelperTests
+{
+    [Fact]
+    public static void CreateEmpty_Valid_Correct()
+    {
+        var path = Path.Join(Path.GetTempPath(), Path.GetRandomFileName());
+        var fileInfo = new FileInfo(path);
+
+        Assert.False(fileInfo.Exists);
+
+        FileHelper.CreateEmpty(path);
+
+        fileInfo.Refresh();
+        Assert.True(fileInfo.Exists);
+
+        File.Delete(path);
+    }
+}

+ 3 - 2
tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs

@@ -6,6 +6,7 @@ using System.Threading.Tasks;
 using AutoFixture;
 using Emby.Server.Implementations.Library;
 using Emby.Server.Implementations.Plugins;
+using Jellyfin.Extensions;
 using Jellyfin.Extensions.Json;
 using Jellyfin.Extensions.Json.Converters;
 using MediaBrowser.Common.Plugins;
@@ -85,7 +86,7 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins
             var dllPath = Path.GetDirectoryName(Path.Combine(_pluginPath, dllFile))!;
 
             Directory.CreateDirectory(dllPath);
-            File.Create(Path.Combine(dllPath, filename));
+            FileHelper.CreateEmpty(Path.Combine(dllPath, filename));
             var metafilePath = Path.Combine(_pluginPath, "meta.json");
 
             File.WriteAllText(metafilePath, JsonSerializer.Serialize(manifest, _options));
@@ -141,7 +142,7 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins
 
             foreach (var file in files)
             {
-                File.Create(Path.Combine(_pluginPath, file));
+                FileHelper.CreateEmpty(Path.Combine(_pluginPath, file));
             }
 
             var metafilePath = Path.Combine(_pluginPath, "meta.json");

+ 2 - 1
tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs

@@ -1,6 +1,7 @@
 using System.IO;
 using System.Reflection;
 using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
 using Xunit;
 using Xunit.Abstractions;
 
@@ -33,7 +34,7 @@ namespace Jellyfin.Server.Integration.Tests
             // Write out for publishing
             string outputPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "openapi.json"));
             _outputHelper.WriteLine("Writing OpenAPI Spec JSON to '{0}'.", outputPath);
-            await using var fs = File.Create(outputPath);
+            await using var fs = AsyncFile.Create(outputPath);
             await response.Content.CopyToAsync(fs);
         }
     }