| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 | 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 Jellyfin.Server.Infrastructure;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.Mvc;using Microsoft.AspNetCore.Mvc.Infrastructure;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;namespace Jellyfin.Server{    /// <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;            });            // TODO remove once this is fixed upstream https://github.com/dotnet/aspnetcore/issues/34371            services.AddSingleton<IActionResultExecutor<PhysicalFileResult>, SymlinkFollowingPhysicalFileResultExecutor>();            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();                }                // This must be injected before any path related middleware.                mainApp.UsePathTrim();                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");                });            });        }    }}
 |