Bond_009 1 год назад
Родитель
Сommit
b62b0ec2b5
29 измененных файлов с 67 добавлено и 69 удалено
  1. 1 0
      Emby.Server.Implementations/ApplicationHost.cs
  2. 1 0
      Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
  3. 2 4
      Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
  4. 2 3
      Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
  5. 8 8
      Jellyfin.Api/Controllers/ImageController.cs
  6. 2 2
      Jellyfin.Api/Helpers/DynamicHlsHelper.cs
  7. 7 7
      Jellyfin.Api/Helpers/StreamingHelpers.cs
  8. 1 1
      Jellyfin.Api/Helpers/TranscodingJobHelper.cs
  9. 3 3
      Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs
  10. 6 5
      Jellyfin.Networking/Manager/NetworkManager.cs
  11. 1 1
      Jellyfin.Server.Implementations/Security/AuthorizationContext.cs
  12. 1 1
      Jellyfin.Server.Implementations/Users/UserManager.cs
  13. 5 3
      Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
  14. 1 5
      Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs
  15. 1 1
      MediaBrowser.Common/Net/NetworkUtils.cs
  16. 1 0
      MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
  17. 2 2
      MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
  18. 3 8
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  19. 1 1
      MediaBrowser.Providers/Lyric/LrcLyricParser.cs
  20. 2 2
      MediaBrowser.Providers/Manager/ItemImageProvider.cs
  21. 2 2
      MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
  22. 1 1
      MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs
  23. 1 1
      MediaBrowser.Providers/Music/AlbumMetadataService.cs
  24. 1 1
      MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumImageProvider.cs
  25. 1 1
      MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs
  26. 1 1
      MediaBrowser.Providers/TV/SeriesMetadataService.cs
  27. 2 2
      MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
  28. 3 0
      jellyfin.ruleset
  29. 4 3
      src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs

+ 1 - 0
Emby.Server.Implementations/ApplicationHost.cs

@@ -99,6 +99,7 @@ using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Prometheus.DotNetRuntime;
 using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
+using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager;
 using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager;
 
 namespace Emby.Server.Implementations

+ 1 - 0
Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs

@@ -12,6 +12,7 @@ using MediaBrowser.Controller;
 using MediaBrowser.Controller.Plugins;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Logging;
+using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager;
 
 namespace Emby.Server.Implementations.EntryPoints
 {

+ 2 - 4
Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs

@@ -84,15 +84,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             return Task.CompletedTask;
         }
 
-        public Task Close()
+        public async Task Close()
         {
             EnableStreamSharing = false;
 
             Logger.LogInformation("Closing {Type}", GetType().Name);
 
-            LiveStreamCancellationTokenSource.Cancel();
-
-            return Task.CompletedTask;
+            await LiveStreamCancellationTokenSource.CancelAsync().ConfigureAwait(false);
         }
 
         public Stream GetStream()

+ 2 - 3
Jellyfin.Api/Auth/CustomAuthenticationHandler.cs

@@ -27,13 +27,12 @@ namespace Jellyfin.Api.Auth
         /// <param name="options">Options monitor.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="encoder">The url encoder.</param>
-        /// <param name="clock">The system clock.</param>
         public CustomAuthenticationHandler(
             IAuthService authService,
             IOptionsMonitor<AuthenticationSchemeOptions> options,
             ILoggerFactory logger,
-            UrlEncoder encoder,
-            ISystemClock clock) : base(options, logger, encoder, clock)
+            UrlEncoder encoder)
+            : base(options, logger, encoder)
         {
             _authService = authService;
             _logger = logger.CreateLogger<CustomAuthenticationHandler>();

+ 8 - 8
Jellyfin.Api/Controllers/ImageController.cs

@@ -2080,30 +2080,30 @@ public class ImageController : BaseJellyfinApiController
 
         foreach (var (key, value) in headers)
         {
-            Response.Headers.Add(key, value);
+            Response.Headers.Append(key, value);
         }
 
         Response.ContentType = imageContentType ?? MediaTypeNames.Text.Plain;
-        Response.Headers.Add(HeaderNames.Age, Convert.ToInt64((DateTime.UtcNow - dateImageModified).TotalSeconds).ToString(CultureInfo.InvariantCulture));
-        Response.Headers.Add(HeaderNames.Vary, HeaderNames.Accept);
+        Response.Headers.Append(HeaderNames.Age, Convert.ToInt64((DateTime.UtcNow - dateImageModified).TotalSeconds).ToString(CultureInfo.InvariantCulture));
+        Response.Headers.Append(HeaderNames.Vary, HeaderNames.Accept);
 
         if (disableCaching)
         {
-            Response.Headers.Add(HeaderNames.CacheControl, "no-cache, no-store, must-revalidate");
-            Response.Headers.Add(HeaderNames.Pragma, "no-cache, no-store, must-revalidate");
+            Response.Headers.Append(HeaderNames.CacheControl, "no-cache, no-store, must-revalidate");
+            Response.Headers.Append(HeaderNames.Pragma, "no-cache, no-store, must-revalidate");
         }
         else
         {
             if (cacheDuration.HasValue)
             {
-                Response.Headers.Add(HeaderNames.CacheControl, "public, max-age=" + cacheDuration.Value.TotalSeconds);
+                Response.Headers.Append(HeaderNames.CacheControl, "public, max-age=" + cacheDuration.Value.TotalSeconds);
             }
             else
             {
-                Response.Headers.Add(HeaderNames.CacheControl, "public");
+                Response.Headers.Append(HeaderNames.CacheControl, "public");
             }
 
-            Response.Headers.Add(HeaderNames.LastModified, dateImageModified.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss \"GMT\"", CultureInfo.InvariantCulture));
+            Response.Headers.Append(HeaderNames.LastModified, dateImageModified.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss \"GMT\"", CultureInfo.InvariantCulture));
 
             // if the image was not modified since "ifModifiedSinceHeader"-header, return a HTTP status code 304 not modified
             if (!(dateImageModified > ifModifiedSinceHeader) && cacheDuration.HasValue)

+ 2 - 2
Jellyfin.Api/Helpers/DynamicHlsHelper.cs

@@ -147,7 +147,7 @@ public class DynamicHlsHelper
                 cancellationTokenSource.Token)
             .ConfigureAwait(false);
 
-        _httpContextAccessor.HttpContext.Response.Headers.Add(HeaderNames.Expires, "0");
+        _httpContextAccessor.HttpContext.Response.Headers.Append(HeaderNames.Expires, "0");
         if (isHeadRequest)
         {
             return new FileContentResult(Array.Empty<byte>(), MimeTypes.GetMimeType("playlist.m3u8"));
@@ -568,7 +568,7 @@ public class DynamicHlsHelper
             && state.VideoStream is not null
             && state.VideoStream.Level.HasValue)
         {
-            levelString = state.VideoStream.Level.ToString() ?? string.Empty;
+            levelString = state.VideoStream.Level.Value.ToString(CultureInfo.InvariantCulture) ?? string.Empty;
         }
         else
         {

+ 7 - 7
Jellyfin.Api/Helpers/StreamingHelpers.cs

@@ -279,15 +279,15 @@ public static class StreamingHelpers
         var profile = state.DeviceProfile;
 
         StringValues transferMode = request.Headers["transferMode.dlna.org"];
-        responseHeaders.Add("transferMode.dlna.org", string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode.ToString());
-        responseHeaders.Add("realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*");
+        responseHeaders.Append("transferMode.dlna.org", string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode.ToString());
+        responseHeaders.Append("realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*");
 
         if (state.RunTimeTicks.HasValue)
         {
             if (string.Equals(request.Headers["getMediaInfo.sec"], "1", StringComparison.OrdinalIgnoreCase))
             {
                 var ms = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds;
-                responseHeaders.Add("MediaInfo.sec", string.Format(
+                responseHeaders.Append("MediaInfo.sec", string.Format(
                     CultureInfo.InvariantCulture,
                     "SEC_Duration={0};",
                     Convert.ToInt32(ms)));
@@ -305,7 +305,7 @@ public static class StreamingHelpers
 
         if (!state.IsVideoRequest)
         {
-            responseHeaders.Add("contentFeatures.dlna.org", ContentFeatureBuilder.BuildAudioHeader(
+            responseHeaders.Append("contentFeatures.dlna.org", ContentFeatureBuilder.BuildAudioHeader(
                 profile,
                 state.OutputContainer,
                 audioCodec,
@@ -321,7 +321,7 @@ public static class StreamingHelpers
         {
             var videoCodec = state.ActualOutputVideoCodec;
 
-            responseHeaders.Add(
+            responseHeaders.Append(
                 "contentFeatures.dlna.org",
                 ContentFeatureBuilder.BuildVideoHeader(profile, state.OutputContainer, videoCodec, audioCodec, state.OutputWidth, state.OutputHeight, state.TargetVideoBitDepth, state.OutputVideoBitrate, state.TargetTimestamp, isStaticallyStreamed, state.RunTimeTicks, state.TargetVideoProfile, state.TargetVideoRangeType, state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, state.TranscodeSeekInfo, state.IsTargetAnamorphic, state.IsTargetInterlaced, state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, state.TargetVideoCodecTag, state.IsTargetAVC).FirstOrDefault() ?? string.Empty);
         }
@@ -404,12 +404,12 @@ public static class StreamingHelpers
         var runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks!.Value).TotalSeconds.ToString(CultureInfo.InvariantCulture);
         var startSeconds = TimeSpan.FromTicks(startTimeTicks ?? 0).TotalSeconds.ToString(CultureInfo.InvariantCulture);
 
-        responseHeaders.Add("TimeSeekRange.dlna.org", string.Format(
+        responseHeaders.Append("TimeSeekRange.dlna.org", string.Format(
             CultureInfo.InvariantCulture,
             "npt={0}-{1}/{1}",
             startSeconds,
             runtimeSeconds));
-        responseHeaders.Add("X-AvailableSeekRange", string.Format(
+        responseHeaders.Append("X-AvailableSeekRange", string.Format(
             CultureInfo.InvariantCulture,
             "1 npt={0}-{1}",
             startSeconds,

+ 1 - 1
Jellyfin.Api/Helpers/TranscodingJobHelper.cs

@@ -280,6 +280,7 @@ public class TranscodingJobHelper : IDisposable
 
             if (job.CancellationTokenSource?.IsCancellationRequested == false)
             {
+#pragma warning disable CA1849 // Can't await in lock block
                 job.CancellationTokenSource.Cancel();
             }
         }
@@ -291,7 +292,6 @@ public class TranscodingJobHelper : IDisposable
 
         lock (job.ProcessLock!)
         {
-#pragma warning disable CA1849 // Can't await in lock block
             job.TranscodingThrottler?.Stop().GetAwaiter().GetResult();
 
             var process = job.Process;

+ 3 - 3
Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs

@@ -65,7 +65,7 @@ namespace Jellyfin.Networking.HappyEyeballs
             // See https://github.com/dotnet/corefx/pull/29792/files#r189415885 for more details.
             if (await Task.WhenAny(tryConnectAsyncIPv6, Task.Delay(200, cancelIPv6.Token)).ConfigureAwait(false) == tryConnectAsyncIPv6 && tryConnectAsyncIPv6.IsCompletedSuccessfully)
             {
-                cancelIPv6.Cancel();
+                await cancelIPv6.CancelAsync().ConfigureAwait(false);
                 return tryConnectAsyncIPv6.GetAwaiter().GetResult();
             }
 
@@ -76,7 +76,7 @@ namespace Jellyfin.Networking.HappyEyeballs
             {
                 if (tryConnectAsyncIPv6.IsCompletedSuccessfully)
                 {
-                    cancelIPv4.Cancel();
+                    await cancelIPv4.CancelAsync().ConfigureAwait(false);
                     return tryConnectAsyncIPv6.GetAwaiter().GetResult();
                 }
 
@@ -86,7 +86,7 @@ namespace Jellyfin.Networking.HappyEyeballs
             {
                 if (tryConnectAsyncIPv4.IsCompletedSuccessfully)
                 {
-                    cancelIPv6.Cancel();
+                    await cancelIPv6.CancelAsync().ConfigureAwait(false);
                     return tryConnectAsyncIPv4.GetAwaiter().GetResult();
                 }
 

+ 6 - 5
Jellyfin.Networking/Manager/NetworkManager.cs

@@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Logging;
 using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
+using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager;
 
 namespace Jellyfin.Networking.Manager
 {
@@ -422,7 +423,7 @@ namespace Jellyfin.Networking.Manager
             {
                 // Parse config values into filter collection
                 var remoteIPFilter = config.RemoteIPFilter;
-                if (remoteIPFilter.Any() && !string.IsNullOrWhiteSpace(remoteIPFilter.First()))
+                if (remoteIPFilter.Length != 0 && !string.IsNullOrWhiteSpace(remoteIPFilter[0]))
                 {
                     // Parse all IPs with netmask to a subnet
                     var remoteAddressFilter = new List<IPNetwork>();
@@ -540,7 +541,7 @@ namespace Jellyfin.Networking.Manager
                     }
                     else if (NetworkUtils.TryParseToSubnet(identifier, out var result) && result is not null)
                     {
-                        var data = new IPData(result.BaseAddress, result);
+                        var data = new IPData(result.Value.BaseAddress, result);
                         publishedServerUrls.Add(
                             new PublishedServerUriOverride(
                                 data,
@@ -606,11 +607,11 @@ namespace Jellyfin.Networking.Manager
                     var parts = details.Split(',');
                     if (NetworkUtils.TryParseToSubnet(parts[0], out var subnet))
                     {
-                        var address = subnet.BaseAddress;
+                        var address = subnet.Value.BaseAddress;
                         var index = int.Parse(parts[1], CultureInfo.InvariantCulture);
                         if (address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.InterNetworkV6)
                         {
-                            var data = new IPData(address, subnet, parts[2])
+                            var data = new IPData(address, subnet.Value, parts[2])
                             {
                                 Index = index
                             };
@@ -880,7 +881,7 @@ namespace Jellyfin.Networking.Manager
         {
             if (NetworkUtils.TryParseToSubnet(address, out var subnet))
             {
-                return IPAddress.IsLoopback(subnet.BaseAddress) || (_lanSubnets.Any(x => x.Contains(subnet.BaseAddress)) && !_excludedSubnets.Any(x => x.Contains(subnet.BaseAddress)));
+                return IPAddress.IsLoopback(subnet.Value.BaseAddress) || (_lanSubnets.Any(x => x.Contains(subnet.Value.BaseAddress)) && !_excludedSubnets.Any(x => x.Contains(subnet.Value.BaseAddress)));
             }
 
             if (NetworkUtils.TryParseHost(address, out var addresses, IsIPv4Enabled, IsIPv6Enabled))

+ 1 - 1
Jellyfin.Server.Implementations/Security/AuthorizationContext.cs

@@ -60,7 +60,7 @@ namespace Jellyfin.Server.Implementations.Security
         }
 
         private async Task<AuthorizationInfo> GetAuthorizationInfoFromDictionary(
-            IReadOnlyDictionary<string, string>? auth,
+            Dictionary<string, string>? auth,
             IHeaderDictionary headers,
             IQueryCollection queryString)
         {

+ 1 - 1
Jellyfin.Server.Implementations/Users/UserManager.cs

@@ -775,7 +775,7 @@ namespace Jellyfin.Server.Implementations.Users
             return providers;
         }
 
-        private IList<IPasswordResetProvider> GetPasswordResetProviders(User user)
+        private IPasswordResetProvider[] GetPasswordResetProviders(User user)
         {
             var passwordResetProviderId = user.PasswordResetProviderId;
             var providers = _passwordResetProviders.Where(i => i.IsEnabled).ToArray();

+ 5 - 3
Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs

@@ -30,12 +30,14 @@ 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.Any;
 using Microsoft.OpenApi.Interfaces;
 using Microsoft.OpenApi.Models;
 using Swashbuckle.AspNetCore.SwaggerGen;
 using AuthenticationSchemes = Jellyfin.Api.Constants.AuthenticationSchemes;
+using IPNetwork = System.Net.IPNetwork;
 
 namespace Jellyfin.Server.Extensions
 {
@@ -277,9 +279,9 @@ namespace Jellyfin.Server.Extensions
                 }
                 else if (NetworkUtils.TryParseToSubnet(allowedProxies[i], out var subnet))
                 {
-                    if (subnet is not null)
+                    if (subnet.HasValue)
                     {
-                        AddIPAddress(config, options, subnet.BaseAddress, subnet.PrefixLength);
+                        AddIPAddress(config, options, subnet.Value.BaseAddress, subnet.Value.PrefixLength);
                     }
                 }
                 else if (NetworkUtils.TryParseHost(allowedProxies[i], out var addresses, config.EnableIPv4, config.EnableIPv6))
@@ -310,7 +312,7 @@ namespace Jellyfin.Server.Extensions
             }
             else
             {
-                options.KnownNetworks.Add(new IPNetwork(addr, prefixLength));
+                options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(addr, prefixLength));
             }
         }
 

+ 1 - 5
Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs

@@ -78,11 +78,7 @@ namespace Jellyfin.Server.Migrations.Routines
                     }
                     else
                     {
-                        var ratingValue = _localizationManager.GetRatingLevel(ratingString).ToString();
-                        if (string.IsNullOrEmpty(ratingValue))
-                        {
-                            ratingValue = "NULL";
-                        }
+                        var ratingValue = _localizationManager.GetRatingLevel(ratingString)?.ToString(CultureInfo.InvariantCulture) ?? "NULL";
 
                         using var statement = connection.PrepareStatement("UPDATE TypedBaseItems SET InheritedParentalRatingValue = @Value WHERE OfficialRating = @Rating;");
                         statement.TryBind("@Value", ratingValue);

+ 1 - 1
MediaBrowser.Common/Net/NetworkUtils.cs

@@ -180,7 +180,7 @@ public static partial class NetworkUtils
         {
             if (TryParseToSubnet(values[a], out var innerResult, negated))
             {
-                tmpResult.Add(innerResult);
+                tmpResult.Add(innerResult.Value);
             }
         }
 

+ 1 - 0
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -20,6 +20,7 @@ using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.MediaInfo;
 using Microsoft.Extensions.Configuration;
+using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager;
 
 namespace MediaBrowser.Controller.MediaEncoding
 {

+ 2 - 2
MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs

@@ -516,7 +516,7 @@ namespace MediaBrowser.MediaEncoding.Probing
 
         private void ProcessPairs(string key, List<NameValuePair> pairs, MediaInfo info)
         {
-            IList<BaseItemPerson> peoples = new List<BaseItemPerson>();
+            List<BaseItemPerson> peoples = new List<BaseItemPerson>();
             if (string.Equals(key, "studio", StringComparison.OrdinalIgnoreCase))
             {
                 info.Studios = pairs.Select(p => p.Value)
@@ -1182,7 +1182,7 @@ namespace MediaBrowser.MediaEncoding.Probing
             info.Size = string.IsNullOrEmpty(data.Format.Size) ? null : long.Parse(data.Format.Size, CultureInfo.InvariantCulture);
         }
 
-        private void SetAudioInfoFromTags(MediaInfo audio, IReadOnlyDictionary<string, string> tags)
+        private void SetAudioInfoFromTags(MediaInfo audio, Dictionary<string, string> tags)
         {
             var people = new List<BaseItemPerson>();
             if (tags.TryGetValue("composer", out var composer) && !string.IsNullOrWhiteSpace(composer))

+ 3 - 8
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -835,11 +835,6 @@ namespace MediaBrowser.Model.Dlna
                 playlistItem.SetOption(qualifier, "profile", videoStream.Profile.ToLowerInvariant());
             }
 
-            if (videoStream is not null && videoStream.Level != 0)
-            {
-                playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString() ?? string.Empty);
-            }
-
             // Prefer matching audio codecs, could do better here
             var audioCodecs = ContainerProfile.SplitValue(audioCodec);
 
@@ -866,16 +861,16 @@ namespace MediaBrowser.Model.Dlna
 
                 // Copy matching audio codec options
                 playlistItem.AudioSampleRate = audioStream.SampleRate;
-                playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels.ToString() ?? string.Empty);
+                playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels?.ToString(CultureInfo.InvariantCulture) ?? string.Empty);
 
                 if (!string.IsNullOrEmpty(audioStream.Profile))
                 {
                     playlistItem.SetOption(audioStream.Codec, "profile", audioStream.Profile.ToLowerInvariant());
                 }
 
-                if (audioStream.Level != 0)
+                if (audioStream.Level.HasValue)
                 {
-                    playlistItem.SetOption(audioStream.Codec, "level", audioStream.Level.ToString() ?? string.Empty);
+                    playlistItem.SetOption(audioStream.Codec, "level", audioStream.Level.Value.ToString(CultureInfo.InvariantCulture));
                 }
             }
 

+ 1 - 1
MediaBrowser.Providers/Lyric/LrcLyricParser.cs

@@ -125,7 +125,7 @@ public class LrcLyricParser : ILyricParser
     /// </summary>
     /// <param name="metaData">The metadata from the LRC file.</param>
     /// <returns>A lyricMetadata object with mapped property data.</returns>
-    private static LyricMetadata MapMetadataValues(IDictionary<string, string> metaData)
+    private static LyricMetadata MapMetadataValues(Dictionary<string, string> metaData)
     {
         LyricMetadata lyricMetadata = new();
 

+ 2 - 2
MediaBrowser.Providers/Manager/ItemImageProvider.cs

@@ -175,7 +175,7 @@ namespace MediaBrowser.Providers.Manager
             IDynamicImageProvider provider,
             ImageRefreshOptions refreshOptions,
             TypeOptions savedOptions,
-            ICollection<ImageType> downloadedImages,
+            List<ImageType> downloadedImages,
             RefreshResult result,
             CancellationToken cancellationToken)
         {
@@ -263,7 +263,7 @@ namespace MediaBrowser.Providers.Manager
             ImageRefreshOptions refreshOptions,
             TypeOptions savedOptions,
             int backdropLimit,
-            ICollection<ImageType> downloadedImages,
+            List<ImageType> downloadedImages,
             RefreshResult result,
             CancellationToken cancellationToken)
         {

+ 2 - 2
MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs

@@ -82,8 +82,8 @@ namespace MediaBrowser.Providers.MediaInfo
             {
                 Directory.CreateDirectory(Path.GetDirectoryName(path));
 
-                var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("front", StringComparison.OrdinalIgnoreCase) != -1) ??
-                    imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ??
+                var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).Contains("front", StringComparison.OrdinalIgnoreCase)) ??
+                    imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).Contains("cover", StringComparison.OrdinalIgnoreCase)) ??
                     imageStreams.FirstOrDefault();
 
                 var imageStreamIndex = imageStream?.Index;

+ 1 - 1
MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs

@@ -183,7 +183,7 @@ namespace MediaBrowser.Providers.MediaInfo
                 files.AddRange(directoryService.GetFilePaths(internalMetadataPath, clearCache, true));
             }
 
-            if (!files.Any())
+            if (files.Count == 0)
             {
                 return Array.Empty<ExternalPathParserResult>();
             }

+ 1 - 1
MediaBrowser.Providers/Music/AlbumMetadataService.cs

@@ -148,7 +148,7 @@ namespace MediaBrowser.Providers.Music
                 .ToArray();
 
             var id = item.GetProviderId(provider);
-            if (ids.Any())
+            if (ids.Length != 0)
             {
                 var firstId = ids[0];
                 if (!string.IsNullOrEmpty(firstId)

+ 1 - 1
MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumImageProvider.cs

@@ -73,7 +73,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
             return Enumerable.Empty<RemoteImageInfo>();
         }
 
-        private IEnumerable<RemoteImageInfo> GetImages(AudioDbAlbumProvider.Album item)
+        private List<RemoteImageInfo> GetImages(AudioDbAlbumProvider.Album item)
         {
             var list = new List<RemoteImageInfo>();
 

+ 1 - 1
MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs

@@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
             return Enumerable.Empty<RemoteImageInfo>();
         }
 
-        private IEnumerable<RemoteImageInfo> GetImages(AudioDbArtistProvider.Artist item)
+        private List<RemoteImageInfo> GetImages(AudioDbArtistProvider.Artist item)
         {
             var list = new List<RemoteImageInfo>();
 

+ 1 - 1
MediaBrowser.Providers/TV/SeriesMetadataService.cs

@@ -121,7 +121,7 @@ namespace MediaBrowser.Providers.TV
                 var seasonNumber = virtualSeason.IndexNumber;
                 // If there's a physical season with the same number or no episodes in the season, delete it
                 if ((seasonNumber.HasValue && physicalSeasonNumbers.Contains(seasonNumber.Value))
-                    || !virtualSeason.GetEpisodes().Any())
+                    || virtualSeason.GetEpisodes().Count == 0)
                 {
                     Logger.LogInformation("Removing virtual season {SeasonNumber} in series {SeriesName}", virtualSeason.IndexNumber, series.Name);
 

+ 2 - 2
MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs

@@ -314,11 +314,11 @@ namespace MediaBrowser.XbmcMetadata.Savers
                 {
                     var codec = stream.Codec;
 
-                    if ((stream.CodecTag ?? string.Empty).IndexOf("xvid", StringComparison.OrdinalIgnoreCase) != -1)
+                    if ((stream.CodecTag ?? string.Empty).Contains("xvid", StringComparison.OrdinalIgnoreCase))
                     {
                         codec = "xvid";
                     }
-                    else if ((stream.CodecTag ?? string.Empty).IndexOf("divx", StringComparison.OrdinalIgnoreCase) != -1)
+                    else if ((stream.CodecTag ?? string.Empty).Contains("divx", StringComparison.OrdinalIgnoreCase))
                     {
                         codec = "divx";
                     }

+ 3 - 0
jellyfin.ruleset

@@ -140,6 +140,9 @@
     <Rule Id="CA1812" Action="Info" />
     <!-- disable warning CA1822: Member does not access instance data and can be marked as static -->
     <Rule Id="CA1822" Action="Info" />
+    <!-- TODO: Enable -->
+    <!-- CA1861: Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array -->
+    <Rule Id="CA1861" Action="Info" />
     <!-- disable warning CA2000: Dispose objects before losing scope -->
     <Rule Id="CA2000" Action="Info" />
     <!-- disable warning CA2253: Named placeholders should not be numeric values -->

+ 4 - 3
src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs

@@ -11,8 +11,6 @@ namespace Jellyfin.MediaEncoding.Keyframes.FfProbe;
 /// </summary>
 public static class FfProbeKeyframeExtractor
 {
-    private const string DefaultArguments = "-fflags +genpts -v error -skip_frame nokey -show_entries format=duration -show_entries stream=duration -show_entries packet=pts_time,flags -select_streams v -of csv \"{0}\"";
-
     /// <summary>
     /// Extracts the keyframes using the ffprobe executable at the specified path.
     /// </summary>
@@ -26,7 +24,10 @@ public static class FfProbeKeyframeExtractor
             StartInfo = new ProcessStartInfo
             {
                 FileName = ffProbePath,
-                Arguments = string.Format(CultureInfo.InvariantCulture, DefaultArguments, filePath),
+                Arguments = string.Format(
+                    CultureInfo.InvariantCulture,
+                    "-fflags +genpts -v error -skip_frame nokey -show_entries format=duration -show_entries stream=duration -show_entries packet=pts_time,flags -select_streams v -of csv \"{0}\"",
+                    filePath),
 
                 CreateNoWindow = true,
                 UseShellExecute = false,