diff options
Diffstat (limited to 'Jellyfin.Server')
| -rw-r--r-- | Jellyfin.Server/CoreAppHost.cs | 2 | ||||
| -rw-r--r-- | Jellyfin.Server/Jellyfin.Server.csproj | 1 | ||||
| -rw-r--r-- | Jellyfin.Server/Program.cs | 84 | ||||
| -rw-r--r-- | Jellyfin.Server/SocketSharp/RequestMono.cs | 25 | ||||
| -rw-r--r-- | Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs | 41 | ||||
| -rw-r--r-- | Jellyfin.Server/StartupOptions.cs | 44 |
6 files changed, 151 insertions, 46 deletions
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index b580f45ca5..5182ab4d7e 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -16,7 +16,7 @@ namespace Jellyfin.Server { } - public override bool CanSelfRestart => StartupOptions.ContainsOption("-restartpath"); + public override bool CanSelfRestart => StartupOptions.RestartPath != null; protected override void RestartInternal() => Program.Restart(); diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 5a4bf5149d..1f72de86d7 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -32,6 +32,7 @@ </PropertyGroup> <ItemGroup> + <PackageReference Include="CommandLineParser" Version="2.4.3" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" /> <PackageReference Include="Serilog.AspNetCore" Version="2.1.1" /> diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 4dfe834411..7826fde35b 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -6,8 +6,10 @@ using System.Net; using System.Net.Security; using System.Reflection; using System.Runtime.InteropServices; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using CommandLine; using Emby.Drawing; using Emby.Server.Implementations; using Emby.Server.Implementations.EnvironmentInfo; @@ -35,19 +37,32 @@ namespace Jellyfin.Server 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")) + // For backwards compatibility. + // Modify any input arguments now which start with single-hyphen to POSIX standard + // double-hyphen to allow parsing by CommandLineParser package. + const string pattern = @"^(-[^-\s]{2})"; // Match -xx, not -x, not --xx, not xx + const string substitution = @"-$1"; // Prepend with additional single-hyphen + var regex = new Regex(pattern); + + for (var i = 0; i < args.Length; i++) { - Console.WriteLine(version.ToString()); + args[i] = regex.Replace(args[i], substitution); } + // Parse the command line arguments and either start the app or exit indicating error + await Parser.Default.ParseArguments<StartupOptions>(args) + .MapResult( + options => StartApp(options), + errs => Task.FromResult(0)).ConfigureAwait(false); + } + + 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); - await createLogger(appPaths); + await CreateLogger(appPaths); _logger = _loggerFactory.CreateLogger("Main"); AppDomain.CurrentDomain.UnhandledException += (sender, e) @@ -78,9 +93,9 @@ namespace Jellyfin.Server Shutdown(); }; - _logger.LogInformation("Jellyfin version: {Version}", version); + _logger.LogInformation("Jellyfin version: {Version}", Assembly.GetEntryAssembly().GetName().Version); - EnvironmentInfo environmentInfo = new EnvironmentInfo(getOperatingSystem()); + EnvironmentInfo environmentInfo = new EnvironmentInfo(GetOperatingSystem()); ApplicationHost.LogEnvironmentInfo(_logger, appPaths, environmentInfo); SQLitePCL.Batteries_V2.Init(); @@ -99,7 +114,7 @@ namespace Jellyfin.Server new NullImageEncoder(), new NetworkManager(_loggerFactory, environmentInfo))) { - appHost.Init(); + await appHost.Init(); appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager); @@ -129,9 +144,9 @@ namespace Jellyfin.Server string programDataPath = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH"); if (string.IsNullOrEmpty(programDataPath)) { - if (options.ContainsOption("-programdata")) + if (options.DataDir != null) { - programDataPath = options.GetOption("-programdata"); + programDataPath = options.DataDir; } else { @@ -167,9 +182,9 @@ namespace Jellyfin.Server string configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR"); if (string.IsNullOrEmpty(configDir)) { - if (options.ContainsOption("-configdir")) + if (options.ConfigDir != null) { - configDir = options.GetOption("-configdir"); + configDir = options.ConfigDir; } else { @@ -183,12 +198,37 @@ namespace Jellyfin.Server Directory.CreateDirectory(configDir); } + string cacheDir = Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR"); + if (string.IsNullOrEmpty(cacheDir)) + { + if (options.CacheDir != null) + { + cacheDir = options.CacheDir; + } + else if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // $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)) + { + cacheDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cache"); + } + cacheDir = Path.Combine(cacheDir, "jellyfin"); + } + } + + if (cacheDir != null) + { + Directory.CreateDirectory(cacheDir); + } + string logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR"); if (string.IsNullOrEmpty(logDir)) { - if (options.ContainsOption("-logdir")) + if (options.LogDir != null) { - logDir = options.GetOption("-logdir"); + logDir = options.LogDir; } else { @@ -204,10 +244,10 @@ namespace Jellyfin.Server string appPath = AppContext.BaseDirectory; - return new ServerApplicationPaths(programDataPath, appPath, appPath, logDir, configDir); + return new ServerApplicationPaths(programDataPath, appPath, appPath, logDir, configDir, cacheDir); } - private static async Task createLogger(IApplicationPaths appPaths) + private static async Task CreateLogger(IApplicationPaths appPaths) { try { @@ -267,7 +307,7 @@ namespace Jellyfin.Server return new NullImageEncoder(); } - private static MediaBrowser.Model.System.OperatingSystem getOperatingSystem() + private static MediaBrowser.Model.System.OperatingSystem GetOperatingSystem() { switch (Environment.OSVersion.Platform) { @@ -311,11 +351,11 @@ namespace Jellyfin.Server Shutdown(); } - private static void StartNewInstance(StartupOptions startupOptions) + private static void StartNewInstance(StartupOptions options) { _logger.LogInformation("Starting new instance"); - string module = startupOptions.GetOption("-restartpath"); + string module = options.RestartPath; if (string.IsNullOrWhiteSpace(module)) { @@ -324,9 +364,9 @@ namespace Jellyfin.Server string commandLineArgsString; - if (startupOptions.ContainsOption("-restartargs")) + if (options.RestartArgs != null) { - commandLineArgsString = startupOptions.GetOption("-restartargs") ?? string.Empty; + commandLineArgsString = options.RestartArgs ?? string.Empty; } else { diff --git a/Jellyfin.Server/SocketSharp/RequestMono.cs b/Jellyfin.Server/SocketSharp/RequestMono.cs index 017690062e..f09197fb35 100644 --- a/Jellyfin.Server/SocketSharp/RequestMono.cs +++ b/Jellyfin.Server/SocketSharp/RequestMono.cs @@ -360,13 +360,13 @@ namespace Jellyfin.SocketSharp int len = buffer.Length; if (dest_offset > len) { - throw new ArgumentException("destination offset is beyond array size"); + throw new ArgumentException("destination offset is beyond array size", nameof(dest_offset)); } // reordered to avoid possible integer overflow if (dest_offset > len - count) { - throw new ArgumentException("Reading would overrun buffer"); + throw new ArgumentException("Reading would overrun buffer", nameof(count)); } if (count > end - position) @@ -528,7 +528,7 @@ namespace Jellyfin.SocketSharp } } - class HttpMultipart + private class HttpMultipart { public class Element @@ -543,19 +543,19 @@ namespace Jellyfin.SocketSharp public override string ToString() { return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " + - Start.ToString() + ", Length " + Length.ToString(); + Start.ToString(CultureInfo.CurrentCulture) + ", Length " + Length.ToString(CultureInfo.CurrentCulture); } } - Stream data; - string boundary; - byte[] boundary_bytes; - byte[] buffer; - bool at_eof; - Encoding encoding; - StringBuilder sb; + private Stream data; + private string boundary; + private byte[] boundary_bytes; + private byte[] buffer; + private bool at_eof; + private Encoding encoding; + private StringBuilder sb; - const byte LF = (byte)'\n', CR = (byte)'\r'; + private const byte LF = (byte)'\n', CR = (byte)'\r'; // See RFC 2046 // In the case of multipart entities, in which one or more different @@ -610,7 +610,6 @@ namespace Jellyfin.SocketSharp } return sb.ToString(); - } private static string GetContentDispositionAttribute(string l, string name) diff --git a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs index 97550e686c..2e8dd9185b 100644 --- a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs +++ b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs @@ -29,12 +29,24 @@ namespace Jellyfin.SocketSharp private static string GetHandlerPathIfAny(string listenerUrl) { - if (listenerUrl == null) return null; + if (listenerUrl == null) + { + return null; + } + var pos = listenerUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase); - if (pos == -1) return null; + if (pos == -1) + { + return null; + } + var startHostUrl = listenerUrl.Substring(pos + "://".Length); var endPos = startHostUrl.IndexOf('/'); - if (endPos == -1) return null; + if (endPos == -1) + { + return null; + } + var endHostUrl = startHostUrl.Substring(endPos + 1); return string.IsNullOrEmpty(endHostUrl) ? null : endHostUrl.TrimEnd('/'); } @@ -210,9 +222,13 @@ namespace Jellyfin.SocketSharp if (acceptsAnything) { if (hasDefaultContentType) + { return defaultContentType; - if (serverDefaultContentType != null) + } + else if (serverDefaultContentType != null) + { return serverDefaultContentType; + } } } @@ -229,11 +245,16 @@ namespace Jellyfin.SocketSharp public static bool HasAnyOfContentTypes(IRequest request, params string[] contentTypes) { - if (contentTypes == null || request.ContentType == null) return false; + if (contentTypes == null || request.ContentType == null) + { + return false; + } + foreach (var contentType in contentTypes) { if (IsContentType(request, contentType)) return true; } + return false; } @@ -264,12 +285,12 @@ namespace Jellyfin.SocketSharp } } - format = LeftPart(format, '.').ToLower(); + format = LeftPart(format, '.').ToLowerInvariant(); if (format.Contains("json", StringComparison.OrdinalIgnoreCase)) { return "application/json"; } - if (format.Contains("xml", StringComparison.OrdinalIgnoreCase)) + else if (format.Contains("xml", StringComparison.OrdinalIgnoreCase)) { return "application/xml"; } @@ -283,10 +304,9 @@ namespace Jellyfin.SocketSharp { return null; } + var pos = strVal.IndexOf(needle); - return pos == -1 - ? strVal - : strVal.Substring(0, pos); + return pos == -1 ? strVal : strVal.Substring(0, pos); } public static string HandlerFactoryPath; @@ -433,6 +453,7 @@ namespace Jellyfin.SocketSharp { return null; } + try { return Encoding.GetEncoding(param); diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs new file mode 100644 index 0000000000..5d3f7b171b --- /dev/null +++ b/Jellyfin.Server/StartupOptions.cs @@ -0,0 +1,44 @@ +using CommandLine; +using Emby.Server.Implementations; + +namespace Jellyfin.Server +{ + /// <summary> + /// Class used by CommandLine package when parsing the command line arguments. + /// </summary> + public class StartupOptions : IStartupOptions + { + [Option('d', "datadir", Required = false, HelpText = "Path to use for the data folder (database files, etc.).")] + public string DataDir { get; set; } + + [Option('C', "cachedir", Required = false, HelpText = "Path to use for caching.")] + public string CacheDir { get; set; } + + [Option('c', "configdir", Required = false, HelpText = "Path to use for configuration data (user settings and pictures).")] + public string ConfigDir { get; set; } + + [Option('l', "logdir", Required = false, HelpText = "Path to use for writing log files.")] + public string LogDir { get; set; } + + [Option("ffmpeg", Required = false, HelpText = "Path to external FFmpeg executable to use in place of default found in PATH. Must be specified along with --ffprobe.")] + public string FFmpegPath { get; set; } + + [Option("ffprobe", Required = false, HelpText = "Path to external FFprobe executable to use in place of default found in PATH. Must be specified along with --ffmpeg.")] + public string FFprobePath { get; set; } + + [Option("service", Required = false, HelpText = "Run as headless service.")] + public bool IsService { get; set; } + + [Option("noautorunwebapp", Required = false, HelpText = "Run headless if startup wizard is complete.")] + public bool NoAutoRunWebApp { get; set; } + + [Option("package-name", Required = false, HelpText = "Used when packaging Jellyfin (example, synology).")] + public string PackageName { get; set; } + + [Option("restartpath", Required = false, HelpText = "Path to restart script.")] + public string RestartPath { get; set; } + + [Option("restartargs", Required = false, HelpText = "Arguments for restart script.")] + public string RestartArgs { get; set; } + } +} |
