Ver código fonte

Rewrite rules for determining app paths and use XDG_CONFIG_HOME for configDir (#781)

Re-write rules for determining dataDir, configDir and logDir.  Generally, arguments from command line take precedence, then JELLYFIN env vars, before using XDG names.

Co-Authored-By: ploughpuff <33969763+ploughpuff@users.noreply.github.com>
ploughpuff 6 anos atrás
pai
commit
a2dd2ddd55

+ 21 - 82
Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs

@@ -1,3 +1,4 @@
+using System;
 using System.IO;
 using MediaBrowser.Common.Configuration;
 
@@ -14,50 +15,44 @@ namespace Emby.Server.Implementations.AppBase
         /// </summary>
         protected BaseApplicationPaths(
             string programDataPath,
-            string appFolderPath,
-            string logDirectoryPath = null,
-            string configurationDirectoryPath = null,
-            string cacheDirectoryPath = null)
+            string logDirectoryPath,
+            string configurationDirectoryPath,
+            string cacheDirectoryPath)
         {
             ProgramDataPath = programDataPath;
-            ProgramSystemPath = appFolderPath;
             LogDirectoryPath = logDirectoryPath;
             ConfigurationDirectoryPath = configurationDirectoryPath;
             CachePath = cacheDirectoryPath;
+
+            DataPath = Path.Combine(ProgramDataPath, "data");
         }
 
+        /// <summary>
+        /// Gets the path to the program data folder
+        /// </summary>
+        /// <value>The program data path.</value>
         public string ProgramDataPath { get; private set; }
 
         /// <summary>
         /// Gets the path to the system folder
         /// </summary>
-        public string ProgramSystemPath { get; private set; }
+        public string ProgramSystemPath { get; } = AppContext.BaseDirectory;
 
-        /// <summary>
-        /// The _data directory
-        /// </summary>
-        private string _dataDirectory;
         /// <summary>
         /// Gets the folder path to the data directory
         /// </summary>
         /// <value>The data directory.</value>
+        private string _dataPath;
         public string DataPath
         {
-            get
-            {
-                if (_dataDirectory == null)
-                {
-                    _dataDirectory = Path.Combine(ProgramDataPath, "data");
-
-                    Directory.CreateDirectory(_dataDirectory);
-                }
-
-                return _dataDirectory;
-            }
+            get => _dataPath;
+            private set => _dataPath = Directory.CreateDirectory(value).FullName;
         }
 
-        private const string _virtualDataPath = "%AppDataPath%";
-        public string VirtualDataPath => _virtualDataPath;
+        /// <summary>
+        /// Gets the magic strings used for virtual path manipulation.
+        /// </summary>
+        public string VirtualDataPath { get; } = "%AppDataPath%";
 
         /// <summary>
         /// Gets the image cache path.
@@ -83,55 +78,17 @@ namespace Emby.Server.Implementations.AppBase
         /// <value>The plugin configurations path.</value>
         public string TempUpdatePath => Path.Combine(ProgramDataPath, "updates");
 
-        /// <summary>
-        /// The _log directory
-        /// </summary>
-        private string _logDirectoryPath;
-
         /// <summary>
         /// Gets the path to the log directory
         /// </summary>
         /// <value>The log directory path.</value>
-        public string LogDirectoryPath
-        {
-            get
-            {
-                if (string.IsNullOrEmpty(_logDirectoryPath))
-                {
-                    _logDirectoryPath = Path.Combine(ProgramDataPath, "logs");
-
-                    Directory.CreateDirectory(_logDirectoryPath);
-                }
-
-                return _logDirectoryPath;
-            }
-            set => _logDirectoryPath = value;
-        }
-
-        /// <summary>
-        /// The _config directory
-        /// </summary>
-        private string _configurationDirectoryPath;
+        public string LogDirectoryPath { get; private set; }
 
         /// <summary>
         /// Gets the path to the application configuration root directory
         /// </summary>
         /// <value>The configuration directory path.</value>
-        public string ConfigurationDirectoryPath
-        {
-            get
-            {
-                if (string.IsNullOrEmpty(_configurationDirectoryPath))
-                {
-                    _configurationDirectoryPath = Path.Combine(ProgramDataPath, "config");
-
-                    Directory.CreateDirectory(_configurationDirectoryPath);
-                }
-
-                return _configurationDirectoryPath;
-            }
-            set => _configurationDirectoryPath = value;
-        }
+        public string ConfigurationDirectoryPath { get; private set; }
 
         /// <summary>
         /// Gets the path to the system configuration file
@@ -139,29 +96,11 @@ namespace Emby.Server.Implementations.AppBase
         /// <value>The system configuration file path.</value>
         public string SystemConfigurationFilePath => Path.Combine(ConfigurationDirectoryPath, "system.xml");
 
-        /// <summary>
-        /// The _cache directory
-        /// </summary>
-        private string _cachePath;
         /// <summary>
         /// Gets the folder path to the cache directory
         /// </summary>
         /// <value>The cache directory.</value>
-        public string CachePath
-        {
-            get
-            {
-                if (string.IsNullOrEmpty(_cachePath))
-                {
-                    _cachePath = Path.Combine(ProgramDataPath, "cache");
-
-                    Directory.CreateDirectory(_cachePath);
-                }
-
-                return _cachePath;
-            }
-            set => _cachePath = value;
-        }
+        public string CachePath { get; set; }
 
         /// <summary>
         /// Gets the folder path to the temp directory within the cache folder

+ 5 - 10
Emby.Server.Implementations/ServerApplicationPaths.cs

@@ -15,21 +15,17 @@ namespace Emby.Server.Implementations
         /// </summary>
         public ServerApplicationPaths(
             string programDataPath,
-            string appFolderPath,
-            string applicationResourcesPath,
-            string logDirectoryPath = null,
-            string configurationDirectoryPath = null,
-            string cacheDirectoryPath = null)
+            string logDirectoryPath,
+            string configurationDirectoryPath,
+            string cacheDirectoryPath)
             : base(programDataPath,
-                appFolderPath,
                 logDirectoryPath,
                 configurationDirectoryPath,
                 cacheDirectoryPath)
         {
-            ApplicationResourcesPath = applicationResourcesPath;
         }
 
-        public string ApplicationResourcesPath { get; private set; }
+        public string ApplicationResourcesPath { get; } = AppContext.BaseDirectory;
 
         /// <summary>
         /// Gets the path to the base root media directory
@@ -148,7 +144,6 @@ namespace Emby.Server.Implementations
             set => _internalMetadataPath = value;
         }
 
-        private const string _virtualInternalMetadataPath = "%MetadataPath%";
-        public string VirtualInternalMetadataPath => _virtualInternalMetadataPath;
+        public string VirtualInternalMetadataPath { get; } = "%MetadataPath%";
     }
 }

+ 106 - 62
Jellyfin.Server/Program.cs

@@ -139,112 +139,156 @@ namespace Jellyfin.Server
             }
         }
 
+        /// <summary>
+        /// Create the data, config and log paths from the variety of inputs(command line args,
+        /// environment variables) or decide on what default to use.  For Windows it's %AppPath%
+        /// for everything else the XDG approach is followed:
+        /// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
+        /// </summary>
+        /// <param name="options"></param>
+        /// <returns>ServerApplicationPaths</returns>
         private static ServerApplicationPaths CreateApplicationPaths(StartupOptions options)
         {
-            string programDataPath = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH");
-            if (string.IsNullOrEmpty(programDataPath))
+            // dataDir
+            // IF      --datadir
+            // ELSE IF $JELLYFIN_DATA_PATH
+            // ELSE IF windows, use <%APPDATA%>/jellyfin
+            // ELSE IF $XDG_DATA_HOME then use $XDG_DATA_HOME/jellyfin
+            // ELSE    use $HOME/.local/share/jellyfin
+            var dataDir = options.DataDir;
+
+            if (string.IsNullOrEmpty(dataDir))
             {
-                if (options.DataDir != null)
-                {
-                    programDataPath = options.DataDir;
-                }
-                else
+                dataDir = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH");
+
+                if (string.IsNullOrEmpty(dataDir))
                 {
                     if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                     {
-                        programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+                        dataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
                     }
                     else
                     {
                         // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
-                        programDataPath = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
-                        // If $XDG_DATA_HOME is either not set or empty, $HOME/.local/share should be used.
-                        if (string.IsNullOrEmpty(programDataPath))
+                        dataDir = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
+
+                        // If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
+                        if (string.IsNullOrEmpty(dataDir))
                         {
-                            programDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share");
+                            dataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share");
                         }
                     }
 
-                    programDataPath = Path.Combine(programDataPath, "jellyfin");
+                    dataDir = Path.Combine(dataDir, "jellyfin");
                 }
             }
 
-            if (string.IsNullOrEmpty(programDataPath))
-            {
-                Console.WriteLine("Cannot continue without path to program data folder (try -programdata)");
-                Environment.Exit(1);
-            }
-            else
-            {
-                Directory.CreateDirectory(programDataPath);
-            }
+            // configDir
+            // IF      --configdir
+            // ELSE IF $JELLYFIN_CONFIG_DIR
+            // ELSE IF --datadir, use <datadir>/config (assume portable run)
+            // ELSE IF <datadir>/config exists, use that
+            // ELSE IF windows, use <datadir>/config
+            // ELSE IF $XDG_CONFIG_HOME use $XDG_CONFIG_HOME/jellyfin
+            // ELSE    $HOME/.config/jellyfin
+            var configDir = options.ConfigDir;
 
-            string configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR");
             if (string.IsNullOrEmpty(configDir))
             {
-                if (options.ConfigDir != null)
-                {
-                    configDir = options.ConfigDir;
-                }
-                else
+                configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR");
+
+                if (string.IsNullOrEmpty(configDir))
                 {
-                    // Let BaseApplicationPaths set up the default value
-                    configDir = null;
+                    if (options.DataDir != null || Directory.Exists(Path.Combine(dataDir, "config")) || RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+                    {
+                        // Hang config folder off already set dataDir
+                        configDir = Path.Combine(dataDir, "config");
+                    }
+                    else
+                    {
+                        // $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should be stored.
+                        configDir = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME");
+
+                        // If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME /.config should be used.
+                        if (string.IsNullOrEmpty(configDir))
+                        {
+                            configDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config");
+                        }
+
+                        configDir = Path.Combine(configDir, "jellyfin");
+                    }
                 }
             }
 
-            if (configDir != null)
-            {
-                Directory.CreateDirectory(configDir);
-            }
+            // cacheDir
+            // IF      --cachedir
+            // ELSE IF $JELLYFIN_CACHE_DIR
+            // ELSE IF windows, use <datadir>/cache
+            // ELSE IF XDG_CACHE_HOME, use $XDG_CACHE_HOME/jellyfin
+            // ELSE    HOME/.cache/jellyfin
+            var cacheDir = options.CacheDir;
 
-            string cacheDir = Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR");
             if (string.IsNullOrEmpty(cacheDir))
             {
-                if (options.CacheDir != null)
-                {
-                    cacheDir = options.CacheDir;
-                }
-                else if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+                cacheDir = Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR");
+
+                if (string.IsNullOrEmpty(cacheDir))
                 {
-                    // $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data files should be stored.
-                    cacheDir = Environment.GetEnvironmentVariable("XDG_CACHE_HOME");
-                    // If $XDG_CACHE_HOME is either not set or empty, $HOME/.cache should be used.
-                    if (string.IsNullOrEmpty(cacheDir))
+                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+                    {
+                        // Hang cache folder off already set dataDir
+                        cacheDir = Path.Combine(dataDir, "cache");
+                    }
+                    else
                     {
-                        cacheDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cache");
+                        // $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data files should be stored.
+                        cacheDir = Environment.GetEnvironmentVariable("XDG_CACHE_HOME");
+
+                        // If $XDG_CACHE_HOME is either not set or empty, a default equal to $HOME/.cache should be used.
+                        if (string.IsNullOrEmpty(cacheDir))
+                        {
+                            cacheDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cache");
+                        }
+
+                        cacheDir = Path.Combine(cacheDir, "jellyfin");
                     }
-                    cacheDir = Path.Combine(cacheDir, "jellyfin");
                 }
             }
 
-            if (cacheDir != null)
-            {
-                Directory.CreateDirectory(cacheDir);
-            }
+            // logDir
+            // IF      --logdir
+            // ELSE IF $JELLYFIN_LOG_DIR
+            // ELSE IF --datadir, use <datadir>/log (assume portable run)
+            // ELSE    <datadir>/log
+            var logDir = options.LogDir;
 
-            string logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR");
             if (string.IsNullOrEmpty(logDir))
             {
-                if (options.LogDir != null)
-                {
-                    logDir = options.LogDir;
-                }
-                else
+                logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR");
+
+                if (string.IsNullOrEmpty(logDir))
                 {
-                    // Let BaseApplicationPaths set up the default value
-                    logDir = null;
+                    // Hang log folder off already set dataDir
+                    logDir = Path.Combine(dataDir, "log");
                 }
             }
 
-            if (logDir != null)
+            // Ensure the main folders exist before we continue
+            try
             {
+                Directory.CreateDirectory(dataDir);
                 Directory.CreateDirectory(logDir);
+                Directory.CreateDirectory(configDir);
+                Directory.CreateDirectory(cacheDir);
+            }
+            catch (IOException ex)
+            {
+                Console.Error.WriteLine("Error whilst attempting to create folder");
+                Console.Error.WriteLine(ex.ToString());
+                Environment.Exit(1);
             }
 
-            string appPath = AppContext.BaseDirectory;
-
-            return new ServerApplicationPaths(programDataPath, appPath, appPath, logDir, configDir, cacheDir);
+            return new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir);
         }
 
         private static async Task CreateLogger(IApplicationPaths appPaths)