diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index c3cdcc2222..bc47817438 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Common.Configuration;
namespace Emby.Server.Implementations.AppBase
{
///
- /// Provides a base class to hold common application paths used by both the Ui and Server.
+ /// Provides a base class to hold common application paths used by both the UI and Server.
/// This can be subclassed to add application-specific paths.
///
public abstract class BaseApplicationPaths : IApplicationPaths
@@ -37,10 +37,7 @@ namespace Emby.Server.Implementations.AppBase
/// The program data path.
public string ProgramDataPath { get; }
- ///
- /// Gets the path to the web UI resources folder.
- ///
- /// The web UI resources path.
+ ///
public string WebPath { get; }
///
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index f9cfae6922..4fbc933a81 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -235,11 +235,6 @@ namespace Emby.Server.Implementations
///
public int HttpsPort { get; private set; }
- ///
- /// Gets the content root for the webhost.
- ///
- public string ContentRoot { get; private set; }
-
///
/// Gets the server configuration manager.
///
@@ -612,13 +607,7 @@ namespace Emby.Server.Implementations
DiscoverTypes();
- await RegisterResources(serviceCollection, startupConfig).ConfigureAwait(false);
-
- ContentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath;
- if (string.IsNullOrEmpty(ContentRoot))
- {
- ContentRoot = ServerConfigurationManager.ApplicationPaths.WebPath;
- }
+ await RegisterServices(serviceCollection, startupConfig).ConfigureAwait(false);
}
public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func next)
@@ -649,9 +638,9 @@ namespace Emby.Server.Implementations
}
///
- /// Registers resources that classes will depend on
+ /// Registers services/resources with the service collection that will be available via DI.
///
- protected async Task RegisterResources(IServiceCollection serviceCollection, IConfiguration startupConfig)
+ protected async Task RegisterServices(IServiceCollection serviceCollection, IConfiguration startupConfig)
{
serviceCollection.AddMemoryCache();
@@ -769,20 +758,8 @@ namespace Emby.Server.Implementations
CertificateInfo = GetCertificateInfo(true);
Certificate = GetCertificate(CertificateInfo);
- HttpServer = new HttpListenerHost(
- this,
- LoggerFactory.CreateLogger(),
- ServerConfigurationManager,
- startupConfig,
- NetworkManager,
- JsonSerializer,
- XmlSerializer,
- CreateHttpListener())
- {
- GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading")
- };
-
- serviceCollection.AddSingleton(HttpServer);
+ serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
serviceCollection.AddSingleton(ImageProcessor);
@@ -895,6 +872,14 @@ namespace Emby.Server.Implementations
((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
}
+ ///
+ /// Create services registered with the service container that need to be initialized at application startup.
+ ///
+ public void InitializeServices()
+ {
+ HttpServer = Resolve();
+ }
+
public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths)
{
// Distinct these to prevent users from reporting problems that aren't actually problems
@@ -1212,8 +1197,6 @@ namespace Emby.Server.Implementations
});
}
- protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(LoggerFactory.CreateLogger());
-
private CertificateInfo GetCertificateInfo(bool generateCertificate)
{
// Custom cert
diff --git a/Emby.Server.Implementations/Browser/BrowserLauncher.cs b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
index f5da0d0183..96096e142a 100644
--- a/Emby.Server.Implementations/Browser/BrowserLauncher.cs
+++ b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
@@ -1,51 +1,48 @@
using System;
using MediaBrowser.Controller;
+using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Browser
{
///
- /// Class BrowserLauncher.
+ /// Assists in opening application URLs in an external browser.
///
public static class BrowserLauncher
{
///
- /// Opens the dashboard page.
- ///
- /// The page.
- /// The app host.
- private static void OpenDashboardPage(string page, IServerApplicationHost appHost)
- {
- var url = appHost.GetLocalApiUrl("localhost") + "/web/" + page;
-
- OpenUrl(appHost, url);
- }
-
- ///
- /// Opens the web client.
+ /// Opens the home page of the web client.
///
/// The app host.
public static void OpenWebApp(IServerApplicationHost appHost)
{
- OpenDashboardPage("index.html", appHost);
+ TryOpenUrl(appHost, "/web/index.html");
}
///
- /// Opens the URL.
+ /// Opens the swagger API page.
///
- /// The application host instance.
+ /// The app host.
+ public static void OpenSwaggerPage(IServerApplicationHost appHost)
+ {
+ TryOpenUrl(appHost, "/swagger/index.html");
+ }
+
+ ///
+ /// Opens the specified URL in an external browser window. Any exceptions will be logged, but ignored.
+ ///
+ /// The application host.
/// The URL.
- private static void OpenUrl(IServerApplicationHost appHost, string url)
+ private static void TryOpenUrl(IServerApplicationHost appHost, string url)
{
try
{
- appHost.LaunchUrl(url);
+ string baseUrl = appHost.GetLocalApiUrl("localhost");
+ appHost.LaunchUrl(baseUrl + url);
}
- catch (NotSupportedException)
- {
-
- }
- catch (Exception)
+ catch (Exception ex)
{
+ var logger = appHost.Resolve();
+ logger?.LogError(ex, "Failed to open browser window with URL {URL}", url);
}
}
}
diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs
index 31fb5ca58c..4574a64fdb 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
{
+ ///
+ /// Static class containing the default configuration options for the web server.
+ ///
public static class ConfigurationOptions
{
- public static Dictionary Configuration => new Dictionary
+ ///
+ /// Gets a new copy of the default configuration options.
+ ///
+ public static Dictionary DefaultConfiguration => new Dictionary
{
- { "HttpListenerHost:DefaultRedirectPath", "web/index.html" },
+ { HostWebClientKey, bool.TrueString },
+ { HttpListenerHost.DefaultRedirectKey, "web/index.html" },
{ FfmpegProbeSizeKey, "1G" },
{ FfmpegAnalyzeDurationKey, "200M" },
{ PlaylistsAllowDuplicatesKey, bool.TrueString }
diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
index 5f2d629fea..8e97719311 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
{
@@ -11,10 +13,8 @@ namespace Emby.Server.Implementations.EntryPoints
///
public sealed class StartupWizard : IServerEntryPoint
{
- ///
- /// The app host.
- ///
private readonly IServerApplicationHost _appHost;
+ private readonly IConfiguration _appConfig;
private readonly IServerConfigurationManager _config;
///
@@ -22,9 +22,10 @@ namespace Emby.Server.Implementations.EntryPoints
///
/// The application host.
/// The configuration manager.
- public StartupWizard(IServerApplicationHost appHost, IServerConfigurationManager config)
+ public StartupWizard(IServerApplicationHost appHost, IConfiguration appConfig, IServerConfigurationManager config)
{
_appHost = appHost;
+ _appConfig = appConfig;
_config = config;
}
@@ -36,7 +37,11 @@ namespace Emby.Server.Implementations.EntryPoints
return Task.CompletedTask;
}
- if (!_config.Configuration.IsStartupWizardCompleted)
+ if (!_appConfig.HostWebClient())
+ {
+ 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 93572b8bfe..655130fcfc 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -17,6 +17,7 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
@@ -29,6 +30,12 @@ namespace Emby.Server.Implementations.HttpServer
{
public class HttpListenerHost : IHttpServer, IDisposable
{
+ ///
+ /// The key for a setting that specifies the default redirect path
+ /// to use for requests where the URL base prefix is invalid or missing.
+ ///
+ public const string DefaultRedirectKey = "HttpListenerHost:DefaultRedirectPath";
+
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
private readonly INetworkManager _networkManager;
@@ -52,12 +59,13 @@ namespace Emby.Server.Implementations.HttpServer
INetworkManager networkManager,
IJsonSerializer jsonSerializer,
IXmlSerializer xmlSerializer,
- IHttpListener socketListener)
+ IHttpListener socketListener,
+ ILocalizationManager localizationManager)
{
_appHost = applicationHost;
_logger = logger;
_config = config;
- _defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
+ _defaultRedirectPath = configuration[DefaultRedirectKey];
_baseUrlPrefix = _config.Configuration.BaseUrl;
_networkManager = networkManager;
_jsonSerializer = jsonSerializer;
@@ -69,6 +77,7 @@ namespace Emby.Server.Implementations.HttpServer
Instance = this;
ResponseFilters = Array.Empty>();
+ GlobalResponse = localizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
}
public event EventHandler> WebSocketConnected;
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index e9e852349c..2939d33782 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -13,12 +13,14 @@ 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 MediaBrowser.WebDashboard.Api;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -112,9 +114,10 @@ namespace Jellyfin.Server
// $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
- // Create an instance of the application configuration to use for application startup
await InitLoggingConfigFile(appPaths).ConfigureAwait(false);
- IConfiguration startupConfig = CreateAppConfiguration(appPaths);
+
+ // Create an instance of the application configuration to use for application startup
+ IConfiguration startupConfig = CreateAppConfiguration(options, appPaths);
// Initialize logging framework
InitializeLoggingFramework(startupConfig, appPaths);
@@ -183,15 +186,31 @@ namespace Jellyfin.Server
new ManagedFileSystem(_loggerFactory.CreateLogger(), appPaths),
GetImageEncoder(appPaths),
new NetworkManager(_loggerFactory.CreateLogger()));
+
try
{
+ // If hosting the web client, validate the client content path
+ if (startupConfig.HostWebClient())
+ {
+ string webContentPath = DashboardService.GetDashboardUIPath(startupConfig, appHost.ServerConfigurationManager);
+ if (!Directory.Exists(webContentPath) || Directory.GetFiles(webContentPath).Length == 0)
+ {
+ throw new InvalidOperationException(
+ "The server is expected to host the web client, but the provided content directory is either " +
+ $"invalid or empty: {webContentPath}. If you do not want to host the web client with the " +
+ "server, you may set the '--nowebclient' command line flag, or set" +
+ $"'{MediaBrowser.Controller.Extensions.ConfigurationExtensions.HostWebClientKey}=false' in your config settings.");
+ }
+ }
+
ServiceCollection serviceCollection = new ServiceCollection();
await appHost.InitAsync(serviceCollection, startupConfig).ConfigureAwait(false);
- var webHost = CreateWebHostBuilder(appHost, serviceCollection, appPaths).Build();
+ var webHost = CreateWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build();
- // A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection.
+ // Re-use the web host service provider in the app host since ASP.NET doesn't allow a custom service collection.
appHost.ServiceProvider = webHost.Services;
+ appHost.InitializeServices();
appHost.FindParts();
Migrations.MigrationRunner.Run(appHost, _loggerFactory);
@@ -233,7 +252,12 @@ namespace Jellyfin.Server
}
}
- private static IWebHostBuilder CreateWebHostBuilder(ApplicationHost appHost, IServiceCollection serviceCollection, IApplicationPaths appPaths)
+ private static IWebHostBuilder CreateWebHostBuilder(
+ ApplicationHost appHost,
+ IServiceCollection serviceCollection,
+ StartupOptions commandLineOpts,
+ IConfiguration startupConfig,
+ IApplicationPaths appPaths)
{
return new WebHostBuilder()
.UseKestrel(options =>
@@ -273,9 +297,8 @@ namespace Jellyfin.Server
}
}
})
- .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(appPaths))
+ .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(commandLineOpts, appPaths, startupConfig))
.UseSerilog()
- .UseContentRoot(appHost.ContentRoot)
.ConfigureServices(services =>
{
// Merge the external ServiceCollection into ASP.NET DI
@@ -398,9 +421,8 @@ namespace Jellyfin.Server
// webDir
// IF --webdir
// ELSE IF $JELLYFIN_WEB_DIR
- // ELSE use /jellyfin-web
+ // ELSE /jellyfin-web
var webDir = options.WebDir;
-
if (string.IsNullOrEmpty(webDir))
{
webDir = Environment.GetEnvironmentVariable("JELLYFIN_WEB_DIR");
@@ -471,21 +493,33 @@ namespace Jellyfin.Server
await resource.CopyToAsync(dst).ConfigureAwait(false);
}
- private static IConfiguration CreateAppConfiguration(IApplicationPaths appPaths)
+ private static IConfiguration CreateAppConfiguration(StartupOptions commandLineOpts, IApplicationPaths appPaths)
{
return new ConfigurationBuilder()
- .ConfigureAppConfiguration(appPaths)
+ .ConfigureAppConfiguration(commandLineOpts, appPaths)
.Build();
}
- private static IConfigurationBuilder ConfigureAppConfiguration(this IConfigurationBuilder config, IApplicationPaths appPaths)
+ private static IConfigurationBuilder ConfigureAppConfiguration(
+ this IConfigurationBuilder config,
+ StartupOptions commandLineOpts,
+ IApplicationPaths appPaths,
+ IConfiguration? startupConfig = null)
{
+ // Use the swagger API page as the default redirect path if not hosting the web client
+ var inMemoryDefaultConfig = ConfigurationOptions.DefaultConfiguration;
+ if (startupConfig != null && !startupConfig.HostWebClient())
+ {
+ 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_");
+ .AddEnvironmentVariables("JELLYFIN_")
+ .AddInMemoryCollection(commandLineOpts.ConvertToConfig());
}
///
diff --git a/Jellyfin.Server/Properties/launchSettings.json b/Jellyfin.Server/Properties/launchSettings.json
new file mode 100644
index 0000000000..53d9fe1656
--- /dev/null
+++ b/Jellyfin.Server/Properties/launchSettings.json
@@ -0,0 +1,11 @@
+{
+ "profiles": {
+ "Jellyfin.Server": {
+ "commandName": "Project"
+ },
+ "Jellyfin.Server (nowebclient)": {
+ "commandName": "Project",
+ "commandLineArgs": "--nowebclient"
+ }
+ }
+}
diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs
index 1fb1c5af8e..c93577d3ea 100644
--- a/Jellyfin.Server/StartupOptions.cs
+++ b/Jellyfin.Server/StartupOptions.cs
@@ -1,5 +1,8 @@
+using System.Collections.Generic;
+using System.Globalization;
using CommandLine;
using Emby.Server.Implementations;
+using MediaBrowser.Controller.Extensions;
namespace Jellyfin.Server
{
@@ -15,6 +18,12 @@ namespace Jellyfin.Server
[Option('d', "datadir", Required = false, HelpText = "Path to use for the data folder (database files, etc.).")]
public string? DataDir { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the server should not host the web client.
+ ///
+ [Option("nowebclient", Required = false, HelpText = "Indicates that the web server should not host the web client.")]
+ public bool NoWebClient { get; set; }
+
///
/// Gets or sets the path to the web directory.
///
@@ -66,5 +75,21 @@ namespace Jellyfin.Server
///
[Option("restartargs", Required = false, HelpText = "Arguments for restart script.")]
public string? RestartArgs { get; set; }
+
+ ///
+ /// Gets the command line options as a dictionary that can be used in the .NET configuration system.
+ ///
+ /// The configuration dictionary.
+ public Dictionary ConvertToConfig()
+ {
+ var config = new Dictionary();
+
+ if (NoWebClient)
+ {
+ config.Add(ConfigurationExtensions.HostWebClientKey, bool.FalseString);
+ }
+
+ return config;
+ }
}
}
diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
index 5bdea7d8b3..870b90796c 100644
--- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs
+++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
@@ -1,3 +1,5 @@
+using MediaBrowser.Model.Configuration;
+
namespace MediaBrowser.Common.Configuration
{
///
@@ -12,9 +14,12 @@ namespace MediaBrowser.Common.Configuration
string ProgramDataPath { get; }
///
- /// Gets the path to the web UI resources folder
+ /// Gets the path to the web UI resources folder.
///
- /// The web UI resources path.
+ ///
+ /// This value is not relevant if the server is configured to not host any static web content. Additionally,
+ /// the value for takes precedence over this one.
+ ///
string WebPath { get; }
///
diff --git a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
index 48316499a4..c0043c0efa 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
@@ -7,6 +8,11 @@ namespace MediaBrowser.Controller.Extensions
///
public static class ConfigurationExtensions
{
+ ///
+ /// The key for a setting that indicates whether the application should host web client content.
+ ///
+ public const string HostWebClientKey = "hostwebclient";
+
///
/// The key for the FFmpeg probe size option.
///
@@ -22,6 +28,15 @@ namespace MediaBrowser.Controller.Extensions
///
public const string PlaylistsAllowDuplicatesKey = "playlists:allowDuplicates";
+ ///
+ /// Gets a value indicating whether the application should host static web content from the .
+ ///
+ /// The configuration to retrieve the value from.
+ /// The parsed config value.
+ /// The config value is not a valid bool string. See .
+ public static bool HostWebClient(this IConfiguration configuration)
+ => configuration.GetValue(HostWebClientKey);
+
///
/// Gets the FFmpeg probe size from the .
///
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index 25f0905eb8..608ffc61c2 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -82,6 +82,11 @@ namespace MediaBrowser.Controller
/// The local API URL.
string GetLocalApiUrl(IPAddress address);
+ ///
+ /// Open a URL in an external browser window.
+ ///
+ /// The URL to open.
+ /// is false.
void LaunchUrl(string url);
void EnableLoopback(string appName);
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index dfb5631807..3107ec2426 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -148,9 +148,9 @@ namespace MediaBrowser.Model.Configuration
public bool EnableDashboardResponseCaching { get; set; }
///
- /// Allows the dashboard to be served from a custom path.
+ /// Gets or sets a custom path to serve the dashboard from.
///
- /// The dashboard source path.
+ /// The dashboard source path, or null if the default path should be used.
public string DashboardSourcePath { get; set; }
///
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index 99d8d044f3..133a35527d 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -12,12 +12,14 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Services;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.WebDashboard.Api
@@ -102,6 +104,7 @@ namespace MediaBrowser.WebDashboard.Api
/// The HTTP result factory.
private readonly IHttpResultFactory _resultFactory;
private readonly IServerApplicationHost _appHost;
+ private readonly IConfiguration _appConfig;
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IFileSystem _fileSystem;
private readonly IResourceFileManager _resourceFileManager;
@@ -111,6 +114,7 @@ namespace MediaBrowser.WebDashboard.Api
///
/// The logger.
/// The application host.
+ /// The application configuration.
/// The resource file manager.
/// The server configuration manager.
/// The file system.
@@ -118,6 +122,7 @@ namespace MediaBrowser.WebDashboard.Api
public DashboardService(
ILogger logger,
IServerApplicationHost appHost,
+ IConfiguration appConfig,
IResourceFileManager resourceFileManager,
IServerConfigurationManager serverConfigurationManager,
IFileSystem fileSystem,
@@ -125,6 +130,7 @@ namespace MediaBrowser.WebDashboard.Api
{
_logger = logger;
_appHost = appHost;
+ _appConfig = appConfig;
_resourceFileManager = resourceFileManager;
_serverConfigurationManager = serverConfigurationManager;
_fileSystem = fileSystem;
@@ -138,20 +144,30 @@ namespace MediaBrowser.WebDashboard.Api
public IRequest Request { get; set; }
///
- /// Gets the path for the web interface.
+ /// Gets the path of the directory containing the static web interface content, or null if the server is not
+ /// hosting the web client.
///
- /// The path for the web interface.
- public string DashboardUIPath
- {
- get
- {
- if (!string.IsNullOrEmpty(_serverConfigurationManager.Configuration.DashboardSourcePath))
- {
- return _serverConfigurationManager.Configuration.DashboardSourcePath;
- }
+ public string DashboardUIPath => GetDashboardUIPath(_appConfig, _serverConfigurationManager);
- return _serverConfigurationManager.ApplicationPaths.WebPath;
+ ///
+ /// Gets the path of the directory containing the static web interface content.
+ ///
+ /// The app configuration.
+ /// The server configuration manager.
+ /// The directory path, or null if the server is not hosting the web client.
+ public static string GetDashboardUIPath(IConfiguration appConfig, IServerConfigurationManager serverConfigManager)
+ {
+ if (!appConfig.HostWebClient())
+ {
+ return null;
}
+
+ if (!string.IsNullOrEmpty(serverConfigManager.Configuration.DashboardSourcePath))
+ {
+ return serverConfigManager.Configuration.DashboardSourcePath;
+ }
+
+ return serverConfigManager.ApplicationPaths.WebPath;
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
@@ -209,7 +225,7 @@ namespace MediaBrowser.WebDashboard.Api
return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => Task.FromResult(stream));
}
- return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator(DashboardUIPath).ModifyHtml("dummy.html", stream, null, _appHost.ApplicationVersionString, null));
+ return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => PackageCreator.ModifyHtml(false, stream, null, _appHost.ApplicationVersionString, null));
}
throw new ResourceNotFoundException();
@@ -307,6 +323,11 @@ namespace MediaBrowser.WebDashboard.Api
/// System.Object.
public async Task