aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Directory.Packages.props2
-rw-r--r--Emby.Server.Implementations/Localization/Core/ab.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-HK.json2
-rw-r--r--Jellyfin.Server.Implementations/StorageHelpers/StorageHelper.cs57
-rw-r--r--MediaBrowser.Model/System/FolderStorageInfo.cs11
5 files changed, 63 insertions, 13 deletions
diff --git a/Directory.Packages.props b/Directory.Packages.props
index f15f7c7a75..bf79ff0e0e 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -47,7 +47,7 @@
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.5" />
- <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
+ <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
<PackageVersion Include="MimeTypes" Version="2.5.2" />
<PackageVersion Include="Morestachio" Version="5.0.1.631" />
<PackageVersion Include="Moq" Version="4.18.4" />
diff --git a/Emby.Server.Implementations/Localization/Core/ab.json b/Emby.Server.Implementations/Localization/Core/ab.json
index bc6062f429..d6d257c5ba 100644
--- a/Emby.Server.Implementations/Localization/Core/ab.json
+++ b/Emby.Server.Implementations/Localization/Core/ab.json
@@ -1,3 +1,5 @@
{
- "Albums": "аальбомқәа"
+ "Albums": "аальбомқәа",
+ "AppDeviceValues": "Апп: {0}, Априбор: {1}",
+ "Application": "Апрограмма"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index 68773599ce..8ae899a73c 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -42,7 +42,7 @@
"MusicVideos": "MV",
"NameInstallFailed": "{0} 安裝失敗",
"NameSeasonNumber": "第 {0} 季",
- "NameSeasonUnknown": "未知的季度",
+ "NameSeasonUnknown": "未知嘅季度",
"NewVersionIsAvailable": "有新版本嘅 Jellyfin 可以下載。",
"NotificationOptionApplicationUpdateAvailable": "有得更新應用程式",
"NotificationOptionApplicationUpdateInstalled": "應用程式更新好咗",
diff --git a/Jellyfin.Server.Implementations/StorageHelpers/StorageHelper.cs b/Jellyfin.Server.Implementations/StorageHelpers/StorageHelper.cs
index ce628a04d0..13c7895f83 100644
--- a/Jellyfin.Server.Implementations/StorageHelpers/StorageHelper.cs
+++ b/Jellyfin.Server.Implementations/StorageHelpers/StorageHelper.cs
@@ -28,22 +28,44 @@ public static class StorageHelper
}
/// <summary>
- /// Gets the free space of a specific directory.
+ /// Gets the free space of the parent filesystem of a specific directory.
/// </summary>
/// <param name="path">Path to a folder.</param>
- /// <returns>The number of bytes available space.</returns>
+ /// <returns>Various details about the parent filesystem containing the directory.</returns>
public static FolderStorageInfo GetFreeSpaceOf(string path)
{
try
{
- var driveInfo = new DriveInfo(path);
+ // Fully resolve the given path to an actual filesystem target, in case it's a symlink or similar.
+ var resolvedPath = ResolvePath(path);
+ // We iterate all filesystems reported by GetDrives() here, and attempt to find the best
+ // match that contains, as deep as possible, the given path.
+ // This is required because simply calling `DriveInfo` on a path returns that path as
+ // the Name and RootDevice, which is not at all how this should work.
+ var allDrives = DriveInfo.GetDrives();
+ DriveInfo? bestMatch = null;
+ foreach (DriveInfo d in allDrives)
+ {
+ if (resolvedPath.StartsWith(d.RootDirectory.FullName, StringComparison.InvariantCultureIgnoreCase) &&
+ (bestMatch is null || d.RootDirectory.FullName.Length > bestMatch.RootDirectory.FullName.Length))
+ {
+ bestMatch = d;
+ }
+ }
+
+ if (bestMatch is null)
+ {
+ throw new InvalidOperationException($"The path `{path}` has no matching parent device. Space check invalid.");
+ }
+
return new FolderStorageInfo()
{
Path = path,
- FreeSpace = driveInfo.AvailableFreeSpace,
- UsedSpace = driveInfo.TotalSize - driveInfo.AvailableFreeSpace,
- StorageType = driveInfo.DriveType.ToString(),
- DeviceId = driveInfo.Name,
+ ResolvedPath = resolvedPath,
+ FreeSpace = bestMatch.AvailableFreeSpace,
+ UsedSpace = bestMatch.TotalSize - bestMatch.AvailableFreeSpace,
+ StorageType = bestMatch.DriveType.ToString(),
+ DeviceId = bestMatch.Name,
};
}
catch
@@ -51,6 +73,7 @@ public static class StorageHelper
return new FolderStorageInfo()
{
Path = path,
+ ResolvedPath = path,
FreeSpace = -1,
UsedSpace = -1,
StorageType = null,
@@ -60,6 +83,26 @@ public static class StorageHelper
}
/// <summary>
+ /// Walk a path and fully resolve any symlinks within it.
+ /// </summary>
+ private static string ResolvePath(string path)
+ {
+ var parts = path.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
+ var current = Path.DirectorySeparatorChar.ToString();
+ foreach (var part in parts)
+ {
+ current = Path.Combine(current, part);
+ var resolved = new DirectoryInfo(current).ResolveLinkTarget(returnFinalTarget: true);
+ if (resolved is not null)
+ {
+ current = resolved.FullName;
+ }
+ }
+
+ return current;
+ }
+
+ /// <summary>
/// Gets the underlying drive data from a given path and checks if the available storage capacity matches the threshold.
/// </summary>
/// <param name="path">The path to a folder to evaluate.</param>
diff --git a/MediaBrowser.Model/System/FolderStorageInfo.cs b/MediaBrowser.Model/System/FolderStorageInfo.cs
index 7b10e4ea58..ebca39228b 100644
--- a/MediaBrowser.Model/System/FolderStorageInfo.cs
+++ b/MediaBrowser.Model/System/FolderStorageInfo.cs
@@ -11,17 +11,22 @@ public record FolderStorageInfo
public required string Path { get; init; }
/// <summary>
- /// Gets the free space of the underlying storage device of the <see cref="Path"/>.
+ /// Gets the fully resolved path of the folder in question (interpolating any symlinks if present).
+ /// </summary>
+ public required string ResolvedPath { get; init; }
+
+ /// <summary>
+ /// Gets the free space of the underlying storage device of the <see cref="ResolvedPath"/>.
/// </summary>
public long FreeSpace { get; init; }
/// <summary>
- /// Gets the used space of the underlying storage device of the <see cref="Path"/>.
+ /// Gets the used space of the underlying storage device of the <see cref="ResolvedPath"/>.
/// </summary>
public long UsedSpace { get; init; }
/// <summary>
- /// Gets the kind of storage device of the <see cref="Path"/>.
+ /// Gets the kind of storage device of the <see cref="ResolvedPath"/>.
/// </summary>
public string? StorageType { get; init; }