Files
pgsql-jellyfin/Jellyfin.Server/Startup.cs
T
wjones af1152b001 Refactor: standardize namespace and using directive style
Refactored all C# test files to use explicit namespace declarations and moved all using directives inside the namespace block for consistency. Updated assembly info and cache files to reflect the new build. Adjusted MvcTestingAppManifest.json to use fully qualified assembly names. No functional changes; all updates are related to code style, organization, and project metadata.
2026-02-20 16:26:53 -05:00

240 lines
10 KiB
C#

// <copyright file="Startup.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace Jellyfin.Server
{
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
using Emby.Server.Implementations.EntryPoints;
using Jellyfin.Api.Middleware;
using Jellyfin.Database.Implementations;
using Jellyfin.LiveTv.Extensions;
using Jellyfin.LiveTv.Recordings;
using Jellyfin.MediaEncoding.Hls.Extensions;
using Jellyfin.Networking;
using Jellyfin.Networking.HappyEyeballs;
using Jellyfin.Server.Extensions;
using Jellyfin.Server.HealthChecks;
using Jellyfin.Server.Implementations.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.XbmcMetadata;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Primitives;
using Prometheus;
/// <summary>
/// Startup configuration for the Kestrel webhost.
/// </summary>
public class Startup
{
private readonly CoreAppHost _serverApplicationHost;
private readonly IConfiguration _configuration;
private readonly IServerConfigurationManager _serverConfigurationManager;
/// <summary>
/// Initializes a new instance of the <see cref="Startup" /> class.
/// </summary>
/// <param name="appHost">The server application host.</param>
/// <param name="configuration">The used Configuration.</param>
public Startup(CoreAppHost appHost, IConfiguration configuration)
{
_serverApplicationHost = appHost;
_configuration = configuration;
_serverConfigurationManager = appHost.ConfigurationManager;
}
/// <summary>
/// Configures the service collection for the webhost.
/// </summary>
/// <param name="services">The service collection.</param>
public void ConfigureServices(IServiceCollection services)
{
services.AddResponseCompression();
services.AddHttpContextAccessor();
services.AddHttpsRedirection(options =>
{
options.HttpsPort = _serverApplicationHost.HttpsPort;
});
services.AddJellyfinApi(_serverApplicationHost.GetApiPluginAssemblies(), _serverConfigurationManager.GetNetworkConfiguration());
services.AddJellyfinDbContext(_serverApplicationHost.ConfigurationManager, _configuration);
services.AddJellyfinApiSwagger();
// configure custom legacy authentication
services.AddCustomAuthentication();
services.AddJellyfinApiAuthorization();
var productHeader = new ProductInfoHeaderValue(
_serverApplicationHost.Name.Replace(' ', '-'),
_serverApplicationHost.ApplicationVersionString);
var acceptJsonHeader = new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json, 1.0);
var acceptXmlHeader = new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Xml, 0.9);
var acceptAnyHeader = new MediaTypeWithQualityHeaderValue("*/*", 0.8);
Func<IServiceProvider, HttpMessageHandler> eyeballsHttpClientHandlerDelegate = (_) => new SocketsHttpHandler()
{
AutomaticDecompression = DecompressionMethods.All,
RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8,
ConnectCallback = HttpClientExtension.OnConnect
};
Func<IServiceProvider, HttpMessageHandler> defaultHttpClientHandlerDelegate = (_) => new SocketsHttpHandler()
{
AutomaticDecompression = DecompressionMethods.All,
RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8
};
services.AddHttpClient(NamedClient.Default, c =>
{
c.DefaultRequestHeaders.UserAgent.Add(productHeader);
c.DefaultRequestHeaders.Accept.Add(acceptJsonHeader);
c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
})
.ConfigurePrimaryHttpMessageHandler(eyeballsHttpClientHandlerDelegate);
services.AddHttpClient(NamedClient.MusicBrainz, c =>
{
c.DefaultRequestHeaders.UserAgent.Add(productHeader);
c.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue($"({_serverApplicationHost.ApplicationUserAgentAddress})"));
c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
})
.ConfigurePrimaryHttpMessageHandler(eyeballsHttpClientHandlerDelegate);
services.AddHttpClient(NamedClient.DirectIp, c =>
{
c.DefaultRequestHeaders.UserAgent.Add(productHeader);
c.DefaultRequestHeaders.Accept.Add(acceptJsonHeader);
c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
})
.ConfigurePrimaryHttpMessageHandler(defaultHttpClientHandlerDelegate);
services.AddHealthChecks()
.AddCheck<DbContextFactoryHealthCheck<JellyfinDbContext>>(nameof(JellyfinDbContext));
services.AddHlsPlaylistGenerator();
services.AddLiveTvServices();
services.AddHostedService<RecordingsHost>();
services.AddHostedService<AutoDiscoveryHost>();
services.AddHostedService<NfoUserDataSaver>();
services.AddHostedService<LibraryChangedNotifier>();
services.AddHostedService<UserDataChangeNotifier>();
services.AddHostedService<RecordingNotifier>();
}
/// <summary>
/// Configures the app builder for the webhost.
/// </summary>
/// <param name="app">The application builder.</param>
/// <param name="env">The webhost environment.</param>
/// <param name="appConfig">The application config.</param>
public void Configure(
IApplicationBuilder app,
IWebHostEnvironment env,
IConfiguration appConfig)
{
app.UseBaseUrlRedirection();
// Wrap rest of configuration so everything only listens on BaseUrl.
var config = _serverConfigurationManager.GetNetworkConfiguration();
app.Map(config.BaseUrl, mainApp =>
{
if (env.IsDevelopment())
{
mainApp.UseDeveloperExceptionPage();
}
mainApp.UseForwardedHeaders();
mainApp.UseMiddleware<ExceptionMiddleware>();
mainApp.UseMiddleware<ResponseTimeMiddleware>();
mainApp.UseWebSockets();
mainApp.UseResponseCompression();
mainApp.UseCors();
if (config.RequireHttps && _serverApplicationHost.ListenWithHttps)
{
mainApp.UseHttpsRedirection();
}
if (appConfig.HostWebClient())
{
var extensionProvider = new FileExtensionContentTypeProvider();
// subtitles octopus requires .data, .mem files.
extensionProvider.Mappings.Add(".data", MediaTypeNames.Application.Octet);
extensionProvider.Mappings.Add(".mem", MediaTypeNames.Application.Octet);
mainApp.UseDefaultFiles(new DefaultFilesOptions
{
FileProvider = new PhysicalFileProvider(_serverConfigurationManager.ApplicationPaths.WebPath),
RequestPath = "/web"
});
mainApp.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(_serverConfigurationManager.ApplicationPaths.WebPath),
RequestPath = "/web",
ContentTypeProvider = extensionProvider,
OnPrepareResponse = (context) =>
{
if (Path.GetFileName(context.File.Name).Equals("index.html", StringComparison.Ordinal))
{
context.Context.Response.Headers.CacheControl = new StringValues("no-cache");
}
}
});
mainApp.UseRobotsRedirection();
}
mainApp.UseStaticFiles();
mainApp.UseAuthentication();
mainApp.UseJellyfinApiSwagger(_serverConfigurationManager);
mainApp.UseQueryStringDecoding();
mainApp.UseRouting();
mainApp.UseAuthorization();
mainApp.UseIPBasedAccessValidation();
mainApp.UseWebSocketHandler();
mainApp.UseServerStartupMessage();
if (_serverConfigurationManager.Configuration.EnableMetrics)
{
// Must be registered after any middleware that could change HTTP response codes or the data will be bad
mainApp.UseHttpMetrics();
}
mainApp.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
if (_serverConfigurationManager.Configuration.EnableMetrics)
{
endpoints.MapMetrics();
}
endpoints.MapHealthChecks("/health");
});
});
}
}
}