mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-04 01:11:55 +01:00
Merge remote-tracking branch 'upstream/master' into api-doc-css
This commit is contained in:
@@ -1,20 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Emby.Drawing;
|
||||
using Emby.Server.Implementations;
|
||||
using Jellyfin.Drawing.Skia;
|
||||
using Jellyfin.Server.Implementations;
|
||||
using Jellyfin.Server.Implementations.Activity;
|
||||
using Jellyfin.Server.Implementations.Events;
|
||||
using Jellyfin.Server.Implementations.Users;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Events;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Activity;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@@ -33,30 +33,33 @@ namespace Jellyfin.Server
|
||||
/// <param name="options">The <see cref="StartupOptions" /> to be used by the <see cref="CoreAppHost" />.</param>
|
||||
/// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
|
||||
/// <param name="networkManager">The <see cref="INetworkManager" /> to be used by the <see cref="CoreAppHost" />.</param>
|
||||
/// <param name="collection">The <see cref="IServiceCollection"/> to be used by the <see cref="CoreAppHost"/>.</param>
|
||||
public CoreAppHost(
|
||||
IServerApplicationPaths applicationPaths,
|
||||
ILoggerFactory loggerFactory,
|
||||
IStartupOptions options,
|
||||
IFileSystem fileSystem,
|
||||
INetworkManager networkManager)
|
||||
INetworkManager networkManager,
|
||||
IServiceCollection collection)
|
||||
: base(
|
||||
applicationPaths,
|
||||
loggerFactory,
|
||||
options,
|
||||
fileSystem,
|
||||
networkManager)
|
||||
networkManager,
|
||||
collection)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void RegisterServices(IServiceCollection serviceCollection)
|
||||
protected override void RegisterServices()
|
||||
{
|
||||
// Register an image encoder
|
||||
bool useSkiaEncoder = SkiaEncoder.IsNativeLibAvailable();
|
||||
Type imageEncoderType = useSkiaEncoder
|
||||
? typeof(SkiaEncoder)
|
||||
: typeof(NullImageEncoder);
|
||||
serviceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType);
|
||||
ServiceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType);
|
||||
|
||||
// Log a warning if the Skia encoder could not be used
|
||||
if (!useSkiaEncoder)
|
||||
@@ -71,13 +74,15 @@ namespace Jellyfin.Server
|
||||
// .UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"),
|
||||
// ServiceLifetime.Transient);
|
||||
|
||||
serviceCollection.AddSingleton<JellyfinDbProvider>();
|
||||
ServiceCollection.AddEventServices();
|
||||
ServiceCollection.AddSingleton<IEventManager, EventManager>();
|
||||
ServiceCollection.AddSingleton<JellyfinDbProvider>();
|
||||
|
||||
serviceCollection.AddSingleton<IActivityManager, ActivityManager>();
|
||||
serviceCollection.AddSingleton<IUserManager, UserManager>();
|
||||
serviceCollection.AddSingleton<IDisplayPreferencesManager, DisplayPreferencesManager>();
|
||||
ServiceCollection.AddSingleton<IActivityManager, ActivityManager>();
|
||||
ServiceCollection.AddSingleton<IUserManager, UserManager>();
|
||||
ServiceCollection.AddSingleton<IDisplayPreferencesManager, DisplayPreferencesManager>();
|
||||
|
||||
base.RegisterServices(serviceCollection);
|
||||
base.RegisterServices();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Jellyfin.Server.Middleware;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
@@ -48,5 +49,55 @@ namespace Jellyfin.Server.Extensions
|
||||
c.InjectStylesheet($"/{baseUrl}api-docs/redoc/custom.css");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds IP based access validation to the application pipeline.
|
||||
/// </summary>
|
||||
/// <param name="appBuilder">The application builder.</param>
|
||||
/// <returns>The updated application builder.</returns>
|
||||
public static IApplicationBuilder UseIpBasedAccessValidation(this IApplicationBuilder appBuilder)
|
||||
{
|
||||
return appBuilder.UseMiddleware<IpBasedAccessValidationMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds LAN based access filtering to the application pipeline.
|
||||
/// </summary>
|
||||
/// <param name="appBuilder">The application builder.</param>
|
||||
/// <returns>The updated application builder.</returns>
|
||||
public static IApplicationBuilder UseLanFiltering(this IApplicationBuilder appBuilder)
|
||||
{
|
||||
return appBuilder.UseMiddleware<LanFilteringMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds base url redirection to the application pipeline.
|
||||
/// </summary>
|
||||
/// <param name="appBuilder">The application builder.</param>
|
||||
/// <returns>The updated application builder.</returns>
|
||||
public static IApplicationBuilder UseBaseUrlRedirection(this IApplicationBuilder appBuilder)
|
||||
{
|
||||
return appBuilder.UseMiddleware<BaseUrlRedirectionMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a custom message during server startup to the application pipeline.
|
||||
/// </summary>
|
||||
/// <param name="appBuilder">The application builder.</param>
|
||||
/// <returns>The updated application builder.</returns>
|
||||
public static IApplicationBuilder UseServerStartupMessage(this IApplicationBuilder appBuilder)
|
||||
{
|
||||
return appBuilder.UseMiddleware<ServerStartupMessageMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a WebSocket request handler to the application pipeline.
|
||||
/// </summary>
|
||||
/// <param name="appBuilder">The application builder.</param>
|
||||
/// <returns>The updated application builder.</returns>
|
||||
public static IApplicationBuilder UseWebSocketHandler(this IApplicationBuilder appBuilder)
|
||||
{
|
||||
return appBuilder.UseMiddleware<WebSocketHandlerMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ using Jellyfin.Api.Constants;
|
||||
using Jellyfin.Api.Controllers;
|
||||
using Jellyfin.Server.Formatters;
|
||||
using Jellyfin.Server.Models;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
@@ -135,10 +136,11 @@ namespace Jellyfin.Server.Extensions
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection">The service collection.</param>
|
||||
/// <param name="baseUrl">The base url for the API.</param>
|
||||
/// <param name="pluginAssemblies">An IEnumberable containing all plugin assemblies with API controllers.</param>
|
||||
/// <returns>The MVC builder.</returns>
|
||||
public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, string baseUrl)
|
||||
public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, string baseUrl, IEnumerable<Assembly> pluginAssemblies)
|
||||
{
|
||||
return serviceCollection
|
||||
IMvcBuilder mvcBuilder = serviceCollection
|
||||
.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy(ServerCorsPolicy.DefaultPolicyName, ServerCorsPolicy.DefaultPolicy);
|
||||
@@ -168,6 +170,9 @@ namespace Jellyfin.Server.Extensions
|
||||
// From JsonDefaults
|
||||
options.JsonSerializerOptions.ReadCommentHandling = jsonOptions.ReadCommentHandling;
|
||||
options.JsonSerializerOptions.WriteIndented = jsonOptions.WriteIndented;
|
||||
options.JsonSerializerOptions.DefaultIgnoreCondition = jsonOptions.DefaultIgnoreCondition;
|
||||
options.JsonSerializerOptions.NumberHandling = jsonOptions.NumberHandling;
|
||||
|
||||
options.JsonSerializerOptions.Converters.Clear();
|
||||
foreach (var converter in jsonOptions.Converters)
|
||||
{
|
||||
@@ -176,8 +181,14 @@ namespace Jellyfin.Server.Extensions
|
||||
|
||||
// From JsonDefaults.PascalCase
|
||||
options.JsonSerializerOptions.PropertyNamingPolicy = jsonOptions.PropertyNamingPolicy;
|
||||
})
|
||||
.AddControllersAsServices();
|
||||
});
|
||||
|
||||
foreach (Assembly pluginAssembly in pluginAssemblies)
|
||||
{
|
||||
mvcBuilder.AddApplicationPart(pluginAssembly);
|
||||
}
|
||||
|
||||
return mvcBuilder.AddControllersAsServices();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -194,7 +205,7 @@ namespace Jellyfin.Server.Extensions
|
||||
{
|
||||
Type = SecuritySchemeType.ApiKey,
|
||||
In = ParameterLocation.Header,
|
||||
Name = "X-Emby-Token",
|
||||
Name = "X-Emby-Authorization",
|
||||
Description = "API key header parameter"
|
||||
});
|
||||
|
||||
|
||||
62
Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs
Normal file
62
Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ConfigurationExtensions = MediaBrowser.Controller.Extensions.ConfigurationExtensions;
|
||||
|
||||
namespace Jellyfin.Server.Middleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Redirect requests without baseurl prefix to the baseurl prefixed URL.
|
||||
/// </summary>
|
||||
public class BaseUrlRedirectionMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<BaseUrlRedirectionMiddleware> _logger;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseUrlRedirectionMiddleware"/> class.
|
||||
/// </summary>
|
||||
/// <param name="next">The next delegate in the pipeline.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="configuration">The application configuration.</param>
|
||||
public BaseUrlRedirectionMiddleware(
|
||||
RequestDelegate next,
|
||||
ILogger<BaseUrlRedirectionMiddleware> logger,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the middleware action.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The current HTTP context.</param>
|
||||
/// <param name="serverConfigurationManager">The server configuration manager.</param>
|
||||
/// <returns>The async task.</returns>
|
||||
public async Task Invoke(HttpContext httpContext, IServerConfigurationManager serverConfigurationManager)
|
||||
{
|
||||
var localPath = httpContext.Request.Path.ToString();
|
||||
var baseUrlPrefix = serverConfigurationManager.Configuration.BaseUrl;
|
||||
|
||||
if (string.Equals(localPath, baseUrlPrefix + "/", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(localPath, baseUrlPrefix, StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.IsNullOrEmpty(localPath)
|
||||
|| !localPath.StartsWith(baseUrlPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Always redirect back to the default path if the base prefix is invalid or missing
|
||||
_logger.LogDebug("Normalizing an URL at {LocalPath}", localPath);
|
||||
httpContext.Response.Redirect(baseUrlPrefix + "/" + _configuration[ConfigurationExtensions.DefaultRedirectKey]);
|
||||
return;
|
||||
}
|
||||
|
||||
await _next(httpContext).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Jellyfin.Server.Middleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates the IP of requests coming from local networks wrt. remote access.
|
||||
/// </summary>
|
||||
public class IpBasedAccessValidationMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IpBasedAccessValidationMiddleware"/> class.
|
||||
/// </summary>
|
||||
/// <param name="next">The next delegate in the pipeline.</param>
|
||||
public IpBasedAccessValidationMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the middleware action.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The current HTTP context.</param>
|
||||
/// <param name="networkManager">The network manager.</param>
|
||||
/// <param name="serverConfigurationManager">The server configuration manager.</param>
|
||||
/// <returns>The async task.</returns>
|
||||
public async Task Invoke(HttpContext httpContext, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager)
|
||||
{
|
||||
if (httpContext.Request.IsLocal())
|
||||
{
|
||||
await _next(httpContext).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var remoteIp = httpContext.Request.RemoteIp();
|
||||
|
||||
if (serverConfigurationManager.Configuration.EnableRemoteAccess)
|
||||
{
|
||||
var addressFilter = serverConfigurationManager.Configuration.RemoteIPFilter.Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
|
||||
|
||||
if (addressFilter.Length > 0 && !networkManager.IsInLocalNetwork(remoteIp))
|
||||
{
|
||||
if (serverConfigurationManager.Configuration.IsRemoteIPFilterBlacklist)
|
||||
{
|
||||
if (networkManager.IsAddressInSubnets(remoteIp, addressFilter))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!networkManager.IsAddressInSubnets(remoteIp, addressFilter))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!networkManager.IsInLocalNetwork(remoteIp))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _next(httpContext).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
76
Jellyfin.Server/Middleware/LanFilteringMiddleware.cs
Normal file
76
Jellyfin.Server/Middleware/LanFilteringMiddleware.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Jellyfin.Server.Middleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates the LAN host IP based on application configuration.
|
||||
/// </summary>
|
||||
public class LanFilteringMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LanFilteringMiddleware"/> class.
|
||||
/// </summary>
|
||||
/// <param name="next">The next delegate in the pipeline.</param>
|
||||
public LanFilteringMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the middleware action.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The current HTTP context.</param>
|
||||
/// <param name="networkManager">The network manager.</param>
|
||||
/// <param name="serverConfigurationManager">The server configuration manager.</param>
|
||||
/// <returns>The async task.</returns>
|
||||
public async Task Invoke(HttpContext httpContext, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager)
|
||||
{
|
||||
var currentHost = httpContext.Request.Host.ToString();
|
||||
var hosts = serverConfigurationManager
|
||||
.Configuration
|
||||
.LocalNetworkAddresses
|
||||
.Select(NormalizeConfiguredLocalAddress)
|
||||
.ToList();
|
||||
|
||||
if (hosts.Count == 0)
|
||||
{
|
||||
await _next(httpContext).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
currentHost ??= string.Empty;
|
||||
|
||||
if (networkManager.IsInPrivateAddressSpace(currentHost))
|
||||
{
|
||||
hosts.Add("localhost");
|
||||
hosts.Add("127.0.0.1");
|
||||
|
||||
if (hosts.All(i => currentHost.IndexOf(i, StringComparison.OrdinalIgnoreCase) == -1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _next(httpContext).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static string NormalizeConfiguredLocalAddress(string address)
|
||||
{
|
||||
var add = address.AsSpan().Trim('/');
|
||||
int index = add.IndexOf('/');
|
||||
if (index != -1)
|
||||
{
|
||||
add = add.Slice(index + 1);
|
||||
}
|
||||
|
||||
return add.TrimStart('/').ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
49
Jellyfin.Server/Middleware/ServerStartupMessageMiddleware.cs
Normal file
49
Jellyfin.Server/Middleware/ServerStartupMessageMiddleware.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System.Net.Mime;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Jellyfin.Server.Middleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows a custom message during server startup.
|
||||
/// </summary>
|
||||
public class ServerStartupMessageMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServerStartupMessageMiddleware"/> class.
|
||||
/// </summary>
|
||||
/// <param name="next">The next delegate in the pipeline.</param>
|
||||
public ServerStartupMessageMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the middleware action.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The current HTTP context.</param>
|
||||
/// <param name="serverApplicationHost">The server application host.</param>
|
||||
/// <param name="localizationManager">The localization manager.</param>
|
||||
/// <returns>The async task.</returns>
|
||||
public async Task Invoke(
|
||||
HttpContext httpContext,
|
||||
IServerApplicationHost serverApplicationHost,
|
||||
ILocalizationManager localizationManager)
|
||||
{
|
||||
if (serverApplicationHost.CoreStartupHasCompleted)
|
||||
{
|
||||
await _next(httpContext).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var message = localizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
|
||||
httpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
|
||||
httpContext.Response.ContentType = MediaTypeNames.Text.Html;
|
||||
await httpContext.Response.WriteAsync(message, httpContext.RequestAborted).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Jellyfin.Server/Middleware/WebSocketHandlerMiddleware.cs
Normal file
40
Jellyfin.Server/Middleware/WebSocketHandlerMiddleware.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Jellyfin.Server.Middleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles WebSocket requests.
|
||||
/// </summary>
|
||||
public class WebSocketHandlerMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebSocketHandlerMiddleware"/> class.
|
||||
/// </summary>
|
||||
/// <param name="next">The next delegate in the pipeline.</param>
|
||||
public WebSocketHandlerMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the middleware action.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The current HTTP context.</param>
|
||||
/// <param name="webSocketManager">The WebSocket connection manager.</param>
|
||||
/// <returns>The async task.</returns>
|
||||
public async Task Invoke(HttpContext httpContext, IWebSocketManager webSocketManager)
|
||||
{
|
||||
if (!httpContext.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
await _next(httpContext).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await webSocketManager.WebSocketRequestHandler(httpContext).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,11 @@ namespace Jellyfin.Server.Migrations.Routines
|
||||
foreach (var result in results)
|
||||
{
|
||||
var dto = JsonSerializer.Deserialize<DisplayPreferencesDto>(result[3].ToString(), _jsonOptions);
|
||||
if (dto == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var chromecastVersion = dto.CustomPrefs.TryGetValue("chromecastVersion", out var version)
|
||||
? chromecastDict[version]
|
||||
: ChromecastVersion.Stable;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Emby.Server.Implementations.Data;
|
||||
using Emby.Server.Implementations.Serialization;
|
||||
using Jellyfin.Data.Entities;
|
||||
@@ -74,7 +76,12 @@ namespace Jellyfin.Server.Migrations.Routines
|
||||
|
||||
foreach (var entry in queryResult)
|
||||
{
|
||||
UserMockup mockup = JsonSerializer.Deserialize<UserMockup>(entry[2].ToBlob(), JsonDefaults.GetOptions());
|
||||
UserMockup? mockup = JsonSerializer.Deserialize<UserMockup>(entry[2].ToBlob(), JsonDefaults.GetOptions());
|
||||
if (mockup == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var userDataDir = Path.Combine(_paths.UserConfigurationDirectoryPath, mockup.Name);
|
||||
|
||||
var config = File.Exists(Path.Combine(userDataDir, "config.xml"))
|
||||
@@ -161,9 +168,9 @@ namespace Jellyfin.Server.Migrations.Routines
|
||||
}
|
||||
|
||||
user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags);
|
||||
user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels);
|
||||
user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray());
|
||||
user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
|
||||
user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders);
|
||||
user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray());
|
||||
user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders);
|
||||
user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews);
|
||||
user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
|
||||
|
||||
@@ -11,7 +11,6 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommandLine;
|
||||
using Emby.Server.Implementations;
|
||||
using Emby.Server.Implementations.HttpServer;
|
||||
using Emby.Server.Implementations.IO;
|
||||
using Emby.Server.Implementations.Networking;
|
||||
using Jellyfin.Api.Controllers;
|
||||
@@ -28,6 +27,7 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
using SQLitePCL;
|
||||
using ConfigurationExtensions = MediaBrowser.Controller.Extensions.ConfigurationExtensions;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace Jellyfin.Server
|
||||
@@ -154,13 +154,15 @@ namespace Jellyfin.Server
|
||||
ApplicationHost.LogEnvironmentInfo(_logger, appPaths);
|
||||
|
||||
PerformStaticInitialization();
|
||||
var serviceCollection = new ServiceCollection();
|
||||
|
||||
var appHost = new CoreAppHost(
|
||||
appPaths,
|
||||
_loggerFactory,
|
||||
options,
|
||||
new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
|
||||
new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()));
|
||||
new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()),
|
||||
serviceCollection);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -178,8 +180,7 @@ namespace Jellyfin.Server
|
||||
}
|
||||
}
|
||||
|
||||
ServiceCollection serviceCollection = new ServiceCollection();
|
||||
appHost.Init(serviceCollection);
|
||||
appHost.Init();
|
||||
|
||||
var webHost = new WebHostBuilder().ConfigureWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build();
|
||||
|
||||
@@ -593,7 +594,7 @@ namespace Jellyfin.Server
|
||||
var inMemoryDefaultConfig = ConfigurationOptions.DefaultConfiguration;
|
||||
if (startupConfig != null && !startupConfig.HostWebClient())
|
||||
{
|
||||
inMemoryDefaultConfig[HttpListenerHost.DefaultRedirectKey] = "api-docs/swagger";
|
||||
inMemoryDefaultConfig[ConfigurationExtensions.DefaultRedirectKey] = "api-docs/swagger";
|
||||
}
|
||||
|
||||
return config
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
using System.Net.Http;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Net.Http.Headers;
|
||||
using Jellyfin.Api.TypeConverters;
|
||||
using Jellyfin.Server.Extensions;
|
||||
using Jellyfin.Server.Middleware;
|
||||
using Jellyfin.Server.Models;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@@ -18,14 +23,19 @@ namespace Jellyfin.Server
|
||||
public class Startup
|
||||
{
|
||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly IServerApplicationHost _serverApplicationHost;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Startup" /> class.
|
||||
/// </summary>
|
||||
/// <param name="serverConfigurationManager">The server configuration manager.</param>
|
||||
public Startup(IServerConfigurationManager serverConfigurationManager)
|
||||
/// <param name="serverApplicationHost">The server application host.</param>
|
||||
public Startup(
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IServerApplicationHost serverApplicationHost)
|
||||
{
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_serverApplicationHost = serverApplicationHost;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -36,7 +46,13 @@ namespace Jellyfin.Server
|
||||
{
|
||||
services.AddResponseCompression();
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddJellyfinApi(_serverConfigurationManager.Configuration.BaseUrl.TrimStart('/'));
|
||||
services.AddHttpsRedirection(options =>
|
||||
{
|
||||
options.HttpsPort = _serverApplicationHost.HttpsPort;
|
||||
});
|
||||
services.AddJellyfinApi(
|
||||
_serverConfigurationManager.Configuration.BaseUrl.TrimStart('/'),
|
||||
_serverApplicationHost.GetApiPluginAssemblies());
|
||||
|
||||
services.AddJellyfinApiSwagger();
|
||||
|
||||
@@ -44,7 +60,23 @@ namespace Jellyfin.Server
|
||||
services.AddCustomAuthentication();
|
||||
|
||||
services.AddJellyfinApiAuthorization();
|
||||
services.AddHttpClient();
|
||||
|
||||
var productHeader = new ProductInfoHeaderValue(
|
||||
_serverApplicationHost.Name.Replace(' ', '-'),
|
||||
_serverApplicationHost.ApplicationVersionString);
|
||||
services
|
||||
.AddHttpClient(NamedClient.Default, c =>
|
||||
{
|
||||
c.DefaultRequestHeaders.UserAgent.Add(productHeader);
|
||||
})
|
||||
.ConfigurePrimaryHttpMessageHandler(x => new DefaultHttpClientHandler());
|
||||
|
||||
services.AddHttpClient(NamedClient.MusicBrainz, c =>
|
||||
{
|
||||
c.DefaultRequestHeaders.UserAgent.Add(productHeader);
|
||||
c.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue($"({_serverApplicationHost.ApplicationUserAgentAddress})"));
|
||||
})
|
||||
.ConfigurePrimaryHttpMessageHandler(x => new DefaultHttpClientHandler());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,11 +84,9 @@ namespace Jellyfin.Server
|
||||
/// </summary>
|
||||
/// <param name="app">The application builder.</param>
|
||||
/// <param name="env">The webhost environment.</param>
|
||||
/// <param name="serverApplicationHost">The server application host.</param>
|
||||
public void Configure(
|
||||
IApplicationBuilder app,
|
||||
IWebHostEnvironment env,
|
||||
IServerApplicationHost serverApplicationHost)
|
||||
IWebHostEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
@@ -71,13 +101,18 @@ namespace Jellyfin.Server
|
||||
|
||||
app.UseResponseCompression();
|
||||
|
||||
// TODO app.UseMiddleware<WebSocketMiddleware>();
|
||||
app.UseCors(ServerCorsPolicy.DefaultPolicyName);
|
||||
|
||||
if (_serverConfigurationManager.Configuration.RequireHttps
|
||||
&& _serverApplicationHost.ListenWithHttps)
|
||||
{
|
||||
app.UseHttpsRedirection();
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseAuthentication();
|
||||
app.UseJellyfinApiSwagger(_serverConfigurationManager);
|
||||
app.UseRouting();
|
||||
app.UseCors(ServerCorsPolicy.DefaultPolicyName);
|
||||
app.UseAuthorization();
|
||||
if (_serverConfigurationManager.Configuration.EnableMetrics)
|
||||
{
|
||||
@@ -85,6 +120,12 @@ namespace Jellyfin.Server
|
||||
app.UseHttpMetrics();
|
||||
}
|
||||
|
||||
app.UseLanFiltering();
|
||||
app.UseIpBasedAccessValidation();
|
||||
app.UseBaseUrlRedirection();
|
||||
app.UseWebSocketHandler();
|
||||
app.UseServerStartupMessage();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
@@ -94,7 +135,8 @@ namespace Jellyfin.Server
|
||||
}
|
||||
});
|
||||
|
||||
app.Use(serverApplicationHost.ExecuteHttpHandlerAsync);
|
||||
// Add type descriptor for legacy datetime parsing.
|
||||
TypeDescriptor.AddAttributes(typeof(DateTime?), new TypeConverterAttribute(typeof(DateTimeTypeConverter)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user