Browse Source

Add Api and startup check for sufficient storage capacity (#13888)

JPVenson 1 tháng trước cách đây
mục cha
commit
a0931baa8e

+ 33 - 1
Emby.Server.Implementations/SystemManager.cs

@@ -1,9 +1,12 @@
+using System;
 using System.Linq;
 using System.Threading.Tasks;
+using Jellyfin.Server.Implementations.StorageHelpers;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Updates;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.System;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Hosting;
@@ -19,6 +22,7 @@ public class SystemManager : ISystemManager
     private readonly IServerConfigurationManager _configurationManager;
     private readonly IStartupOptions _startupOptions;
     private readonly IInstallationManager _installationManager;
+    private readonly ILibraryManager _libraryManager;
 
     /// <summary>
     /// Initializes a new instance of the <see cref="SystemManager"/> class.
@@ -29,13 +33,15 @@ public class SystemManager : ISystemManager
     /// <param name="configurationManager">Instance of <see cref="IServerConfigurationManager"/>.</param>
     /// <param name="startupOptions">Instance of <see cref="IStartupOptions"/>.</param>
     /// <param name="installationManager">Instance of <see cref="IInstallationManager"/>.</param>
+    /// <param name="libraryManager">Instance of <see cref="ILibraryManager"/>.</param>
     public SystemManager(
         IHostApplicationLifetime applicationLifetime,
         IServerApplicationHost applicationHost,
         IServerApplicationPaths applicationPaths,
         IServerConfigurationManager configurationManager,
         IStartupOptions startupOptions,
-        IInstallationManager installationManager)
+        IInstallationManager installationManager,
+        ILibraryManager libraryManager)
     {
         _applicationLifetime = applicationLifetime;
         _applicationHost = applicationHost;
@@ -43,6 +49,7 @@ public class SystemManager : ISystemManager
         _configurationManager = configurationManager;
         _startupOptions = startupOptions;
         _installationManager = installationManager;
+        _libraryManager = libraryManager;
     }
 
     /// <inheritdoc />
@@ -57,6 +64,7 @@ public class SystemManager : ISystemManager
             WebSocketPortNumber = _applicationHost.HttpPort,
             CompletedInstallations = _installationManager.CompletedInstallations.ToArray(),
             Id = _applicationHost.SystemId,
+#pragma warning disable CS0618 // Type or member is obsolete
             ProgramDataPath = _applicationPaths.ProgramDataPath,
             WebPath = _applicationPaths.WebPath,
             LogPath = _applicationPaths.LogDirectoryPath,
@@ -64,6 +72,7 @@ public class SystemManager : ISystemManager
             InternalMetadataPath = _applicationPaths.InternalMetadataPath,
             CachePath = _applicationPaths.CachePath,
             TranscodingTempPath = _configurationManager.GetTranscodePath(),
+#pragma warning restore CS0618 // Type or member is obsolete
             ServerName = _applicationHost.FriendlyName,
             LocalAddress = _applicationHost.GetSmartApiUrl(request),
             StartupWizardCompleted = _configurationManager.CommonConfiguration.IsStartupWizardCompleted,
@@ -73,6 +82,29 @@ public class SystemManager : ISystemManager
         };
     }
 
+    /// <inheritdoc/>
+    public SystemStorageInfo GetSystemStorageInfo()
+    {
+        var virtualFolderInfos = _libraryManager.GetVirtualFolders().Select(e => new LibraryStorageInfo()
+        {
+            Id = Guid.Parse(e.ItemId),
+            Name = e.Name,
+            Folders = e.Locations.Select(f => StorageHelper.GetFreeSpaceOf(f)).ToArray()
+        });
+
+        return new SystemStorageInfo()
+        {
+            ProgramDataFolder = StorageHelper.GetFreeSpaceOf(_applicationPaths.ProgramDataPath),
+            WebFolder = StorageHelper.GetFreeSpaceOf(_applicationPaths.WebPath),
+            LogFolder = StorageHelper.GetFreeSpaceOf(_applicationPaths.LogDirectoryPath),
+            ImageCacheFolder = StorageHelper.GetFreeSpaceOf(_applicationPaths.ImageCachePath),
+            InternalMetadataFolder = StorageHelper.GetFreeSpaceOf(_applicationPaths.InternalMetadataPath),
+            CacheFolder = StorageHelper.GetFreeSpaceOf(_applicationPaths.CachePath),
+            TranscodingTempFolder = StorageHelper.GetFreeSpaceOf(_configurationManager.GetTranscodePath()),
+            Libraries = virtualFolderInfos.ToArray()
+        };
+    }
+
     /// <inheritdoc />
     public PublicSystemInfo GetPublicSystemInfo(HttpRequest request)
     {

+ 14 - 0
Jellyfin.Api/Controllers/SystemController.cs

@@ -6,6 +6,7 @@ using System.Linq;
 using System.Net.Mime;
 using Jellyfin.Api.Attributes;
 using Jellyfin.Api.Constants;
+using Jellyfin.Api.Models.SystemInfoDtos;
 using MediaBrowser.Common.Api;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
@@ -71,6 +72,19 @@ public class SystemController : BaseJellyfinApiController
     public ActionResult<SystemInfo> GetSystemInfo()
         => _systemManager.GetSystemInfo(Request);
 
+    /// <summary>
+    /// Gets information about the server.
+    /// </summary>
+    /// <response code="200">Information retrieved.</response>
+    /// <response code="403">User does not have permission to retrieve information.</response>
+    /// <returns>A <see cref="SystemInfo"/> with info about the system.</returns>
+    [HttpGet("Info/Storage")]
+    [Authorize(Policy = Policies.RequiresElevation)]
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status403Forbidden)]
+    public ActionResult<SystemStorageDto> GetSystemStorage()
+        => Ok(SystemStorageDto.FromSystemStorageInfo(_systemManager.GetSystemStorageInfo()));
+
     /// <summary>
     /// Gets public information about the server.
     /// </summary>

+ 46 - 0
Jellyfin.Api/Models/SystemInfoDtos/FolderStorageDto.cs

@@ -0,0 +1,46 @@
+using MediaBrowser.Model.System;
+
+namespace Jellyfin.Api.Models.SystemInfoDtos;
+
+/// <summary>
+/// Contains information about a specific folder.
+/// </summary>
+public record FolderStorageDto
+{
+    /// <summary>
+    /// Gets the path of the folder in question.
+    /// </summary>
+    public required string Path { get; init; }
+
+    /// <summary>
+    /// Gets the free space of the underlying storage device of the <see cref="Path"/>.
+    /// </summary>
+    public long FreeSpace { get; init; }
+
+    /// <summary>
+    /// Gets the used space of the underlying storage device of the <see cref="Path"/>.
+    /// </summary>
+    public long UsedSpace { get; init; }
+
+    /// <summary>
+    /// Gets the kind of storage device of the <see cref="Path"/>.
+    /// </summary>
+    public string? StorageType { get; init; }
+
+    /// <summary>
+    /// Gets the Device Identifier.
+    /// </summary>
+    public string? DeviceId { get; init; }
+
+    internal static FolderStorageDto FromFolderStorageInfo(FolderStorageInfo model)
+    {
+        return new()
+        {
+            Path = model.Path,
+            FreeSpace = model.FreeSpace,
+            UsedSpace = model.UsedSpace,
+            StorageType = model.StorageType,
+            DeviceId = model.DeviceId
+        };
+    }
+}

+ 37 - 0
Jellyfin.Api/Models/SystemInfoDtos/LibraryStorageDto.cs

@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using MediaBrowser.Model.System;
+
+namespace Jellyfin.Api.Models.SystemInfoDtos;
+
+/// <summary>
+/// Contains informations about a libraries storage informations.
+/// </summary>
+public record LibraryStorageDto
+{
+      /// <summary>
+    /// Gets or sets the Library Id.
+    /// </summary>
+    public required Guid Id { get; set; }
+
+    /// <summary>
+    /// Gets or sets the name of the library.
+    /// </summary>
+    public required string Name { get; set; }
+
+    /// <summary>
+    /// Gets or sets the storage informations about the folders used in a library.
+    /// </summary>
+    public required IReadOnlyCollection<FolderStorageDto> Folders { get; set; }
+
+    internal static LibraryStorageDto FromLibraryStorageModel(LibraryStorageInfo model)
+    {
+        return new()
+        {
+            Id = model.Id,
+            Name = model.Name,
+            Folders = model.Folders.Select(FolderStorageDto.FromFolderStorageInfo).ToArray()
+        };
+    }
+}

+ 67 - 0
Jellyfin.Api/Models/SystemInfoDtos/SystemStorageDto.cs

@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using MediaBrowser.Model.System;
+
+namespace Jellyfin.Api.Models.SystemInfoDtos;
+
+/// <summary>
+/// Contains informations about the systems storage.
+/// </summary>
+public record SystemStorageDto
+{
+    /// <summary>
+    /// Gets or sets the Storage information of the program data folder.
+    /// </summary>
+    public required FolderStorageDto ProgramDataFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the Storage information of the web UI resources folder.
+    /// </summary>
+    public required FolderStorageDto WebFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the Storage information of the folder where images are cached.
+    /// </summary>
+    public required FolderStorageDto ImageCacheFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the Storage information of the cache folder.
+    /// </summary>
+    public required FolderStorageDto CacheFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the Storage information of the folder where logfiles are saved to.
+    /// </summary>
+    public required FolderStorageDto LogFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the Storage information of the folder where metadata is stored.
+    /// </summary>
+    public required FolderStorageDto InternalMetadataFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the Storage information of the transcoding cache.
+    /// </summary>
+    public required FolderStorageDto TranscodingTempFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the storage informations of all libraries.
+    /// </summary>
+    public required IReadOnlyCollection<LibraryStorageDto> Libraries { get; set; }
+
+    internal static SystemStorageDto FromSystemStorageInfo(SystemStorageInfo model)
+    {
+        return new SystemStorageDto()
+        {
+            ProgramDataFolder = FolderStorageDto.FromFolderStorageInfo(model.ProgramDataFolder),
+            WebFolder = FolderStorageDto.FromFolderStorageInfo(model.WebFolder),
+            ImageCacheFolder = FolderStorageDto.FromFolderStorageInfo(model.ImageCacheFolder),
+            CacheFolder = FolderStorageDto.FromFolderStorageInfo(model.CacheFolder),
+            LogFolder = FolderStorageDto.FromFolderStorageInfo(model.LogFolder),
+            InternalMetadataFolder = FolderStorageDto.FromFolderStorageInfo(model.InternalMetadataFolder),
+            TranscodingTempFolder = FolderStorageDto.FromFolderStorageInfo(model.TranscodingTempFolder),
+            Libraries = model.Libraries.Select(LibraryStorageDto.FromLibraryStorageModel).ToArray()
+        };
+    }
+}

+ 109 - 0
Jellyfin.Server.Implementations/StorageHelpers/StorageHelper.cs

@@ -0,0 +1,109 @@
+using System;
+using System.Globalization;
+using System.IO;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.System;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.StorageHelpers;
+
+/// <summary>
+/// Contains methods to help with checking for storage and returning storage data for jellyfin folders.
+/// </summary>
+public static class StorageHelper
+{
+    private const long TwoGigabyte = 2_147_483_647L;
+    private const long FiveHundredAndTwelveMegaByte = 536_870_911L;
+    private static readonly string[] _byteHumanizedSuffixes = ["B", "KB", "MB", "GB", "TB", "PB", "EB"];
+
+    /// <summary>
+    /// Tests the available storage capacity on the jellyfin paths with estimated minimum values.
+    /// </summary>
+    /// <param name="applicationPaths">The application paths.</param>
+    /// <param name="logger">Logger.</param>
+    public static void TestCommonPathsForStorageCapacity(IApplicationPaths applicationPaths, ILogger logger)
+    {
+        TestDataDirectorySize(applicationPaths.DataPath, logger, TwoGigabyte);
+        TestDataDirectorySize(applicationPaths.LogDirectoryPath, logger, FiveHundredAndTwelveMegaByte);
+        TestDataDirectorySize(applicationPaths.CachePath, logger, TwoGigabyte);
+        TestDataDirectorySize(applicationPaths.ProgramDataPath, logger, TwoGigabyte);
+        TestDataDirectorySize(applicationPaths.TempDirectory, logger, TwoGigabyte);
+    }
+
+    /// <summary>
+    /// Gets the free space of a specific directory.
+    /// </summary>
+    /// <param name="path">Path to a folder.</param>
+    /// <returns>The number of bytes available space.</returns>
+    public static FolderStorageInfo GetFreeSpaceOf(string path)
+    {
+        try
+        {
+            var driveInfo = new DriveInfo(path);
+            return new FolderStorageInfo()
+            {
+                Path = path,
+                FreeSpace = driveInfo.AvailableFreeSpace,
+                UsedSpace = driveInfo.TotalSize - driveInfo.AvailableFreeSpace,
+                StorageType = driveInfo.DriveType.ToString(),
+                DeviceId = driveInfo.Name,
+            };
+        }
+        catch
+        {
+            return new FolderStorageInfo()
+            {
+                Path = path,
+                FreeSpace = -1,
+                UsedSpace = -1,
+                StorageType = null,
+                DeviceId = null
+            };
+        }
+    }
+
+    /// <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>
+    /// <param name="logger">The logger.</param>
+    /// <param name="threshold">The threshold to check for or -1 to just log the data.</param>
+    /// <exception cref="InvalidOperationException">Thrown when the threshold is not available on the underlying storage.</exception>
+    private static void TestDataDirectorySize(string path, ILogger logger, long threshold = -1)
+    {
+        logger.LogDebug("Check path {TestPath} for storage capacity", path);
+        var drive = new DriveInfo(path);
+        if (threshold != -1 && drive.AvailableFreeSpace < threshold)
+        {
+            throw new InvalidOperationException($"The path `{path}` has insufficient free space. Required: at least {HumanizeStorageSize(threshold)}.");
+        }
+
+        logger.LogInformation(
+            "Storage path `{TestPath}` ({StorageType}) successfully checked with {FreeSpace} free which is over the minimum of {MinFree}.",
+            path,
+            drive.DriveType,
+            HumanizeStorageSize(drive.AvailableFreeSpace),
+            HumanizeStorageSize(threshold));
+    }
+
+    /// <summary>
+    /// Formats a size in bytes into a common human readable form.
+    /// </summary>
+    /// <remarks>
+    /// Taken and slightly modified from https://stackoverflow.com/a/4975942/1786007 .
+    /// </remarks>
+    /// <param name="byteCount">The size in bytes.</param>
+    /// <returns>A human readable approximate representation of the argument.</returns>
+    public static string HumanizeStorageSize(long byteCount)
+    {
+        if (byteCount == 0)
+        {
+            return $"0{_byteHumanizedSuffixes[0]}";
+        }
+
+        var bytes = Math.Abs(byteCount);
+        var place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
+        var num = Math.Round(bytes / Math.Pow(1024, place), 1);
+        return (Math.Sign(byteCount) * num).ToString(CultureInfo.InvariantCulture) + _byteHumanizedSuffixes[place];
+    }
+}

+ 4 - 0
Jellyfin.Server/Program.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Reflection;
@@ -11,6 +12,7 @@ using Emby.Server.Implementations;
 using Jellyfin.Database.Implementations;
 using Jellyfin.Server.Extensions;
 using Jellyfin.Server.Helpers;
+using Jellyfin.Server.Implementations.StorageHelpers;
 using Jellyfin.Server.ServerSetupApp;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Net;
@@ -120,6 +122,8 @@ namespace Jellyfin.Server
                 }
             }
 
+            StorageHelper.TestCommonPathsForStorageCapacity(appPaths, _loggerFactory.CreateLogger<Startup>());
+
             StartupHelpers.PerformStaticInitialization();
             await Migrations.MigrationRunner.RunPreStartup(appPaths, _loggerFactory).ConfigureAwait(false);
 

+ 7 - 0
MediaBrowser.Controller/ISystemManager.cs

@@ -1,5 +1,6 @@
 using MediaBrowser.Model.System;
 using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
 
 namespace MediaBrowser.Controller;
 
@@ -31,4 +32,10 @@ public interface ISystemManager
     /// Starts the application shutdown process.
     /// </summary>
     void Shutdown();
+
+    /// <summary>
+    /// Gets the systems storage resources.
+    /// </summary>
+    /// <returns>The <see cref="SystemStorageInfo"/>.</returns>
+    SystemStorageInfo GetSystemStorageInfo();
 }

+ 32 - 0
MediaBrowser.Model/System/FolderStorageInfo.cs

@@ -0,0 +1,32 @@
+namespace MediaBrowser.Model.System;
+
+/// <summary>
+/// Contains information about a specific folder.
+/// </summary>
+public record FolderStorageInfo
+{
+    /// <summary>
+    /// Gets the path of the folder in question.
+    /// </summary>
+    public required string Path { get; init; }
+
+    /// <summary>
+    /// Gets the free space of the underlying storage device of the <see cref="Path"/>.
+    /// </summary>
+    public long FreeSpace { get; init; }
+
+    /// <summary>
+    /// Gets the used space of the underlying storage device of the <see cref="Path"/>.
+    /// </summary>
+    public long UsedSpace { get; init; }
+
+    /// <summary>
+    /// Gets the kind of storage device of the <see cref="Path"/>.
+    /// </summary>
+    public string? StorageType { get; init; }
+
+    /// <summary>
+    /// Gets the Device Identifier.
+    /// </summary>
+    public string? DeviceId { get; init; }
+}

+ 25 - 0
MediaBrowser.Model/System/LibraryStorageInfo.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.System;
+
+/// <summary>
+/// Contains informations about a libraries storage informations.
+/// </summary>
+public class LibraryStorageInfo
+{
+    /// <summary>
+    /// Gets or sets the Library Id.
+    /// </summary>
+    public required Guid Id { get; set; }
+
+    /// <summary>
+    /// Gets or sets the name of the library.
+    /// </summary>
+    public required string Name { get; set; }
+
+    /// <summary>
+    /// Gets or sets the storage informations about the folders used in a library.
+    /// </summary>
+    public required IReadOnlyCollection<FolderStorageInfo> Folders { get; set; }
+}

+ 130 - 124
MediaBrowser.Model/System/SystemInfo.cs

@@ -6,133 +6,139 @@ using System.Collections.Generic;
 using System.ComponentModel;
 using MediaBrowser.Model.Updates;
 
-namespace MediaBrowser.Model.System
+namespace MediaBrowser.Model.System;
+
+/// <summary>
+/// Class SystemInfo.
+/// </summary>
+public class SystemInfo : PublicSystemInfo
 {
     /// <summary>
-    /// Class SystemInfo.
+    /// Initializes a new instance of the <see cref="SystemInfo" /> class.
     /// </summary>
-    public class SystemInfo : PublicSystemInfo
+    public SystemInfo()
     {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="SystemInfo" /> class.
-        /// </summary>
-        public SystemInfo()
-        {
-            CompletedInstallations = Array.Empty<InstallationInfo>();
-        }
-
-        /// <summary>
-        /// Gets or sets the display name of the operating system.
-        /// </summary>
-        /// <value>The display name of the operating system.</value>
-        [Obsolete("This is no longer set")]
-        public string OperatingSystemDisplayName { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Gets or sets the package name.
-        /// </summary>
-        /// <value>The value of the '-package' command line argument.</value>
-        public string PackageName { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance has pending restart.
-        /// </summary>
-        /// <value><c>true</c> if this instance has pending restart; otherwise, <c>false</c>.</value>
-        public bool HasPendingRestart { get; set; }
-
-        public bool IsShuttingDown { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether [supports library monitor].
-        /// </summary>
-        /// <value><c>true</c> if [supports library monitor]; otherwise, <c>false</c>.</value>
-        public bool SupportsLibraryMonitor { get; set; }
-
-        /// <summary>
-        /// Gets or sets the web socket port number.
-        /// </summary>
-        /// <value>The web socket port number.</value>
-        public int WebSocketPortNumber { get; set; }
-
-        /// <summary>
-        /// Gets or sets the completed installations.
-        /// </summary>
-        /// <value>The completed installations.</value>
-        public InstallationInfo[] CompletedInstallations { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance can self restart.
-        /// </summary>
-        /// <value><c>true</c>.</value>
-        [Obsolete("This is always true")]
-        [DefaultValue(true)]
-        public bool CanSelfRestart { get; set; } = true;
-
-        [Obsolete("This is always false")]
-        [DefaultValue(false)]
-        public bool CanLaunchWebBrowser { get; set; } = false;
-
-        /// <summary>
-        /// Gets or sets the program data path.
-        /// </summary>
-        /// <value>The program data path.</value>
-        public string ProgramDataPath { get; set; }
-
-        /// <summary>
-        /// Gets or sets the web UI resources path.
-        /// </summary>
-        /// <value>The web UI resources path.</value>
-        public string WebPath { get; set; }
-
-        /// <summary>
-        /// Gets or sets the items by name path.
-        /// </summary>
-        /// <value>The items by name path.</value>
-        public string ItemsByNamePath { get; set; }
-
-        /// <summary>
-        /// Gets or sets the cache path.
-        /// </summary>
-        /// <value>The cache path.</value>
-        public string CachePath { get; set; }
-
-        /// <summary>
-        /// Gets or sets the log path.
-        /// </summary>
-        /// <value>The log path.</value>
-        public string LogPath { get; set; }
-
-        /// <summary>
-        /// Gets or sets the internal metadata path.
-        /// </summary>
-        /// <value>The internal metadata path.</value>
-        public string InternalMetadataPath { get; set; }
-
-        /// <summary>
-        /// Gets or sets the transcode path.
-        /// </summary>
-        /// <value>The transcode path.</value>
-        public string TranscodingTempPath { get; set; }
-
-        /// <summary>
-        /// Gets or sets the list of cast receiver applications.
-        /// </summary>
-        public IReadOnlyList<CastReceiverApplication> CastReceiverApplications { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance has update available.
-        /// </summary>
-        /// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value>
-        [Obsolete("This should be handled by the package manager")]
-        [DefaultValue(false)]
-        public bool HasUpdateAvailable { get; set; }
-
-        [Obsolete("This isn't set correctly anymore")]
-        [DefaultValue("System")]
-        public string EncoderLocation { get; set; } = "System";
-
-        [Obsolete("This is no longer set")]
-        [DefaultValue("X64")]
-        public string SystemArchitecture { get; set; } = "X64";
+        CompletedInstallations = Array.Empty<InstallationInfo>();
     }
+
+    /// <summary>
+    /// Gets or sets the display name of the operating system.
+    /// </summary>
+    /// <value>The display name of the operating system.</value>
+    [Obsolete("This is no longer set")]
+    public string OperatingSystemDisplayName { get; set; } = string.Empty;
+
+    /// <summary>
+    /// Gets or sets the package name.
+    /// </summary>
+    /// <value>The value of the '-package' command line argument.</value>
+    public string PackageName { get; set; }
+
+    /// <summary>
+    /// Gets or sets a value indicating whether this instance has pending restart.
+    /// </summary>
+    /// <value><c>true</c> if this instance has pending restart; otherwise, <c>false</c>.</value>
+    public bool HasPendingRestart { get; set; }
+
+    public bool IsShuttingDown { get; set; }
+
+    /// <summary>
+    /// Gets or sets a value indicating whether [supports library monitor].
+    /// </summary>
+    /// <value><c>true</c> if [supports library monitor]; otherwise, <c>false</c>.</value>
+    public bool SupportsLibraryMonitor { get; set; }
+
+    /// <summary>
+    /// Gets or sets the web socket port number.
+    /// </summary>
+    /// <value>The web socket port number.</value>
+    public int WebSocketPortNumber { get; set; }
+
+    /// <summary>
+    /// Gets or sets the completed installations.
+    /// </summary>
+    /// <value>The completed installations.</value>
+    public InstallationInfo[] CompletedInstallations { get; set; }
+
+    /// <summary>
+    /// Gets or sets a value indicating whether this instance can self restart.
+    /// </summary>
+    /// <value><c>true</c>.</value>
+    [Obsolete("This is always true")]
+    [DefaultValue(true)]
+    public bool CanSelfRestart { get; set; } = true;
+
+    [Obsolete("This is always false")]
+    [DefaultValue(false)]
+    public bool CanLaunchWebBrowser { get; set; } = false;
+
+    /// <summary>
+    /// Gets or sets the program data path.
+    /// </summary>
+    /// <value>The program data path.</value>
+    [Obsolete("Use the newer SystemStorageDto instead")]
+    public string ProgramDataPath { get; set; }
+
+    /// <summary>
+    /// Gets or sets the web UI resources path.
+    /// </summary>
+    /// <value>The web UI resources path.</value>
+    [Obsolete("Use the newer SystemStorageDto instead")]
+    public string WebPath { get; set; }
+
+    /// <summary>
+    /// Gets or sets the items by name path.
+    /// </summary>
+    /// <value>The items by name path.</value>
+    [Obsolete("Use the newer SystemStorageDto instead")]
+    public string ItemsByNamePath { get; set; }
+
+    /// <summary>
+    /// Gets or sets the cache path.
+    /// </summary>
+    /// <value>The cache path.</value>
+    [Obsolete("Use the newer SystemStorageDto instead")]
+    public string CachePath { get; set; }
+
+    /// <summary>
+    /// Gets or sets the log path.
+    /// </summary>
+    /// <value>The log path.</value>
+    [Obsolete("Use the newer SystemStorageDto instead")]
+    public string LogPath { get; set; }
+
+    /// <summary>
+    /// Gets or sets the internal metadata path.
+    /// </summary>
+    /// <value>The internal metadata path.</value>
+    [Obsolete("Use the newer SystemStorageDto instead")]
+    public string InternalMetadataPath { get; set; }
+
+    /// <summary>
+    /// Gets or sets the transcode path.
+    /// </summary>
+    /// <value>The transcode path.</value>
+    [Obsolete("Use the newer SystemStorageDto instead")]
+    public string TranscodingTempPath { get; set; }
+
+    /// <summary>
+    /// Gets or sets the list of cast receiver applications.
+    /// </summary>
+    public IReadOnlyList<CastReceiverApplication> CastReceiverApplications { get; set; }
+
+    /// <summary>
+    /// Gets or sets a value indicating whether this instance has update available.
+    /// </summary>
+    /// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value>
+    [Obsolete("This should be handled by the package manager")]
+    [DefaultValue(false)]
+    public bool HasUpdateAvailable { get; set; }
+
+    [Obsolete("This isn't set correctly anymore")]
+    [DefaultValue("System")]
+    public string EncoderLocation { get; set; } = "System";
+
+    [Obsolete("This is no longer set")]
+    [DefaultValue("X64")]
+    public string SystemArchitecture { get; set; } = "X64";
 }

+ 56 - 0
MediaBrowser.Model/System/SystemStorageInfo.cs

@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.System;
+
+/// <summary>
+/// Contains informations about the systems storage.
+/// </summary>
+public class SystemStorageInfo
+{
+    /// <summary>
+    /// Gets or sets the program data path.
+    /// </summary>
+    /// <value>The program data path.</value>
+    public required FolderStorageInfo ProgramDataFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the web UI resources path.
+    /// </summary>
+    /// <value>The web UI resources path.</value>
+    public required FolderStorageInfo WebFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the items by name path.
+    /// </summary>
+    /// <value>The items by name path.</value>
+    public required FolderStorageInfo ImageCacheFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the cache path.
+    /// </summary>
+    /// <value>The cache path.</value>
+    public required FolderStorageInfo CacheFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the log path.
+    /// </summary>
+    /// <value>The log path.</value>
+    public required FolderStorageInfo LogFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the internal metadata path.
+    /// </summary>
+    /// <value>The internal metadata path.</value>
+    public required FolderStorageInfo InternalMetadataFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the transcode path.
+    /// </summary>
+    /// <value>The transcode path.</value>
+    public required FolderStorageInfo TranscodingTempFolder { get; set; }
+
+    /// <summary>
+    /// Gets or sets the storage informations of all libraries.
+    /// </summary>
+    public required IReadOnlyCollection<LibraryStorageInfo> Libraries { get; set; }
+}