瀏覽代碼

Merge branch 'master' into network-rewrite

Shadowghost 2 年之前
父節點
當前提交
a6e9824e8a
共有 26 個文件被更改,包括 103 次插入244 次删除
  1. 3 3
      .github/workflows/codeql-analysis.yml
  2. 10 10
      Directory.Packages.props
  3. 15 1
      Emby.Server.Implementations/Localization/Core/ne.json
  4. 4 16
      Jellyfin.Api/Controllers/LibraryController.cs
  5. 2 2
      Jellyfin.Api/Models/LiveTvDtos/ChannelMappingOptionsDto.cs
  6. 1 1
      Jellyfin.Api/Models/UserDtos/CreateUserByName.cs
  7. 1 1
      Jellyfin.Api/Models/UserDtos/ForgotPasswordDto.cs
  8. 1 1
      Jellyfin.Api/Models/UserDtos/ForgotPasswordPinDto.cs
  9. 7 58
      MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
  10. 0 5
      MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
  11. 1 1
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  12. 8 25
      MediaBrowser.Controller/Entities/BaseItem.cs
  13. 9 29
      MediaBrowser.Controller/Entities/Folder.cs
  14. 12 5
      MediaBrowser.Controller/Library/MetadataConfigurationExtensions.cs
  15. 0 17
      MediaBrowser.Controller/Providers/IProviderManager.cs
  16. 4 1
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  17. 4 4
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  18. 1 1
      MediaBrowser.Model/Dlna/MediaOptions.cs
  19. 12 51
      MediaBrowser.Providers/Manager/ProviderManager.cs
  20. 3 5
      MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs
  21. 1 1
      deployment/Dockerfile.centos.amd64
  22. 1 1
      deployment/Dockerfile.fedora.amd64
  23. 1 1
      deployment/Dockerfile.ubuntu.amd64
  24. 1 1
      deployment/Dockerfile.ubuntu.arm64
  25. 1 1
      deployment/Dockerfile.ubuntu.armhf
  26. 0 2
      src/Jellyfin.Drawing/ImageProcessor.cs

+ 3 - 3
.github/workflows/codeql-analysis.yml

@@ -27,11 +27,11 @@ jobs:
         dotnet-version: '7.0.x'
 
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@6c089f53dd51dc3fc7e599c3cb5356453a52ca9e # v2.20.0
+      uses: github/codeql-action/init@f6e388ebf0efc915c6c5b165b019ee61a6746a38 # v2.20.1
       with:
         languages: ${{ matrix.language }}
         queries: +security-extended
     - name: Autobuild
-      uses: github/codeql-action/autobuild@6c089f53dd51dc3fc7e599c3cb5356453a52ca9e # v2.20.0
+      uses: github/codeql-action/autobuild@f6e388ebf0efc915c6c5b165b019ee61a6746a38 # v2.20.1
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@6c089f53dd51dc3fc7e599c3cb5356453a52ca9e # v2.20.0
+      uses: github/codeql-action/analyze@f6e388ebf0efc915c6c5b165b019ee61a6746a38 # v2.20.1

+ 10 - 10
Directory.Packages.props

@@ -23,14 +23,14 @@
     <PackageVersion Include="libse" Version="3.6.13" />
     <PackageVersion Include="LrcParser" Version="2023.524.0" />
     <PackageVersion Include="MetaBrainz.MusicBrainz" Version="5.0.0" />
-    <PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="7.0.7" />
+    <PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="7.0.8" />
     <PackageVersion Include="Microsoft.AspNetCore.HttpOverrides" Version="2.2.0" />
-    <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.7" />
+    <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.8" />
     <PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" />
-    <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.7" />
-    <PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.7" />
-    <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.7" />
-    <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.7" />
+    <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.8" />
+    <PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.8" />
+    <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.8" />
+    <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.8" />
     <PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
@@ -39,8 +39,8 @@
     <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
-    <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.7" />
-    <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.7" />
+    <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.8" />
+    <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.8" />
     <PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Http" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
@@ -65,14 +65,14 @@
     <PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
     <PackageVersion Include="Serilog.Sinks.Graylog" Version="3.0.1" />
     <PackageVersion Include="SerilogAnalyzer" Version="0.15.0" />
-    <PackageVersion Include="SharpFuzz" Version="2.0.2" />
+    <PackageVersion Include="SharpFuzz" Version="2.1.0" />
     <PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.3" />
     <PackageVersion Include="SkiaSharp.Svg" Version="1.60.0" />
     <PackageVersion Include="SkiaSharp" Version="2.88.3" />
     <PackageVersion Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
     <PackageVersion Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
     <PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.5" />
-    <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.435" />
+    <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.507" />
     <PackageVersion Include="Swashbuckle.AspNetCore.ReDoc" Version="6.4.0" />
     <PackageVersion Include="Swashbuckle.AspNetCore" Version="6.2.3" />
     <PackageVersion Include="System.Globalization" Version="4.3.0" />

+ 15 - 1
Emby.Server.Implementations/Localization/Core/ne.json

@@ -109,5 +109,19 @@
     "Sync": "समकालीन",
     "SubtitleDownloadFailureFromForItem": "उपशीर्षकहरू {0} बाट {1} को लागि डाउनलोड गर्न असफल",
     "PluginUpdatedWithName": "{0} अद्यावधिक गरिएको थियो",
-    "PluginUninstalledWithName": "{0} को स्थापना रद्द गरिएको थियो"
+    "PluginUninstalledWithName": "{0} को स्थापना रद्द गरिएको थियो",
+    "HearingImpaired": "सुन्न नसक्ने",
+    "TaskUpdatePluginsDescription": "स्वचालित रूपमा अद्यावधिक गर्न कन्फिगर गरिएका प्लगइनहरूका लागि अद्यावधिकहरू डाउनलोड र स्थापना गर्दछ।",
+    "TaskCleanTranscode": "सफा ट्रान्सकोड निर्देशिका",
+    "TaskCleanTranscodeDescription": "एक दिन भन्दा पुराना ट्रान्सकोड फाइलहरू मेटाउँछ।",
+    "TaskRefreshChannels": "च्यानलहरू ताजा गर्नुहोस्",
+    "TaskDownloadMissingSubtitlesDescription": "मेटाडेटा कन्फिगरेसनमा आधारित हराइरहेको उपशीर्षकहरूको लागि इन्टरनेट खोज्छ।",
+    "TaskOptimizeDatabase": "डेटाबेस अप्टिमाइज गर्नुहोस्",
+    "TaskOptimizeDatabaseDescription": "डाटाबेस कम्प्याक्ट र खाली ठाउँ काट्छ। पुस्तकालय स्क्यान गरेपछि वा डाटाबेस परिमार्जनलाई संकेत गर्ने अन्य परिवर्तनहरू गरेपछि यो कार्य चलाउँदा कार्यसम्पादनमा सुधार हुन सक्छ।",
+    "TaskKeyframeExtractorDescription": "थप सटीक एचएलएस प्लेलिस्टहरू सिर्जना गर्न भिडियो फाइलहरूबाट कीफ्रेमहरू निकाल्छ। यो कार्य लामो समय सम्म चल्न सक्छ।",
+    "TaskUpdatePlugins": "प्लगइनहरू अपडेट गर्नुहोस्",
+    "TaskRefreshPeopleDescription": "तपाईंको मिडिया लाइब्रेरीमा अभिनेता र निर्देशकहरूको लागि मेटाडेटा अपडेट गर्दछ।",
+    "TaskRefreshChannelsDescription": "इन्टरनेट च्यानल जानकारी ताजा गर्दछ।",
+    "TaskDownloadMissingSubtitles": "छुटेका उपशीर्षकहरू डाउनलोड गर्नुहोस्",
+    "TaskKeyframeExtractor": "कीफ्रेम एक्स्ट्रक्टर"
 }

+ 4 - 16
Jellyfin.Api/Controllers/LibraryController.cs

@@ -969,12 +969,8 @@ public class LibraryController : BaseJellyfinApiController
                    || string.Equals(name, "MusicBrainz", StringComparison.OrdinalIgnoreCase);
         }
 
-        var metadataOptions = _serverConfigurationManager.Configuration.MetadataOptions
-            .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
-            .ToArray();
-
-        return metadataOptions.Length == 0
-               || metadataOptions.Any(i => !i.DisabledMetadataFetchers.Contains(name, StringComparison.OrdinalIgnoreCase));
+        var metadataOptions = _serverConfigurationManager.GetMetadataOptionsForType(type);
+        return metadataOptions is null || !metadataOptions.DisabledMetadataFetchers.Contains(name, StringComparison.OrdinalIgnoreCase);
     }
 
     private bool IsImageFetcherEnabledByDefault(string name, string type, bool isNewLibrary)
@@ -995,15 +991,7 @@ public class LibraryController : BaseJellyfinApiController
                    || string.Equals(name, "Image Extractor", StringComparison.OrdinalIgnoreCase);
         }
 
-        var metadataOptions = _serverConfigurationManager.Configuration.MetadataOptions
-            .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
-            .ToArray();
-
-        if (metadataOptions.Length == 0)
-        {
-            return true;
-        }
-
-        return metadataOptions.Any(i => !i.DisabledImageFetchers.Contains(name, StringComparison.OrdinalIgnoreCase));
+        var metadataOptions = _serverConfigurationManager.GetMetadataOptionsForType(type);
+        return metadataOptions is null || !metadataOptions.DisabledImageFetchers.Contains(name, StringComparison.OrdinalIgnoreCase);
     }
 }

+ 2 - 2
Jellyfin.Api/Models/LiveTvDtos/ChannelMappingOptionsDto.cs

@@ -13,12 +13,12 @@ public class ChannelMappingOptionsDto
     /// <summary>
     /// Gets or sets list of tuner channels.
     /// </summary>
-    required public IReadOnlyList<TunerChannelMapping> TunerChannels { get; set; }
+    public required IReadOnlyList<TunerChannelMapping> TunerChannels { get; set; }
 
     /// <summary>
     /// Gets or sets list of provider channels.
     /// </summary>
-    required public IReadOnlyList<NameIdPair> ProviderChannels { get; set; }
+    public required IReadOnlyList<NameIdPair> ProviderChannels { get; set; }
 
     /// <summary>
     /// Gets or sets list of mappings.

+ 1 - 1
Jellyfin.Api/Models/UserDtos/CreateUserByName.cs

@@ -11,7 +11,7 @@ public class CreateUserByName
     /// Gets or sets the username.
     /// </summary>
     [Required]
-    required public string Name { get; set; }
+    public required string Name { get; set; }
 
     /// <summary>
     /// Gets or sets the password.

+ 1 - 1
Jellyfin.Api/Models/UserDtos/ForgotPasswordDto.cs

@@ -11,5 +11,5 @@ public class ForgotPasswordDto
     /// Gets or sets the entered username to have its password reset.
     /// </summary>
     [Required]
-    required public string EnteredUsername { get; set; }
+    public required string EnteredUsername { get; set; }
 }

+ 1 - 1
Jellyfin.Api/Models/UserDtos/ForgotPasswordPinDto.cs

@@ -11,5 +11,5 @@ public class ForgotPasswordPinDto
     /// Gets or sets the entered pin to have the password reset.
     /// </summary>
     [Required]
-    required public string Pin { get; set; }
+    public required string Pin { get; set; }
 }

+ 7 - 58
MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs

@@ -1,11 +1,10 @@
 using System;
-using System.Diagnostics.CodeAnalysis;
 using System.Linq;
-using System.Threading;
 using Jellyfin.Extensions;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Configuration;
 
 namespace MediaBrowser.Controller.BaseItemManager
@@ -15,8 +14,6 @@ namespace MediaBrowser.Controller.BaseItemManager
     {
         private readonly IServerConfigurationManager _serverConfigurationManager;
 
-        private int _metadataRefreshConcurrency;
-
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseItemManager"/> class.
         /// </summary>
@@ -24,16 +21,8 @@ namespace MediaBrowser.Controller.BaseItemManager
         public BaseItemManager(IServerConfigurationManager serverConfigurationManager)
         {
             _serverConfigurationManager = serverConfigurationManager;
-
-            _metadataRefreshConcurrency = GetMetadataRefreshConcurrency();
-            SetupMetadataThrottler();
-
-            _serverConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
         }
 
-        /// <inheritdoc />
-        public SemaphoreSlim MetadataRefreshThrottler { get; private set; }
-
         /// <inheritdoc />
         public bool IsMetadataFetcherEnabled(BaseItem baseItem, TypeOptions? libraryTypeOptions, string name)
         {
@@ -51,12 +40,11 @@ namespace MediaBrowser.Controller.BaseItemManager
 
             if (libraryTypeOptions is not null)
             {
-                return libraryTypeOptions.MetadataFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
+                return libraryTypeOptions.MetadataFetchers.Contains(name, StringComparison.OrdinalIgnoreCase);
             }
 
-            var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, baseItem.GetType().Name, StringComparison.OrdinalIgnoreCase));
-
-            return itemConfig is null || !itemConfig.DisabledMetadataFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
+            var itemConfig = _serverConfigurationManager.GetMetadataOptionsForType(baseItem.GetType().Name);
+            return itemConfig is null || !itemConfig.DisabledMetadataFetchers.Contains(name, StringComparison.OrdinalIgnoreCase);
         }
 
         /// <inheritdoc />
@@ -76,50 +64,11 @@ namespace MediaBrowser.Controller.BaseItemManager
 
             if (libraryTypeOptions is not null)
             {
-                return libraryTypeOptions.ImageFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
-            }
-
-            var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, baseItem.GetType().Name, StringComparison.OrdinalIgnoreCase));
-
-            return itemConfig is null || !itemConfig.DisabledImageFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
-        }
-
-        /// <summary>
-        /// Called when the configuration is updated.
-        /// It will refresh the metadata throttler if the relevant config changed.
-        /// </summary>
-        private void OnConfigurationUpdated(object? sender, EventArgs e)
-        {
-            int newMetadataRefreshConcurrency = GetMetadataRefreshConcurrency();
-            if (_metadataRefreshConcurrency != newMetadataRefreshConcurrency)
-            {
-                _metadataRefreshConcurrency = newMetadataRefreshConcurrency;
-                SetupMetadataThrottler();
-            }
-        }
-
-        /// <summary>
-        /// Creates the metadata refresh throttler.
-        /// </summary>
-        [MemberNotNull(nameof(MetadataRefreshThrottler))]
-        private void SetupMetadataThrottler()
-        {
-            MetadataRefreshThrottler = new SemaphoreSlim(_metadataRefreshConcurrency);
-        }
-
-        /// <summary>
-        /// Returns the metadata refresh concurrency.
-        /// </summary>
-        private int GetMetadataRefreshConcurrency()
-        {
-            var concurrency = _serverConfigurationManager.Configuration.LibraryMetadataRefreshConcurrency;
-
-            if (concurrency <= 0)
-            {
-                concurrency = Environment.ProcessorCount;
+                return libraryTypeOptions.ImageFetchers.Contains(name, StringComparison.OrdinalIgnoreCase);
             }
 
-            return concurrency;
+            var itemConfig = _serverConfigurationManager.GetMetadataOptionsForType(baseItem.GetType().Name);
+            return itemConfig is null || !itemConfig.DisabledImageFetchers.Contains(name, StringComparison.OrdinalIgnoreCase);
         }
     }
 }

+ 0 - 5
MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs

@@ -9,11 +9,6 @@ namespace MediaBrowser.Controller.BaseItemManager
     /// </summary>
     public interface IBaseItemManager
     {
-        /// <summary>
-        /// Gets the semaphore used to limit the amount of concurrent metadata refreshes.
-        /// </summary>
-        SemaphoreSlim MetadataRefreshThrottler { get; }
-
         /// <summary>
         /// Is metadata fetcher enabled.
         /// </summary>

+ 1 - 1
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -59,7 +59,7 @@ namespace MediaBrowser.Controller.Entities.Audio
             {
                 if (IsAccessedByName)
                 {
-                    return new List<BaseItem>();
+                    return Enumerable.Empty<BaseItem>();
                 }
 
                 return base.Children;

+ 8 - 25
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -1244,14 +1244,6 @@ namespace MediaBrowser.Controller.Entities
             return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(FileSystem)), cancellationToken);
         }
 
-        protected virtual void TriggerOnRefreshStart()
-        {
-        }
-
-        protected virtual void TriggerOnRefreshComplete()
-        {
-        }
-
         /// <summary>
         /// Overrides the base implementation to refresh metadata for local trailers.
         /// </summary>
@@ -1260,8 +1252,6 @@ namespace MediaBrowser.Controller.Entities
         /// <returns>true if a provider reports we changed.</returns>
         public async Task<ItemUpdateType> RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken)
         {
-            TriggerOnRefreshStart();
-
             var requiresSave = false;
 
             if (SupportsOwnedItems)
@@ -1281,21 +1271,14 @@ namespace MediaBrowser.Controller.Entities
                 }
             }
 
-            try
-            {
-                var refreshOptions = requiresSave
-                    ? new MetadataRefreshOptions(options)
-                    {
-                        ForceSave = true
-                    }
-                    : options;
+            var refreshOptions = requiresSave
+                ? new MetadataRefreshOptions(options)
+                {
+                    ForceSave = true
+                }
+                : options;
 
-                return await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false);
-            }
-            finally
-            {
-                TriggerOnRefreshComplete();
-            }
+            return await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false);
         }
 
         protected bool IsVisibleStandaloneInternal(User user, bool checkFolders)
@@ -1367,7 +1350,7 @@ namespace MediaBrowser.Controller.Entities
         private async Task<bool> RefreshExtras(BaseItem item, MetadataRefreshOptions options, IReadOnlyList<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
         {
             var extras = LibraryManager.FindExtras(item, fileSystemChildren, options.DirectoryService).ToArray();
-            var newExtraIds = extras.Select(i => i.Id).ToArray();
+            var newExtraIds = Array.ConvertAll(extras, x => x.Id);
             var extrasChanged = !item.ExtraIds.SequenceEqual(newExtraIds);
 
             if (!extrasChanged && !options.ReplaceAllMetadata && options.MetadataRefreshMode != MetadataRefreshMode.FullRefresh)

+ 9 - 29
MediaBrowser.Controller/Entities/Folder.cs

@@ -301,14 +301,6 @@ namespace MediaBrowser.Controller.Entities
             return dictionary;
         }
 
-        protected override void TriggerOnRefreshStart()
-        {
-        }
-
-        protected override void TriggerOnRefreshComplete()
-        {
-        }
-
         /// <summary>
         /// Validates the children internal.
         /// </summary>
@@ -510,26 +502,17 @@ namespace MediaBrowser.Controller.Entities
 
         private async Task RefreshAllMetadataForContainer(IMetadataContainer container, MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
         {
-            // limit the amount of concurrent metadata refreshes
-            await ProviderManager.RunMetadataRefresh(
-                async () =>
-                {
-                    var series = container as Series;
-                    if (series is not null)
-                    {
-                        await series.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-                    }
+            if (container is Series series)
+            {
+                await series.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+            }
 
-                    await container.RefreshAllMetadata(refreshOptions, progress, cancellationToken).ConfigureAwait(false);
-                },
-                cancellationToken).ConfigureAwait(false);
+            await container.RefreshAllMetadata(refreshOptions, progress, cancellationToken).ConfigureAwait(false);
         }
 
         private async Task RefreshChildMetadata(BaseItem child, MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
         {
-            var container = child as IMetadataContainer;
-
-            if (container is not null)
+            if (child is IMetadataContainer container)
             {
                 await RefreshAllMetadataForContainer(container, refreshOptions, progress, cancellationToken).ConfigureAwait(false);
             }
@@ -537,10 +520,7 @@ namespace MediaBrowser.Controller.Entities
             {
                 if (refreshOptions.RefreshItem(child))
                 {
-                    // limit the amount of concurrent metadata refreshes
-                    await ProviderManager.RunMetadataRefresh(
-                        async () => await child.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false),
-                        cancellationToken).ConfigureAwait(false);
+                    await child.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
                 }
 
                 if (recursive && child is Folder folder)
@@ -586,7 +566,7 @@ namespace MediaBrowser.Controller.Entities
             }
 
             var fanoutConcurrency = ConfigurationManager.Configuration.LibraryScanFanoutConcurrency;
-            var parallelism = fanoutConcurrency == 0 ? Environment.ProcessorCount : fanoutConcurrency;
+            var parallelism = fanoutConcurrency > 0 ? fanoutConcurrency : 2 * Environment.ProcessorCount;
 
             var actionBlock = new ActionBlock<int>(
                 async i =>
@@ -618,7 +598,7 @@ namespace MediaBrowser.Controller.Entities
 
             for (var i = 0; i < childrenCount; i++)
             {
-                actionBlock.Post(i);
+                await actionBlock.SendAsync(i).ConfigureAwait(false);
             }
 
             actionBlock.Complete();

+ 12 - 5
MediaBrowser.Controller/Library/MetadataConfigurationExtensions.cs

@@ -1,8 +1,8 @@
-#nullable disable
-
 #pragma warning disable CS1591
 
+using System;
 using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Model.Configuration;
 
 namespace MediaBrowser.Controller.Library
@@ -10,8 +10,15 @@ namespace MediaBrowser.Controller.Library
     public static class MetadataConfigurationExtensions
     {
         public static MetadataConfiguration GetMetadataConfiguration(this IConfigurationManager config)
-        {
-            return config.GetConfiguration<MetadataConfiguration>("metadata");
-        }
+            => config.GetConfiguration<MetadataConfiguration>("metadata");
+
+        /// <summary>
+        /// Gets the <see cref="MetadataOptions" /> for the specified type.
+        /// </summary>
+        /// <param name="config">The <see cref="IServerConfigurationManager"/>.</param>
+        /// <param name="type">The type to get the <see cref="MetadataOptions" /> for.</param>
+        /// <returns>The <see cref="MetadataOptions" /> for the specified type or <c>null</c>.</returns>
+        public static MetadataOptions? GetMetadataOptionsForType(this IServerConfigurationManager config, string type)
+            => Array.Find(config.Configuration.MetadataOptions, i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase));
     }
 }

+ 0 - 17
MediaBrowser.Controller/Providers/IProviderManager.cs

@@ -54,14 +54,6 @@ namespace MediaBrowser.Controller.Providers
         /// <returns>Task.</returns>
         Task<ItemUpdateType> RefreshSingleItem(BaseItem item, MetadataRefreshOptions options, CancellationToken cancellationToken);
 
-        /// <summary>
-        /// Runs multiple metadata refreshes concurrently.
-        /// </summary>
-        /// <param name="action">The action to run.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
-        Task RunMetadataRefresh(Func<Task> action, CancellationToken cancellationToken);
-
         /// <summary>
         /// Saves the image.
         /// </summary>
@@ -207,15 +199,6 @@ namespace MediaBrowser.Controller.Providers
             where TItemType : BaseItem, new()
             where TLookupType : ItemLookupInfo;
 
-        /// <summary>
-        /// Gets the search image.
-        /// </summary>
-        /// <param name="providerName">Name of the provider.</param>
-        /// <param name="url">The URL.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{HttpResponseInfo}.</returns>
-        Task<HttpResponseMessage> GetSearchImage(string providerName, string url, CancellationToken cancellationToken);
-
         HashSet<Guid> GetRefreshQueue();
 
         void OnRefreshStart(BaseItem item);

+ 4 - 1
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -57,7 +57,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
         private readonly IServerConfigurationManager _serverConfig;
         private readonly string _startupOptionFFmpegPath;
 
-        private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2);
+        private readonly SemaphoreSlim _thumbnailResourcePool;
 
         private readonly object _runningProcessesLock = new object();
         private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
@@ -113,6 +113,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             _jsonSerializerOptions = new JsonSerializerOptions(JsonDefaults.Options);
             _jsonSerializerOptions.Converters.Add(new JsonBoolStringConverter());
+
+            var semaphoreCount = 2 * Environment.ProcessorCount;
+            _thumbnailResourcePool = new SemaphoreSlim(semaphoreCount, semaphoreCount);
         }
 
         /// <inheritdoc />

+ 4 - 4
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -189,7 +189,7 @@ namespace MediaBrowser.Model.Configuration
 
         public NameValuePair[] ContentTypes { get; set; } = Array.Empty<NameValuePair>();
 
-        public int RemoteClientBitrateLimit { get; set; } = 0;
+        public int RemoteClientBitrateLimit { get; set; }
 
         public bool EnableFolderView { get; set; } = false;
 
@@ -203,7 +203,7 @@ namespace MediaBrowser.Model.Configuration
 
         public bool EnableExternalContentInSuggestions { get; set; } = true;
 
-        public int ImageExtractionTimeoutMs { get; set; } = 0;
+        public int ImageExtractionTimeoutMs { get; set; }
 
         public PathSubstitution[] PathSubstitutions { get; set; } = Array.Empty<PathSubstitution>();
 
@@ -251,7 +251,7 @@ namespace MediaBrowser.Model.Configuration
         /// Gets or sets the dummy chapter duration in seconds, use 0 (zero) or less to disable generation alltogether.
         /// </summary>
         /// <value>The dummy chapters duration.</value>
-        public int DummyChapterDuration { get; set; } = 0;
+        public int DummyChapterDuration { get; set; }
 
         /// <summary>
         /// Gets or sets the chapter image resolution.
@@ -263,6 +263,6 @@ namespace MediaBrowser.Model.Configuration
         /// Gets or sets the limit for parallel image encoding.
         /// </summary>
         /// <value>The limit for parallel image encoding.</value>
-        public int ParallelImageEncodingLimit { get; set; } = 0;
+        public int ParallelImageEncodingLimit { get; set; }
     }
 }

+ 1 - 1
MediaBrowser.Model/Dlna/MediaOptions.cs

@@ -62,7 +62,7 @@ namespace MediaBrowser.Model.Dlna
         /// <summary>
         /// Gets or sets the device profile.
         /// </summary>
-        required public DeviceProfile Profile { get; set; }
+        public required DeviceProfile Profile { get; set; }
 
         /// <summary>
         /// Gets or sets a media source id. Optional. Only needed if a specific AudioStreamIndex or SubtitleStreamIndex are requested.

+ 12 - 51
MediaBrowser.Providers/Manager/ProviderManager.cs

@@ -131,12 +131,12 @@ namespace MediaBrowser.Providers.Manager
         {
             var type = item.GetType();
 
-            var service = _metadataServices.FirstOrDefault(current => current.CanRefreshPrimary(type));
-            service ??= _metadataServices.FirstOrDefault(current => current.CanRefresh(item));
+            var service = _metadataServices.FirstOrDefault(current => current.CanRefreshPrimary(type))
+                ?? _metadataServices.FirstOrDefault(current => current.CanRefresh(item));
 
             if (service is null)
             {
-                _logger.LogError("Unable to find a metadata service for item of type {TypeName}", item.GetType().Name);
+                _logger.LogError("Unable to find a metadata service for item of type {TypeName}", type.Name);
                 return Task.FromResult(ItemUpdateType.None);
             }
 
@@ -160,7 +160,7 @@ namespace MediaBrowser.Providers.Manager
             // TODO: Isolate this hack into the tvh plugin
             if (string.IsNullOrEmpty(contentType))
             {
-                if (url.IndexOf("/imagecache/", StringComparison.OrdinalIgnoreCase) != -1)
+                if (url.Contains("/imagecache/", StringComparison.OrdinalIgnoreCase))
                 {
                     contentType = "image/png";
                 }
@@ -232,6 +232,11 @@ namespace MediaBrowser.Providers.Manager
                 providers = providers.Where(i => string.Equals(i.Name, providerName, StringComparison.OrdinalIgnoreCase));
             }
 
+            if (query.ImageType is not null)
+            {
+                providers = providers.Where(i => i.GetSupportedImages(item).Contains(query.ImageType.Value));
+            }
+
             var preferredLanguage = item.GetPreferredMetadataLanguage();
 
             var tasks = providers.Select(i => GetImages(item, i, preferredLanguage, query.IncludeAllLanguages, cancellationToken, query.ImageType));
@@ -568,13 +573,7 @@ namespace MediaBrowser.Providers.Manager
 
         /// <inheritdoc/>
         public MetadataOptions GetMetadataOptions(BaseItem item)
-        {
-            var type = item.GetType().Name;
-
-            return _configurationManager.Configuration.MetadataOptions
-                .FirstOrDefault(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) ??
-                new MetadataOptions();
-        }
+            => _configurationManager.GetMetadataOptionsForType(item.GetType().Name) ?? new MetadataOptions();
 
         /// <inheritdoc/>
         public Task SaveMetadataAsync(BaseItem item, ItemUpdateType updateType)
@@ -809,27 +808,12 @@ namespace MediaBrowser.Providers.Manager
         {
             var results = await provider.GetSearchResults(searchInfo, cancellationToken).ConfigureAwait(false);
 
-            var list = results.ToList();
-
-            foreach (var item in list)
+            foreach (var item in results)
             {
                 item.SearchProviderName = provider.Name;
             }
 
-            return list;
-        }
-
-        /// <inheritdoc/>
-        public Task<HttpResponseMessage> GetSearchImage(string providerName, string url, CancellationToken cancellationToken)
-        {
-            var provider = _metadataProviders.OfType<IRemoteSearchProvider>().FirstOrDefault(i => string.Equals(i.Name, providerName, StringComparison.OrdinalIgnoreCase));
-
-            if (provider is null)
-            {
-                throw new ArgumentException("Search provider not found.");
-            }
-
-            return provider.GetImageResponse(url, cancellationToken);
+            return results;
         }
 
         private IEnumerable<IExternalId> GetExternalIds(IHasProviderIds item)
@@ -1102,29 +1086,6 @@ namespace MediaBrowser.Providers.Manager
             return RefreshItem(item, options, cancellationToken);
         }
 
-        /// <summary>
-        /// Runs multiple metadata refreshes concurrently.
-        /// </summary>
-        /// <param name="action">The action to run.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
-        public async Task RunMetadataRefresh(Func<Task> action, CancellationToken cancellationToken)
-        {
-            // create a variable for this since it is possible MetadataRefreshThrottler could change due to a config update during a scan
-            var metadataRefreshThrottler = _baseItemManager.MetadataRefreshThrottler;
-
-            await metadataRefreshThrottler.WaitAsync(cancellationToken).ConfigureAwait(false);
-
-            try
-            {
-                await action().ConfigureAwait(false);
-            }
-            finally
-            {
-                metadataRefreshThrottler.Release();
-            }
-        }
-
         /// <inheritdoc/>
         public void Dispose()
         {

+ 3 - 5
MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs

@@ -64,7 +64,7 @@ namespace MediaBrowser.Providers.Plugins.StudioImages
         {
             var thumbsPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotestudiothumbs.txt");
 
-            thumbsPath = await EnsureThumbsList(thumbsPath, cancellationToken).ConfigureAwait(false);
+            await EnsureThumbsList(thumbsPath, cancellationToken).ConfigureAwait(false);
 
             cancellationToken.ThrowIfCancellationRequested();
 
@@ -107,7 +107,7 @@ namespace MediaBrowser.Providers.Plugins.StudioImages
             return string.Format(CultureInfo.InvariantCulture, "{0}/images/{1}/{2}.jpg", GetRepositoryUrl(), image, filename);
         }
 
-        private Task<string> EnsureThumbsList(string file, CancellationToken cancellationToken)
+        private Task EnsureThumbsList(string file, CancellationToken cancellationToken)
         {
             string url = string.Format(CultureInfo.InvariantCulture, "{0}/thumbs.txt", GetRepositoryUrl());
 
@@ -129,7 +129,7 @@ namespace MediaBrowser.Providers.Plugins.StudioImages
         /// <param name="fileSystem">The file system.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>A Task to ensure existence of a file listing.</returns>
-        public async Task<string> EnsureList(string url, string file, IFileSystem fileSystem, CancellationToken cancellationToken)
+        public async Task EnsureList(string url, string file, IFileSystem fileSystem, CancellationToken cancellationToken)
         {
             var fileInfo = fileSystem.GetFileInfo(file);
 
@@ -148,8 +148,6 @@ namespace MediaBrowser.Providers.Plugins.StudioImages
                     }
                 }
             }
-
-            return file;
         }
 
         /// <summary>

+ 1 - 1
deployment/Dockerfile.centos.amd64

@@ -13,7 +13,7 @@ RUN yum update -yq \
   && yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget
 
 # Install DotNET SDK
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9c86d7b4-acb2-4be4-8a89-d13bc3c3f28f/1d044c7c29df018e8f2837bb343e8a84/dotnet-sdk-7.0.304-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/87a55ae3-917d-449e-a4e8-776f82976e91/03380e598c326c2f9465d262c6a88c45/dotnet-sdk-7.0.305-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
   && mkdir -p dotnet-sdk \
   && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
   && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet

+ 1 - 1
deployment/Dockerfile.fedora.amd64

@@ -12,7 +12,7 @@ RUN dnf update -yq \
   && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget make
 
 # Install DotNET SDK
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9c86d7b4-acb2-4be4-8a89-d13bc3c3f28f/1d044c7c29df018e8f2837bb343e8a84/dotnet-sdk-7.0.304-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/87a55ae3-917d-449e-a4e8-776f82976e91/03380e598c326c2f9465d262c6a88c45/dotnet-sdk-7.0.305-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
   && mkdir -p dotnet-sdk \
   && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
   && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet

+ 1 - 1
deployment/Dockerfile.ubuntu.amd64

@@ -17,7 +17,7 @@ RUN apt-get update -yqq \
     libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
 
 # Install dotnet repository
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9c86d7b4-acb2-4be4-8a89-d13bc3c3f28f/1d044c7c29df018e8f2837bb343e8a84/dotnet-sdk-7.0.304-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/87a55ae3-917d-449e-a4e8-776f82976e91/03380e598c326c2f9465d262c6a88c45/dotnet-sdk-7.0.305-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
   && mkdir -p dotnet-sdk \
   && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
   && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet

+ 1 - 1
deployment/Dockerfile.ubuntu.arm64

@@ -16,7 +16,7 @@ RUN apt-get update -yqq \
     mmv build-essential lsb-release
 
 # Install dotnet repository
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9c86d7b4-acb2-4be4-8a89-d13bc3c3f28f/1d044c7c29df018e8f2837bb343e8a84/dotnet-sdk-7.0.304-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/87a55ae3-917d-449e-a4e8-776f82976e91/03380e598c326c2f9465d262c6a88c45/dotnet-sdk-7.0.305-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
   && mkdir -p dotnet-sdk \
   && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
   && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet

+ 1 - 1
deployment/Dockerfile.ubuntu.armhf

@@ -16,7 +16,7 @@ RUN apt-get update -yqq \
     mmv build-essential lsb-release
 
 # Install dotnet repository
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9c86d7b4-acb2-4be4-8a89-d13bc3c3f28f/1d044c7c29df018e8f2837bb343e8a84/dotnet-sdk-7.0.304-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/87a55ae3-917d-449e-a4e8-776f82976e91/03380e598c326c2f9465d262c6a88c45/dotnet-sdk-7.0.305-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
   && mkdir -p dotnet-sdk \
   && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
   && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet

+ 0 - 2
src/Jellyfin.Drawing/ImageProcessor.cs

@@ -50,14 +50,12 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
     /// <param name="appPaths">The server application paths.</param>
     /// <param name="fileSystem">The filesystem.</param>
     /// <param name="imageEncoder">The image encoder.</param>
-    /// <param name="mediaEncoder">The media encoder.</param>
     /// <param name="config">The configuration.</param>
     public ImageProcessor(
         ILogger<ImageProcessor> logger,
         IServerApplicationPaths appPaths,
         IFileSystem fileSystem,
         IImageEncoder imageEncoder,
-        IMediaEncoder mediaEncoder,
         IServerConfigurationManager config)
     {
         _logger = logger;