Beispiel #1
0
        /// <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
        /// <a href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html">XDG approach</a>
        /// is followed.
        /// </summary>
        /// <param name="options">The <see cref="StartupOptions" /> for this instance.</param>
        /// <returns><see cref="ServerApplicationPaths" />.</returns>
        private static ServerApplicationPaths CreateApplicationPaths(StartupOptions options)
        {
            // dataDir
            // IF      --datadir
            // ELSE IF $JELLYFIN_DATA_DIR
            // 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))
            {
                dataDir = Environment.GetEnvironmentVariable("JELLYFIN_DATA_DIR");

                if (string.IsNullOrEmpty(dataDir))
                {
                    // LocalApplicationData follows the XDG spec on unix machines
                    dataDir = Path.Combine(
                        Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                        "jellyfin");
                }
            }

            // 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;

            if (string.IsNullOrEmpty(configDir))
            {
                configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR");

                if (string.IsNullOrEmpty(configDir))
                {
                    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");
                    }
                }
            }

            // 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;

            if (string.IsNullOrEmpty(cacheDir))
            {
                cacheDir = Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR");

                if (string.IsNullOrEmpty(cacheDir))
                {
                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                    {
                        // Hang cache folder off already set dataDir
                        cacheDir = Path.Combine(dataDir, "cache");
                    }
                    else
                    {
                        // $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");
                    }
                }
            }

            // webDir
            // IF      --webdir
            // ELSE IF $JELLYFIN_WEB_DIR
            // ELSE    use <bindir>/jellyfin-web
            var webDir = options.WebDir;

            if (string.IsNullOrEmpty(webDir))
            {
                webDir = Environment.GetEnvironmentVariable("JELLYFIN_WEB_DIR");

                if (string.IsNullOrEmpty(webDir))
                {
                    // Use default location under ResourcesPath
                    webDir = Path.Combine(AppContext.BaseDirectory, "jellyfin-web");
                }
            }

            // logDir
            // IF      --logdir
            // ELSE IF $JELLYFIN_LOG_DIR
            // ELSE IF --datadir, use <datadir>/log (assume portable run)
            // ELSE    <datadir>/log
            var logDir = options.LogDir;

            if (string.IsNullOrEmpty(logDir))
            {
                logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR");

                if (string.IsNullOrEmpty(logDir))
                {
                    // Hang log folder off already set dataDir
                    logDir = Path.Combine(dataDir, "log");
                }
            }

            // 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);
            }

            return(new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir, webDir));
        }
Beispiel #2
0
        private static async Task StartApp(StartupOptions options)
        {
            ServerApplicationPaths appPaths = CreateApplicationPaths(options);

            // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
            Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);

            IConfiguration appConfig = await CreateConfiguration(appPaths).ConfigureAwait(false);

            CreateLogger(appConfig, appPaths);

            _logger = _loggerFactory.CreateLogger("Main");

            AppDomain.CurrentDomain.UnhandledException += (sender, e)
                                                          => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception");

            // Intercept Ctrl+C and Ctrl+Break
            Console.CancelKeyPress += (sender, e) =>
            {
                if (_tokenSource.IsCancellationRequested)
                {
                    return; // Already shutting down
                }

                e.Cancel = true;
                _logger.LogInformation("Ctrl+C, shutting down");
                Environment.ExitCode = 128 + 2;
                Shutdown();
            };

            // Register a SIGTERM handler
            AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
            {
                if (_tokenSource.IsCancellationRequested)
                {
                    return; // Already shutting down
                }

                _logger.LogInformation("Received a SIGTERM signal, shutting down");
                Environment.ExitCode = 128 + 15;
                Shutdown();
            };

            _logger.LogInformation("Jellyfin version: {Version}", Assembly.GetEntryAssembly().GetName().Version);

            ApplicationHost.LogEnvironmentInfo(_logger, appPaths);

            // Increase the max http request limit
            // The default connection limit is 10 for ASP.NET hosted applications and 2 for all others.
            ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit);

            // Disable the "Expect: 100-Continue" header by default
            // http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
            ServicePointManager.Expect100Continue = false;

// CA5359: Do Not Disable Certificate Validation
#pragma warning disable CA5359

            // Allow all https requests
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return(true); });
#pragma warning restore CA5359

            Batteries_V2.Init();
            if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK)
            {
                _logger.LogWarning("Failed to enable shared cache for SQLite");
            }

            var appHost = new CoreAppHost(
                appPaths,
                _loggerFactory,
                options,
                new ManagedFileSystem(_loggerFactory.CreateLogger <ManagedFileSystem>(), appPaths),
                new NullImageEncoder(),
                new NetworkManager(_loggerFactory.CreateLogger <NetworkManager>()),
                appConfig);
            try
            {
                await appHost.InitAsync(new ServiceCollection()).ConfigureAwait(false);

                appHost.ImageProcessor.ImageEncoder = GetImageEncoder(appPaths, appHost.LocalizationManager);

                await appHost.RunStartupTasksAsync().ConfigureAwait(false);

                // Block main thread until shutdown
                await Task.Delay(-1, _tokenSource.Token).ConfigureAwait(false);
            }
            catch (TaskCanceledException)
            {
                // Don't throw on cancellation
            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex, "Error while starting server.");
            }
            finally
            {
                appHost?.Dispose();
            }

            if (_restartOnShutdown)
            {
                StartNewInstance(options);
            }
        }
Beispiel #3
0
        public static async Task Main(string[] args)
        {
            StartupOptions options = new StartupOptions(args);
            Version        version = Assembly.GetEntryAssembly().GetName().Version;

            if (options.ContainsOption("-v") || options.ContainsOption("--version"))
            {
                Console.WriteLine(version.ToString());
            }

            ServerApplicationPaths appPaths = CreateApplicationPaths(options);

            // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
            Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
            await createLogger(appPaths);

            _logger = _loggerFactory.CreateLogger("Main");

            AppDomain.CurrentDomain.UnhandledException += (sender, e)
                                                          => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception");

            // Intercept Ctrl+C and Ctrl+Break
            Console.CancelKeyPress += (sender, e) =>
            {
                if (_tokenSource.IsCancellationRequested)
                {
                    return; // Already shutting down
                }
                e.Cancel = true;
                _logger.LogInformation("Ctrl+C, shutting down");
                Environment.ExitCode = 128 + 2;
                Shutdown();
            };

            // Register a SIGTERM handler
            AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
            {
                if (_tokenSource.IsCancellationRequested)
                {
                    return; // Already shutting down
                }
                _logger.LogInformation("Received a SIGTERM signal, shutting down");
                Environment.ExitCode = 128 + 15;
                Shutdown();
            };

            _logger.LogInformation("Jellyfin version: {Version}", version);

            EnvironmentInfo environmentInfo = new EnvironmentInfo(getOperatingSystem());

            ApplicationHost.LogEnvironmentInfo(_logger, appPaths, environmentInfo);

            SQLitePCL.Batteries_V2.Init();

            // Allow all https requests
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return(true); });

            var fileSystem = new ManagedFileSystem(_loggerFactory, environmentInfo, null, appPaths.TempDirectory, true);

            using (var appHost = new CoreAppHost(
                       appPaths,
                       _loggerFactory,
                       options,
                       fileSystem,
                       environmentInfo,
                       new NullImageEncoder(),
                       new SystemEvents(),
                       new NetworkManager(_loggerFactory, environmentInfo)))
            {
                await appHost.InitAsync();

                appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager);

                _logger.LogInformation("Running startup tasks");

                await appHost.RunStartupTasks();

                // TODO: read input for a stop command
                try
                {
                    // Block main thread until shutdown
                    await Task.Delay(-1, _tokenSource.Token);
                }
                catch (TaskCanceledException)
                {
                    // Don't throw on cancellation
                }

                _logger.LogInformation("Disposing app host");
            }

            if (_restartOnShutdown)
            {
                StartNewInstance(options);
            }
        }
Beispiel #4
0
        private static ServerApplicationPaths CreateApplicationPaths(StartupOptions options)
        {
            string programDataPath = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH");

            if (string.IsNullOrEmpty(programDataPath))
            {
                if (options.ContainsOption("-programdata"))
                {
                    programDataPath = options.GetOption("-programdata");
                }
                else
                {
                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                    {
                        programDataPath = 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))
                        {
                            programDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share");
                        }
                    }

                    programDataPath = Path.Combine(programDataPath, "jellyfin");
                }
            }

            if (string.IsNullOrEmpty(programDataPath))
            {
                Console.WriteLine("Cannot continue without path to program data folder (try -programdata)");
                Environment.Exit(1);
            }
            else
            {
                Directory.CreateDirectory(programDataPath);
            }
            string configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR");

            if (string.IsNullOrEmpty(configDir))
            {
                if (options.ContainsOption("-configdir"))
                {
                    configDir = options.GetOption("-configdir");
                }
                else
                {
                    // Let BaseApplicationPaths set up the default value
                    configDir = null;
                }
            }

            if (configDir != null)
            {
                Directory.CreateDirectory(configDir);
            }

            string logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR");

            if (string.IsNullOrEmpty(logDir))
            {
                if (options.ContainsOption("-logdir"))
                {
                    logDir = options.GetOption("-logdir");
                }
                else
                {
                    // Let BaseApplicationPaths set up the default value
                    logDir = null;
                }
            }

            if (logDir != null)
            {
                Directory.CreateDirectory(logDir);
            }

            string appPath = AppContext.BaseDirectory;

            return(new ServerApplicationPaths(programDataPath, appPath, appPath, logDir, configDir));
        }
Beispiel #5
0
        private static async Task StartApp(StartupOptions options)
        {
            var stopWatch = new Stopwatch();

            stopWatch.Start();
Beispiel #6
0
        public static async Task <int> Main(string[] args)
        {
            StartupOptions options = new StartupOptions(args);
            Version        version = Assembly.GetEntryAssembly().GetName().Version;

            if (options.ContainsOption("-v") || options.ContainsOption("--version"))
            {
                Console.WriteLine(version.ToString());
                return(0);
            }

            ServerApplicationPaths appPaths = createApplicationPaths(options);

            // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
            Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
            await createLogger(appPaths);

            _loggerFactory = new SerilogLoggerFactory();
            _logger        = _loggerFactory.CreateLogger("Main");

            AppDomain.CurrentDomain.UnhandledException += (sender, e)
                                                          => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception");

            _logger.LogInformation("Jellyfin version: {Version}", version);

            EnvironmentInfo environmentInfo = new EnvironmentInfo(getOperatingSystem());

            ApplicationHost.LogEnvironmentInfo(_logger, appPaths, environmentInfo);

            SQLitePCL.Batteries_V2.Init();

            // Allow all https requests
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return(true); });

            var fileSystem = new ManagedFileSystem(_loggerFactory.CreateLogger("FileSystem"), environmentInfo, null, appPaths.TempDirectory, true);

            using (var appHost = new CoreAppHost(
                       appPaths,
                       _loggerFactory,
                       options,
                       fileSystem,
                       environmentInfo,
                       new NullImageEncoder(),
                       new SystemEvents(_loggerFactory.CreateLogger("SystemEvents")),
                       new NetworkManager(_loggerFactory.CreateLogger("NetworkManager"), environmentInfo)))
            {
                appHost.Init();

                appHost.ImageProcessor.ImageEncoder = getImageEncoder(_logger, fileSystem, options, () => appHost.HttpClient, appPaths, environmentInfo, appHost.LocalizationManager);

                _logger.LogInformation("Running startup tasks");

                await appHost.RunStartupTasks();

                // TODO: read input for a stop command
                // Block main thread until shutdown
                await ApplicationTaskCompletionSource.Task;

                _logger.LogInformation("Disposing app host");
            }

            if (_restartOnShutdown)
            {
                StartNewInstance(options);
            }

            return(0);
        }
Beispiel #7
0
 public CoreAppHost(ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, MediaBrowser.Common.Net.INetworkManager networkManager)
     : base(applicationPaths, loggerFactory, options, fileSystem, environmentInfo, imageEncoder, networkManager)
 {
 }