From 5e6cdc8842c3c81eb7e0363e6d36fac6630304e1 Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 14 Aug 2020 08:54:21 -0600 Subject: [PATCH 01/40] Install specific plugin version if requested --- .../Updates/InstallationManager.cs | 13 +++++++++---- Jellyfin.Api/Controllers/PackageController.cs | 10 ++++++---- MediaBrowser.Common/Updates/IInstallationManager.cs | 4 +++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 4f54c06dd..8d0c4c350 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -191,7 +191,8 @@ namespace Emby.Server.Implementations.Updates IEnumerable availablePackages, string name = null, Guid guid = default, - Version minVersion = null) + Version minVersion = null, + Version specificVersion = null) { var package = FilterPackages(availablePackages, name, guid).FirstOrDefault(); @@ -205,7 +206,11 @@ namespace Emby.Server.Implementations.Updates var availableVersions = package.versions .Where(x => Version.Parse(x.targetAbi) <= appVer); - if (minVersion != null) + if (specificVersion != null) + { + availableVersions = availableVersions.Where(x => new Version(x.version) == specificVersion); + } + else if (minVersion != null) { availableVersions = availableVersions.Where(x => new Version(x.version) >= minVersion); } @@ -235,8 +240,8 @@ namespace Emby.Server.Implementations.Updates { foreach (var plugin in _applicationHost.Plugins) { - var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version); - var version = compatibleversions.FirstOrDefault(y => y.Version > plugin.Version); + var compatibleVersions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, minVersion: plugin.Version); + var version = compatibleVersions.FirstOrDefault(y => y.Version > plugin.Version); if (version != null && CompletedInstallations.All(x => x.Guid != version.Guid)) { yield return version; diff --git a/Jellyfin.Api/Controllers/PackageController.cs b/Jellyfin.Api/Controllers/PackageController.cs index 3d6a87909..ae3d0081f 100644 --- a/Jellyfin.Api/Controllers/PackageController.cs +++ b/Jellyfin.Api/Controllers/PackageController.cs @@ -49,9 +49,10 @@ namespace Jellyfin.Api.Controllers { var packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false); var result = _installationManager.FilterPackages( - packages, - name, - string.IsNullOrEmpty(assemblyGuid) ? default : Guid.Parse(assemblyGuid)).FirstOrDefault(); + packages, + name, + string.IsNullOrEmpty(assemblyGuid) ? default : Guid.Parse(assemblyGuid)) + .FirstOrDefault(); return result; } @@ -93,7 +94,8 @@ namespace Jellyfin.Api.Controllers packages, name, string.IsNullOrEmpty(assemblyGuid) ? Guid.Empty : Guid.Parse(assemblyGuid), - string.IsNullOrEmpty(version) ? null : Version.Parse(version)).FirstOrDefault(); + specificVersion: string.IsNullOrEmpty(version) ? null : Version.Parse(version)) + .FirstOrDefault(); if (package == null) { diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs index 4b4030bc2..169aca2ca 100644 --- a/MediaBrowser.Common/Updates/IInstallationManager.cs +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -73,12 +73,14 @@ namespace MediaBrowser.Common.Updates /// The name. /// The guid of the plugin. /// The minimum required version of the plugin. + /// The specific version of the plugin to install. /// All compatible versions ordered from newest to oldest. IEnumerable GetCompatibleVersions( IEnumerable availablePackages, string name = null, Guid guid = default, - Version minVersion = null); + Version minVersion = null, + Version specificVersion = null); /// /// Returns the available plugin updates. From 3c0484cc9730c06892b996d0b884a05ecada07af Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 30 Aug 2020 09:32:14 -0600 Subject: [PATCH 02/40] Allow for dynamic cors response --- .../ApiServiceCollectionExtensions.cs | 8 ++- .../Middleware/DynamicCorsMiddleware.cs | 68 +++++++++++++++++++ Jellyfin.Server/Models/ServerCorsPolicy.cs | 43 ++++++++---- Jellyfin.Server/Startup.cs | 8 ++- .../Configuration/ServerConfiguration.cs | 6 ++ 5 files changed, 115 insertions(+), 18 deletions(-) create mode 100644 Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 0fd599cfc..b2f861542 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -135,13 +135,17 @@ namespace Jellyfin.Server.Extensions /// /// The service collection. /// The base url for the API. + /// The configured cors hosts. /// The MVC builder. - public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, string baseUrl) + public static IMvcBuilder AddJellyfinApi( + this IServiceCollection serviceCollection, + string baseUrl, + string[] corsHosts) { return serviceCollection .AddCors(options => { - options.AddPolicy(ServerCorsPolicy.DefaultPolicyName, ServerCorsPolicy.DefaultPolicy); + options.AddPolicy(ServerCorsPolicy.DefaultPolicyName, new ServerCorsPolicy(corsHosts).Policy); }) .Configure(options => { diff --git a/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs b/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs new file mode 100644 index 000000000..4fad898a7 --- /dev/null +++ b/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs @@ -0,0 +1,68 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Cors.Infrastructure; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; + +namespace Jellyfin.Server.Middleware +{ + /// + /// Dynamic cors middleware. + /// + public class DynamicCorsMiddleware + { + private readonly RequestDelegate _next; + private readonly ILogger _logger; + private readonly CorsMiddleware _corsMiddleware; + + /// + /// Initializes a new instance of the class. + /// + /// Next request delegate. + /// Instance of the interface. + /// Instance of the interface. + /// The cors policy name. + public DynamicCorsMiddleware( + RequestDelegate next, + ICorsService corsService, + ILoggerFactory loggerFactory, + string policyName) + { + _corsMiddleware = new CorsMiddleware(next, corsService, loggerFactory, policyName); + _next = next; + _logger = loggerFactory.CreateLogger(); + } + + /// + /// Invoke request. + /// + /// Request context. + /// Instance of the interface. + /// Task. + /// + public async Task Invoke(HttpContext context, ICorsPolicyProvider corsPolicyProvider) + { + // Only execute if is preflight request. + if (string.Equals(context.Request.Method, CorsConstants.PreflightHttpMethod, StringComparison.OrdinalIgnoreCase)) + { + // Invoke original cors middleware. + await _corsMiddleware.Invoke(context, corsPolicyProvider).ConfigureAwait(false); + if (context.Response.Headers.TryGetValue(HeaderNames.AccessControlAllowOrigin, out var headerValue) + && string.Equals(headerValue, "*", StringComparison.Ordinal)) + { + context.Response.Headers[HeaderNames.AccessControlAllowOrigin] = context.Request.Host.Value; + _logger.LogDebug("Overwriting CORS response header: {HeaderName}: {HeaderValue}", HeaderNames.AccessControlAllowOrigin, context.Request.Host.Value); + + if (!context.Response.Headers.ContainsKey(HeaderNames.AccessControlAllowCredentials)) + { + context.Response.Headers[HeaderNames.AccessControlAllowCredentials] = "true"; + } + } + } + + // Call the next delegate/middleware in the pipeline + await this._next(context).ConfigureAwait(false); + } + } +} diff --git a/Jellyfin.Server/Models/ServerCorsPolicy.cs b/Jellyfin.Server/Models/ServerCorsPolicy.cs index ae010c042..3a45db3b4 100644 --- a/Jellyfin.Server/Models/ServerCorsPolicy.cs +++ b/Jellyfin.Server/Models/ServerCorsPolicy.cs @@ -1,30 +1,47 @@ -using Microsoft.AspNetCore.Cors.Infrastructure; +using System; +using Microsoft.AspNetCore.Cors.Infrastructure; namespace Jellyfin.Server.Models { /// /// Server Cors Policy. /// - public static class ServerCorsPolicy + public class ServerCorsPolicy { /// /// Default policy name. /// - public const string DefaultPolicyName = "DefaultCorsPolicy"; + public const string DefaultPolicyName = nameof(ServerCorsPolicy); /// - /// Default Policy. Allow Everything. + /// Initializes a new instance of the class. /// - public static readonly CorsPolicy DefaultPolicy = new CorsPolicy + /// The configured cors hosts. + public ServerCorsPolicy(string[] corsHosts) { - // Allow any origin - Origins = { "*" }, + var builder = new CorsPolicyBuilder() + .AllowAnyMethod() + .AllowAnyHeader(); - // Allow any method - Methods = { "*" }, + // No hosts configured or only default configured. + if (corsHosts.Length == 0 + || (corsHosts.Length == 1 + && string.Equals(corsHosts[0], "*", StringComparison.Ordinal))) + { + builder.AllowAnyOrigin(); + } + else + { + builder.WithOrigins(corsHosts) + .AllowCredentials(); + } - // Allow any header - Headers = { "*" } - }; + Policy = builder.Build(); + } + + /// + /// Gets the cors policy. + /// + public CorsPolicy Policy { get; } } -} \ No newline at end of file +} diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index d0dd183c6..76f5e69ce 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -38,7 +38,9 @@ namespace Jellyfin.Server { services.AddResponseCompression(); services.AddHttpContextAccessor(); - services.AddJellyfinApi(_serverConfigurationManager.Configuration.BaseUrl.TrimStart('/')); + services.AddJellyfinApi( + _serverConfigurationManager.Configuration.BaseUrl.TrimStart('/'), + _serverConfigurationManager.Configuration.CorsHosts); services.AddJellyfinApiSwagger(); @@ -78,11 +80,11 @@ namespace Jellyfin.Server app.UseAuthentication(); app.UseJellyfinApiSwagger(_serverConfigurationManager); app.UseRouting(); - app.UseCors(ServerCorsPolicy.DefaultPolicyName); + app.UseMiddleware(ServerCorsPolicy.DefaultPolicyName); app.UseAuthorization(); if (_serverConfigurationManager.Configuration.EnableMetrics) { - // Must be registered after any middleware that could chagne HTTP response codes or the data will be bad + // Must be registered after any middleware that could change HTTP response codes or the data will be bad app.UseHttpMetrics(); } diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index c66091f9d..a743277d7 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -264,6 +264,11 @@ namespace MediaBrowser.Model.Configuration /// public long SlowResponseThresholdMs { get; set; } + /// + /// Gets or sets the cors hosts. + /// + public string[] CorsHosts { get; set; } + /// /// Initializes a new instance of the class. /// @@ -372,6 +377,7 @@ namespace MediaBrowser.Model.Configuration EnableSlowResponseWarning = true; SlowResponseThresholdMs = 500; + CorsHosts = new[] { "*" }; } } From eba0d9e387dc0375628acfd81957ff180c82d8df Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 30 Aug 2020 10:05:21 -0600 Subject: [PATCH 03/40] Always allow set credentials header --- Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs b/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs index 4fad898a7..c4c491cdd 100644 --- a/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs +++ b/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs @@ -52,12 +52,10 @@ namespace Jellyfin.Server.Middleware && string.Equals(headerValue, "*", StringComparison.Ordinal)) { context.Response.Headers[HeaderNames.AccessControlAllowOrigin] = context.Request.Host.Value; - _logger.LogDebug("Overwriting CORS response header: {HeaderName}: {HeaderValue}", HeaderNames.AccessControlAllowOrigin, context.Request.Host.Value); - if (!context.Response.Headers.ContainsKey(HeaderNames.AccessControlAllowCredentials)) - { - context.Response.Headers[HeaderNames.AccessControlAllowCredentials] = "true"; - } + // Always allow credentials. + context.Response.Headers[HeaderNames.AccessControlAllowCredentials] = "true"; + _logger.LogDebug("Overwriting CORS response header: {HeaderName}: {HeaderValue}", HeaderNames.AccessControlAllowOrigin, context.Request.Host.Value); } } From e97ccd87fb74c34222eccf03493a56144065eaa4 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 31 Aug 2020 07:21:07 -0600 Subject: [PATCH 04/40] Remove DynamicCorsMiddleware --- .../Middleware/DynamicCorsMiddleware.cs | 66 ------------------- Jellyfin.Server/Startup.cs | 2 +- 2 files changed, 1 insertion(+), 67 deletions(-) delete mode 100644 Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs diff --git a/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs b/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs deleted file mode 100644 index c4c491cdd..000000000 --- a/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Cors.Infrastructure; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Microsoft.Net.Http.Headers; - -namespace Jellyfin.Server.Middleware -{ - /// - /// Dynamic cors middleware. - /// - public class DynamicCorsMiddleware - { - private readonly RequestDelegate _next; - private readonly ILogger _logger; - private readonly CorsMiddleware _corsMiddleware; - - /// - /// Initializes a new instance of the class. - /// - /// Next request delegate. - /// Instance of the interface. - /// Instance of the interface. - /// The cors policy name. - public DynamicCorsMiddleware( - RequestDelegate next, - ICorsService corsService, - ILoggerFactory loggerFactory, - string policyName) - { - _corsMiddleware = new CorsMiddleware(next, corsService, loggerFactory, policyName); - _next = next; - _logger = loggerFactory.CreateLogger(); - } - - /// - /// Invoke request. - /// - /// Request context. - /// Instance of the interface. - /// Task. - /// - public async Task Invoke(HttpContext context, ICorsPolicyProvider corsPolicyProvider) - { - // Only execute if is preflight request. - if (string.Equals(context.Request.Method, CorsConstants.PreflightHttpMethod, StringComparison.OrdinalIgnoreCase)) - { - // Invoke original cors middleware. - await _corsMiddleware.Invoke(context, corsPolicyProvider).ConfigureAwait(false); - if (context.Response.Headers.TryGetValue(HeaderNames.AccessControlAllowOrigin, out var headerValue) - && string.Equals(headerValue, "*", StringComparison.Ordinal)) - { - context.Response.Headers[HeaderNames.AccessControlAllowOrigin] = context.Request.Host.Value; - - // Always allow credentials. - context.Response.Headers[HeaderNames.AccessControlAllowCredentials] = "true"; - _logger.LogDebug("Overwriting CORS response header: {HeaderName}: {HeaderValue}", HeaderNames.AccessControlAllowOrigin, context.Request.Host.Value); - } - } - - // Call the next delegate/middleware in the pipeline - await this._next(context).ConfigureAwait(false); - } - } -} diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 76f5e69ce..966587a6a 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -80,7 +80,7 @@ namespace Jellyfin.Server app.UseAuthentication(); app.UseJellyfinApiSwagger(_serverConfigurationManager); app.UseRouting(); - app.UseMiddleware(ServerCorsPolicy.DefaultPolicyName); + app.UseCors(ServerCorsPolicy.DefaultPolicyName); app.UseAuthorization(); if (_serverConfigurationManager.Configuration.EnableMetrics) { From 2c05d53b06855eab76012ba70da00ec62af787c3 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 5 Sep 2020 09:10:05 -0600 Subject: [PATCH 05/40] Convert to ICorsPolicyProvider --- .../ApiServiceCollectionExtensions.cs | 14 +++--- .../Middleware/CorsPolicyProvider.cs | 48 +++++++++++++++++-- Jellyfin.Server/Models/ServerCorsPolicy.cs | 47 ------------------ Jellyfin.Server/Startup.cs | 7 +-- 4 files changed, 53 insertions(+), 63 deletions(-) delete mode 100644 Jellyfin.Server/Models/ServerCorsPolicy.cs diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 8dcce93a4..65db33155 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -16,12 +16,13 @@ using Jellyfin.Api.Auth.RequiresElevationPolicy; using Jellyfin.Api.Constants; using Jellyfin.Api.Controllers; using Jellyfin.Server.Formatters; -using Jellyfin.Server.Models; +using Jellyfin.Server.Middleware; using MediaBrowser.Common.Json; using MediaBrowser.Model.Entities; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Cors.Infrastructure; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; @@ -134,18 +135,15 @@ namespace Jellyfin.Server.Extensions /// /// The service collection. /// An IEnumerable containing all plugin assemblies with API controllers. - /// /// The configured cors hosts. + /// /// /// The MVC builder. public static IMvcBuilder AddJellyfinApi( this IServiceCollection serviceCollection, - IEnumerable pluginAssemblies, - string[] corsHosts) + IEnumerable pluginAssemblies) { IMvcBuilder mvcBuilder = serviceCollection - .AddCors(options => - { - options.AddPolicy(ServerCorsPolicy.DefaultPolicyName, new ServerCorsPolicy(corsHosts).Policy); - }) + .AddCors() + .AddTransient() .Configure(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; diff --git a/Jellyfin.Server/Middleware/CorsPolicyProvider.cs b/Jellyfin.Server/Middleware/CorsPolicyProvider.cs index 7c2b28ed8..02178e29c 100644 --- a/Jellyfin.Server/Middleware/CorsPolicyProvider.cs +++ b/Jellyfin.Server/Middleware/CorsPolicyProvider.cs @@ -1,7 +1,49 @@ -namespace Jellyfin.Server.Middleware +using System; +using System.Threading.Tasks; +using MediaBrowser.Controller.Configuration; +using Microsoft.AspNetCore.Cors.Infrastructure; +using Microsoft.AspNetCore.Http; + +namespace Jellyfin.Server.Middleware { - public class CorsPolicyProvider + /// + /// Cors policy provider. + /// + public class CorsPolicyProvider : ICorsPolicyProvider { - + private readonly IServerConfigurationManager _serverConfigurationManager; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + public CorsPolicyProvider(IServerConfigurationManager serverConfigurationManager) + { + _serverConfigurationManager = serverConfigurationManager; + } + + /// + public Task GetPolicyAsync(HttpContext context, string policyName) + { + var corsHosts = _serverConfigurationManager.Configuration.CorsHosts; + var builder = new CorsPolicyBuilder() + .AllowAnyMethod() + .AllowAnyHeader(); + + // No hosts configured or only default configured. + if (corsHosts.Length == 0 + || (corsHosts.Length == 1 + && string.Equals(corsHosts[0], CorsConstants.AnyOrigin, StringComparison.Ordinal))) + { + builder.AllowAnyOrigin(); + } + else + { + builder.WithOrigins(corsHosts) + .AllowCredentials(); + } + + return Task.FromResult(builder.Build()); + } } } diff --git a/Jellyfin.Server/Models/ServerCorsPolicy.cs b/Jellyfin.Server/Models/ServerCorsPolicy.cs deleted file mode 100644 index 3a45db3b4..000000000 --- a/Jellyfin.Server/Models/ServerCorsPolicy.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using Microsoft.AspNetCore.Cors.Infrastructure; - -namespace Jellyfin.Server.Models -{ - /// - /// Server Cors Policy. - /// - public class ServerCorsPolicy - { - /// - /// Default policy name. - /// - public const string DefaultPolicyName = nameof(ServerCorsPolicy); - - /// - /// Initializes a new instance of the class. - /// - /// The configured cors hosts. - public ServerCorsPolicy(string[] corsHosts) - { - var builder = new CorsPolicyBuilder() - .AllowAnyMethod() - .AllowAnyHeader(); - - // No hosts configured or only default configured. - if (corsHosts.Length == 0 - || (corsHosts.Length == 1 - && string.Equals(corsHosts[0], "*", StringComparison.Ordinal))) - { - builder.AllowAnyOrigin(); - } - else - { - builder.WithOrigins(corsHosts) - .AllowCredentials(); - } - - Policy = builder.Build(); - } - - /// - /// Gets the cors policy. - /// - public CorsPolicy Policy { get; } - } -} diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 5601915a3..16629b5d9 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -5,7 +5,6 @@ using Jellyfin.Api.TypeConverters; using Jellyfin.Server.Extensions; using Jellyfin.Server.Implementations; using Jellyfin.Server.Middleware; -using Jellyfin.Server.Models; using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; @@ -53,9 +52,7 @@ namespace Jellyfin.Server { options.HttpsPort = _serverApplicationHost.HttpsPort; }); - services.AddJellyfinApi( - _serverApplicationHost.GetApiPluginAssemblies(), - _serverConfigurationManager.Configuration.CorsHosts); + services.AddJellyfinApi(_serverApplicationHost.GetApiPluginAssemblies()); services.AddJellyfinApiSwagger(); @@ -118,7 +115,7 @@ namespace Jellyfin.Server mainApp.UseResponseCompression(); - mainApp.UseCors(ServerCorsPolicy.DefaultPolicyName); + mainApp.UseCors(); if (_serverConfigurationManager.Configuration.RequireHttps && _serverApplicationHost.ListenWithHttps) From 527ffaa90c5f041474b24f68735ba7e24e1b8358 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 5 Sep 2020 09:12:50 -0600 Subject: [PATCH 06/40] clean docs --- Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 65db33155..ce9e24558 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -135,11 +135,8 @@ namespace Jellyfin.Server.Extensions /// /// The service collection. /// An IEnumerable containing all plugin assemblies with API controllers. - /// /// /// The MVC builder. - public static IMvcBuilder AddJellyfinApi( - this IServiceCollection serviceCollection, - IEnumerable pluginAssemblies) + public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, IEnumerable pluginAssemblies) { IMvcBuilder mvcBuilder = serviceCollection .AddCors() From 9a74ace84bed207952c0b34a820dcb231bb8b805 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 5 Sep 2020 09:20:58 -0600 Subject: [PATCH 07/40] Add flag for startup completed --- Emby.Server.Implementations/ApplicationHost.cs | 3 ++- MediaBrowser.Model/System/PublicSystemInfo.cs | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c37e87d96..7b70be88f 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1137,7 +1137,8 @@ namespace Emby.Server.Implementations Id = SystemId, OperatingSystem = OperatingSystem.Id.ToString(), ServerName = FriendlyName, - LocalAddress = localAddress + LocalAddress = localAddress, + StartupCompleted = CoreStartupHasCompleted }; } diff --git a/MediaBrowser.Model/System/PublicSystemInfo.cs b/MediaBrowser.Model/System/PublicSystemInfo.cs index b6196a43f..012001aea 100644 --- a/MediaBrowser.Model/System/PublicSystemInfo.cs +++ b/MediaBrowser.Model/System/PublicSystemInfo.cs @@ -39,5 +39,11 @@ namespace MediaBrowser.Model.System /// /// The id. public string Id { get; set; } + + /// + /// Gets or sets a value indicating whether startup is completed. + /// + /// The startup completion status. + public bool StartupCompleted { get; set; } } } From e3fdea2ec947eb191354873b456cdcdc9e7b13b0 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 5 Sep 2020 19:48:37 +0100 Subject: [PATCH 08/40] Update DlnaManager.cs Fix for #4060 --- Emby.Dlna/DlnaManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index d5629684c..28e8df92b 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -220,7 +220,7 @@ namespace Emby.Dlna { try { - return Regex.IsMatch(input, pattern); + return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) || input.Contains(pattern, StringComparison.OrdinalIgnoreCase); } catch (ArgumentException ex) { From 342de39d78431503a0429b76e0ba9d3501b746db Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 5 Sep 2020 13:02:53 -0600 Subject: [PATCH 09/40] Move CorsPolicyProvider to Jellyfin.Server.Configuration --- .../{Middleware => Configuration}/CorsPolicyProvider.cs | 2 +- Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) rename Jellyfin.Server/{Middleware => Configuration}/CorsPolicyProvider.cs (97%) diff --git a/Jellyfin.Server/Middleware/CorsPolicyProvider.cs b/Jellyfin.Server/Configuration/CorsPolicyProvider.cs similarity index 97% rename from Jellyfin.Server/Middleware/CorsPolicyProvider.cs rename to Jellyfin.Server/Configuration/CorsPolicyProvider.cs index 02178e29c..0d04b6bb1 100644 --- a/Jellyfin.Server/Middleware/CorsPolicyProvider.cs +++ b/Jellyfin.Server/Configuration/CorsPolicyProvider.cs @@ -4,7 +4,7 @@ using MediaBrowser.Controller.Configuration; using Microsoft.AspNetCore.Cors.Infrastructure; using Microsoft.AspNetCore.Http; -namespace Jellyfin.Server.Middleware +namespace Jellyfin.Server.Configuration { /// /// Cors policy provider. diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index ce9e24558..9319b573a 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -15,6 +15,7 @@ using Jellyfin.Api.Auth.LocalAccessPolicy; using Jellyfin.Api.Auth.RequiresElevationPolicy; using Jellyfin.Api.Constants; using Jellyfin.Api.Controllers; +using Jellyfin.Server.Configuration; using Jellyfin.Server.Formatters; using Jellyfin.Server.Middleware; using MediaBrowser.Common.Json; From d3e8834e807ebba78ada3a726ceea8dc37de8950 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 5 Sep 2020 20:03:21 +0100 Subject: [PATCH 10/40] Removed memoryStream --- Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index 884bfbe44..6af1b3791 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Net.Http; using System.Threading; @@ -123,10 +123,14 @@ namespace Jellyfin.Api.Helpers state.Dispose(); } - var memoryStream = new MemoryStream(); - await new ProgressiveFileCopier(outputPath, job, transcodingJobHelper, CancellationToken.None).WriteToAsync(memoryStream, CancellationToken.None).ConfigureAwait(false); - memoryStream.Position = 0; - return new FileStreamResult(memoryStream, contentType); + await new ProgressiveFileCopier(outputPath, job, transcodingJobHelper, CancellationToken.None) + .WriteToAsync(httpContext.Response.Body, CancellationToken.None).ConfigureAwait(false); + return new FileStreamResult(httpContext.Response.Body, contentType); + + // var memoryStream = new MemoryStream(); + // await new ProgressiveFileCopier(outputPath, job, transcodingJobHelper, CancellationToken.None).WriteToAsync(memoryStream, CancellationToken.None).ConfigureAwait(false); + // memoryStream.Position = 0; + // return new FileStreamResult(memoryStream, contentType); } finally { From 25e965b85c6b0989e78a81e9167d9eabab81b5be Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 5 Sep 2020 20:33:18 +0100 Subject: [PATCH 11/40] Update FileStreamResponseHelpers.cs --- Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index 6af1b3791..6b516977e 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -126,11 +126,6 @@ namespace Jellyfin.Api.Helpers await new ProgressiveFileCopier(outputPath, job, transcodingJobHelper, CancellationToken.None) .WriteToAsync(httpContext.Response.Body, CancellationToken.None).ConfigureAwait(false); return new FileStreamResult(httpContext.Response.Body, contentType); - - // var memoryStream = new MemoryStream(); - // await new ProgressiveFileCopier(outputPath, job, transcodingJobHelper, CancellationToken.None).WriteToAsync(memoryStream, CancellationToken.None).ConfigureAwait(false); - // memoryStream.Position = 0; - // return new FileStreamResult(memoryStream, contentType); } finally { From ebad504f3bba895d1ccf4d95fe43090301cfc413 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 5 Sep 2020 21:25:47 +0100 Subject: [PATCH 12/40] Fixed profile --- Emby.Dlna/DlnaManager.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index d5629684c..7ab20ab79 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -511,8 +511,7 @@ namespace Emby.Dlna public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress) { - var profile = GetProfile(headers) ?? - GetDefaultProfile(); + var profile = GetDefaultProfile(); var serverId = _appHost.SystemId; From 3c13489cb9de84a4eb9505470e3bed4a00af8a14 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 5 Sep 2020 21:31:05 +0100 Subject: [PATCH 13/40] Update StreamingHelpers.cs --- Jellyfin.Api/Helpers/StreamingHelpers.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index c11799bc8..89ab2da62 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -92,7 +92,6 @@ namespace Jellyfin.Api.Helpers } var enableDlnaHeaders = !string.IsNullOrWhiteSpace(streamingRequest.Params) || - streamingRequest.StreamOptions.ContainsKey("dlnaheaders") || string.Equals(httpRequest.Headers["GetContentFeatures.DLNA.ORG"], "1", StringComparison.OrdinalIgnoreCase); var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper) From f7a56f70c6f87f7e6a64682a89e8c56ef66ea5ae Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 5 Sep 2020 21:31:23 +0100 Subject: [PATCH 14/40] Update StreamingHelpers.cs --- Jellyfin.Api/Helpers/StreamingHelpers.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 89ab2da62..8bcffc6b5 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Globalization; using System.IO; From babdd30a46a19164f63e5b3d56e110bbea6628ef Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 5 Sep 2020 21:49:30 +0100 Subject: [PATCH 15/40] Renamed IsRegExMatch to IsPropertyMatch --- Emby.Dlna/DlnaManager.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 28e8df92b..1145366fa 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -143,7 +143,7 @@ namespace Emby.Dlna { if (!string.IsNullOrEmpty(profileInfo.DeviceDescription)) { - if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription)) + if (deviceInfo.DeviceDescription == null || !IsPropertyMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription)) { return false; } @@ -151,7 +151,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.FriendlyName)) { - if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) + if (deviceInfo.FriendlyName == null || !IsPropertyMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) { return false; } @@ -159,7 +159,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.Manufacturer)) { - if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) + if (deviceInfo.Manufacturer == null || !IsPropertyMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) { return false; } @@ -167,7 +167,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl)) { - if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)) + if (deviceInfo.ManufacturerUrl == null || !IsPropertyMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)) { return false; } @@ -175,7 +175,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.ModelDescription)) { - if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)) + if (deviceInfo.ModelDescription == null || !IsPropertyMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)) { return false; } @@ -183,7 +183,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.ModelName)) { - if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName)) + if (deviceInfo.ModelName == null || !IsPropertyMatch(deviceInfo.ModelName, profileInfo.ModelName)) { return false; } @@ -191,7 +191,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.ModelNumber)) { - if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) + if (deviceInfo.ModelNumber == null || !IsPropertyMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) { return false; } @@ -199,7 +199,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.ModelUrl)) { - if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)) + if (deviceInfo.ModelUrl == null || !IsPropertyMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)) { return false; } @@ -207,7 +207,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.SerialNumber)) { - if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber)) + if (deviceInfo.SerialNumber == null || !IsPropertyMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber)) { return false; } @@ -216,7 +216,7 @@ namespace Emby.Dlna return true; } - private bool IsRegexMatch(string input, string pattern) + private bool IsPropertyMatch(string input, string pattern) { try { From 15e064cb73e80c1dad72c90e8c6db52d3cad6e10 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 5 Sep 2020 22:09:21 +0100 Subject: [PATCH 16/40] Update StreamingHelpers.cs --- Jellyfin.Api/Helpers/StreamingHelpers.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 8bcffc6b5..89ab2da62 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Globalization; using System.IO; From db46a9e7c883c2008055a7c75905c0efe42d5677 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 5 Sep 2020 17:54:34 -0400 Subject: [PATCH 17/40] Clean up JellyfinDb and fix display preferences index. --- Jellyfin.Server.Implementations/JellyfinDb.cs | 40 ++----------------- 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index 08e4db388..92636daa3 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -1,6 +1,5 @@ #pragma warning disable CS1591 -using System; using System.Linq; using Jellyfin.Data.Entities; using Jellyfin.Data.Interfaces; @@ -9,7 +8,7 @@ using Microsoft.EntityFrameworkCore; namespace Jellyfin.Server.Implementations { /// - public partial class JellyfinDb : DbContext + public class JellyfinDb : DbContext { /// /// Initializes a new instance of the class. @@ -138,47 +137,16 @@ namespace Jellyfin.Server.Implementations return base.SaveChanges(); } - /// - public override void Dispose() - { - foreach (var entry in ChangeTracker.Entries()) - { - entry.State = EntityState.Detached; - } - - GC.SuppressFinalize(this); - base.Dispose(); - } - - /// - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - CustomInit(optionsBuilder); - } - /// protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); - OnModelCreatingImpl(modelBuilder); modelBuilder.HasDefaultSchema("jellyfin"); - /*modelBuilder.Entity().HasIndex(t => t.Kind); - - modelBuilder.Entity().HasIndex(t => t.Name) - .IsUnique(); - - modelBuilder.Entity().HasIndex(t => t.UrlId) - .IsUnique();*/ - - OnModelCreatedImpl(modelBuilder); + modelBuilder.Entity() + .HasIndex(entity => new { entity.UserId, entity.Client }) + .IsUnique(); } - - partial void CustomInit(DbContextOptionsBuilder optionsBuilder); - - partial void OnModelCreatingImpl(ModelBuilder modelBuilder); - - partial void OnModelCreatedImpl(ModelBuilder modelBuilder); } } From 46c705b54502ba72abbe459c0e7dd26f9d95ffdb Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 5 Sep 2020 17:55:02 -0400 Subject: [PATCH 18/40] Generate migration for display preferences fix. --- Jellyfin.Server.Implementations/JellyfinDb.cs | 4 + ...533_FixDisplayPreferencesIndex.Designer.cs | 461 ++++++++++++++++++ ...200905220533_FixDisplayPreferencesIndex.cs | 51 ++ .../Migrations/JellyfinDbModelSnapshot.cs | 6 +- 4 files changed, 520 insertions(+), 2 deletions(-) create mode 100644 Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs create mode 100644 Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.cs diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index 92636daa3..45e71f16e 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -144,6 +144,10 @@ namespace Jellyfin.Server.Implementations modelBuilder.HasDefaultSchema("jellyfin"); + modelBuilder.Entity() + .HasIndex(entity => entity.UserId) + .IsUnique(false); + modelBuilder.Entity() .HasIndex(entity => new { entity.UserId, entity.Client }) .IsUnique(); diff --git a/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs new file mode 100644 index 000000000..2234f9d5f --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs @@ -0,0 +1,461 @@ +#pragma warning disable CS1591 + +// +using System; +using Jellyfin.Server.Implementations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Jellyfin.Server.Implementations.Migrations +{ + [DbContext(typeof(JellyfinDb))] + [Migration("20200905220533_FixDisplayPreferencesIndex")] + partial class FixDisplayPreferencesIndex + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("jellyfin") + .HasAnnotation("ProductVersion", "3.1.7"); + + modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("EndHour") + .HasColumnType("REAL"); + + b.Property("StartHour") + .HasColumnType("REAL"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AccessSchedules"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateCreated") + .HasColumnType("TEXT"); + + b.Property("ItemId") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("Overview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("ShortOverview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("Type") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActivityLogs"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChromecastVersion") + .HasColumnType("INTEGER"); + + b.Property("Client") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(32); + + b.Property("DashboardTheme") + .HasColumnType("TEXT") + .HasMaxLength(32); + + b.Property("EnableNextVideoInfoOverlay") + .HasColumnType("INTEGER"); + + b.Property("IndexBy") + .HasColumnType("INTEGER"); + + b.Property("ScrollDirection") + .HasColumnType("INTEGER"); + + b.Property("ShowBackdrop") + .HasColumnType("INTEGER"); + + b.Property("ShowSidebar") + .HasColumnType("INTEGER"); + + b.Property("SkipBackwardLength") + .HasColumnType("INTEGER"); + + b.Property("SkipForwardLength") + .HasColumnType("INTEGER"); + + b.Property("TvHome") + .HasColumnType("TEXT") + .HasMaxLength(32); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "Client") + .IsUnique(); + + b.ToTable("DisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DisplayPreferencesId") + .HasColumnType("INTEGER"); + + b.Property("Order") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DisplayPreferencesId"); + + b.ToTable("HomeSection"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("ImageInfos"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Client") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(32); + + b.Property("IndexBy") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("TEXT"); + + b.Property("RememberIndexing") + .HasColumnType("INTEGER"); + + b.Property("RememberSorting") + .HasColumnType("INTEGER"); + + b.Property("SortBy") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(64); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("ViewType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ItemDisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Permission_Permissions_Guid") + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Permission_Permissions_Guid"); + + b.ToTable("Permissions"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Preference_Preferences_Guid") + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.HasKey("Id"); + + b.HasIndex("Preference_Preferences_Guid"); + + b.ToTable("Preferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AudioLanguagePreference") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("AuthenticationProviderId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("DisplayCollectionsView") + .HasColumnType("INTEGER"); + + b.Property("DisplayMissingEpisodes") + .HasColumnType("INTEGER"); + + b.Property("EasyPassword") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("EnableAutoLogin") + .HasColumnType("INTEGER"); + + b.Property("EnableLocalPassword") + .HasColumnType("INTEGER"); + + b.Property("EnableNextEpisodeAutoPlay") + .HasColumnType("INTEGER"); + + b.Property("EnableUserPreferenceAccess") + .HasColumnType("INTEGER"); + + b.Property("HidePlayedInLatest") + .HasColumnType("INTEGER"); + + b.Property("InternalId") + .HasColumnType("INTEGER"); + + b.Property("InvalidLoginAttemptCount") + .HasColumnType("INTEGER"); + + b.Property("LastActivityDate") + .HasColumnType("TEXT"); + + b.Property("LastLoginDate") + .HasColumnType("TEXT"); + + b.Property("LoginAttemptsBeforeLockout") + .HasColumnType("INTEGER"); + + b.Property("MaxParentalAgeRating") + .HasColumnType("INTEGER"); + + b.Property("MustUpdatePassword") + .HasColumnType("INTEGER"); + + b.Property("Password") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("PasswordResetProviderId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("PlayDefaultAudioTrack") + .HasColumnType("INTEGER"); + + b.Property("RememberAudioSelections") + .HasColumnType("INTEGER"); + + b.Property("RememberSubtitleSelections") + .HasColumnType("INTEGER"); + + b.Property("RemoteClientBitrateLimit") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("SubtitleLanguagePreference") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("SubtitleMode") + .HasColumnType("INTEGER"); + + b.Property("SyncPlayAccess") + .HasColumnType("INTEGER"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("AccessSchedules") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithOne("DisplayPreferences") + .HasForeignKey("Jellyfin.Data.Entities.DisplayPreferences", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b => + { + b.HasOne("Jellyfin.Data.Entities.DisplayPreferences", null) + .WithMany("HomeSections") + .HasForeignKey("DisplayPreferencesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithOne("ProfileImage") + .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("ItemDisplayPreferences") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Permissions") + .HasForeignKey("Permission_Permissions_Guid"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Preferences") + .HasForeignKey("Preference_Preferences_Guid"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.cs b/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.cs new file mode 100644 index 000000000..33c5bb4ca --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.cs @@ -0,0 +1,51 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1601 + +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Jellyfin.Server.Implementations.Migrations +{ + public partial class FixDisplayPreferencesIndex : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_DisplayPreferences_UserId", + schema: "jellyfin", + table: "DisplayPreferences"); + + migrationBuilder.CreateIndex( + name: "IX_DisplayPreferences_UserId", + schema: "jellyfin", + table: "DisplayPreferences", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_DisplayPreferences_UserId_Client", + schema: "jellyfin", + table: "DisplayPreferences", + columns: new[] { "UserId", "Client" }, + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_DisplayPreferences_UserId", + schema: "jellyfin", + table: "DisplayPreferences"); + + migrationBuilder.DropIndex( + name: "IX_DisplayPreferences_UserId_Client", + schema: "jellyfin", + table: "DisplayPreferences"); + + migrationBuilder.CreateIndex( + name: "IX_DisplayPreferences_UserId", + schema: "jellyfin", + table: "DisplayPreferences", + column: "UserId", + unique: true); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs index a6e6a2324..ccfcf96b1 100644 --- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs +++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs @@ -15,7 +15,7 @@ namespace Jellyfin.Server.Implementations.Migrations #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("jellyfin") - .HasAnnotation("ProductVersion", "3.1.6"); + .HasAnnotation("ProductVersion", "3.1.7"); modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => { @@ -136,7 +136,9 @@ namespace Jellyfin.Server.Implementations.Migrations b.HasKey("Id"); - b.HasIndex("UserId") + b.HasIndex("UserId"); + + b.HasIndex("UserId", "Client") .IsUnique(); b.ToTable("DisplayPreferences"); From db9bcdcdc9318c9158249a669922bd464fe8b08e Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 5 Sep 2020 18:48:19 -0600 Subject: [PATCH 19/40] Use proper StartupCompleted flag --- Emby.Server.Implementations/ApplicationHost.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 7b70be88f..eca40b0f2 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -120,6 +120,7 @@ namespace Emby.Server.Implementations private readonly INetworkManager _networkManager; private readonly IXmlSerializer _xmlSerializer; private readonly IStartupOptions _startupOptions; + private readonly IConfigurationManager _configurationManager; private IMediaEncoder _mediaEncoder; private ISessionManager _sessionManager; @@ -238,18 +239,27 @@ namespace Emby.Server.Implementations public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. public ApplicationHost( IServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IStartupOptions options, IFileSystem fileSystem, INetworkManager networkManager, - IServiceCollection serviceCollection) + IServiceCollection serviceCollection, + IConfigurationManager configurationManager) { _xmlSerializer = new MyXmlSerializer(); ServiceCollection = serviceCollection; + _configurationManager = configurationManager; _networkManager = networkManager; networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; @@ -1138,7 +1148,7 @@ namespace Emby.Server.Implementations OperatingSystem = OperatingSystem.Id.ToString(), ServerName = FriendlyName, LocalAddress = localAddress, - StartupCompleted = CoreStartupHasCompleted + StartupCompleted = _configurationManager.CommonConfiguration.IsStartupWizardCompleted }; } From b99446f27a195a71df6260b13fd54a99a5f4ae80 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 6 Sep 2020 13:35:22 +0200 Subject: [PATCH 20/40] Fix sln file --- MediaBrowser.sln | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.sln b/MediaBrowser.sln index fd45dca2a..25402aee1 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -208,6 +208,5 @@ Global {A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {462584F7-5023-4019-9EAC-B98CA458C0A0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} - {7C93C84F-105C-48E5-A878-406FA0A5B296} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} EndGlobalSection EndGlobal From 26c432b56489ec68c0cb708982c6939bf8e6e0e5 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 6 Sep 2020 14:27:31 -0600 Subject: [PATCH 21/40] Rename StartupCompleted to StartupWizardCompleted --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- MediaBrowser.Model/System/PublicSystemInfo.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index eca40b0f2..a9baf893c 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1148,7 +1148,7 @@ namespace Emby.Server.Implementations OperatingSystem = OperatingSystem.Id.ToString(), ServerName = FriendlyName, LocalAddress = localAddress, - StartupCompleted = _configurationManager.CommonConfiguration.IsStartupWizardCompleted + StartupWizardCompleted = _configurationManager.CommonConfiguration.IsStartupWizardCompleted }; } diff --git a/MediaBrowser.Model/System/PublicSystemInfo.cs b/MediaBrowser.Model/System/PublicSystemInfo.cs index 012001aea..d2f7556a5 100644 --- a/MediaBrowser.Model/System/PublicSystemInfo.cs +++ b/MediaBrowser.Model/System/PublicSystemInfo.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Model.System public string Version { get; set; } /// - /// The product name. This is the AssemblyProduct name. + /// Gets or sets the product name. This is the AssemblyProduct name. /// public string ProductName { get; set; } @@ -41,9 +41,9 @@ namespace MediaBrowser.Model.System public string Id { get; set; } /// - /// Gets or sets a value indicating whether startup is completed. + /// Gets or sets a value indicating whether the startup wizard is completed. /// /// The startup completion status. - public bool StartupCompleted { get; set; } + public bool StartupWizardCompleted { get; set; } } } From 343fc8c66804013c4499c487137f12f47f4ac765 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 7 Sep 2020 12:22:33 +0200 Subject: [PATCH 22/40] Fix ObjectDisposedException ``` System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.ObjectDisposedException: Cannot access a disposed object. Object name: 'System.Net.Http.StringContent'. at System.Net.Http.HttpContent.CheckDisposed() at System.Net.Http.HttpContent.CopyToAsync(Stream stream, TransportContext context, CancellationToken cancellationToken) at System.Net.Http.HttpConnection.SendRequestContentAsync(HttpRequestMessage request, HttpContentWriteStream stream, CancellationToken cancellationToken) at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithNtConnectionAuthAsync(HttpConnection connection, HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts) at Emby.Dlna.PlayTo.SsdpHttpClient.SendCommandAsync(String baseUrl, DeviceService service, String command, String postData, String header, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/SsdpHttpClient.cs:line 41 at Emby.Dlna.PlayTo.Device.GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 629 at Emby.Dlna.PlayTo.Device.TimerCallback(Object sender) in /home/loma/dev/jellyfin/Emby.Dlna/PlayTo/Device.cs:line 445 ``` --- Emby.Dlna/PlayTo/SsdpHttpClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs index 8683c8997..c8c36fc97 100644 --- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs +++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs @@ -101,7 +101,7 @@ namespace Emby.Dlna.PlayTo LoadOptions.PreserveWhitespace); } - private Task PostSoapDataAsync( + private async Task PostSoapDataAsync( string url, string soapAction, string postData, @@ -126,7 +126,7 @@ namespace Emby.Dlna.PlayTo options.Content = new StringContent(postData, Encoding.UTF8, MediaTypeNames.Text.Xml); - return _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + return await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); } } } From f85ab53bd9c643edd7eeae037eac3c1fc175f687 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 7 Sep 2020 12:39:16 +0200 Subject: [PATCH 23/40] Fix null exception in tmdb episode provider --- MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index 90e3cea93..ed4739acb 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -109,7 +109,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV item.ParentIndexNumber = info.ParentIndexNumber; item.IndexNumberEnd = info.IndexNumberEnd; - if (response.External_Ids.Tvdb_Id > 0) + if (response.External_Ids != null && response.External_Ids.Tvdb_Id > 0) { item.SetProviderId(MetadataProvider.Tvdb, response.External_Ids.Tvdb_Id.Value.ToString(CultureInfo.InvariantCulture)); } From 57db856fd8157c33c8559a44553c86e4be6b5197 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 7 Sep 2020 12:42:41 +0200 Subject: [PATCH 24/40] Fix null exception in other tmdb providers --- MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs | 2 +- MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs index 73ed13267..f59b75c51 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs @@ -75,7 +75,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV result.Item.Overview = seasonInfo.Overview; - if (seasonInfo.External_Ids.Tvdb_Id > 0) + if (seasonInfo.External_Ids != null && seasonInfo.External_Ids.Tvdb_Id > 0) { result.Item.SetProviderId(MetadataProvider.Tvdb, seasonInfo.External_Ids.Tvdb_Id.Value.ToString(CultureInfo.InvariantCulture)); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index aaba6ffc0..a71699571 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -92,7 +92,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV remoteResult.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture)); remoteResult.SetProviderId(MetadataProvider.Imdb, obj.External_Ids.Imdb_Id); - if (obj.External_Ids.Tvdb_Id > 0) + if (obj.External_Ids != null && obj.External_Ids.Tvdb_Id > 0) { remoteResult.SetProviderId(MetadataProvider.Tvdb, obj.External_Ids.Tvdb_Id.Value.ToString(_usCulture)); } From 12d0f29deada70b2a2504c8fbdcf1c5bb357fdf4 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Sep 2020 12:07:57 +0100 Subject: [PATCH 25/40] Update Emby.Dlna/DlnaManager.cs Co-authored-by: Bond-009 --- Emby.Dlna/DlnaManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 1145366fa..ff6835ce4 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -220,7 +220,7 @@ namespace Emby.Dlna { try { - return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) || input.Contains(pattern, StringComparison.OrdinalIgnoreCase); + return input.Contains(pattern, StringComparison.OrdinalIgnoreCase) || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } catch (ArgumentException ex) { From 6a5df73151d1d16327269943e78b5caa0515e936 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Sep 2020 12:09:15 +0100 Subject: [PATCH 26/40] Update DlnaManager.cs Changed function name to IsRegexOrSubstringMatch --- Emby.Dlna/DlnaManager.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index ff6835ce4..f7a07ac69 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -143,7 +143,7 @@ namespace Emby.Dlna { if (!string.IsNullOrEmpty(profileInfo.DeviceDescription)) { - if (deviceInfo.DeviceDescription == null || !IsPropertyMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription)) + if (deviceInfo.DeviceDescription == null || !IsRegexOrSubstringMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription)) { return false; } @@ -151,7 +151,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.FriendlyName)) { - if (deviceInfo.FriendlyName == null || !IsPropertyMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) + if (deviceInfo.FriendlyName == null || !IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) { return false; } @@ -159,7 +159,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.Manufacturer)) { - if (deviceInfo.Manufacturer == null || !IsPropertyMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) + if (deviceInfo.Manufacturer == null || !IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) { return false; } @@ -167,7 +167,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl)) { - if (deviceInfo.ManufacturerUrl == null || !IsPropertyMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)) + if (deviceInfo.ManufacturerUrl == null || !IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)) { return false; } @@ -175,7 +175,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.ModelDescription)) { - if (deviceInfo.ModelDescription == null || !IsPropertyMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)) + if (deviceInfo.ModelDescription == null || !IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)) { return false; } @@ -183,7 +183,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.ModelName)) { - if (deviceInfo.ModelName == null || !IsPropertyMatch(deviceInfo.ModelName, profileInfo.ModelName)) + if (deviceInfo.ModelName == null || !IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName)) { return false; } @@ -191,7 +191,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.ModelNumber)) { - if (deviceInfo.ModelNumber == null || !IsPropertyMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) + if (deviceInfo.ModelNumber == null || !IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) { return false; } @@ -199,7 +199,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.ModelUrl)) { - if (deviceInfo.ModelUrl == null || !IsPropertyMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)) + if (deviceInfo.ModelUrl == null || !IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)) { return false; } @@ -207,7 +207,7 @@ namespace Emby.Dlna if (!string.IsNullOrEmpty(profileInfo.SerialNumber)) { - if (deviceInfo.SerialNumber == null || !IsPropertyMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber)) + if (deviceInfo.SerialNumber == null || !IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber)) { return false; } @@ -216,7 +216,7 @@ namespace Emby.Dlna return true; } - private bool IsPropertyMatch(string input, string pattern) + private bool IsRegexOrSubstringMatch(string input, string pattern) { try { From eedb520af117e24fd97acbd5513aa354d7567291 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Sep 2020 12:14:02 +0100 Subject: [PATCH 27/40] Removed code that wasn't used. --- Emby.Dlna/DlnaManager.cs | 17 ----------------- MediaBrowser.Model/Dlna/DeviceIdentification.cs | 6 ------ 2 files changed, 23 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index d5629684c..5b1538ffa 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -126,7 +126,6 @@ namespace Emby.Dlna var builder = new StringBuilder(); builder.AppendLine("No matching device profile found. The default will need to be used."); - builder.AppendFormat(CultureInfo.InvariantCulture, "DeviceDescription:{0}", profile.DeviceDescription ?? string.Empty).AppendLine(); builder.AppendFormat(CultureInfo.InvariantCulture, "FriendlyName:{0}", profile.FriendlyName ?? string.Empty).AppendLine(); builder.AppendFormat(CultureInfo.InvariantCulture, "Manufacturer:{0}", profile.Manufacturer ?? string.Empty).AppendLine(); builder.AppendFormat(CultureInfo.InvariantCulture, "ManufacturerUrl:{0}", profile.ManufacturerUrl ?? string.Empty).AppendLine(); @@ -141,22 +140,6 @@ namespace Emby.Dlna private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo) { - if (!string.IsNullOrEmpty(profileInfo.DeviceDescription)) - { - if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription)) - { - return false; - } - } - - if (!string.IsNullOrEmpty(profileInfo.FriendlyName)) - { - if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) - { - return false; - } - } - if (!string.IsNullOrEmpty(profileInfo.Manufacturer)) { if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) diff --git a/MediaBrowser.Model/Dlna/DeviceIdentification.cs b/MediaBrowser.Model/Dlna/DeviceIdentification.cs index 85cc9e3c1..43407383a 100644 --- a/MediaBrowser.Model/Dlna/DeviceIdentification.cs +++ b/MediaBrowser.Model/Dlna/DeviceIdentification.cs @@ -37,12 +37,6 @@ namespace MediaBrowser.Model.Dlna /// The model description. public string ModelDescription { get; set; } - /// - /// Gets or sets the device description. - /// - /// The device description. - public string DeviceDescription { get; set; } - /// /// Gets or sets the model URL. /// From 384ab39f5b3dfb940cb9f612f5b64541c9ec935b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 7 Sep 2020 13:20:39 +0200 Subject: [PATCH 28/40] Fix some warnings --- .../LiveTv/Listings/SchedulesDirect.cs | 8 +- .../CollectionFolderMetadataService.cs | 1 - ...ataService.cs => LiveTvMetadataService.cs} | 0 MediaBrowser.Providers/Manager/ImageSaver.cs | 32 +++--- .../Manager/ItemImageProvider.cs | 56 +++++----- .../Manager/MetadataService.cs | 61 +++++----- .../Manager/ProviderManager.cs | 4 +- .../Manager/ProviderUtils.cs | 4 +- .../Manager/RefreshResult.cs | 15 +++ .../MediaInfo/AudioImageProvider.cs | 16 +-- .../MediaInfo/FFProbeAudioInfo.cs | 29 ++--- .../MediaInfo/FFProbeProvider.cs | 72 ++++++------ .../MediaInfo/FFProbeVideoInfo.cs | 23 ++-- .../MediaInfo/SubtitleDownloader.cs | 31 +++++- .../MediaInfo/SubtitleResolver.cs | 18 +-- .../MediaInfo/SubtitleScheduledTask.cs | 104 +++++++++--------- .../MediaInfo/VideoImageProvider.cs | 9 +- ...{MovieExternalIds.cs => ImdbExternalId.cs} | 18 --- .../Movies/ImdbPersonExternalId.cs | 27 +++++ .../{Extensions.cs => AlbumInfoExtensions.cs} | 0 .../Music/{MusicExternalIds.cs => ImvdbId.cs} | 0 .../Playlists/PlaylistItemsProvider.cs | 10 +- .../Plugins/AudioDb/ArtistProvider.cs | 10 +- .../Plugins/AudioDb/AudioDbAlbumExternalId.cs | 27 +++++ .../AudioDb/AudioDbArtistExternalId.cs | 27 +++++ .../AudioDb/AudioDbOtherAlbumExternalId.cs | 27 +++++ .../AudioDb/AudioDbOtherArtistExternalId.cs | 27 +++++ .../Plugins/AudioDb/ExternalIds.cs | 81 -------------- .../Plugins/AudioDb/Plugin.cs | 12 +- ...rovider.cs => MusicBrainzAlbumProvider.cs} | 29 ++--- .../Plugins/Omdb/OmdbImageProvider.cs | 12 +- .../Plugins/Omdb/OmdbItemProvider.cs | 10 +- .../TV/DummySeasonProvider.cs | 14 ++- .../TV/MissingEpisodeProvider.cs | 31 ++++-- MediaBrowser.Providers/TV/TvExternalIds.cs | 82 -------------- .../TV/TvdbEpisodeExternalId.cs | 28 +++++ MediaBrowser.Providers/TV/TvdbExternalId.cs | 28 +++++ .../TV/TvdbSeasonExternalId.cs | 28 +++++ MediaBrowser.Providers/TV/Zap2ItExternalId.cs | 28 +++++ 39 files changed, 572 insertions(+), 467 deletions(-) rename MediaBrowser.Providers/LiveTv/{ProgramMetadataService.cs => LiveTvMetadataService.cs} (100%) create mode 100644 MediaBrowser.Providers/Manager/RefreshResult.cs rename MediaBrowser.Providers/Movies/{MovieExternalIds.cs => ImdbExternalId.cs} (68%) create mode 100644 MediaBrowser.Providers/Movies/ImdbPersonExternalId.cs rename MediaBrowser.Providers/Music/{Extensions.cs => AlbumInfoExtensions.cs} (100%) rename MediaBrowser.Providers/Music/{MusicExternalIds.cs => ImvdbId.cs} (100%) create mode 100644 MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumExternalId.cs create mode 100644 MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistExternalId.cs create mode 100644 MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherAlbumExternalId.cs create mode 100644 MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherArtistExternalId.cs delete mode 100644 MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs rename MediaBrowser.Providers/Plugins/MusicBrainz/{AlbumProvider.cs => MusicBrainzAlbumProvider.cs} (98%) delete mode 100644 MediaBrowser.Providers/TV/TvExternalIds.cs create mode 100644 MediaBrowser.Providers/TV/TvdbEpisodeExternalId.cs create mode 100644 MediaBrowser.Providers/TV/TvdbExternalId.cs create mode 100644 MediaBrowser.Providers/TV/TvdbSeasonExternalId.cs create mode 100644 MediaBrowser.Providers/TV/Zap2ItExternalId.cs diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index f9ae55af8..655ff5853 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -468,13 +468,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings imageIdString = imageIdString.TrimEnd(',') + "]"; - using var message = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/metadata/programs"); - message.Content = new StringContent(imageIdString, Encoding.UTF8, MediaTypeNames.Application.Json); + using var message = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/metadata/programs") + { + Content = new StringContent(imageIdString, Encoding.UTF8, MediaTypeNames.Application.Json) + }; try { using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false); - await using var response = await innerResponse2.Content.ReadAsStreamAsync(); + await using var response = await innerResponse2.Content.ReadAsStreamAsync().ConfigureAwait(false); return await _jsonSerializer.DeserializeFromStreamAsync>( response).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs index 46f368f72..e0f3131fd 100644 --- a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs +++ b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs @@ -1,6 +1,5 @@ #pragma warning disable CS1591 - using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; diff --git a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs b/MediaBrowser.Providers/LiveTv/LiveTvMetadataService.cs similarity index 100% rename from MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs rename to MediaBrowser.Providers/LiveTv/LiveTvMetadataService.cs diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 413d297cb..19a42d506 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Data.Entities; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -59,6 +58,16 @@ namespace MediaBrowser.Providers.Manager _logger = logger; } + private bool EnableExtraThumbsDuplication + { + get + { + var config = _config.GetConfiguration("xbmcmetadata"); + + return config.EnableExtraThumbsDuplication; + } + } + /// /// Saves the image. /// @@ -69,7 +78,7 @@ namespace MediaBrowser.Providers.Manager /// Index of the image. /// The cancellation token. /// Task. - /// mimeType + /// mimeType. public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken) { return SaveImage(item, source, mimeType, type, imageIndex, null, cancellationToken); @@ -312,7 +321,7 @@ namespace MediaBrowser.Providers.Manager /// /// imageIndex /// or - /// imageIndex + /// imageIndex. /// private ItemImageInfo GetCurrentImage(BaseItem item, ImageType type, int imageIndex) { @@ -328,7 +337,8 @@ namespace MediaBrowser.Providers.Manager /// The path. /// imageIndex /// or - /// imageIndex + /// imageIndex. + /// private void SetImagePath(BaseItem item, ImageType type, int? imageIndex, string path) { item.SetImagePath(type, imageIndex ?? 0, _fileSystem.GetFileInfo(path)); @@ -346,7 +356,7 @@ namespace MediaBrowser.Providers.Manager /// /// imageIndex /// or - /// imageIndex + /// imageIndex. /// private string GetStandardSavePath(BaseItem item, ImageType type, int? imageIndex, string mimeType, bool saveLocally) { @@ -500,7 +510,7 @@ namespace MediaBrowser.Providers.Manager /// Index of the image. /// Type of the MIME. /// IEnumerable{System.String}. - /// imageIndex + /// imageIndex. private string[] GetCompatibleSavePaths(BaseItem item, ImageType type, int? imageIndex, string mimeType) { var season = item as Season; @@ -604,16 +614,6 @@ namespace MediaBrowser.Providers.Manager return new[] { GetStandardSavePath(item, type, imageIndex, mimeType, true) }; } - private bool EnableExtraThumbsDuplication - { - get - { - var config = _config.GetConfiguration("xbmcmetadata"); - - return config.EnableExtraThumbsDuplication; - } - } - /// /// Gets the save path for item in mixed folder. /// diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index 9227b6d93..d0bdbd7c9 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -28,6 +28,22 @@ namespace MediaBrowser.Providers.Manager private readonly IProviderManager _providerManager; private readonly IFileSystem _fileSystem; + /// + /// Image types that are only one per item. + /// + private readonly ImageType[] _singularImages = + { + ImageType.Primary, + ImageType.Art, + ImageType.Banner, + ImageType.Box, + ImageType.BoxRear, + ImageType.Disc, + ImageType.Logo, + ImageType.Menu, + ImageType.Thumb + }; + public ItemImageProvider(ILogger logger, IProviderManager providerManager, IFileSystem fileSystem) { _logger = logger; @@ -175,22 +191,6 @@ namespace MediaBrowser.Providers.Manager } } - /// - /// Image types that are only one per item. - /// - private readonly ImageType[] _singularImages = - { - ImageType.Primary, - ImageType.Art, - ImageType.Banner, - ImageType.Box, - ImageType.BoxRear, - ImageType.Disc, - ImageType.Logo, - ImageType.Menu, - ImageType.Thumb - }; - private bool HasImage(BaseItem item, ImageType type) { return item.HasImage(type); @@ -378,7 +378,6 @@ namespace MediaBrowser.Providers.Manager } else { - var newDateModified = _fileSystem.GetLastWriteTimeUtc(image.FileInfo); // If date changed then we need to reset saved image dimensions @@ -441,7 +440,9 @@ namespace MediaBrowser.Providers.Manager return changed; } - private async Task DownloadImage(BaseItem item, LibraryOptions libraryOptions, + private async Task DownloadImage( + BaseItem item, + LibraryOptions libraryOptions, IRemoteImageProvider provider, RefreshResult result, IEnumerable images, @@ -522,11 +523,6 @@ namespace MediaBrowser.Providers.Manager return false; } - // if (!item.IsSaveLocalMetadataEnabled()) - //{ - // return true; - //} - return true; } @@ -539,13 +535,15 @@ namespace MediaBrowser.Providers.Manager private void SaveImageStub(BaseItem item, ImageType imageType, IEnumerable urls, int newIndex) { - var path = string.Join("|", urls.Take(1).ToArray()); + var path = string.Join('|', urls.Take(1)); - item.SetImage(new ItemImageInfo - { - Path = path, - Type = imageType - }, newIndex); + item.SetImage( + new ItemImageInfo + { + Path = path, + Type = imageType + }, + newIndex); } private async Task DownloadBackdrops(BaseItem item, LibraryOptions libraryOptions, ImageType imageType, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable images, int minWidth, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index d0de58427..42785b057 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -21,12 +21,6 @@ namespace MediaBrowser.Providers.Manager where TItemType : BaseItem, IHasLookupInfo, new() where TIdType : ItemLookupInfo, new() { - protected readonly IServerConfigurationManager ServerConfigurationManager; - protected readonly ILogger> Logger; - protected readonly IProviderManager ProviderManager; - protected readonly IFileSystem FileSystem; - protected readonly ILibraryManager LibraryManager; - protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger> logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) { ServerConfigurationManager = serverConfigurationManager; @@ -36,6 +30,26 @@ namespace MediaBrowser.Providers.Manager LibraryManager = libraryManager; } + protected IServerConfigurationManager ServerConfigurationManager { get; } + + protected ILogger> Logger { get; } + + protected IProviderManager ProviderManager { get; } + + protected IFileSystem FileSystem { get; } + + protected ILibraryManager LibraryManager { get; } + + protected virtual bool EnableUpdatingPremiereDateFromChildren => false; + + protected virtual bool EnableUpdatingGenresFromChildren => false; + + protected virtual bool EnableUpdatingStudiosFromChildren => false; + + protected virtual bool EnableUpdatingOfficialRatingFromChildren => false; + + public virtual int Order => 0; + private FileSystemMetadata TryGetFile(string path, IDirectoryService directoryService) { try @@ -442,14 +456,6 @@ namespace MediaBrowser.Providers.Manager return updateType; } - protected virtual bool EnableUpdatingPremiereDateFromChildren => false; - - protected virtual bool EnableUpdatingGenresFromChildren => false; - - protected virtual bool EnableUpdatingStudiosFromChildren => false; - - protected virtual bool EnableUpdatingOfficialRatingFromChildren => false; - private ItemUpdateType UpdatePremiereDate(TItemType item, IList children) { var updateType = ItemUpdateType.None; @@ -658,7 +664,8 @@ namespace MediaBrowser.Providers.Manager return type == typeof(TItemType); } - protected virtual async Task RefreshWithProviders(MetadataResult metadata, + protected virtual async Task RefreshWithProviders( + MetadataResult metadata, TIdType id, MetadataRefreshOptions options, List providers, @@ -773,7 +780,7 @@ namespace MediaBrowser.Providers.Manager else { // TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields - MergeData(metadata, temp, new MetadataField[] { }, false, false); + MergeData(metadata, temp, Array.Empty(), false, false); MergeData(temp, metadata, item.LockedFields, true, false); } } @@ -900,24 +907,23 @@ namespace MediaBrowser.Providers.Manager } } - protected abstract void MergeData(MetadataResult source, + protected abstract void MergeData( + MetadataResult source, MetadataResult target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings); - public virtual int Order => 0; - private bool HasChanged(BaseItem item, IHasItemChangeMonitor changeMonitor, IDirectoryService directoryService) { try { var hasChanged = changeMonitor.HasChanged(item, directoryService); - // if (hasChanged) - //{ - // logger.LogDebug("{0} reports change to {1}", changeMonitor.GetType().Name, item.Path ?? item.Name); - //} + if (hasChanged) + { + Logger.LogDebug("{0} reports change to {1}", changeMonitor.GetType().Name, item.Path ?? item.Name); + } return hasChanged; } @@ -928,13 +934,4 @@ namespace MediaBrowser.Providers.Manager } } } - - public class RefreshResult - { - public ItemUpdateType UpdateType { get; set; } - - public string ErrorMessage { get; set; } - - public int Failures { get; set; } - } } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 171b824ca..b6fb4267f 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -9,7 +9,6 @@ using System.Net.Http; using System.Net.Mime; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Data.Entities; using Jellyfin.Data.Events; using MediaBrowser.Common.Net; using MediaBrowser.Common.Progress; @@ -905,8 +904,7 @@ namespace MediaBrowser.Providers.Manager return provider.GetImageResponse(url, cancellationToken); } - /// - public IEnumerable GetExternalIds(IHasProviderIds item) + private IEnumerable GetExternalIds(IHasProviderIds item) { return _externalIds.Where(i => { diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs index a4fd6ca84..70a5a6ac1 100644 --- a/MediaBrowser.Providers/Manager/ProviderUtils.cs +++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs @@ -26,12 +26,12 @@ namespace MediaBrowser.Providers.Manager if (source == null) { - throw new ArgumentNullException(nameof(source)); + throw new ArgumentException("Item cannot be null.", nameof(sourceResult)); } if (target == null) { - throw new ArgumentNullException(nameof(target)); + throw new ArgumentException("Item cannot be null.", nameof(targetResult)); } if (!lockedFields.Contains(MetadataField.Name)) diff --git a/MediaBrowser.Providers/Manager/RefreshResult.cs b/MediaBrowser.Providers/Manager/RefreshResult.cs new file mode 100644 index 000000000..72fc61e42 --- /dev/null +++ b/MediaBrowser.Providers/Manager/RefreshResult.cs @@ -0,0 +1,15 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Library; + +namespace MediaBrowser.Providers.Manager +{ + public class RefreshResult + { + public ItemUpdateType UpdateType { get; set; } + + public string ErrorMessage { get; set; } + + public int Failures { get; set; } + } +} diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index f69ec9744..64ad1bddf 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -34,6 +34,10 @@ namespace MediaBrowser.Providers.MediaInfo _fileSystem = fileSystem; } + public string AudioImagesPath => Path.Combine(_config.ApplicationPaths.CachePath, "extracted-audio-images"); + + public string Name => "Image Extractor"; + public IEnumerable GetSupportedImages(BaseItem item) { return new List { ImageType.Primary }; @@ -97,11 +101,11 @@ namespace MediaBrowser.Providers.MediaInfo if (item.GetType() == typeof(Audio)) { - var albumArtist = item.AlbumArtists.FirstOrDefault(); - - if (!string.IsNullOrWhiteSpace(item.Album) && !string.IsNullOrWhiteSpace(albumArtist)) + if (item.AlbumArtists.Count > 0 + && !string.IsNullOrWhiteSpace(item.Album) + && !string.IsNullOrWhiteSpace(item.AlbumArtists[0])) { - filename = (item.Album + "-" + albumArtist).GetMD5().ToString("N", CultureInfo.InvariantCulture); + filename = (item.Album + "-" + item.AlbumArtists[0]).GetMD5().ToString("N", CultureInfo.InvariantCulture); } else { @@ -121,10 +125,6 @@ namespace MediaBrowser.Providers.MediaInfo return Path.Join(AudioImagesPath, prefix, filename); } - public string AudioImagesPath => Path.Combine(_config.ApplicationPaths.CachePath, "extracted-audio-images"); - - public string Name => "Image Extractor"; - public bool Supports(BaseItem item) { if (item.IsShortcut) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index 77f03580a..945463666 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -37,7 +37,9 @@ namespace MediaBrowser.Providers.MediaInfo _mediaSourceManager = mediaSourceManager; } - public async Task Probe(T item, MetadataRefreshOptions options, + public async Task Probe( + T item, + MetadataRefreshOptions options, CancellationToken cancellationToken) where T : Audio { @@ -52,19 +54,21 @@ namespace MediaBrowser.Providers.MediaInfo protocol = _mediaSourceManager.GetPathProtocol(path); } - var result = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest - { - MediaType = DlnaProfileType.Audio, - MediaSource = new MediaSourceInfo + var result = await _mediaEncoder.GetMediaInfo( + new MediaInfoRequest { - Path = path, - Protocol = protocol - } - }, cancellationToken).ConfigureAwait(false); + MediaType = DlnaProfileType.Audio, + MediaSource = new MediaSourceInfo + { + Path = path, + Protocol = protocol + } + }, + cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); - Fetch(item, cancellationToken, result); + Fetch(item, result, cancellationToken); } return ItemUpdateType.MetadataImport; @@ -74,10 +78,9 @@ namespace MediaBrowser.Providers.MediaInfo /// Fetches the specified audio. /// /// The audio. - /// The cancellation token. /// The media information. - /// Task. - protected void Fetch(Audio audio, CancellationToken cancellationToken, Model.MediaInfo.MediaInfo mediaInfo) + /// The cancellation token. + protected void Fetch(Audio audio, Model.MediaInfo.MediaInfo mediaInfo, CancellationToken cancellationToken) { var mediaStreams = mediaInfo.MediaStreams; diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index 9926275ae..c61187fdf 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -5,8 +5,6 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -20,9 +18,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.MediaInfo @@ -50,9 +46,43 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IChapterManager _chapterManager; private readonly ILibraryManager _libraryManager; private readonly IMediaSourceManager _mediaSourceManager; + private readonly SubtitleResolver _subtitleResolver; + + private readonly Task _cachedTask = Task.FromResult(ItemUpdateType.None); + + public FFProbeProvider( + ILogger logger, + IMediaSourceManager mediaSourceManager, + IMediaEncoder mediaEncoder, + IItemRepository itemRepo, + IBlurayExaminer blurayExaminer, + ILocalizationManager localization, + IEncodingManager encodingManager, + IServerConfigurationManager config, + ISubtitleManager subtitleManager, + IChapterManager chapterManager, + ILibraryManager libraryManager) + { + _logger = logger; + _mediaEncoder = mediaEncoder; + _itemRepo = itemRepo; + _blurayExaminer = blurayExaminer; + _localization = localization; + _encodingManager = encodingManager; + _config = config; + _subtitleManager = subtitleManager; + _chapterManager = chapterManager; + _libraryManager = libraryManager; + _mediaSourceManager = mediaSourceManager; + + _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager); + } public string Name => "ffprobe"; + // Run last + public int Order => 100; + public bool HasChanged(BaseItem item, IDirectoryService directoryService) { var video = item as Video; @@ -117,37 +147,6 @@ namespace MediaBrowser.Providers.MediaInfo return FetchAudioInfo(item, options, cancellationToken); } - private SubtitleResolver _subtitleResolver; - - public FFProbeProvider( - ILogger logger, - IMediaSourceManager mediaSourceManager, - IMediaEncoder mediaEncoder, - IItemRepository itemRepo, - IBlurayExaminer blurayExaminer, - ILocalizationManager localization, - IEncodingManager encodingManager, - IServerConfigurationManager config, - ISubtitleManager subtitleManager, - IChapterManager chapterManager, - ILibraryManager libraryManager) - { - _logger = logger; - _mediaEncoder = mediaEncoder; - _itemRepo = itemRepo; - _blurayExaminer = blurayExaminer; - _localization = localization; - _encodingManager = encodingManager; - _config = config; - _subtitleManager = subtitleManager; - _chapterManager = chapterManager; - _libraryManager = libraryManager; - _mediaSourceManager = mediaSourceManager; - - _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager); - } - - private readonly Task _cachedTask = Task.FromResult(ItemUpdateType.None); public Task FetchVideoInfo(T item, MetadataRefreshOptions options, CancellationToken cancellationToken) where T : Video { @@ -234,8 +233,5 @@ namespace MediaBrowser.Providers.MediaInfo return prober.Probe(item, options, cancellationToken); } - - // Run last - public int Order => 100; } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 53a6bb619..776dee780 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -539,17 +539,18 @@ namespace MediaBrowser.Providers.MediaInfo if (enableSubtitleDownloading && enabled) { - var downloadedLanguages = await new SubtitleDownloader(_logger, - _subtitleManager) - .DownloadSubtitles(video, - currentStreams.Concat(externalSubtitleStreams).ToList(), - skipIfEmbeddedSubtitlesPresent, - skipIfAudioTrackMatches, - requirePerfectMatch, - subtitleDownloadLanguages, - libraryOptions.DisabledSubtitleFetchers, - libraryOptions.SubtitleFetcherOrder, - cancellationToken).ConfigureAwait(false); + var downloadedLanguages = await new SubtitleDownloader( + _logger, + _subtitleManager).DownloadSubtitles( + video, + currentStreams.Concat(externalSubtitleStreams).ToList(), + skipIfEmbeddedSubtitlesPresent, + skipIfAudioTrackMatches, + requirePerfectMatch, + subtitleDownloadLanguages, + libraryOptions.DisabledSubtitleFetchers, + libraryOptions.SubtitleFetcherOrder, + cancellationToken).ConfigureAwait(false); // Rescan if (downloadedLanguages.Count > 0) diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs index acddb73d0..912aedb0d 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs @@ -42,8 +42,16 @@ namespace MediaBrowser.Providers.MediaInfo foreach (var lang in languages) { - var downloaded = await DownloadSubtitles(video, mediaStreams, skipIfEmbeddedSubtitlesPresent, - skipIfAudioTrackMatches, requirePerfectMatch, lang, disabledSubtitleFetchers, subtitleFetcherOrder, cancellationToken).ConfigureAwait(false); + var downloaded = await DownloadSubtitles( + video, + mediaStreams, + skipIfEmbeddedSubtitlesPresent, + skipIfAudioTrackMatches, + requirePerfectMatch, + lang, + disabledSubtitleFetchers, + subtitleFetcherOrder, + cancellationToken).ConfigureAwait(false); if (downloaded) { @@ -54,7 +62,8 @@ namespace MediaBrowser.Providers.MediaInfo return downloadedLanguages; } - public Task DownloadSubtitles(Video video, + public Task DownloadSubtitles( + Video video, List mediaStreams, bool skipIfEmbeddedSubtitlesPresent, bool skipIfAudioTrackMatches, @@ -90,11 +99,21 @@ namespace MediaBrowser.Providers.MediaInfo return Task.FromResult(false); } - return DownloadSubtitles(video, mediaStreams, skipIfEmbeddedSubtitlesPresent, skipIfAudioTrackMatches, - requirePerfectMatch, lang, disabledSubtitleFetchers, subtitleFetcherOrder, mediaType, cancellationToken); + return DownloadSubtitles( + video, + mediaStreams, + skipIfEmbeddedSubtitlesPresent, + skipIfAudioTrackMatches, + requirePerfectMatch, + lang, + disabledSubtitleFetchers, + subtitleFetcherOrder, + mediaType, + cancellationToken); } - private async Task DownloadSubtitles(Video video, + private async Task DownloadSubtitles( + Video video, List mediaStreams, bool skipIfEmbeddedSubtitlesPresent, bool skipIfAudioTrackMatches, diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs index 43659b68c..e9f999c6d 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs @@ -66,9 +66,10 @@ namespace MediaBrowser.Providers.MediaInfo return streams; } - public List GetExternalSubtitleFiles(Video video, - IDirectoryService directoryService, - bool clearCache) + public List GetExternalSubtitleFiles( + Video video, + IDirectoryService directoryService, + bool clearCache) { var list = new List(); @@ -87,7 +88,9 @@ namespace MediaBrowser.Providers.MediaInfo return list; } - private void AddExternalSubtitleStreams(List streams, string folder, + private void AddExternalSubtitleStreams( + List streams, + string folder, string videoPath, int startIndex, IDirectoryService directoryService, @@ -98,7 +101,8 @@ namespace MediaBrowser.Providers.MediaInfo AddExternalSubtitleStreams(streams, videoPath, startIndex, files); } - public void AddExternalSubtitleStreams(List streams, + public void AddExternalSubtitleStreams( + List streams, string videoPath, int startIndex, string[] files) @@ -185,8 +189,8 @@ namespace MediaBrowser.Providers.MediaInfo private string NormalizeFilenameForSubtitleComparison(string filename) { // Try to account for sloppy file naming - filename = filename.Replace("_", string.Empty); - filename = filename.Replace(" ", string.Empty); + filename = filename.Replace("_", string.Empty, StringComparison.Ordinal); + filename = filename.Replace(" ", string.Empty, StringComparison.Ordinal); // can't normalize this due to languages such as pt-br // filename = filename.Replace("-", string.Empty); diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs index 91ab7b4ac..d231bfa2f 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs @@ -12,11 +12,10 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.MediaInfo { @@ -25,29 +24,37 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ILibraryManager _libraryManager; private readonly IServerConfigurationManager _config; private readonly ISubtitleManager _subtitleManager; - private readonly IMediaSourceManager _mediaSourceManager; private readonly ILogger _logger; - private readonly IJsonSerializer _json; private readonly ILocalizationManager _localization; public SubtitleScheduledTask( ILibraryManager libraryManager, - IJsonSerializer json, IServerConfigurationManager config, ISubtitleManager subtitleManager, ILogger logger, - IMediaSourceManager mediaSourceManager, ILocalizationManager localization) { _libraryManager = libraryManager; _config = config; _subtitleManager = subtitleManager; _logger = logger; - _mediaSourceManager = mediaSourceManager; - _json = json; _localization = localization; } + public string Name => _localization.GetLocalizedString("TaskDownloadMissingSubtitles"); + + public string Description => _localization.GetLocalizedString("TaskDownloadMissingSubtitlesDescription"); + + public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); + + public string Key => "DownloadSubtitles"; + + public bool IsHidden => false; + + public bool IsEnabled => true; + + public bool IsLogged => true; + private SubtitleOptions GetOptions() { return _config.GetConfiguration("subtitles"); @@ -66,23 +73,23 @@ namespace MediaBrowser.Providers.MediaInfo var libraryOptions = _libraryManager.GetLibraryOptions(library); string[] subtitleDownloadLanguages; - bool SkipIfEmbeddedSubtitlesPresent; - bool SkipIfAudioTrackMatches; - bool RequirePerfectMatch; + bool skipIfEmbeddedSubtitlesPresent; + bool skipIfAudioTrackMatches; + bool requirePerfectMatch; if (libraryOptions.SubtitleDownloadLanguages == null) { subtitleDownloadLanguages = options.DownloadLanguages; - SkipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent; - SkipIfAudioTrackMatches = options.SkipIfAudioTrackMatches; - RequirePerfectMatch = options.RequirePerfectMatch; + skipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent; + skipIfAudioTrackMatches = options.SkipIfAudioTrackMatches; + requirePerfectMatch = options.RequirePerfectMatch; } else { subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages; - SkipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent; - SkipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches; - RequirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch; + skipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent; + skipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches; + requirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch; } foreach (var lang in subtitleDownloadLanguages) @@ -98,12 +105,12 @@ namespace MediaBrowser.Providers.MediaInfo Recursive = true }; - if (SkipIfAudioTrackMatches) + if (skipIfAudioTrackMatches) { query.HasNoAudioTrackWithLanguage = lang; } - if (SkipIfEmbeddedSubtitlesPresent) + if (skipIfEmbeddedSubtitlesPresent) { // Exclude if it already has any subtitles of the same language query.HasNoSubtitleTrackWithLanguage = lang; @@ -160,36 +167,37 @@ namespace MediaBrowser.Providers.MediaInfo var libraryOptions = _libraryManager.GetLibraryOptions(video); string[] subtitleDownloadLanguages; - bool SkipIfEmbeddedSubtitlesPresent; - bool SkipIfAudioTrackMatches; - bool RequirePerfectMatch; + bool skipIfEmbeddedSubtitlesPresent; + bool skipIfAudioTrackMatches; + bool requirePerfectMatch; if (libraryOptions.SubtitleDownloadLanguages == null) { subtitleDownloadLanguages = options.DownloadLanguages; - SkipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent; - SkipIfAudioTrackMatches = options.SkipIfAudioTrackMatches; - RequirePerfectMatch = options.RequirePerfectMatch; + skipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent; + skipIfAudioTrackMatches = options.SkipIfAudioTrackMatches; + requirePerfectMatch = options.RequirePerfectMatch; } else { subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages; - SkipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent; - SkipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches; - RequirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch; + skipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent; + skipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches; + requirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch; } - var downloadedLanguages = await new SubtitleDownloader(_logger, - _subtitleManager) - .DownloadSubtitles(video, - mediaStreams, - SkipIfEmbeddedSubtitlesPresent, - SkipIfAudioTrackMatches, - RequirePerfectMatch, - subtitleDownloadLanguages, - libraryOptions.DisabledSubtitleFetchers, - libraryOptions.SubtitleFetcherOrder, - cancellationToken).ConfigureAwait(false); + var downloadedLanguages = await new SubtitleDownloader( + _logger, + _subtitleManager).DownloadSubtitles( + video, + mediaStreams, + skipIfEmbeddedSubtitlesPresent, + skipIfAudioTrackMatches, + requirePerfectMatch, + subtitleDownloadLanguages, + libraryOptions.DisabledSubtitleFetchers, + libraryOptions.SubtitleFetcherOrder, + cancellationToken).ConfigureAwait(false); // Rescan if (downloadedLanguages.Count > 0) @@ -203,25 +211,11 @@ namespace MediaBrowser.Providers.MediaInfo public IEnumerable GetDefaultTriggers() { - return new[] { - + return new[] + { // Every so often new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} }; } - - public string Name => _localization.GetLocalizedString("TaskDownloadMissingSubtitles"); - - public string Description => _localization.GetLocalizedString("TaskDownloadMissingSubtitlesDescription"); - - public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); - - public string Key => "DownloadSubtitles"; - - public bool IsHidden => false; - - public bool IsEnabled => true; - - public bool IsLogged => true; } } diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index e23854d90..fc38d3832 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -29,6 +29,11 @@ namespace MediaBrowser.Providers.MediaInfo _fileSystem = fileSystem; } + public string Name => "Screen Grabber"; + + // Make sure this comes after internet image providers + public int Order => 100; + public IEnumerable GetSupportedImages(BaseItem item) { return new List { ImageType.Primary }; @@ -127,8 +132,6 @@ namespace MediaBrowser.Providers.MediaInfo }; } - public string Name => "Screen Grabber"; - public bool Supports(BaseItem item) { if (item.IsShortcut) @@ -150,7 +153,5 @@ namespace MediaBrowser.Providers.MediaInfo return false; } - // Make sure this comes after internet image providers - public int Order => 100; } } diff --git a/MediaBrowser.Providers/Movies/MovieExternalIds.cs b/MediaBrowser.Providers/Movies/ImdbExternalId.cs similarity index 68% rename from MediaBrowser.Providers/Movies/MovieExternalIds.cs rename to MediaBrowser.Providers/Movies/ImdbExternalId.cs index 14080841c..a8d74aa0b 100644 --- a/MediaBrowser.Providers/Movies/MovieExternalIds.cs +++ b/MediaBrowser.Providers/Movies/ImdbExternalId.cs @@ -36,22 +36,4 @@ namespace MediaBrowser.Providers.Movies return item is Movie || item is MusicVideo || item is Series || item is Episode || item is Trailer; } } - - public class ImdbPersonExternalId : IExternalId - { - /// - public string ProviderName => "IMDb"; - - /// - public string Key => MetadataProvider.Imdb.ToString(); - - /// - public ExternalIdMediaType? Type => ExternalIdMediaType.Person; - - /// - public string UrlFormatString => "https://www.imdb.com/name/{0}"; - - /// - public bool Supports(IHasProviderIds item) => item is Person; - } } diff --git a/MediaBrowser.Providers/Movies/ImdbPersonExternalId.cs b/MediaBrowser.Providers/Movies/ImdbPersonExternalId.cs new file mode 100644 index 000000000..8151ab471 --- /dev/null +++ b/MediaBrowser.Providers/Movies/ImdbPersonExternalId.cs @@ -0,0 +1,27 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Providers.Movies +{ + public class ImdbPersonExternalId : IExternalId + { + /// + public string ProviderName => "IMDb"; + + /// + public string Key => MetadataProvider.Imdb.ToString(); + + /// + public ExternalIdMediaType? Type => ExternalIdMediaType.Person; + + /// + public string UrlFormatString => "https://www.imdb.com/name/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is Person; + } +} diff --git a/MediaBrowser.Providers/Music/Extensions.cs b/MediaBrowser.Providers/Music/AlbumInfoExtensions.cs similarity index 100% rename from MediaBrowser.Providers/Music/Extensions.cs rename to MediaBrowser.Providers/Music/AlbumInfoExtensions.cs diff --git a/MediaBrowser.Providers/Music/MusicExternalIds.cs b/MediaBrowser.Providers/Music/ImvdbId.cs similarity index 100% rename from MediaBrowser.Providers/Music/MusicExternalIds.cs rename to MediaBrowser.Providers/Music/ImvdbId.cs diff --git a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs index 5cc0a527e..067d585cb 100644 --- a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs +++ b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs @@ -10,7 +10,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; using PlaylistsNET.Content; @@ -23,16 +22,17 @@ namespace MediaBrowser.Providers.Playlists IHasItemChangeMonitor { private readonly ILogger _logger; - private IFileSystem _fileSystem; - public PlaylistItemsProvider(IFileSystem fileSystem, ILogger logger) + public PlaylistItemsProvider(ILogger logger) { - _fileSystem = fileSystem; _logger = logger; } public string Name => "Playlist Reader"; + // Run last + public int Order => 100; + public Task FetchAsync(Playlist item, MetadataRefreshOptions options, CancellationToken cancellationToken) { var path = item.Path; @@ -163,7 +163,5 @@ namespace MediaBrowser.Providers.Playlists return false; } - // Run last - public int Order => 100; } } diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs index 670c0cd05..72dad8a25 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs @@ -23,16 +23,14 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { public class AudioDbArtistProvider : IRemoteMetadataProvider, IHasOrder { + private const string ApiKey = "195003"; + public const string BaseUrl = "https://www.theaudiodb.com/api/v1/json/" + ApiKey; + private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; private readonly IHttpClientFactory _httpClientFactory; private readonly IJsonSerializer _json; - public static AudioDbArtistProvider Current; - - private const string ApiKey = "195003"; - public const string BaseUrl = "https://www.theaudiodb.com/api/v1/json/" + ApiKey; - public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json) { _config = config; @@ -42,6 +40,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb Current = this; } + public static AudioDbArtistProvider Current { get; private set; } + /// public string Name => "TheAudioDB"; diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumExternalId.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumExternalId.cs new file mode 100644 index 000000000..138cfef19 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumExternalId.cs @@ -0,0 +1,27 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Providers.Plugins.AudioDb +{ + public class AudioDbAlbumExternalId : IExternalId + { + /// + public string ProviderName => "TheAudioDb"; + + /// + public string Key => MetadataProvider.AudioDbAlbum.ToString(); + + /// + public ExternalIdMediaType? Type => null; + + /// + public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is MusicAlbum; + } +} diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistExternalId.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistExternalId.cs new file mode 100644 index 000000000..8aceb48c0 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistExternalId.cs @@ -0,0 +1,27 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Providers.Plugins.AudioDb +{ + public class AudioDbArtistExternalId : IExternalId + { + /// + public string ProviderName => "TheAudioDb"; + + /// + public string Key => MetadataProvider.AudioDbArtist.ToString(); + + /// + public ExternalIdMediaType? Type => ExternalIdMediaType.Artist; + + /// + public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is MusicArtist; + } +} diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherAlbumExternalId.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherAlbumExternalId.cs new file mode 100644 index 000000000..014481da2 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherAlbumExternalId.cs @@ -0,0 +1,27 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Providers.Plugins.AudioDb +{ + public class AudioDbOtherAlbumExternalId : IExternalId + { + /// + public string ProviderName => "TheAudioDb"; + + /// + public string Key => MetadataProvider.AudioDbAlbum.ToString(); + + /// + public ExternalIdMediaType? Type => ExternalIdMediaType.Album; + + /// + public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is Audio; + } +} diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherArtistExternalId.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherArtistExternalId.cs new file mode 100644 index 000000000..787539104 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherArtistExternalId.cs @@ -0,0 +1,27 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Providers.Plugins.AudioDb +{ + public class AudioDbOtherArtistExternalId : IExternalId + { + /// + public string ProviderName => "TheAudioDb"; + + /// + public string Key => MetadataProvider.AudioDbArtist.ToString(); + + /// + public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist; + + /// + public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum; + } +} diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs b/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs deleted file mode 100644 index 1cc1f0fa1..000000000 --- a/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs +++ /dev/null @@ -1,81 +0,0 @@ -#pragma warning disable CS1591 - -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; - -namespace MediaBrowser.Providers.Plugins.AudioDb -{ - public class AudioDbAlbumExternalId : IExternalId - { - /// - public string ProviderName => "TheAudioDb"; - - /// - public string Key => MetadataProvider.AudioDbAlbum.ToString(); - - /// - public ExternalIdMediaType? Type => null; - - /// - public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; - - /// - public bool Supports(IHasProviderIds item) => item is MusicAlbum; - } - - public class AudioDbOtherAlbumExternalId : IExternalId - { - /// - public string ProviderName => "TheAudioDb"; - - /// - public string Key => MetadataProvider.AudioDbAlbum.ToString(); - - /// - public ExternalIdMediaType? Type => ExternalIdMediaType.Album; - - /// - public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; - - /// - public bool Supports(IHasProviderIds item) => item is Audio; - } - - public class AudioDbArtistExternalId : IExternalId - { - /// - public string ProviderName => "TheAudioDb"; - - /// - public string Key => MetadataProvider.AudioDbArtist.ToString(); - - /// - public ExternalIdMediaType? Type => ExternalIdMediaType.Artist; - - /// - public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; - - /// - public bool Supports(IHasProviderIds item) => item is MusicArtist; - } - - public class AudioDbOtherArtistExternalId : IExternalId - { - /// - public string ProviderName => "TheAudioDb"; - - /// - public string Key => MetadataProvider.AudioDbArtist.ToString(); - - /// - public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist; - - /// - public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; - - /// - public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum; - } -} diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs b/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs index 54054d015..b5bd72ff0 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs @@ -11,6 +11,12 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { public class Plugin : BasePlugin, IHasWebPages { + public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + : base(applicationPaths, xmlSerializer) + { + Instance = this; + } + public static Plugin Instance { get; private set; } public override Guid Id => new Guid("a629c0da-fac5-4c7e-931a-7174223f14c8"); @@ -22,12 +28,6 @@ namespace MediaBrowser.Providers.Plugins.AudioDb // TODO remove when plugin removed from server. public override string ConfigurationFileName => "Jellyfin.Plugin.AudioDb.xml"; - public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) - : base(applicationPaths, xmlSerializer) - { - Instance = this; - } - public IEnumerable GetPages() { yield return new PluginPageInfo diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs similarity index 98% rename from MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs rename to MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs index 8414c9328..46f8988f2 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs @@ -8,7 +8,6 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; -using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -26,14 +25,6 @@ namespace MediaBrowser.Providers.Music { public class MusicBrainzAlbumProvider : IRemoteMetadataProvider, IHasOrder { - /// - /// The Jellyfin user-agent is unrestricted but source IP must not exceed - /// one request per second, therefore we rate limit to avoid throttling. - /// Be prudent, use a value slightly above the minimun required. - /// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting - /// - private readonly long _musicBrainzQueryIntervalMs; - /// /// For each single MB lookup/search, this is the maximum number of /// attempts that shall be made whilst receiving a 503 Server @@ -41,7 +32,13 @@ namespace MediaBrowser.Providers.Music /// private const uint MusicBrainzQueryAttempts = 5u; - internal static MusicBrainzAlbumProvider Current; + /// + /// The Jellyfin user-agent is unrestricted but source IP must not exceed + /// one request per second, therefore we rate limit to avoid throttling. + /// Be prudent, use a value slightly above the minimun required. + /// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting + /// + private readonly long _musicBrainzQueryIntervalMs; private readonly IHttpClientFactory _httpClientFactory; private readonly IApplicationHost _appHost; @@ -69,6 +66,8 @@ namespace MediaBrowser.Providers.Music Current = this; } + internal static MusicBrainzAlbumProvider Current { get; private set; } + /// public string Name => "MusicBrainz"; @@ -112,7 +111,7 @@ namespace MediaBrowser.Providers.Music else { // I'm sure there is a better way but for now it resolves search for 12" Mixes - var queryName = searchInfo.Name.Replace("\"", string.Empty); + var queryName = searchInfo.Name.Replace("\"", string.Empty, StringComparison.Ordinal); url = string.Format( CultureInfo.InvariantCulture, @@ -277,7 +276,9 @@ namespace MediaBrowser.Providers.Music private async Task GetReleaseResult(string albumName, string artistId, CancellationToken cancellationToken) { - var url = string.Format(CultureInfo.InvariantCulture, "/ws/2/release/?query=\"{0}\" AND arid:{1}", + var url = string.Format( + CultureInfo.InvariantCulture, + "/ws/2/release/?query=\"{0}\" AND arid:{1}", WebUtility.UrlEncode(albumName), artistId); @@ -496,7 +497,7 @@ namespace MediaBrowser.Providers.Music } } - private static ValueTuple ParseArtistCredit(XmlReader reader) + private static (string, string) ParseArtistCredit(XmlReader reader) { reader.MoveToContent(); reader.Read(); @@ -531,7 +532,7 @@ namespace MediaBrowser.Providers.Music } } - return new ValueTuple(); + return default; } private static (string, string) ParseArtistNameCredit(XmlReader reader) diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs index 8b8fea09e..8f4240dc1 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs @@ -36,6 +36,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb _appHost = appHost; } + public string Name => "The Open Movie Database"; + + // After other internet providers, because they're better + // But before fallback providers like screengrab + public int Order => 90; + public IEnumerable GetSupportedImages(BaseItem item) { return new List @@ -86,15 +92,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } - public string Name => "The Open Movie Database"; - public bool Supports(BaseItem item) { return item is Movie || item is Trailer || item is Episode; } - - // After other internet providers, because they're better - // But before fallback providers like screengrab - public int Order => 90; } } diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs index d53eba7e9..705359d2c 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs @@ -49,6 +49,8 @@ namespace MediaBrowser.Providers.Plugins.Omdb _appHost = appHost; } + public string Name => "The Open Movie Database"; + // After primary option public int Order => 2; @@ -199,8 +201,6 @@ namespace MediaBrowser.Providers.Plugins.Omdb return GetSearchResults(searchInfo, "movie", cancellationToken); } - public string Name => "The Open Movie Database"; - public async Task> GetMetadata(SeriesInfo info, CancellationToken cancellationToken) { var result = new MetadataResult @@ -263,14 +263,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb { var results = await GetSearchResultsInternal(info, "movie", false, cancellationToken).ConfigureAwait(false); var first = results.FirstOrDefault(); - return first == null ? null : first.GetProviderId(MetadataProvider.Imdb); + return first?.GetProviderId(MetadataProvider.Imdb); } private async Task GetSeriesImdbId(SeriesInfo info, CancellationToken cancellationToken) { var results = await GetSearchResultsInternal(info, "series", false, cancellationToken).ConfigureAwait(false); var first = results.FirstOrDefault(); - return first == null ? null : first.GetProviderId(MetadataProvider.Imdb); + return first?.GetProviderId(MetadataProvider.Imdb); } public Task GetImageResponse(string url, CancellationToken cancellationToken) @@ -278,7 +278,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } - class SearchResult + private class SearchResult { public string Title { get; set; } diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs index 0c09cdef6..a0f7f6cfd 100644 --- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs +++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs @@ -124,7 +124,8 @@ namespace MediaBrowser.Providers.TV /// /// Adds the season. /// - public async Task AddSeason(Series series, + public async Task AddSeason( + Series series, int? seasonNumber, bool isVirtualItem, CancellationToken cancellationToken) @@ -211,11 +212,14 @@ namespace MediaBrowser.Providers.TV { _logger.LogInformation("Removing virtual season {0} {1}", series.Name, seasonToRemove.IndexNumber); - _libraryManager.DeleteItem(seasonToRemove, new DeleteOptions - { - DeleteFileLocation = true + _libraryManager.DeleteItem( + seasonToRemove, + new DeleteOptions + { + DeleteFileLocation = true - }, false); + }, + false); hasChanges = true; } diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 09850beb0..616c61ec0 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -159,7 +159,7 @@ namespace MediaBrowser.Providers.TV var now = DateTime.UtcNow.AddDays(-UnairedEpisodeThresholdDays); - if (airDate < now && addMissingEpisodes || airDate > now) + if ((airDate < now && addMissingEpisodes) || airDate > now) { // tvdb has a lot of nearly blank episodes _logger.LogInformation("Creating virtual missing/unaired episode {0} {1}x{2}", series.Name, tuple.seasonNumber, tuple.episodenumber); @@ -232,10 +232,13 @@ namespace MediaBrowser.Providers.TV foreach (var episodeToRemove in episodesToRemove) { - _libraryManager.DeleteItem(episodeToRemove, new DeleteOptions - { - DeleteFileLocation = true - }, false); + _libraryManager.DeleteItem( + episodeToRemove, + new DeleteOptions + { + DeleteFileLocation = true + }, + false); hasChanges = true; } @@ -246,7 +249,7 @@ namespace MediaBrowser.Providers.TV /// /// Removes the obsolete or missing seasons. /// - /// + /// All recursive children. /// The episode lookup. /// . private bool RemoveObsoleteOrMissingSeasons( @@ -297,10 +300,13 @@ namespace MediaBrowser.Providers.TV foreach (var seasonToRemove in seasonsToRemove) { - _libraryManager.DeleteItem(seasonToRemove, new DeleteOptions - { - DeleteFileLocation = true - }, false); + _libraryManager.DeleteItem( + seasonToRemove, + new DeleteOptions + { + DeleteFileLocation = true + }, + false); hasChanges = true; } @@ -354,7 +360,10 @@ namespace MediaBrowser.Providers.TV /// /// /// Episode. - private Episode GetExistingEpisode(IEnumerable existingEpisodes, IReadOnlyDictionary seasonCounts, (int seasonNumber, int episodeNumber, DateTime firstAired) episodeTuple) + private Episode GetExistingEpisode( + IEnumerable existingEpisodes, + IReadOnlyDictionary seasonCounts, + (int seasonNumber, int episodeNumber, DateTime firstAired) episodeTuple) { var seasonNumber = episodeTuple.seasonNumber; var episodeNumber = episodeTuple.episodeNumber; diff --git a/MediaBrowser.Providers/TV/TvExternalIds.cs b/MediaBrowser.Providers/TV/TvExternalIds.cs deleted file mode 100644 index a6040edd1..000000000 --- a/MediaBrowser.Providers/TV/TvExternalIds.cs +++ /dev/null @@ -1,82 +0,0 @@ -#pragma warning disable CS1591 - -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.Plugins.TheTvdb; - -namespace MediaBrowser.Providers.TV -{ - public class Zap2ItExternalId : IExternalId - { - /// - public string ProviderName => "Zap2It"; - - /// - public string Key => MetadataProvider.Zap2It.ToString(); - - /// - public ExternalIdMediaType? Type => null; - - /// - public string UrlFormatString => "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}"; - - /// - public bool Supports(IHasProviderIds item) => item is Series; - } - - public class TvdbExternalId : IExternalId - { - /// - public string ProviderName => "TheTVDB"; - - /// - public string Key => MetadataProvider.Tvdb.ToString(); - - /// - public ExternalIdMediaType? Type => null; - - /// - public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=series&id={0}"; - - /// - public bool Supports(IHasProviderIds item) => item is Series; - } - - public class TvdbSeasonExternalId : IExternalId - { - /// - public string ProviderName => "TheTVDB"; - - /// - public string Key => MetadataProvider.Tvdb.ToString(); - - /// - public ExternalIdMediaType? Type => ExternalIdMediaType.Season; - - /// - public string UrlFormatString => null; - - /// - public bool Supports(IHasProviderIds item) => item is Season; - } - - public class TvdbEpisodeExternalId : IExternalId - { - /// - public string ProviderName => "TheTVDB"; - - /// - public string Key => MetadataProvider.Tvdb.ToString(); - - /// - public ExternalIdMediaType? Type => ExternalIdMediaType.Episode; - - /// - public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=episode&id={0}"; - - /// - public bool Supports(IHasProviderIds item) => item is Episode; - } -} diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeExternalId.cs b/MediaBrowser.Providers/TV/TvdbEpisodeExternalId.cs new file mode 100644 index 000000000..40c5f2d78 --- /dev/null +++ b/MediaBrowser.Providers/TV/TvdbEpisodeExternalId.cs @@ -0,0 +1,28 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Plugins.TheTvdb; + +namespace MediaBrowser.Providers.TV +{ + public class TvdbEpisodeExternalId : IExternalId + { + /// + public string ProviderName => "TheTVDB"; + + /// + public string Key => MetadataProvider.Tvdb.ToString(); + + /// + public ExternalIdMediaType? Type => ExternalIdMediaType.Episode; + + /// + public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=episode&id={0}"; + + /// + public bool Supports(IHasProviderIds item) => item is Episode; + } +} diff --git a/MediaBrowser.Providers/TV/TvdbExternalId.cs b/MediaBrowser.Providers/TV/TvdbExternalId.cs new file mode 100644 index 000000000..4c54de9f8 --- /dev/null +++ b/MediaBrowser.Providers/TV/TvdbExternalId.cs @@ -0,0 +1,28 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Plugins.TheTvdb; + +namespace MediaBrowser.Providers.TV +{ + public class TvdbExternalId : IExternalId + { + /// + public string ProviderName => "TheTVDB"; + + /// + public string Key => MetadataProvider.Tvdb.ToString(); + + /// + public ExternalIdMediaType? Type => null; + + /// + public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=series&id={0}"; + + /// + public bool Supports(IHasProviderIds item) => item is Series; + } +} diff --git a/MediaBrowser.Providers/TV/TvdbSeasonExternalId.cs b/MediaBrowser.Providers/TV/TvdbSeasonExternalId.cs new file mode 100644 index 000000000..807ebb3ee --- /dev/null +++ b/MediaBrowser.Providers/TV/TvdbSeasonExternalId.cs @@ -0,0 +1,28 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Plugins.TheTvdb; + +namespace MediaBrowser.Providers.TV +{ + public class TvdbSeasonExternalId : IExternalId + { + /// + public string ProviderName => "TheTVDB"; + + /// + public string Key => MetadataProvider.Tvdb.ToString(); + + /// + public ExternalIdMediaType? Type => ExternalIdMediaType.Season; + + /// + public string UrlFormatString => null; + + /// + public bool Supports(IHasProviderIds item) => item is Season; + } +} diff --git a/MediaBrowser.Providers/TV/Zap2ItExternalId.cs b/MediaBrowser.Providers/TV/Zap2ItExternalId.cs new file mode 100644 index 000000000..c9f314af9 --- /dev/null +++ b/MediaBrowser.Providers/TV/Zap2ItExternalId.cs @@ -0,0 +1,28 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Plugins.TheTvdb; + +namespace MediaBrowser.Providers.TV +{ + public class Zap2ItExternalId : IExternalId + { + /// + public string ProviderName => "Zap2It"; + + /// + public string Key => MetadataProvider.Zap2It.ToString(); + + /// + public ExternalIdMediaType? Type => null; + + /// + public string UrlFormatString => "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}"; + + /// + public bool Supports(IHasProviderIds item) => item is Series; + } +} From b673f5bcde1e115dc830fe3f67bf6544ca2f935f Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Sep 2020 12:27:26 +0100 Subject: [PATCH 29/40] Update DlnaManager.cs --- Emby.Dlna/DlnaManager.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 5b1538ffa..94d7e21d7 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -140,6 +140,14 @@ namespace Emby.Dlna private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo) { + if (!string.IsNullOrEmpty(profileInfo.FriendlyName)) + { + if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) + { + return false; + } + } + if (!string.IsNullOrEmpty(profileInfo.Manufacturer)) { if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) From 03d8f6f43bc78008a5db1911cb731456a98f452b Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Sep 2020 12:27:55 +0100 Subject: [PATCH 30/40] Update DlnaManager.cs removed space. --- Emby.Dlna/DlnaManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 94d7e21d7..f542f151a 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -147,7 +147,7 @@ namespace Emby.Dlna return false; } } - + if (!string.IsNullOrEmpty(profileInfo.Manufacturer)) { if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) From cf87b3afb7c64bd1102dc9bd445f75a4042e0442 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Sep 2020 12:28:48 +0100 Subject: [PATCH 31/40] Remove excess code. --- Emby.Server.Implementations/ApplicationHost.cs | 4 ---- Emby.Server.Implementations/Udp/UdpServer.cs | 6 ------ MediaBrowser.Controller/IServerApplicationHost.cs | 2 -- 3 files changed, 12 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c37e87d96..318a853c5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1431,10 +1431,6 @@ namespace Emby.Server.Implementations } } - public virtual void EnableLoopback(string appName) - { - } - private bool _disposed = false; /// diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index 73fcbcec3..b7a59cee2 100644 --- a/Emby.Server.Implementations/Udp/UdpServer.cs +++ b/Emby.Server.Implementations/Udp/UdpServer.cs @@ -68,12 +68,6 @@ namespace Emby.Server.Implementations.Udp { _logger.LogError(ex, "Error sending response message"); } - - var parts = messageText.Split('|'); - if (parts.Length > 1) - { - _appHost.EnableLoopback(parts[1]); - } } else { diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 9f4c00e1c..cfad17fb7 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -114,8 +114,6 @@ namespace MediaBrowser.Controller /// is false. void LaunchUrl(string url); - void EnableLoopback(string appName); - IEnumerable GetWakeOnLanInfo(); string ExpandVirtualPath(string path); From 5d9e2903d238dc810a109fd51a5e78133c042d15 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 7 Sep 2020 13:31:05 +0200 Subject: [PATCH 32/40] Minor improvements --- .../Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs | 9 +++------ .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 10 +++++----- .../Plugins/Tmdb/Movies/TmdbSearch.cs | 4 ++-- .../Plugins/Tmdb/People/TmdbPersonProvider.cs | 4 ++-- .../Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs | 10 ++++++++-- .../Plugins/Tmdb/TV/TmdbSeasonProvider.cs | 9 +++++++-- .../Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 14 ++++++++------ 7 files changed, 35 insertions(+), 25 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index e627550f1..e7328b553 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -37,7 +37,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets private readonly IJsonSerializer _json; private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; - private readonly ILocalizationManager _localization; private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; @@ -46,7 +45,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets IJsonSerializer json, IServerConfigurationManager config, IFileSystem fileSystem, - ILocalizationManager localization, IHttpClientFactory httpClientFactory, ILibraryManager libraryManager) { @@ -54,7 +52,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets _json = json; _config = config; _fileSystem = fileSystem; - _localization = localization; _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; Current = this; @@ -177,7 +174,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets private async Task FetchMainResult(string id, string language, CancellationToken cancellationToken) { - var url = string.Format(GetCollectionInfo3, id, TmdbUtils.ApiKey); + var url = string.Format(CultureInfo.InvariantCulture, GetCollectionInfo3, id, TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { @@ -195,7 +192,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage); + using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); await using var stream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); var mainResult = await _json.DeserializeFromStreamAsync(stream).ConfigureAwait(false); @@ -205,7 +202,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { if (!string.IsNullOrEmpty(language) && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) { - url = string.Format(GetCollectionInfo3, id, TmdbUtils.ApiKey) + "&language=en"; + url = string.Format(CultureInfo.InvariantCulture, GetCollectionInfo3, id, TmdbUtils.ApiKey) + "&language=en"; if (!string.IsNullOrEmpty(language)) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index f8bc19395..5d383722a 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -155,7 +155,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var response = await GetMovieDbResponse(requestMessage).ConfigureAwait(false); + using var response = await GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); _tmdbSettings = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); return _tmdbSettings; @@ -335,7 +335,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var mainResponse = await GetMovieDbResponse(requestMessage).ConfigureAwait(false); + using var mainResponse = await GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); if (mainResponse.StatusCode == HttpStatusCode.NotFound) { return null; @@ -368,7 +368,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var langResponse = await GetMovieDbResponse(langRequestMessage).ConfigureAwait(false); + using var langResponse = await GetMovieDbResponse(langRequestMessage, cancellationToken).ConfigureAwait(false); await using var langStream = await langResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); var langResult = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); @@ -381,10 +381,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies /// /// Gets the movie db response. /// - internal Task GetMovieDbResponse(HttpRequestMessage message) + internal Task GetMovieDbResponse(HttpRequestMessage message, CancellationToken cancellationToken = default) { message.Headers.UserAgent.ParseAdd(_appHost.ApplicationUserAgent); - return _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(message); + return _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(message, cancellationToken); } /// diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs index 2a6c6d035..d885cd90b 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs @@ -198,7 +198,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); var searchResults = await _json.DeserializeFromStreamAsync>(stream).ConfigureAwait(false); @@ -261,7 +261,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); var searchResults = await _json.DeserializeFromStreamAsync>(stream).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index 6bf04b81a..8d1a854d6 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -110,7 +110,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); + var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); var result2 = await _jsonSerializer.DeserializeFromStreamAsync>(stream).ConfigureAwait(false) @@ -243,7 +243,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); await using var fs = new FileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); await response.Content.CopyToAsync(fs).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs index 30b7674e3..55b0f0409 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs @@ -113,7 +113,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV internal async Task FetchMainResult(string urlPattern, string id, int seasonNumber, int episodeNumber, string language, CancellationToken cancellationToken) { - var url = string.Format(urlPattern, id, seasonNumber.ToString(CultureInfo.InvariantCulture), episodeNumber, TmdbUtils.ApiKey); + var url = string.Format( + CultureInfo.InvariantCulture, + urlPattern, + id, + seasonNumber.ToString(CultureInfo.InvariantCulture), + episodeNumber, + TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { @@ -132,7 +138,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); return await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs index 73ed13267..11a02c1ed 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs @@ -200,7 +200,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV internal async Task FetchMainResult(string id, int seasonNumber, string language, CancellationToken cancellationToken) { - var url = string.Format(GetTvInfo3, id, seasonNumber.ToString(CultureInfo.InvariantCulture), TmdbUtils.ApiKey); + var url = string.Format( + CultureInfo.InvariantCulture, + GetTvInfo3, + id, + seasonNumber.ToString(CultureInfo.InvariantCulture), + TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { @@ -219,7 +224,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); return await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index aaba6ffc0..430d2525c 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -405,7 +405,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV internal async Task FetchMainResult(string id, string language, CancellationToken cancellationToken) { - var url = string.Format(GetTvInfo3, id, TmdbUtils.ApiKey); + var url = string.Format(CultureInfo.InvariantCulture, GetTvInfo3, id, TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { @@ -421,7 +421,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(mainRequestMessage); + using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(mainRequestMessage, cancellationToken).ConfigureAwait(false); await using var mainStream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); var mainResult = await _jsonSerializer.DeserializeFromStreamAsync(mainStream).ConfigureAwait(false); @@ -440,7 +440,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV { _logger.LogInformation("MovieDbSeriesProvider couldn't find meta for language {Language}. Trying English...", language); - url = string.Format(GetTvInfo3, id, TmdbUtils.ApiKey) + "&language=en"; + url = string.Format(CultureInfo.InvariantCulture, GetTvInfo3, id, TmdbUtils.ApiKey) + "&language=en"; if (!string.IsNullOrEmpty(language)) { @@ -454,7 +454,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); var englishResult = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); @@ -504,7 +504,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV private async Task FindByExternalId(string id, string externalSource, CancellationToken cancellationToken) { - var url = string.Format(TmdbUtils.BaseTmdbApiUrl + @"3/find/{0}?api_key={1}&external_source={2}", + var url = string.Format( + CultureInfo.InvariantCulture, + TmdbUtils.BaseTmdbApiUrl + @"3/find/{0}?api_key={1}&external_source={2}", id, TmdbUtils.ApiKey, externalSource); @@ -515,7 +517,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); var result = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); From 37a8be5db26cfe5afe50bb6ff82e5abe79a786be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2020 12:02:16 +0000 Subject: [PATCH 33/40] Bump SQLitePCLRaw.bundle_e_sqlite3 from 2.0.3 to 2.0.4 Bumps [SQLitePCLRaw.bundle_e_sqlite3](https://github.com/ericsink/SQLitePCL.raw) from 2.0.3 to 2.0.4. - [Release notes](https://github.com/ericsink/SQLitePCL.raw/releases) - [Commits](https://github.com/ericsink/SQLitePCL.raw/compare/v2.0.3...v2.0.4) Signed-off-by: dependabot[bot] --- Jellyfin.Server/Jellyfin.Server.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index c3bec1c71..0ac309a0b 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -54,7 +54,7 @@ - + From 5751d4765a92de7a8f9340ce4a25d6b0d39587b4 Mon Sep 17 00:00:00 2001 From: linzack Date: Mon, 7 Sep 2020 14:49:17 +0000 Subject: [PATCH 34/40] Translated using Weblate (Chinese (Traditional)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant/ --- .../Localization/Core/zh-TW.json | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index a21cdad95..4f66f7807 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -1,6 +1,6 @@ { "Albums": "專輯", - "AppDeviceValues": "軟體: {0}, 裝置: {1}", + "AppDeviceValues": "軟體:{0},裝置:{1}", "Application": "應用程式", "Artists": "演出者", "AuthenticationSucceededWithUserName": "{0} 成功授權", @@ -11,7 +11,7 @@ "Collections": "合輯", "DeviceOfflineWithName": "{0} 已經斷線", "DeviceOnlineWithName": "{0} 已經連線", - "FailedLoginAttemptWithUserName": "來自 {0} 的失敗登入嘗試", + "FailedLoginAttemptWithUserName": "來自使用者 {0} 的失敗登入", "Favorites": "我的最愛", "Folders": "資料夾", "Genres": "風格", @@ -28,8 +28,8 @@ "HomeVideos": "自製影片", "ItemAddedWithName": "{0} 已新增至媒體庫", "ItemRemovedWithName": "{0} 已從媒體庫移除", - "LabelIpAddressValue": "IP 位置: {0}", - "LabelRunningTimeValue": "運行時間: {0}", + "LabelIpAddressValue": "IP 位址:{0}", + "LabelRunningTimeValue": "運行時間:{0}", "Latest": "最新", "MessageApplicationUpdated": "Jellyfin Server 已經更新", "MessageApplicationUpdatedTo": "Jellyfin Server 已經更新至 {0}", @@ -42,18 +42,18 @@ "NameInstallFailed": "{0} 安裝失敗", "NameSeasonNumber": "第 {0} 季", "NameSeasonUnknown": "未知季數", - "NewVersionIsAvailable": "新版本的Jellyfin Server 軟體已經推出可供下載。", + "NewVersionIsAvailable": "新版本的 Jellyfin Server 軟體已經可供下載。", "NotificationOptionApplicationUpdateAvailable": "有可用的應用程式更新", - "NotificationOptionApplicationUpdateInstalled": "應用程式已更新", + "NotificationOptionApplicationUpdateInstalled": "軟體更新已安裝", "NotificationOptionAudioPlayback": "音樂開始播放", "NotificationOptionAudioPlaybackStopped": "音樂停止播放", "NotificationOptionCameraImageUploaded": "相機相片已上傳", "NotificationOptionInstallationFailed": "安裝失敗", "NotificationOptionNewLibraryContent": "已新增新內容", - "NotificationOptionPluginError": "插件安裝錯誤", - "NotificationOptionPluginInstalled": "插件已安裝", - "NotificationOptionPluginUninstalled": "插件已移除", - "NotificationOptionPluginUpdateInstalled": "插件已更新", + "NotificationOptionPluginError": "外掛安裝失敗", + "NotificationOptionPluginInstalled": "外掛已安裝", + "NotificationOptionPluginUninstalled": "外掛已移除", + "NotificationOptionPluginUpdateInstalled": "外掛已更新", "NotificationOptionServerRestartRequired": "伺服器需要重新啟動", "NotificationOptionTaskFailed": "排程任務失敗", "NotificationOptionUserLockedOut": "使用者已鎖定", @@ -61,14 +61,14 @@ "NotificationOptionVideoPlaybackStopped": "影片停止播放", "Photos": "相片", "Playlists": "播放清單", - "Plugin": "插件", + "Plugin": "外掛", "PluginInstalledWithName": "{0} 已安裝", "PluginUninstalledWithName": "{0} 已移除", "PluginUpdatedWithName": "{0} 已更新", "ProviderValue": "提供商: {0}", - "ScheduledTaskFailedWithName": "{0} 已失敗", - "ScheduledTaskStartedWithName": "{0} 已開始", - "ServerNameNeedsToBeRestarted": "{0} 需要重新啟動", + "ScheduledTaskFailedWithName": "排程任務 {0} 已失敗", + "ScheduledTaskStartedWithName": "排程任務 {0} 已開始", + "ServerNameNeedsToBeRestarted": "伺服器 {0} 需要重新啟動", "Shows": "節目", "Songs": "歌曲", "StartupEmbyServerIsLoading": "Jellyfin Server正在啟動,請稍後再試一次。", @@ -78,10 +78,10 @@ "User": "使用者", "UserCreatedWithName": "使用者 {0} 已建立", "UserDeletedWithName": "使用者 {0} 已移除", - "UserDownloadingItemWithValues": "{0} 正在下載 {1}", + "UserDownloadingItemWithValues": "使用者 {0} 正在下載 {1}", "UserLockedOutWithName": "使用者 {0} 已鎖定", - "UserOfflineFromDevice": "{0} 已從 {1} 斷線", - "UserOnlineFromDevice": "{0} 已連線,來自 {1}", + "UserOfflineFromDevice": "使用者 {0} 已從 {1} 斷線", + "UserOnlineFromDevice": "使用者 {0} 已從 {1} 連線", "UserPasswordChangedWithName": "使用者 {0} 的密碼已變更", "UserPolicyUpdatedWithName": "使用者條約已更新為 {0}", "UserStartedPlayingItemWithValues": "{0}正在使用 {2} 播放 {1}", @@ -102,7 +102,7 @@ "TaskRefreshLibraryDescription": "掃描媒體庫內新的檔案並重新整理描述資料。", "TaskRefreshLibrary": "掃描媒體庫", "TaskRefreshChapterImages": "擷取章節圖片", - "TaskCleanCacheDescription": "刪除系統長時間不需要的快取。", + "TaskCleanCacheDescription": "刪除系統已不需要的快取。", "TaskCleanCache": "清除快取資料夾", "TasksLibraryCategory": "媒體庫", "TaskRefreshChannelsDescription": "重新整理網絡頻道資料。", @@ -110,8 +110,8 @@ "TaskCleanTranscode": "清除轉碼資料夾", "TaskUpdatePluginsDescription": "下載並安裝配置為自動更新的插件的更新。", "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的中繼資料。", - "TaskRefreshChapterImagesDescription": "為有章節的視頻創建縮圖。", - "TasksChannelsCategory": "網絡頻道", + "TaskRefreshChapterImagesDescription": "為有章節的影片建立縮圖。", + "TasksChannelsCategory": "網路頻道", "TasksApplicationCategory": "應用程式", "TasksMaintenanceCategory": "維修" } From b711e78fe2d6d027dc016b15bf38dfcc1c6b306b Mon Sep 17 00:00:00 2001 From: linzack Date: Mon, 7 Sep 2020 15:25:57 +0000 Subject: [PATCH 35/40] Translated using Weblate (Chinese (Traditional)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant/ --- Emby.Server.Implementations/Localization/Core/zh-TW.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index 4f66f7807..05834d52c 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -99,8 +99,8 @@ "TaskRefreshPeople": "重新整理人員", "TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔案。", "TaskCleanLogs": "清空紀錄資料夾", - "TaskRefreshLibraryDescription": "掃描媒體庫內新的檔案並重新整理描述資料。", - "TaskRefreshLibrary": "掃描媒體庫", + "TaskRefreshLibraryDescription": "重新掃描媒體庫的新檔案並更新描述資料。", + "TaskRefreshLibrary": "重新掃描媒體庫", "TaskRefreshChapterImages": "擷取章節圖片", "TaskCleanCacheDescription": "刪除系統已不需要的快取。", "TaskCleanCache": "清除快取資料夾", From 23f6616e63c23a4c1741a206db19408a6515d4d8 Mon Sep 17 00:00:00 2001 From: linzack Date: Mon, 7 Sep 2020 15:28:57 +0000 Subject: [PATCH 36/40] Translated using Weblate (Chinese (Traditional)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant/ --- Emby.Server.Implementations/Localization/Core/zh-TW.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index 05834d52c..01108fe84 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -95,9 +95,9 @@ "TaskDownloadMissingSubtitlesDescription": "在網路上透過中繼資料搜尋遺失的字幕。", "TaskDownloadMissingSubtitles": "下載遺失的字幕", "TaskRefreshChannels": "重新整理頻道", - "TaskUpdatePlugins": "更新插件", + "TaskUpdatePlugins": "更新外掛", "TaskRefreshPeople": "重新整理人員", - "TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔案。", + "TaskCleanLogsDescription": "刪除超過 {0} 天的舊紀錄檔。", "TaskCleanLogs": "清空紀錄資料夾", "TaskRefreshLibraryDescription": "重新掃描媒體庫的新檔案並更新描述資料。", "TaskRefreshLibrary": "重新掃描媒體庫", @@ -105,10 +105,10 @@ "TaskCleanCacheDescription": "刪除系統已不需要的快取。", "TaskCleanCache": "清除快取資料夾", "TasksLibraryCategory": "媒體庫", - "TaskRefreshChannelsDescription": "重新整理網絡頻道資料。", + "TaskRefreshChannelsDescription": "重新整理網路頻道資料。", "TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。", "TaskCleanTranscode": "清除轉碼資料夾", - "TaskUpdatePluginsDescription": "下載並安裝配置為自動更新的插件的更新。", + "TaskUpdatePluginsDescription": "為設置自動更新的外掛下載並安裝更新。", "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的中繼資料。", "TaskRefreshChapterImagesDescription": "為有章節的影片建立縮圖。", "TasksChannelsCategory": "網路頻道", From a2e90170da52c8cde92a2e813dbaf930c6697dc2 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 7 Sep 2020 14:21:30 -0600 Subject: [PATCH 37/40] Use existing configuration manager --- Emby.Server.Implementations/ApplicationHost.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 93d55cc67..642e2fdbe 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -120,7 +120,6 @@ namespace Emby.Server.Implementations private readonly INetworkManager _networkManager; private readonly IXmlSerializer _xmlSerializer; private readonly IStartupOptions _startupOptions; - private readonly IConfigurationManager _configurationManager; private IMediaEncoder _mediaEncoder; private ISessionManager _sessionManager; @@ -247,19 +246,16 @@ namespace Emby.Server.Implementations /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. public ApplicationHost( IServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IStartupOptions options, IFileSystem fileSystem, INetworkManager networkManager, - IServiceCollection serviceCollection, - IConfigurationManager configurationManager) + IServiceCollection serviceCollection) { _xmlSerializer = new MyXmlSerializer(); ServiceCollection = serviceCollection; - _configurationManager = configurationManager; _networkManager = networkManager; networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; @@ -1148,7 +1144,7 @@ namespace Emby.Server.Implementations OperatingSystem = OperatingSystem.Id.ToString(), ServerName = FriendlyName, LocalAddress = localAddress, - StartupWizardCompleted = _configurationManager.CommonConfiguration.IsStartupWizardCompleted + StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted }; } From afc8a307c78bdaea1db802715465c59fa4dd2bfd Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 7 Sep 2020 17:17:26 -0600 Subject: [PATCH 38/40] Add missing FromRoute, Required attribute --- Jellyfin.Api/Controllers/LiveTvController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 8678844d2..500907a9a 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -447,7 +447,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Timers/{timerId}")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize(Policy = Policies.DefaultAuthorization)] - public async Task> GetTimer(string timerId) + public async Task> GetTimer([FromRoute, Required] string timerId) { return await _liveTvManager.GetTimer(timerId, CancellationToken.None).ConfigureAwait(false); } From 68e5a95fdb2d0c99e2490dc117651edb413733c5 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 7 Sep 2020 19:10:14 -0600 Subject: [PATCH 39/40] Fix redirection --- Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs | 6 +++++- Jellyfin.Server/Startup.cs | 6 +----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs b/Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs index ae3a3a1c5..9316737bd 100644 --- a/Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs +++ b/Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs @@ -44,7 +44,11 @@ namespace Jellyfin.Server.Middleware var localPath = httpContext.Request.Path.ToString(); var baseUrlPrefix = serverConfigurationManager.Configuration.BaseUrl; - if (!localPath.StartsWith(baseUrlPrefix, StringComparison.OrdinalIgnoreCase)) + 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); diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 16629b5d9..9e969c0c1 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -93,11 +93,7 @@ namespace Jellyfin.Server IWebHostEnvironment env, IConfiguration appConfig) { - // Only add base url redirection if a base url is set. - if (!string.IsNullOrEmpty(_serverConfigurationManager.Configuration.BaseUrl)) - { - app.UseBaseUrlRedirection(); - } + app.UseBaseUrlRedirection(); // Wrap rest of configuration so everything only listens on BaseUrl. app.Map(_serverConfigurationManager.Configuration.BaseUrl, mainApp => From e7727563288480e6eee541b96a4b97dbb0fef9d7 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 7 Sep 2020 20:51:12 -0600 Subject: [PATCH 40/40] Fix catching authentication exception --- Jellyfin.Server/Middleware/ExceptionMiddleware.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Jellyfin.Server/Middleware/ExceptionMiddleware.cs b/Jellyfin.Server/Middleware/ExceptionMiddleware.cs index 63effafc1..fb1ee3b2b 100644 --- a/Jellyfin.Server/Middleware/ExceptionMiddleware.cs +++ b/Jellyfin.Server/Middleware/ExceptionMiddleware.cs @@ -125,6 +125,7 @@ namespace Jellyfin.Server.Middleware switch (ex) { case ArgumentException _: return StatusCodes.Status400BadRequest; + case AuthenticationException _: case SecurityException _: return StatusCodes.Status401Unauthorized; case DirectoryNotFoundException _: case FileNotFoundException _: