Merge pull request #2601 from mark-monteiro/support-running-without-web-content
Support Running Server Without Web Content
This commit is contained in:
commit
07ea120ba9
|
@ -5,7 +5,7 @@ using MediaBrowser.Common.Configuration;
|
||||||
namespace Emby.Server.Implementations.AppBase
|
namespace Emby.Server.Implementations.AppBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
/// This can be subclassed to add application-specific paths.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BaseApplicationPaths : IApplicationPaths
|
public abstract class BaseApplicationPaths : IApplicationPaths
|
||||||
|
@ -37,10 +37,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||||
/// <value>The program data path.</value>
|
/// <value>The program data path.</value>
|
||||||
public string ProgramDataPath { get; }
|
public string ProgramDataPath { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets the path to the web UI resources folder.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The web UI resources path.</value>
|
|
||||||
public string WebPath { get; }
|
public string WebPath { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -235,11 +235,6 @@ namespace Emby.Server.Implementations
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int HttpsPort { get; private set; }
|
public int HttpsPort { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the content root for the webhost.
|
|
||||||
/// </summary>
|
|
||||||
public string ContentRoot { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the server configuration manager.
|
/// Gets the server configuration manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -612,13 +607,7 @@ namespace Emby.Server.Implementations
|
||||||
|
|
||||||
DiscoverTypes();
|
DiscoverTypes();
|
||||||
|
|
||||||
await RegisterResources(serviceCollection, startupConfig).ConfigureAwait(false);
|
await RegisterServices(serviceCollection, startupConfig).ConfigureAwait(false);
|
||||||
|
|
||||||
ContentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath;
|
|
||||||
if (string.IsNullOrEmpty(ContentRoot))
|
|
||||||
{
|
|
||||||
ContentRoot = ServerConfigurationManager.ApplicationPaths.WebPath;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next)
|
public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next)
|
||||||
|
@ -649,9 +638,9 @@ namespace Emby.Server.Implementations
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers resources that classes will depend on
|
/// Registers services/resources with the service collection that will be available via DI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected async Task RegisterResources(IServiceCollection serviceCollection, IConfiguration startupConfig)
|
protected async Task RegisterServices(IServiceCollection serviceCollection, IConfiguration startupConfig)
|
||||||
{
|
{
|
||||||
serviceCollection.AddMemoryCache();
|
serviceCollection.AddMemoryCache();
|
||||||
|
|
||||||
|
@ -769,20 +758,8 @@ namespace Emby.Server.Implementations
|
||||||
CertificateInfo = GetCertificateInfo(true);
|
CertificateInfo = GetCertificateInfo(true);
|
||||||
Certificate = GetCertificate(CertificateInfo);
|
Certificate = GetCertificate(CertificateInfo);
|
||||||
|
|
||||||
HttpServer = new HttpListenerHost(
|
serviceCollection.AddSingleton<IHttpListener, WebSocketSharpListener>();
|
||||||
this,
|
serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
|
||||||
LoggerFactory.CreateLogger<HttpListenerHost>(),
|
|
||||||
ServerConfigurationManager,
|
|
||||||
startupConfig,
|
|
||||||
NetworkManager,
|
|
||||||
JsonSerializer,
|
|
||||||
XmlSerializer,
|
|
||||||
CreateHttpListener())
|
|
||||||
{
|
|
||||||
GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading")
|
|
||||||
};
|
|
||||||
|
|
||||||
serviceCollection.AddSingleton(HttpServer);
|
|
||||||
|
|
||||||
ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger<ImageProcessor>(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
|
ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger<ImageProcessor>(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
|
||||||
serviceCollection.AddSingleton(ImageProcessor);
|
serviceCollection.AddSingleton(ImageProcessor);
|
||||||
|
@ -895,6 +872,14 @@ namespace Emby.Server.Implementations
|
||||||
((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
|
((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create services registered with the service container that need to be initialized at application startup.
|
||||||
|
/// </summary>
|
||||||
|
public void InitializeServices()
|
||||||
|
{
|
||||||
|
HttpServer = Resolve<IHttpServer>();
|
||||||
|
}
|
||||||
|
|
||||||
public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths)
|
public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths)
|
||||||
{
|
{
|
||||||
// Distinct these to prevent users from reporting problems that aren't actually problems
|
// 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<WebSocketSharpListener>());
|
|
||||||
|
|
||||||
private CertificateInfo GetCertificateInfo(bool generateCertificate)
|
private CertificateInfo GetCertificateInfo(bool generateCertificate)
|
||||||
{
|
{
|
||||||
// Custom cert
|
// Custom cert
|
||||||
|
|
|
@ -1,51 +1,48 @@
|
||||||
using System;
|
using System;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Browser
|
namespace Emby.Server.Implementations.Browser
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class BrowserLauncher.
|
/// Assists in opening application URLs in an external browser.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class BrowserLauncher
|
public static class BrowserLauncher
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens the dashboard page.
|
/// Opens the home page of the web client.
|
||||||
/// </summary>
|
|
||||||
/// <param name="page">The page.</param>
|
|
||||||
/// <param name="appHost">The app host.</param>
|
|
||||||
private static void OpenDashboardPage(string page, IServerApplicationHost appHost)
|
|
||||||
{
|
|
||||||
var url = appHost.GetLocalApiUrl("localhost") + "/web/" + page;
|
|
||||||
|
|
||||||
OpenUrl(appHost, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Opens the web client.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="appHost">The app host.</param>
|
/// <param name="appHost">The app host.</param>
|
||||||
public static void OpenWebApp(IServerApplicationHost appHost)
|
public static void OpenWebApp(IServerApplicationHost appHost)
|
||||||
{
|
{
|
||||||
OpenDashboardPage("index.html", appHost);
|
TryOpenUrl(appHost, "/web/index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens the URL.
|
/// Opens the swagger API page.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="appHost">The application host instance.</param>
|
/// <param name="appHost">The app host.</param>
|
||||||
|
public static void OpenSwaggerPage(IServerApplicationHost appHost)
|
||||||
|
{
|
||||||
|
TryOpenUrl(appHost, "/swagger/index.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the specified URL in an external browser window. Any exceptions will be logged, but ignored.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="appHost">The application host.</param>
|
||||||
/// <param name="url">The URL.</param>
|
/// <param name="url">The URL.</param>
|
||||||
private static void OpenUrl(IServerApplicationHost appHost, string url)
|
private static void TryOpenUrl(IServerApplicationHost appHost, string url)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
appHost.LaunchUrl(url);
|
string baseUrl = appHost.GetLocalApiUrl("localhost");
|
||||||
|
appHost.LaunchUrl(baseUrl + url);
|
||||||
}
|
}
|
||||||
catch (NotSupportedException)
|
catch (Exception ex)
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
{
|
||||||
|
var logger = appHost.Resolve<ILogger>();
|
||||||
|
logger?.LogError(ex, "Failed to open browser window with URL {URL}", url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Emby.Server.Implementations.HttpServer;
|
||||||
|
using MediaBrowser.Providers.Music;
|
||||||
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
|
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations
|
namespace Emby.Server.Implementations
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Static class containing the default configuration options for the web server.
|
||||||
|
/// </summary>
|
||||||
public static class ConfigurationOptions
|
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" },
|
{ HostWebClientKey, bool.TrueString },
|
||||||
|
{ HttpListenerHost.DefaultRedirectKey, "web/index.html" },
|
||||||
{ FfmpegProbeSizeKey, "1G" },
|
{ FfmpegProbeSizeKey, "1G" },
|
||||||
{ FfmpegAnalyzeDurationKey, "200M" },
|
{ FfmpegAnalyzeDurationKey, "200M" },
|
||||||
{ PlaylistsAllowDuplicatesKey, bool.TrueString }
|
{ PlaylistsAllowDuplicatesKey, bool.TrueString }
|
||||||
|
|
|
@ -2,7 +2,9 @@ using System.Threading.Tasks;
|
||||||
using Emby.Server.Implementations.Browser;
|
using Emby.Server.Implementations.Browser;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
using MediaBrowser.Controller.Extensions;
|
||||||
using MediaBrowser.Controller.Plugins;
|
using MediaBrowser.Controller.Plugins;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.EntryPoints
|
namespace Emby.Server.Implementations.EntryPoints
|
||||||
{
|
{
|
||||||
|
@ -11,10 +13,8 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class StartupWizard : IServerEntryPoint
|
public sealed class StartupWizard : IServerEntryPoint
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The app host.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IServerApplicationHost _appHost;
|
private readonly IServerApplicationHost _appHost;
|
||||||
|
private readonly IConfiguration _appConfig;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -22,9 +22,10 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="appHost">The application host.</param>
|
/// <param name="appHost">The application host.</param>
|
||||||
/// <param name="config">The configuration manager.</param>
|
/// <param name="config">The configuration manager.</param>
|
||||||
public StartupWizard(IServerApplicationHost appHost, IServerConfigurationManager config)
|
public StartupWizard(IServerApplicationHost appHost, IConfiguration appConfig, IServerConfigurationManager config)
|
||||||
{
|
{
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
|
_appConfig = appConfig;
|
||||||
_config = config;
|
_config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +37,11 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_config.Configuration.IsStartupWizardCompleted)
|
if (!_appConfig.HostWebClient())
|
||||||
|
{
|
||||||
|
BrowserLauncher.OpenSwaggerPage(_appHost);
|
||||||
|
}
|
||||||
|
else if (!_config.Configuration.IsStartupWizardCompleted)
|
||||||
{
|
{
|
||||||
BrowserLauncher.OpenWebApp(_appHost);
|
BrowserLauncher.OpenWebApp(_appHost);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
@ -29,6 +30,12 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
{
|
{
|
||||||
public class HttpListenerHost : IHttpServer, IDisposable
|
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 ILogger _logger;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly INetworkManager _networkManager;
|
private readonly INetworkManager _networkManager;
|
||||||
|
@ -52,12 +59,13 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
INetworkManager networkManager,
|
INetworkManager networkManager,
|
||||||
IJsonSerializer jsonSerializer,
|
IJsonSerializer jsonSerializer,
|
||||||
IXmlSerializer xmlSerializer,
|
IXmlSerializer xmlSerializer,
|
||||||
IHttpListener socketListener)
|
IHttpListener socketListener,
|
||||||
|
ILocalizationManager localizationManager)
|
||||||
{
|
{
|
||||||
_appHost = applicationHost;
|
_appHost = applicationHost;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_config = config;
|
_config = config;
|
||||||
_defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
|
_defaultRedirectPath = configuration[DefaultRedirectKey];
|
||||||
_baseUrlPrefix = _config.Configuration.BaseUrl;
|
_baseUrlPrefix = _config.Configuration.BaseUrl;
|
||||||
_networkManager = networkManager;
|
_networkManager = networkManager;
|
||||||
_jsonSerializer = jsonSerializer;
|
_jsonSerializer = jsonSerializer;
|
||||||
|
@ -69,6 +77,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
|
|
||||||
Instance = this;
|
Instance = this;
|
||||||
ResponseFilters = Array.Empty<Action<IRequest, HttpResponse, object>>();
|
ResponseFilters = Array.Empty<Action<IRequest, HttpResponse, object>>();
|
||||||
|
GlobalResponse = localizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
|
public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
|
||||||
|
|
|
@ -13,12 +13,14 @@ using System.Threading.Tasks;
|
||||||
using CommandLine;
|
using CommandLine;
|
||||||
using Emby.Drawing;
|
using Emby.Drawing;
|
||||||
using Emby.Server.Implementations;
|
using Emby.Server.Implementations;
|
||||||
|
using Emby.Server.Implementations.HttpServer;
|
||||||
using Emby.Server.Implementations.IO;
|
using Emby.Server.Implementations.IO;
|
||||||
using Emby.Server.Implementations.Networking;
|
using Emby.Server.Implementations.Networking;
|
||||||
using Jellyfin.Drawing.Skia;
|
using Jellyfin.Drawing.Skia;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Controller.Extensions;
|
||||||
|
using MediaBrowser.WebDashboard.Api;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
@ -112,9 +114,10 @@ namespace Jellyfin.Server
|
||||||
// $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
|
// $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
|
||||||
Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
|
Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
|
||||||
|
|
||||||
// Create an instance of the application configuration to use for application startup
|
|
||||||
await InitLoggingConfigFile(appPaths).ConfigureAwait(false);
|
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
|
// Initialize logging framework
|
||||||
InitializeLoggingFramework(startupConfig, appPaths);
|
InitializeLoggingFramework(startupConfig, appPaths);
|
||||||
|
@ -183,15 +186,31 @@ namespace Jellyfin.Server
|
||||||
new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
|
new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
|
||||||
GetImageEncoder(appPaths),
|
GetImageEncoder(appPaths),
|
||||||
new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()));
|
new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()));
|
||||||
|
|
||||||
try
|
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();
|
ServiceCollection serviceCollection = new ServiceCollection();
|
||||||
await appHost.InitAsync(serviceCollection, startupConfig).ConfigureAwait(false);
|
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.ServiceProvider = webHost.Services;
|
||||||
|
appHost.InitializeServices();
|
||||||
appHost.FindParts();
|
appHost.FindParts();
|
||||||
Migrations.MigrationRunner.Run(appHost, _loggerFactory);
|
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()
|
return new WebHostBuilder()
|
||||||
.UseKestrel(options =>
|
.UseKestrel(options =>
|
||||||
|
@ -273,9 +297,8 @@ namespace Jellyfin.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(appPaths))
|
.ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(commandLineOpts, appPaths, startupConfig))
|
||||||
.UseSerilog()
|
.UseSerilog()
|
||||||
.UseContentRoot(appHost.ContentRoot)
|
|
||||||
.ConfigureServices(services =>
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
// Merge the external ServiceCollection into ASP.NET DI
|
// Merge the external ServiceCollection into ASP.NET DI
|
||||||
|
@ -398,9 +421,8 @@ namespace Jellyfin.Server
|
||||||
// webDir
|
// webDir
|
||||||
// IF --webdir
|
// IF --webdir
|
||||||
// ELSE IF $JELLYFIN_WEB_DIR
|
// ELSE IF $JELLYFIN_WEB_DIR
|
||||||
// ELSE use <bindir>/jellyfin-web
|
// ELSE <bindir>/jellyfin-web
|
||||||
var webDir = options.WebDir;
|
var webDir = options.WebDir;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(webDir))
|
if (string.IsNullOrEmpty(webDir))
|
||||||
{
|
{
|
||||||
webDir = Environment.GetEnvironmentVariable("JELLYFIN_WEB_DIR");
|
webDir = Environment.GetEnvironmentVariable("JELLYFIN_WEB_DIR");
|
||||||
|
@ -471,21 +493,33 @@ namespace Jellyfin.Server
|
||||||
await resource.CopyToAsync(dst).ConfigureAwait(false);
|
await resource.CopyToAsync(dst).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IConfiguration CreateAppConfiguration(IApplicationPaths appPaths)
|
private static IConfiguration CreateAppConfiguration(StartupOptions commandLineOpts, IApplicationPaths appPaths)
|
||||||
{
|
{
|
||||||
return new ConfigurationBuilder()
|
return new ConfigurationBuilder()
|
||||||
.ConfigureAppConfiguration(appPaths)
|
.ConfigureAppConfiguration(commandLineOpts, appPaths)
|
||||||
.Build();
|
.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
|
return config
|
||||||
.SetBasePath(appPaths.ConfigurationDirectoryPath)
|
.SetBasePath(appPaths.ConfigurationDirectoryPath)
|
||||||
.AddInMemoryCollection(ConfigurationOptions.Configuration)
|
.AddInMemoryCollection(inMemoryDefaultConfig)
|
||||||
.AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true)
|
.AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true)
|
||||||
.AddJsonFile(LoggingConfigFileSystem, optional: true, reloadOnChange: true)
|
.AddJsonFile(LoggingConfigFileSystem, optional: true, reloadOnChange: true)
|
||||||
.AddEnvironmentVariables("JELLYFIN_");
|
.AddEnvironmentVariables("JELLYFIN_")
|
||||||
|
.AddInMemoryCollection(commandLineOpts.ConvertToConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
11
Jellyfin.Server/Properties/launchSettings.json
Normal file
11
Jellyfin.Server/Properties/launchSettings.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"Jellyfin.Server": {
|
||||||
|
"commandName": "Project"
|
||||||
|
},
|
||||||
|
"Jellyfin.Server (nowebclient)": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "--nowebclient"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using CommandLine;
|
using CommandLine;
|
||||||
using Emby.Server.Implementations;
|
using Emby.Server.Implementations;
|
||||||
|
using MediaBrowser.Controller.Extensions;
|
||||||
|
|
||||||
namespace Jellyfin.Server
|
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.).")]
|
[Option('d', "datadir", Required = false, HelpText = "Path to use for the data folder (database files, etc.).")]
|
||||||
public string? DataDir { get; set; }
|
public string? DataDir { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the server should not host the web client.
|
||||||
|
/// </summary>
|
||||||
|
[Option("nowebclient", Required = false, HelpText = "Indicates that the web server should not host the web client.")]
|
||||||
|
public bool NoWebClient { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the path to the web directory.
|
/// Gets or sets the path to the web directory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -66,5 +75,21 @@ namespace Jellyfin.Server
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[Option("restartargs", Required = false, HelpText = "Arguments for restart script.")]
|
[Option("restartargs", Required = false, HelpText = "Arguments for restart script.")]
|
||||||
public string? RestartArgs { get; set; }
|
public string? RestartArgs { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the command line options as a dictionary that can be used in the .NET configuration system.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The configuration dictionary.</returns>
|
||||||
|
public Dictionary<string, string> ConvertToConfig()
|
||||||
|
{
|
||||||
|
var config = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
if (NoWebClient)
|
||||||
|
{
|
||||||
|
config.Add(ConfigurationExtensions.HostWebClientKey, bool.FalseString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
using MediaBrowser.Model.Configuration;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Configuration
|
namespace MediaBrowser.Common.Configuration
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -12,9 +14,12 @@ namespace MediaBrowser.Common.Configuration
|
||||||
string ProgramDataPath { get; }
|
string ProgramDataPath { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the path to the web UI resources folder
|
/// Gets the path to the web UI resources folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The web UI resources path.</value>
|
/// <remarks>
|
||||||
|
/// This value is not relevant if the server is configured to not host any static web content. Additionally,
|
||||||
|
/// the value for <see cref="ServerConfiguration.DashboardSourcePath"/> takes precedence over this one.
|
||||||
|
/// </remarks>
|
||||||
string WebPath { get; }
|
string WebPath { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Extensions
|
namespace MediaBrowser.Controller.Extensions
|
||||||
|
@ -7,6 +8,11 @@ namespace MediaBrowser.Controller.Extensions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ConfigurationExtensions
|
public static class ConfigurationExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The key for a setting that indicates whether the application should host web client content.
|
||||||
|
/// </summary>
|
||||||
|
public const string HostWebClientKey = "hostwebclient";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The key for the FFmpeg probe size option.
|
/// The key for the FFmpeg probe size option.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -22,6 +28,15 @@ namespace MediaBrowser.Controller.Extensions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string PlaylistsAllowDuplicatesKey = "playlists:allowDuplicates";
|
public const string PlaylistsAllowDuplicatesKey = "playlists:allowDuplicates";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the application should 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 HostWebClient(this IConfiguration configuration)
|
||||||
|
=> configuration.GetValue<bool>(HostWebClientKey);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the FFmpeg probe size from the <see cref="IConfiguration" />.
|
/// Gets the FFmpeg probe size from the <see cref="IConfiguration" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -82,6 +82,11 @@ namespace MediaBrowser.Controller
|
||||||
/// <returns>The local API URL.</returns>
|
/// <returns>The local API URL.</returns>
|
||||||
string GetLocalApiUrl(IPAddress address);
|
string GetLocalApiUrl(IPAddress address);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Open a URL in an external browser window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="url">The URL to open.</param>
|
||||||
|
/// <exception cref="NotSupportedException"><see cref="CanLaunchWebBrowser"/> is false.</exception>
|
||||||
void LaunchUrl(string url);
|
void LaunchUrl(string url);
|
||||||
|
|
||||||
void EnableLoopback(string appName);
|
void EnableLoopback(string appName);
|
||||||
|
|
|
@ -148,9 +148,9 @@ namespace MediaBrowser.Model.Configuration
|
||||||
public bool EnableDashboardResponseCaching { get; set; }
|
public bool EnableDashboardResponseCaching { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows the dashboard to be served from a custom path.
|
/// Gets or sets a custom path to serve the dashboard from.
|
||||||
/// </summary>
|
/// </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; }
|
public string DashboardSourcePath { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -12,12 +12,14 @@ using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Plugins;
|
using MediaBrowser.Common.Plugins;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
using MediaBrowser.Controller.Extensions;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Controller.Plugins;
|
using MediaBrowser.Controller.Plugins;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using MediaBrowser.Model.Plugins;
|
using MediaBrowser.Model.Plugins;
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MediaBrowser.WebDashboard.Api
|
namespace MediaBrowser.WebDashboard.Api
|
||||||
|
@ -102,6 +104,7 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
/// <value>The HTTP result factory.</value>
|
/// <value>The HTTP result factory.</value>
|
||||||
private readonly IHttpResultFactory _resultFactory;
|
private readonly IHttpResultFactory _resultFactory;
|
||||||
private readonly IServerApplicationHost _appHost;
|
private readonly IServerApplicationHost _appHost;
|
||||||
|
private readonly IConfiguration _appConfig;
|
||||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly IResourceFileManager _resourceFileManager;
|
private readonly IResourceFileManager _resourceFileManager;
|
||||||
|
@ -111,6 +114,7 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="logger">The logger.</param>
|
/// <param name="logger">The logger.</param>
|
||||||
/// <param name="appHost">The application host.</param>
|
/// <param name="appHost">The application host.</param>
|
||||||
|
/// <param name="appConfig">The application configuration.</param>
|
||||||
/// <param name="resourceFileManager">The resource file manager.</param>
|
/// <param name="resourceFileManager">The resource file manager.</param>
|
||||||
/// <param name="serverConfigurationManager">The server configuration manager.</param>
|
/// <param name="serverConfigurationManager">The server configuration manager.</param>
|
||||||
/// <param name="fileSystem">The file system.</param>
|
/// <param name="fileSystem">The file system.</param>
|
||||||
|
@ -118,6 +122,7 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
public DashboardService(
|
public DashboardService(
|
||||||
ILogger<DashboardService> logger,
|
ILogger<DashboardService> logger,
|
||||||
IServerApplicationHost appHost,
|
IServerApplicationHost appHost,
|
||||||
|
IConfiguration appConfig,
|
||||||
IResourceFileManager resourceFileManager,
|
IResourceFileManager resourceFileManager,
|
||||||
IServerConfigurationManager serverConfigurationManager,
|
IServerConfigurationManager serverConfigurationManager,
|
||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
|
@ -125,6 +130,7 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
|
_appConfig = appConfig;
|
||||||
_resourceFileManager = resourceFileManager;
|
_resourceFileManager = resourceFileManager;
|
||||||
_serverConfigurationManager = serverConfigurationManager;
|
_serverConfigurationManager = serverConfigurationManager;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
|
@ -138,20 +144,30 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
public IRequest Request { get; set; }
|
public IRequest Request { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The path for the web interface.</value>
|
public string DashboardUIPath => GetDashboardUIPath(_appConfig, _serverConfigurationManager);
|
||||||
public string DashboardUIPath
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path of the directory containing the static web interface content.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="appConfig">The app configuration.</param>
|
||||||
|
/// <param name="serverConfigManager">The server configuration manager.</param>
|
||||||
|
/// <returns>The directory path, or null if the server is not hosting the web client.</returns>
|
||||||
|
public static string GetDashboardUIPath(IConfiguration appConfig, IServerConfigurationManager serverConfigManager)
|
||||||
{
|
{
|
||||||
get
|
if (!appConfig.HostWebClient())
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(_serverConfigurationManager.Configuration.DashboardSourcePath))
|
return null;
|
||||||
{
|
|
||||||
return _serverConfigurationManager.Configuration.DashboardSourcePath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _serverConfigurationManager.ApplicationPaths.WebPath;
|
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")]
|
[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"), () => 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();
|
throw new ResourceNotFoundException();
|
||||||
|
@ -307,6 +323,11 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
/// <returns>System.Object.</returns>
|
/// <returns>System.Object.</returns>
|
||||||
public async Task<object> Get(GetDashboardResource request)
|
public async Task<object> Get(GetDashboardResource request)
|
||||||
{
|
{
|
||||||
|
if (!_appConfig.HostWebClient() || DashboardUIPath == null)
|
||||||
|
{
|
||||||
|
throw new ResourceNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
var path = request.ResourceName;
|
var path = request.ResourceName;
|
||||||
|
|
||||||
var contentType = MimeTypes.GetMimeType(path);
|
var contentType = MimeTypes.GetMimeType(path);
|
||||||
|
@ -378,6 +399,11 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
|
|
||||||
public async Task<object> Get(GetDashboardPackage request)
|
public async Task<object> Get(GetDashboardPackage request)
|
||||||
{
|
{
|
||||||
|
if (!_appConfig.HostWebClient() || DashboardUIPath == null)
|
||||||
|
{
|
||||||
|
throw new ResourceNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
var mode = request.Mode;
|
var mode = request.Mode;
|
||||||
|
|
||||||
var inputPath = string.IsNullOrWhiteSpace(mode) ?
|
var inputPath = string.IsNullOrWhiteSpace(mode) ?
|
||||||
|
|
|
@ -31,7 +31,8 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
|
|
||||||
if (resourceStream != null && IsCoreHtml(virtualPath))
|
if (resourceStream != null && IsCoreHtml(virtualPath))
|
||||||
{
|
{
|
||||||
resourceStream = await ModifyHtml(virtualPath, resourceStream, mode, appVersion, localizationCulture).ConfigureAwait(false);
|
bool isMainIndexPage = string.Equals(virtualPath, "index.html", StringComparison.OrdinalIgnoreCase);
|
||||||
|
resourceStream = await ModifyHtml(isMainIndexPage, resourceStream, mode, appVersion, localizationCulture).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return resourceStream;
|
return resourceStream;
|
||||||
|
@ -47,16 +48,25 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
return string.Equals(Path.GetExtension(path), ".html", StringComparison.OrdinalIgnoreCase);
|
return string.Equals(Path.GetExtension(path), ".html", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modifies the HTML by adding common meta tags, css and js.
|
/// <summary>
|
||||||
public async Task<Stream> ModifyHtml(
|
/// Modifies the source HTML stream by adding common meta tags, css and js.
|
||||||
string path,
|
/// </summary>
|
||||||
|
/// <param name="isMainIndexPage">True if the stream contains content for the main index page.</param>
|
||||||
|
/// <param name="sourceStream">The stream whose content should be modified.</param>
|
||||||
|
/// <param name="mode">The client mode ('cordova', 'android', etc).</param>
|
||||||
|
/// <param name="appVersion">The application version.</param>
|
||||||
|
/// <param name="localizationCulture">The localization culture.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the async operation to read and modify the input stream.
|
||||||
|
/// The task result contains a stream containing the modified HTML content.
|
||||||
|
/// </returns>
|
||||||
|
public static async Task<Stream> ModifyHtml(
|
||||||
|
bool isMainIndexPage,
|
||||||
Stream sourceStream,
|
Stream sourceStream,
|
||||||
string mode,
|
string mode,
|
||||||
string appVersion,
|
string appVersion,
|
||||||
string localizationCulture)
|
string localizationCulture)
|
||||||
{
|
{
|
||||||
var isMainIndexPage = string.Equals(path, "index.html", StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
string html;
|
string html;
|
||||||
using (var reader = new StreamReader(sourceStream, Encoding.UTF8))
|
using (var reader = new StreamReader(sourceStream, Encoding.UTF8))
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user