aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs2
-rw-r--r--Emby.Server.Implementations/Browser/BrowserLauncher.cs10
-rw-r--r--Emby.Server.Implementations/ConfigurationOptions.cs13
-rw-r--r--Emby.Server.Implementations/EntryPoints/StartupWizard.cs8
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs8
-rw-r--r--Jellyfin.Server/Program.cs43
-rw-r--r--MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs16
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj1
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs4
9 files changed, 90 insertions, 15 deletions
diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index c3cdcc222..be2d198ef 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.AppBase
/// <summary>
/// Gets the path to the web UI resources folder.
/// </summary>
- /// <value>The web UI resources path.</value>
+ /// <value>The web UI resources path, or null if the server is not hosting any web content.</value>
public string WebPath { get; }
/// <summary>
diff --git a/Emby.Server.Implementations/Browser/BrowserLauncher.cs b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
index f5da0d018..b17c2b270 100644
--- a/Emby.Server.Implementations/Browser/BrowserLauncher.cs
+++ b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
@@ -30,6 +30,16 @@ namespace Emby.Server.Implementations.Browser
}
/// <summary>
+ /// Opens the swagger API page.
+ /// </summary>
+ /// <param name="appHost">The app host.</param>
+ public static void OpenSwaggerPage(IServerApplicationHost appHost)
+ {
+ var url = appHost.GetLocalApiUrl("localhost") + "/swagger/index.html";
+ OpenUrl(appHost, url);
+ }
+
+ /// <summary>
/// Opens the URL.
/// </summary>
/// <param name="appHost">The application host instance.</param>
diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs
index d0f3d6723..a7e9369cf 100644
--- a/Emby.Server.Implementations/ConfigurationOptions.cs
+++ b/Emby.Server.Implementations/ConfigurationOptions.cs
@@ -1,13 +1,22 @@
using System.Collections.Generic;
+using Emby.Server.Implementations.HttpServer;
+using MediaBrowser.Providers.Music;
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
namespace Emby.Server.Implementations
{
+ /// <summary>
+ /// Static class containing the default configuration options for the web server.
+ /// </summary>
public static class ConfigurationOptions
{
- public static Dictionary<string, string> Configuration => new Dictionary<string, string>
+ /// <summary>
+ /// Gets a new copy of the default configuration options.
+ /// </summary>
+ public static Dictionary<string, string> DefaultConfiguration => new Dictionary<string, string>
{
- { "HttpListenerHost:DefaultRedirectPath", "web/index.html" },
+ { NoWebContentKey, bool.FalseString },
+ { HttpListenerHost.DefaultRedirectKey, "web/index.html" },
{ FfmpegProbeSizeKey, "1G" },
{ FfmpegAnalyzeDurationKey, "200M" }
};
diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
index 5f2d629fe..7c2b3cd5f 100644
--- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
+++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
@@ -2,7 +2,9 @@ using System.Threading.Tasks;
using Emby.Server.Implementations.Browser;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Plugins;
+using Microsoft.Extensions.Configuration;
namespace Emby.Server.Implementations.EntryPoints
{
@@ -36,7 +38,11 @@ namespace Emby.Server.Implementations.EntryPoints
return Task.CompletedTask;
}
- if (!_config.Configuration.IsStartupWizardCompleted)
+ if (_appHost.Resolve<IConfiguration>().IsNoWebContent())
+ {
+ BrowserLauncher.OpenSwaggerPage(_appHost);
+ }
+ else if (!_config.Configuration.IsStartupWizardCompleted)
{
BrowserLauncher.OpenWebApp(_appHost);
}
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 85602a67f..546a59517 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -30,6 +30,12 @@ namespace Emby.Server.Implementations.HttpServer
{
public class HttpListenerHost : IHttpServer, IDisposable
{
+ /// <summary>
+ /// The key for a setting that specifies the default redirect path
+ /// to use for requests where the URL base prefix is invalid or missing.
+ /// </summary>
+ public const string DefaultRedirectKey = "HttpListenerHost:DefaultRedirectPath";
+
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
private readonly INetworkManager _networkManager;
@@ -58,7 +64,7 @@ namespace Emby.Server.Implementations.HttpServer
_appHost = applicationHost;
_logger = logger;
_config = config;
- _defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
+ _defaultRedirectPath = configuration[DefaultRedirectKey];
_baseUrlPrefix = _config.Configuration.BaseUrl;
_networkManager = networkManager;
_jsonSerializer = jsonSerializer;
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index e9e852349..9450fee70 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -13,12 +13,13 @@ using System.Threading.Tasks;
using CommandLine;
using Emby.Drawing;
using Emby.Server.Implementations;
+using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.IO;
using Emby.Server.Implementations.Networking;
using Jellyfin.Drawing.Skia;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Model.Globalization;
+using MediaBrowser.Controller.Extensions;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -188,7 +189,7 @@ namespace Jellyfin.Server
ServiceCollection serviceCollection = new ServiceCollection();
await appHost.InitAsync(serviceCollection, startupConfig).ConfigureAwait(false);
- var webHost = CreateWebHostBuilder(appHost, serviceCollection, appPaths).Build();
+ var webHost = CreateWebHostBuilder(appHost, serviceCollection, startupConfig, appPaths).Build();
// A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection.
appHost.ServiceProvider = webHost.Services;
@@ -233,9 +234,13 @@ namespace Jellyfin.Server
}
}
- private static IWebHostBuilder CreateWebHostBuilder(ApplicationHost appHost, IServiceCollection serviceCollection, IApplicationPaths appPaths)
+ private static IWebHostBuilder CreateWebHostBuilder(
+ ApplicationHost appHost,
+ IServiceCollection serviceCollection,
+ IConfiguration startupConfig,
+ IApplicationPaths appPaths)
{
- return new WebHostBuilder()
+ var webhostBuilder = new WebHostBuilder()
.UseKestrel(options =>
{
var addresses = appHost.ServerConfigurationManager
@@ -275,13 +280,29 @@ namespace Jellyfin.Server
})
.ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(appPaths))
.UseSerilog()
- .UseContentRoot(appHost.ContentRoot)
.ConfigureServices(services =>
{
// Merge the external ServiceCollection into ASP.NET DI
services.TryAdd(serviceCollection);
})
.UseStartup<Startup>();
+
+ if (!startupConfig.IsNoWebContent())
+ {
+ // Fail startup if the web content does not exist
+ if (!Directory.Exists(appHost.ContentRoot) || !Directory.GetFiles(appHost.ContentRoot).Any())
+ {
+ throw new InvalidOperationException(
+ "The server is expected to host web content, but the provided content directory is either " +
+ $"invalid or empty: {appHost.ContentRoot}. If you do not want to host web content with the " +
+ $"server, you may set the '{MediaBrowser.Controller.Extensions.ConfigurationExtensions.NoWebContentKey}' flag.");
+ }
+
+ // Configure the web host to host the static web content
+ webhostBuilder.UseContentRoot(appHost.ContentRoot);
+ }
+
+ return webhostBuilder;
}
/// <summary>
@@ -398,9 +419,8 @@ namespace Jellyfin.Server
// webDir
// IF --webdir
// ELSE IF $JELLYFIN_WEB_DIR
- // ELSE use <bindir>/jellyfin-web
+ // ELSE <bindir>/jellyfin-web
var webDir = options.WebDir;
-
if (string.IsNullOrEmpty(webDir))
{
webDir = Environment.GetEnvironmentVariable("JELLYFIN_WEB_DIR");
@@ -480,9 +500,16 @@ namespace Jellyfin.Server
private static IConfigurationBuilder ConfigureAppConfiguration(this IConfigurationBuilder config, IApplicationPaths appPaths)
{
+ // Use the swagger API page as the default redirect path if not hosting the jellyfin-web content
+ var inMemoryDefaultConfig = ConfigurationOptions.DefaultConfiguration;
+ if (string.IsNullOrEmpty(appPaths.WebPath))
+ {
+ inMemoryDefaultConfig[HttpListenerHost.DefaultRedirectKey] = "swagger/index.html";
+ }
+
return config
.SetBasePath(appPaths.ConfigurationDirectoryPath)
- .AddInMemoryCollection(ConfigurationOptions.Configuration)
+ .AddInMemoryCollection(inMemoryDefaultConfig)
.AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true)
.AddJsonFile(LoggingConfigFileSystem, optional: true, reloadOnChange: true)
.AddEnvironmentVariables("JELLYFIN_");
diff --git a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
index 76c9b4b26..1a9ac09ee 100644
--- a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
+++ b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
@@ -1,3 +1,4 @@
+using System;
using Microsoft.Extensions.Configuration;
namespace MediaBrowser.Controller.Extensions
@@ -8,6 +9,11 @@ namespace MediaBrowser.Controller.Extensions
public static class ConfigurationExtensions
{
/// <summary>
+ /// The key for a setting that indicates whether the application should host static web content.
+ /// </summary>
+ public const string NoWebContentKey = "nowebcontent";
+
+ /// <summary>
/// The key for the FFmpeg probe size option.
/// </summary>
public const string FfmpegProbeSizeKey = "FFmpeg:probesize";
@@ -18,6 +24,16 @@ namespace MediaBrowser.Controller.Extensions
public const string FfmpegAnalyzeDurationKey = "FFmpeg:analyzeduration";
/// <summary>
+ /// Retrieves a config value indicating whether the application should not host
+ /// static web content from the <see cref="IConfiguration"/>.
+ /// </summary>
+ /// <param name="configuration">The configuration to retrieve the value from.</param>
+ /// <returns>The parsed config value.</returns>
+ /// <exception cref="FormatException">The config value is not a valid bool string. See <see cref="bool.Parse(string)"/>.</exception>
+ public static bool IsNoWebContent(this IConfiguration configuration)
+ => configuration.GetValue<bool>(NoWebContentKey);
+
+ /// <summary>
/// Retrieves the FFmpeg probe size from the <see cref="IConfiguration" />.
/// </summary>
/// <param name="configuration">This configuration.</param>
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 88e9055e8..bcca9e4a1 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.1" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.1" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index c8f18e69e..7a8a4d3a5 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -149,9 +149,9 @@ namespace MediaBrowser.Model.Configuration
public bool EnableDashboardResponseCaching { get; set; }
/// <summary>
- /// Allows the dashboard to be served from a custom path.
+ /// Gets or sets a custom path to serve the dashboard from.
/// </summary>
- /// <value>The dashboard source path.</value>
+ /// <value>The dashboard source path, or null if the default path should be used.</value>
public string DashboardSourcePath { get; set; }
/// <summary>