aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Server')
-rw-r--r--Jellyfin.Server/CoreAppHost.cs2
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj1
-rw-r--r--Jellyfin.Server/Program.cs84
-rw-r--r--Jellyfin.Server/SocketSharp/RequestMono.cs25
-rw-r--r--Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs41
-rw-r--r--Jellyfin.Server/StartupOptions.cs44
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; }
+ }
+}