diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index d1376f18ad..18ebd628d1 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/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
///
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);
}
}
}
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index ac5933a694..077eb79458 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/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);
}
}
diff --git a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
index 2c186c9173..b0ed1de8de 100644
--- a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
+++ b/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;
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 64a96c4e5a..51f3307465 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/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);
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index c6cfd5391a..ab30971e27 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/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);
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 17db7ad4c4..242f2af565 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/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;
diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs
index fbab2a7845..3d6874079d 100644
--- a/Jellyfin.Api/Controllers/SyncPlayController.cs
+++ b/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);
}
///
diff --git a/Jellyfin.Server.Implementations/StorageHelpers/StorageHelper.cs b/Jellyfin.Server.Implementations/StorageHelpers/StorageHelper.cs
index e351160c16..b2f54be7e2 100644
--- a/Jellyfin.Server.Implementations/StorageHelpers/StorageHelper.cs
+++ b/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)
diff --git a/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs b/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs
index bf39f13a77..f7dd92e011 100644
--- a/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs
+++ b/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)
diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index b04e55baa6..09a4e2ed31 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/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);
diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
index 1f2bc24037..48a0654bb1 100644
--- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
+++ b/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
diff --git a/MediaBrowser.Model/IO/AsyncFile.cs b/MediaBrowser.Model/IO/AsyncFile.cs
index 3c8007d1c4..a9db6b81cd 100644
--- a/MediaBrowser.Model/IO/AsyncFile.cs
+++ b/MediaBrowser.Model/IO/AsyncFile.cs
@@ -26,6 +26,14 @@ namespace MediaBrowser.Model.IO
Options = FileOptions.Asynchronous
};
+ ///
+ /// Creates, or truncates and overwrites, a file in the specified path.
+ ///
+ /// The path and name of the file to create.
+ /// A that provides read/write access to the file specified in path.
+ public static FileStream Create(string path)
+ => new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
+
///
/// Opens an existing file for reading.
///
diff --git a/src/Jellyfin.Extensions/FileHelper.cs b/src/Jellyfin.Extensions/FileHelper.cs
new file mode 100644
index 0000000000..b1ccf8d472
--- /dev/null
+++ b/src/Jellyfin.Extensions/FileHelper.cs
@@ -0,0 +1,20 @@
+using System.IO;
+
+namespace Jellyfin.Extensions;
+
+///
+/// Provides helper functions for .
+///
+public static class FileHelper
+{
+ ///
+ /// Creates, or truncates a file in the specified path.
+ ///
+ /// The path and name of the file to create.
+ public static void CreateEmpty(string path)
+ {
+ using (File.OpenHandle(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
+ {
+ }
+ }
+}
diff --git a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs
index 0ca294a289..5addcd26e0 100644
--- a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs
+++ b/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);
diff --git a/tests/Jellyfin.Extensions.Tests/FileHelperTests.cs b/tests/Jellyfin.Extensions.Tests/FileHelperTests.cs
new file mode 100644
index 0000000000..fb6a5dd0ac
--- /dev/null
+++ b/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);
+ }
+}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs
index 934024826b..b289c763b5 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs
+++ b/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");
diff --git a/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs b/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs
index 98195a2943..62cdd25aec 100644
--- a/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs
+++ b/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);
}
}