Browse Source

Merge branch 'master' into keyframe_extraction_v1

cvium 3 years ago
parent
commit
30f3be1da0
100 changed files with 248 additions and 284 deletions
  1. 2 1
      .ci/azure-pipelines-abi.yml
  2. 2 1
      .ci/azure-pipelines-main.yml
  3. 3 2
      .ci/azure-pipelines-package.yml
  4. 3 2
      .ci/azure-pipelines-test.yml
  5. 0 2
      .ci/azure-pipelines.yml
  6. 3 1
      .github/workflows/codeql-analysis.yml
  7. 1 1
      Dockerfile
  8. 1 1
      Dockerfile.arm
  9. 1 1
      Dockerfile.arm64
  10. 1 1
      DvdLib/DvdLib.csproj
  11. 2 1
      DvdLib/Ifo/Dvd.cs
  12. 9 5
      Emby.Dlna/DlnaManager.cs
  13. 1 1
      Emby.Dlna/Emby.Dlna.csproj
  14. 1 2
      Emby.Dlna/Server/DescriptionXmlBuilder.cs
  15. 4 4
      Emby.Dlna/Service/BaseService.cs
  16. 1 1
      Emby.Drawing/Emby.Drawing.csproj
  17. 1 1
      Emby.Drawing/ImageProcessor.cs
  18. 1 1
      Emby.Naming/Emby.Naming.csproj
  19. 1 1
      Emby.Notifications/Emby.Notifications.csproj
  20. 1 1
      Emby.Photos/Emby.Photos.csproj
  21. 11 16
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  22. 1 1
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  23. 2 2
      Emby.Server.Implementations/IO/ManagedFileSystem.cs
  24. 1 3
      Emby.Server.Implementations/Library/LibraryManager.cs
  25. 1 1
      Emby.Server.Implementations/Library/LiveStreamHelper.cs
  26. 2 2
      Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
  27. 1 1
      Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
  28. 1 1
      Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
  29. 10 8
      Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
  30. 3 2
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
  31. 12 12
      Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
  32. 1 1
      Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
  33. 2 2
      Emby.Server.Implementations/Localization/Core/ar.json
  34. 1 1
      Emby.Server.Implementations/Localization/Core/hr.json
  35. 9 9
      Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
  36. 10 10
      Emby.Server.Implementations/Plugins/PluginManager.cs
  37. 3 25
      Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs
  38. 3 3
      Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs
  39. 3 3
      Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs
  40. 5 5
      Jellyfin.Api/Controllers/ImageController.cs
  41. 1 1
      Jellyfin.Api/Controllers/RemoteImageController.cs
  42. 1 1
      Jellyfin.Api/Controllers/SystemController.cs
  43. 2 1
      Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
  44. 1 1
      Jellyfin.Api/Helpers/HlsHelpers.cs
  45. 2 22
      Jellyfin.Api/Helpers/ProgressiveFileStream.cs
  46. 1 1
      Jellyfin.Api/Helpers/TranscodingJobHelper.cs
  47. 1 1
      Jellyfin.Api/Jellyfin.Api.csproj
  48. 3 2
      Jellyfin.Api/ModelBinders/NullableEnumModelBinder.cs
  49. 1 1
      Jellyfin.Data/Jellyfin.Data.csproj
  50. 1 6
      Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
  51. 1 1
      Jellyfin.Networking/Jellyfin.Networking.csproj
  52. 1 1
      Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
  53. 2 2
      Jellyfin.Server/Configuration/CorsPolicyProvider.cs
  54. 4 4
      Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs
  55. 1 1
      Jellyfin.Server/Jellyfin.Server.csproj
  56. 5 1
      Jellyfin.Server/Middleware/QueryStringDecodingMiddleware.cs
  57. 2 8
      Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs
  58. 3 3
      Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
  59. 1 1
      Jellyfin.Server/Program.cs
  60. 1 1
      MediaBrowser.Common/MediaBrowser.Common.csproj
  61. 1 1
      MediaBrowser.Controller/Dlna/IDlnaManager.cs
  62. 1 1
      MediaBrowser.Controller/Drawing/IImageProcessor.cs
  63. 7 2
      MediaBrowser.Controller/Drawing/ImageStream.cs
  64. 1 1
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  65. 6 1
      MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
  66. 1 1
      MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
  67. 1 1
      MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
  68. 1 1
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs
  69. 1 1
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
  70. 2 9
      MediaBrowser.Model/IO/AsyncFile.cs
  71. 1 1
      MediaBrowser.Model/MediaBrowser.Model.csproj
  72. 7 5
      MediaBrowser.Model/Net/MimeTypes.cs
  73. 1 1
      MediaBrowser.Providers/Manager/ImageSaver.cs
  74. 1 1
      MediaBrowser.Providers/Manager/ItemImageProvider.cs
  75. 1 1
      MediaBrowser.Providers/MediaBrowser.Providers.csproj
  76. 1 1
      MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs
  77. 1 1
      MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs
  78. 1 1
      MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableInt32Converter.cs
  79. 2 2
      MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
  80. 1 1
      MediaBrowser.Providers/Studios/StudiosImageProvider.cs
  81. 1 1
      MediaBrowser.Providers/Subtitles/SubtitleManager.cs
  82. 1 1
      MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
  83. 3 2
      RSSDP/DisposableManagedObjectBase.cs
  84. 1 1
      RSSDP/RSSDP.csproj
  85. 8 5
      RSSDP/SsdpDevice.cs
  86. 8 7
      RSSDP/SsdpDevicePublisher.cs
  87. 1 1
      debian/control
  88. 5 5
      deployment/Dockerfile.centos.amd64
  89. 1 1
      deployment/Dockerfile.debian.amd64
  90. 5 5
      deployment/Dockerfile.debian.arm64
  91. 5 5
      deployment/Dockerfile.debian.armhf
  92. 1 3
      deployment/Dockerfile.docker.amd64
  93. 1 3
      deployment/Dockerfile.docker.arm64
  94. 1 3
      deployment/Dockerfile.docker.armhf
  95. 6 3
      deployment/Dockerfile.fedora.amd64
  96. 1 1
      deployment/Dockerfile.linux.amd64
  97. 1 1
      deployment/Dockerfile.linux.amd64-musl
  98. 1 1
      deployment/Dockerfile.linux.arm64
  99. 1 1
      deployment/Dockerfile.linux.armhf
  100. 1 1
      deployment/Dockerfile.macos

+ 2 - 1
.ci/azure-pipelines-abi.yml

@@ -7,7 +7,7 @@ parameters:
   default: "ubuntu-latest"
 - name: DotNetSdkVersion
   type: string
-  default: 5.0.302
+  default: 6.0.x
 
 jobs:
   - job: CompatibilityCheck
@@ -34,6 +34,7 @@ jobs:
         inputs:
           packageType: sdk
           version: ${{ parameters.DotNetSdkVersion }}
+          includePreviewVersions: true
 
       - task: DotNetCoreCLI@2
         displayName: 'Install ABI CompatibilityChecker Tool'

+ 2 - 1
.ci/azure-pipelines-main.yml

@@ -1,7 +1,7 @@
 parameters:
   LinuxImage: 'ubuntu-latest'
   RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj'
-  DotNetSdkVersion: 5.0.302
+  DotNetSdkVersion: 6.0.x
 
 jobs:
   - job: Build
@@ -54,6 +54,7 @@ jobs:
         inputs:
           packageType: sdk
           version: ${{ parameters.DotNetSdkVersion }}
+          includePreviewVersions: true
 
       - task: DotNetCoreCLI@2
         displayName: 'Publish Server'

+ 3 - 2
.ci/azure-pipelines-package.yml

@@ -195,10 +195,11 @@ jobs:
 
   steps:
   - task: UseDotNet@2
-    displayName: 'Use .NET 5.0 sdk'
+    displayName: 'Use .NET 6.0 sdk'
     inputs:
       packageType: 'sdk'
-      version: '5.0.x'
+      version: '6.0.x'
+      includePreviewVersions: true
 
   - task: DotNetCoreCLI@2
     displayName: 'Build Stable Nuget packages'

+ 3 - 2
.ci/azure-pipelines-test.yml

@@ -10,7 +10,7 @@ parameters:
   default: "tests/**/*Tests.csproj"
 - name: DotNetSdkVersion
   type: string
-  default: 5.0.302
+  default: 6.0.x
 
 jobs:
   - job: Test
@@ -41,6 +41,7 @@ jobs:
         inputs:
           packageType: sdk
           version: ${{ parameters.DotNetSdkVersion }}
+          includePreviewVersions: true
 
       - task: SonarCloudPrepare@1
         displayName: 'Prepare analysis on SonarCloud'
@@ -94,5 +95,5 @@ jobs:
         displayName: 'Publish OpenAPI Artifact'
         condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
         inputs:
-          targetPath: "tests/Jellyfin.Server.Integration.Tests/bin/Release/net5.0/openapi.json"
+          targetPath: "tests/Jellyfin.Server.Integration.Tests/bin/Release/net6.0/openapi.json"
           artifactName: 'OpenAPI Spec'

+ 0 - 2
.ci/azure-pipelines.yml

@@ -5,8 +5,6 @@ variables:
   value: 'tests/**/*Tests.csproj'
 - name: RestoreBuildProjects
   value: 'Jellyfin.Server/Jellyfin.Server.csproj'
-- name: DotNetSdkVersion
-  value: 5.0.302
 
 pr:
   autoCancel: true

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

@@ -24,7 +24,9 @@ jobs:
     - name: Setup .NET Core
       uses: actions/setup-dotnet@v1
       with:
-        dotnet-version: '5.0.x'
+        dotnet-version: '6.0.x'
+        include-prerelease: true
+        
     - name: Initialize CodeQL
       uses: github/codeql-action/init@v1
       with:

+ 1 - 1
Dockerfile

@@ -2,7 +2,7 @@
 #####################################
 # Requires binfm_misc registration
 # https://github.com/multiarch/qemu-user-static#binfmt_misc-register
-ARG DOTNET_VERSION=5.0
+ARG DOTNET_VERSION=6.0
 
 FROM node:lts-alpine as web-builder
 ARG JELLYFIN_WEB_VERSION=master

+ 1 - 1
Dockerfile.arm

@@ -2,7 +2,7 @@
 #####################################
 # Requires binfm_misc registration
 # https://github.com/multiarch/qemu-user-static#binfmt_misc-register
-ARG DOTNET_VERSION=5.0
+ARG DOTNET_VERSION=6.0
 
 
 FROM node:lts-alpine as web-builder

+ 1 - 1
Dockerfile.arm64

@@ -2,7 +2,7 @@
 #####################################
 # Requires binfm_misc registration
 # https://github.com/multiarch/qemu-user-static#binfmt_misc-register
-ARG DOTNET_VERSION=5.0
+ARG DOTNET_VERSION=6.0
 
 
 FROM node:lts-alpine as web-builder

+ 1 - 1
DvdLib/DvdLib.csproj

@@ -10,7 +10,7 @@
   </ItemGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <AnalysisMode>AllDisabledByDefault</AnalysisMode>

+ 2 - 1
DvdLib/Ifo/Dvd.cs

@@ -2,6 +2,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Linq;
 
@@ -76,7 +77,7 @@ namespace DvdLib.Ifo
 
         private void ReadVTS(ushort vtsNum, IReadOnlyList<FileInfo> allFiles)
         {
-            var filename = string.Format("VTS_{0:00}_0.IFO", vtsNum);
+            var filename = string.Format(CultureInfo.InvariantCulture, "VTS_{0:00}_0.IFO", vtsNum);
 
             var vtsPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase)) ??
                 allFiles.FirstOrDefault(i => string.Equals(i.Name, Path.ChangeExtension(filename, ".bup"), StringComparison.OrdinalIgnoreCase));

+ 9 - 5
Emby.Dlna/DlnaManager.cs

@@ -366,7 +366,7 @@ namespace Emby.Dlna
                         Directory.CreateDirectory(systemProfilesPath);
 
                         // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
-                        using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
+                        using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
                         {
                             await stream.CopyToAsync(fileStream).ConfigureAwait(false);
                         }
@@ -486,18 +486,22 @@ namespace Emby.Dlna
         }
 
         /// <inheritdoc />
-        public ImageStream GetIcon(string filename)
+        public ImageStream? GetIcon(string filename)
         {
             var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
                 ? ImageFormat.Png
                 : ImageFormat.Jpg;
 
             var resource = GetType().Namespace + ".Images." + filename.ToLowerInvariant();
+            var stream = _assembly.GetManifestResourceStream(resource);
+            if (stream == null)
+            {
+                return null;
+            }
 
-            return new ImageStream
+            return new ImageStream(stream)
             {
-                Format = format,
-                Stream = _assembly.GetManifestResourceStream(resource)
+                Format = format
             };
         }
 

+ 1 - 1
Emby.Dlna/Emby.Dlna.csproj

@@ -17,7 +17,7 @@
   </ItemGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <AnalysisMode>AllDisabledByDefault</AnalysisMode>

+ 1 - 2
Emby.Dlna/Server/DescriptionXmlBuilder.cs

@@ -250,8 +250,7 @@ namespace Emby.Dlna.Server
 
             url = _serverAddress.TrimEnd('/') + "/dlna/" + _serverUdn + "/" + url.TrimStart('/');
 
-            // TODO: @bond remove null-coalescing operator when https://github.com/dotnet/runtime/pull/52442 is merged/released
-            return SecurityElement.Escape(url) ?? string.Empty;
+            return SecurityElement.Escape(url);
         }
 
         private IEnumerable<DeviceIcon> GetIcons()

+ 4 - 4
Emby.Dlna/Service/BaseService.cs

@@ -23,14 +23,14 @@ namespace Emby.Dlna.Service
             return EventManager.CancelEventSubscription(subscriptionId);
         }
 
-        public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string timeoutString, string callbackUrl)
+        public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl)
         {
-            return EventManager.RenewEventSubscription(subscriptionId, notificationType, timeoutString, callbackUrl);
+            return EventManager.RenewEventSubscription(subscriptionId, notificationType, requestedTimeoutString, callbackUrl);
         }
 
-        public EventSubscriptionResponse CreateEventSubscription(string notificationType, string timeoutString, string callbackUrl)
+        public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
         {
-            return EventManager.CreateEventSubscription(notificationType, timeoutString, callbackUrl);
+            return EventManager.CreateEventSubscription(notificationType, requestedTimeoutString, callbackUrl);
         }
     }
 }

+ 1 - 1
Emby.Drawing/Emby.Drawing.csproj

@@ -6,7 +6,7 @@
   </PropertyGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <AnalysisMode>AllDisabledByDefault</AnalysisMode>

+ 1 - 1
Emby.Drawing/ImageProcessor.cs

@@ -102,7 +102,7 @@ namespace Emby.Drawing
         {
             var file = await ProcessImage(options).ConfigureAwait(false);
 
-            using (var fileStream = new FileStream(file.Item1, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
+            using (var fileStream = new FileStream(file.Item1, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
             {
                 await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
             }

+ 1 - 1
Emby.Naming/Emby.Naming.csproj

@@ -6,7 +6,7 @@
   </PropertyGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <PublishRepositoryUrl>true</PublishRepositoryUrl>

+ 1 - 1
Emby.Notifications/Emby.Notifications.csproj

@@ -6,7 +6,7 @@
   </PropertyGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
   </PropertyGroup>

+ 1 - 1
Emby.Photos/Emby.Photos.csproj

@@ -19,7 +19,7 @@
   </ItemGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
   </PropertyGroup>

+ 11 - 16
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -1150,7 +1150,7 @@ namespace Emby.Server.Implementations.Data
                 return null;
             }
 
-            if (Enum.TryParse(imageType.ToString(), true, out ImageType type))
+            if (Enum.TryParse(imageType, true, out ImageType type))
             {
                 image.Type = type;
             }
@@ -1571,7 +1571,6 @@ namespace Emby.Server.Implementations.Data
 
             if (reader.TryGetString(index++, out var audioString))
             {
-                // TODO Span overload coming in the future https://github.com/dotnet/runtime/issues/1916
                 if (Enum.TryParse(audioString, true, out ProgramAudio audio))
                 {
                     item.Audio = audio;
@@ -1610,18 +1609,16 @@ namespace Emby.Server.Implementations.Data
             {
                 if (reader.TryGetString(index++, out var lockedFields))
                 {
-                    IEnumerable<MetadataField> GetLockedFields(string s)
+                    List<MetadataField> fields = null;
+                    foreach (var i in lockedFields.AsSpan().Split('|'))
                     {
-                        foreach (var i in s.Split('|', StringSplitOptions.RemoveEmptyEntries))
+                        if (Enum.TryParse(i, true, out MetadataField parsedValue))
                         {
-                            if (Enum.TryParse(i, true, out MetadataField parsedValue))
-                            {
-                                yield return parsedValue;
-                            }
+                            (fields ??= new List<MetadataField>()).Add(parsedValue);
                         }
                     }
 
-                    item.LockedFields = GetLockedFields(lockedFields).ToArray();
+                    item.LockedFields = fields?.ToArray() ?? Array.Empty<MetadataField>();
                 }
             }
 
@@ -1647,18 +1644,16 @@ namespace Emby.Server.Implementations.Data
                 {
                     if (reader.TryGetString(index, out var trailerTypes))
                     {
-                        IEnumerable<TrailerType> GetTrailerTypes(string s)
+                        List<TrailerType> types = null;
+                        foreach (var i in trailerTypes.AsSpan().Split('|'))
                         {
-                            foreach (var i in s.Split('|', StringSplitOptions.RemoveEmptyEntries))
+                            if (Enum.TryParse(i, true, out TrailerType parsedValue))
                             {
-                                if (Enum.TryParse(i, true, out TrailerType parsedValue))
-                                {
-                                    yield return parsedValue;
-                                }
+                                (types ??= new List<TrailerType>()).Add(parsedValue);
                             }
                         }
 
-                        trailer.TrailerTypes = GetTrailerTypes(trailerTypes).ToArray();
+                        trailer.TrailerTypes = types?.ToArray() ?? Array.Empty<TrailerType>();
                     }
                 }
 

+ 1 - 1
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -42,7 +42,7 @@
   </ItemGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 -->

+ 2 - 2
Emby.Server.Implementations/IO/ManagedFileSystem.cs

@@ -246,9 +246,9 @@ namespace Emby.Server.Implementations.IO
                     {
                         try
                         {
-                            using (Stream thisFileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, 1))
+                            using (var fileHandle = File.OpenHandle(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                             {
-                                result.Length = thisFileStream.Length;
+                                result.Length = RandomAccess.GetLength(fileHandle);
                             }
                         }
                         catch (FileNotFoundException ex)

+ 1 - 3
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -1250,10 +1250,8 @@ namespace Emby.Server.Implementations.Library
         private CollectionTypeOptions? GetCollectionType(string path)
         {
             var files = _fileSystem.GetFilePaths(path, new[] { ".collection" }, true, false);
-            foreach (var file in files)
+            foreach (ReadOnlySpan<char> file in files)
             {
-                // TODO: @bond use a ReadOnlySpan<char> here when Enum.TryParse supports it
-                // https://github.com/dotnet/runtime/issues/20008
                 if (Enum.TryParse<CollectionTypeOptions>(Path.GetFileNameWithoutExtension(file), true, out var res))
                 {
                     return res;

+ 1 - 1
Emby.Server.Implementations/Library/LiveStreamHelper.cs

@@ -10,9 +10,9 @@ using System.Linq;
 using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
-using Jellyfin.Extensions.Json;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dto;

+ 2 - 2
Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs

@@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
 
             // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
-            using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
+            using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
             {
                 onStarted();
 
@@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
 
             // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
-            await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, AsyncFile.UseAsyncIO);
+            await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous);
 
             onStarted();
 

+ 1 - 1
Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
 
             // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
-            _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
+            _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
 
             await JsonSerializer.SerializeAsync(_logFileStream, mediaSource, _jsonOptions, cancellationToken).ConfigureAwait(false);
             await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false);

+ 1 - 1
Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs

@@ -82,7 +82,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
             using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(path, cancellationToken).ConfigureAwait(false);
             await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-            await using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, AsyncFile.UseAsyncIO))
+            await using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous))
             {
                 await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
             }

+ 10 - 8
Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs

@@ -23,10 +23,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 {
     public abstract class BaseTunerHost
     {
-        protected readonly IServerConfigurationManager Config;
-        protected readonly ILogger<BaseTunerHost> Logger;
-        protected readonly IFileSystem FileSystem;
-
         private readonly IMemoryCache _memoryCache;
 
         protected BaseTunerHost(IServerConfigurationManager config, ILogger<BaseTunerHost> logger, IFileSystem fileSystem, IMemoryCache memoryCache)
@@ -37,12 +33,20 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             FileSystem = fileSystem;
         }
 
-        public virtual bool IsSupported => true;
+        protected IServerConfigurationManager Config { get; }
 
-        protected abstract Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
+        protected ILogger<BaseTunerHost> Logger { get; }
+
+        protected IFileSystem FileSystem { get; }
+
+        public virtual bool IsSupported => true;
 
         public abstract string Type { get; }
 
+        protected virtual string ChannelIdPrefix => Type + "_";
+
+        protected abstract Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
+
         public async Task<List<ChannelInfo>> GetChannels(TunerHostInfo tuner, bool enableCache, CancellationToken cancellationToken)
         {
             var key = tuner.Id;
@@ -217,8 +221,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             throw new LiveTvConflictException();
         }
 
-        protected virtual string ChannelIdPrefix => Type + "_";
-
         protected virtual bool IsValidChannelId(string channelId)
         {
             if (string.IsNullOrEmpty(channelId))

+ 3 - 2
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs

@@ -101,7 +101,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 }
             }
 
-            if (localAddress.IsIPv4MappedToIPv6) {
+            if (localAddress.IsIPv4MappedToIPv6)
+            {
                 localAddress = localAddress.MapToIPv4();
             }
 
@@ -196,7 +197,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                         cancellationToken,
                         timeOutSource.Token))
                     {
-                        var resTask = udpClient.ReceiveAsync();
+                        var resTask = udpClient.ReceiveAsync(linkedSource.Token).AsTask();
                         if (await Task.WhenAny(resTask, Task.Delay(30000, linkedSource.Token)).ConfigureAwait(false) != resTask)
                         {
                             resTask.Dispose();

+ 12 - 12
Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs

@@ -20,14 +20,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
     {
         private readonly IConfigurationManager _configurationManager;
 
-        protected readonly IFileSystem FileSystem;
-
-        protected readonly IStreamHelper StreamHelper;
-
-        protected string TempFilePath;
-        protected readonly ILogger Logger;
-        protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource();
-
         public LiveStream(
             MediaSourceInfo mediaSource,
             TunerHostInfo tuner,
@@ -55,7 +47,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             SetTempFilePath("ts");
         }
 
-        protected virtual int EmptyReadLimit => 1000;
+        protected IFileSystem FileSystem { get; }
+
+        protected IStreamHelper StreamHelper { get; }
+
+        protected ILogger Logger { get; }
+
+        protected CancellationTokenSource LiveStreamCancellationTokenSource { get; } = new CancellationTokenSource();
+
+        protected string TempFilePath { get; set; }
 
         public MediaSourceInfo OriginalMediaSource { get; set; }
 
@@ -97,7 +97,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
         public Stream GetStream()
         {
-            var stream = GetInputStream(TempFilePath, AsyncFile.UseAsyncIO);
+            var stream = GetInputStream(TempFilePath);
             bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10;
             if (seekFile)
             {
@@ -107,14 +107,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             return stream;
         }
 
-        protected FileStream GetInputStream(string path, bool allowAsyncFileRead)
+        protected FileStream GetInputStream(string path)
             => new FileStream(
                 path,
                 FileMode.Open,
                 FileAccess.Read,
                 FileShare.ReadWrite,
                 IODefaults.FileStreamBufferSize,
-                allowAsyncFileRead ? FileOptions.SequentialScan | FileOptions.Asynchronous : FileOptions.SequentialScan);
+                FileOptions.SequentialScan | FileOptions.Asynchronous);
 
         protected async Task DeleteTempFiles(string path, int retryCount = 0)
         {

+ 1 - 1
Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs

@@ -118,7 +118,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                         Logger.LogInformation("Beginning {StreamType} stream to {FilePath}", GetType().Name, TempFilePath);
                         using var message = response;
                         await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-                        await using var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
+                        await using var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
                         await StreamHelper.CopyToAsync(
                             stream,
                             fileStream,

+ 2 - 2
Emby.Server.Implementations/Localization/Core/ar.json

@@ -1,5 +1,5 @@
 {
-    "Albums": "البومات",
+    "Albums": "ألبومات",
     "AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
     "Application": "تطبيق",
     "Artists": "الفنانين",
@@ -8,7 +8,7 @@
     "CameraImageUploadedFrom": "صورة كاميرا جديدة تم رفعها من {0}",
     "Channels": "القنوات",
     "ChapterNameValue": "الفصل {0}",
-    "Collections": "مجموعات",
+    "Collections": "التجميعات",
     "DeviceOfflineWithName": "قُطِع الاتصال ب{0}",
     "DeviceOnlineWithName": "{0} متصل",
     "FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",

+ 1 - 1
Emby.Server.Implementations/Localization/Core/hr.json

@@ -15,7 +15,7 @@
     "Favorites": "Favoriti",
     "Folders": "Mape",
     "Genres": "Žanrovi",
-    "HeaderAlbumArtists": "Izvođači na albumu",
+    "HeaderAlbumArtists": "Album od izvođača",
     "HeaderContinueWatching": "Nastavi gledati",
     "HeaderFavoriteAlbums": "Omiljeni albumi",
     "HeaderFavoriteArtists": "Omiljeni izvođači",

+ 9 - 9
Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs

@@ -17,6 +17,15 @@ namespace Emby.Server.Implementations.Playlists
             Name = "Playlists";
         }
 
+        [JsonIgnore]
+        public override bool IsHidden => true;
+
+        [JsonIgnore]
+        public override bool SupportsInheritedParentImages => false;
+
+        [JsonIgnore]
+        public override string CollectionType => MediaBrowser.Model.Entities.CollectionType.Playlists;
+
         public override bool IsVisible(User user)
         {
             return base.IsVisible(user) && GetChildren(user, true).Any();
@@ -27,15 +36,6 @@ namespace Emby.Server.Implementations.Playlists
             return base.GetEligibleChildrenForRecursiveChildren(user).OfType<Playlist>();
         }
 
-        [JsonIgnore]
-        public override bool IsHidden => true;
-
-        [JsonIgnore]
-        public override bool SupportsInheritedParentImages => false;
-
-        [JsonIgnore]
-        public override string CollectionType => MediaBrowser.Model.Entities.CollectionType.Playlists;
-
         protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
         {
             if (query.User == null)

+ 10 - 10
Emby.Server.Implementations/Plugins/PluginManager.cs

@@ -8,10 +8,10 @@ using System.Reflection;
 using System.Text;
 using System.Text.Json;
 using System.Threading.Tasks;
-using MediaBrowser.Common;
-using MediaBrowser.Common.Extensions;
 using Jellyfin.Extensions.Json;
 using Jellyfin.Extensions.Json.Converters;
+using MediaBrowser.Common;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Model.Configuration;
@@ -39,14 +39,6 @@ namespace Emby.Server.Implementations.Plugins
 
         private IHttpClientFactory? _httpClientFactory;
 
-        private IHttpClientFactory HttpClientFactory
-        {
-            get
-            {
-                return _httpClientFactory ?? (_httpClientFactory = _appHost.Resolve<IHttpClientFactory>());
-            }
-        }
-
         /// <summary>
         /// Initializes a new instance of the <see cref="PluginManager"/> class.
         /// </summary>
@@ -86,6 +78,14 @@ namespace Emby.Server.Implementations.Plugins
             _plugins = Directory.Exists(_pluginsPath) ? DiscoverPlugins().ToList() : new List<LocalPlugin>();
         }
 
+        private IHttpClientFactory HttpClientFactory
+        {
+            get
+            {
+                return _httpClientFactory ??= _appHost.Resolve<IHttpClientFactory>();
+            }
+        }
+
         /// <summary>
         /// Gets the Plugins.
         /// </summary>

+ 3 - 25
Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs

@@ -18,7 +18,7 @@ namespace Emby.Server.Implementations.QuickConnect
     /// <summary>
     /// Quick connect implementation.
     /// </summary>
-    public class QuickConnectManager : IQuickConnect, IDisposable
+    public class QuickConnectManager : IQuickConnect
     {
         /// <summary>
         /// The length of user facing codes.
@@ -30,7 +30,6 @@ namespace Emby.Server.Implementations.QuickConnect
         /// </summary>
         private const int Timeout = 10;
 
-        private readonly RNGCryptoServiceProvider _rng = new ();
         private readonly ConcurrentDictionary<string, QuickConnectResult> _currentRequests = new ();
         private readonly ConcurrentDictionary<string, (DateTime Timestamp, AuthenticationResult AuthenticationResult)> _authorizedSecrets = new ();
 
@@ -140,7 +139,7 @@ namespace Emby.Server.Implementations.QuickConnect
             uint scale = uint.MaxValue;
             while (scale == uint.MaxValue)
             {
-                _rng.GetBytes(raw);
+                RandomNumberGenerator.Fill(raw);
                 scale = BitConverter.ToUInt32(raw);
             }
 
@@ -199,31 +198,10 @@ namespace Emby.Server.Implementations.QuickConnect
             return result.AuthenticationResult;
         }
 
-        /// <summary>
-        /// Dispose.
-        /// </summary>
-        public void Dispose()
-        {
-            Dispose(true);
-            GC.SuppressFinalize(this);
-        }
-
-        /// <summary>
-        /// Dispose.
-        /// </summary>
-        /// <param name="disposing">Dispose unmanaged resources.</param>
-        protected virtual void Dispose(bool disposing)
-        {
-            if (disposing)
-            {
-                _rng.Dispose();
-            }
-        }
-
         private string GenerateSecureRandom(int length = 32)
         {
             Span<byte> bytes = stackalloc byte[length];
-            _rng.GetBytes(bytes);
+            RandomNumberGenerator.Fill(bytes);
 
             return Convert.ToHexString(bytes);
         }

+ 3 - 3
Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs

@@ -32,18 +32,18 @@ namespace Jellyfin.Api.Auth.FirstTimeSetupOrDefaultPolicy
         }
 
         /// <inheritdoc />
-        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrDefaultRequirement firstTimeSetupOrDefaultRequirement)
+        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrDefaultRequirement requirement)
         {
             if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
             {
-                context.Succeed(firstTimeSetupOrDefaultRequirement);
+                context.Succeed(requirement);
                 return Task.CompletedTask;
             }
 
             var validated = ValidateClaims(context.User);
             if (validated)
             {
-                context.Succeed(firstTimeSetupOrDefaultRequirement);
+                context.Succeed(requirement);
             }
             else
             {

+ 3 - 3
Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs

@@ -33,18 +33,18 @@ namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy
         }
 
         /// <inheritdoc />
-        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrElevatedRequirement firstTimeSetupOrElevatedRequirement)
+        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrElevatedRequirement requirement)
         {
             if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
             {
-                context.Succeed(firstTimeSetupOrElevatedRequirement);
+                context.Succeed(requirement);
                 return Task.CompletedTask;
             }
 
             var validated = ValidateClaims(context.User);
             if (validated && context.User.IsInRole(UserRoles.Administrator))
             {
-                context.Succeed(firstTimeSetupOrElevatedRequirement);
+                context.Succeed(requirement);
             }
             else
             {

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

@@ -106,7 +106,7 @@ namespace Jellyfin.Api.Controllers
             await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
 
             // Handle image/png; charset=utf-8
-            var mimeType = Request.ContentType.Split(';').FirstOrDefault();
+            var mimeType = Request.ContentType?.Split(';').FirstOrDefault();
             var userDataPath = Path.Combine(_serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
             if (user.ProfileImage != null)
             {
@@ -153,7 +153,7 @@ namespace Jellyfin.Api.Controllers
             await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
 
             // Handle image/png; charset=utf-8
-            var mimeType = Request.ContentType.Split(';').FirstOrDefault();
+            var mimeType = Request.ContentType?.Split(';').FirstOrDefault();
             var userDataPath = Path.Combine(_serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
             if (user.ProfileImage != null)
             {
@@ -341,7 +341,7 @@ namespace Jellyfin.Api.Controllers
             await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
 
             // Handle image/png; charset=utf-8
-            var mimeType = Request.ContentType.Split(';').FirstOrDefault();
+            var mimeType = Request.ContentType?.Split(';').FirstOrDefault();
             await _providerManager.SaveImage(item, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
             await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
 
@@ -377,7 +377,7 @@ namespace Jellyfin.Api.Controllers
             await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
 
             // Handle image/png; charset=utf-8
-            var mimeType = Request.ContentType.Split(';').FirstOrDefault();
+            var mimeType = Request.ContentType?.Split(';').FirstOrDefault();
             await _providerManager.SaveImage(item, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
             await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
 
@@ -2026,7 +2026,7 @@ namespace Jellyfin.Api.Controllers
                 return NoContent();
             }
 
-            return PhysicalFile(imagePath, imageContentType);
+            return PhysicalFile(imagePath, imageContentType ?? MediaTypeNames.Text.Plain);
         }
     }
 }

+ 1 - 1
Jellyfin.Api/Controllers/RemoteImageController.cs

@@ -206,7 +206,7 @@ namespace Jellyfin.Api.Controllers
             var fullCacheDirectory = Path.GetDirectoryName(fullCachePath) ?? throw new ResourceNotFoundException($"Provided path ({fullCachePath}) is not valid.");
             Directory.CreateDirectory(fullCacheDirectory);
             // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
-            await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
+            await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
             await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
 
             var pointerCacheDirectory = Path.GetDirectoryName(pointerCachePath) ?? throw new ArgumentException($"Provided path ({pointerCachePath}) is not valid.", nameof(pointerCachePath));

+ 1 - 1
Jellyfin.Api/Controllers/SystemController.cs

@@ -201,7 +201,7 @@ namespace Jellyfin.Api.Controllers
 
             // For older files, assume fully static
             var fileShare = file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1) ? FileShare.Read : FileShare.ReadWrite;
-            FileStream stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, fileShare, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
+            FileStream stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, fileShare, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
             return File(stream, "text/plain; charset=utf-8");
         }
 

+ 2 - 1
Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs

@@ -1,6 +1,7 @@
 using System;
 using System.IO;
 using System.Net.Http;
+using System.Net.Mime;
 using System.Threading;
 using System.Threading.Tasks;
 using Jellyfin.Api.Models.PlaybackDtos;
@@ -40,7 +41,7 @@ namespace Jellyfin.Api.Helpers
 
             // Can't dispose the response as it's required up the call chain.
             var response = await httpClient.GetAsync(new Uri(state.MediaPath), cancellationToken).ConfigureAwait(false);
-            var contentType = response.Content.Headers.ContentType?.ToString();
+            var contentType = response.Content.Headers.ContentType?.ToString() ?? MediaTypeNames.Text.Plain;
 
             httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none";
 

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

@@ -38,7 +38,7 @@ namespace Jellyfin.Api.Helpers
                         FileAccess.Read,
                         FileShare.ReadWrite,
                         IODefaults.FileStreamBufferSize,
-                        (AsyncFile.UseAsyncIO ? FileOptions.Asynchronous : FileOptions.None) | FileOptions.SequentialScan);
+                        FileOptions.Asynchronous | FileOptions.SequentialScan);
                     await using (fileStream.ConfigureAwait(false))
                     {
                         using var reader = new StreamReader(fileStream);

+ 2 - 22
Jellyfin.Api/Helpers/ProgressiveFileStream.cs

@@ -17,7 +17,6 @@ namespace Jellyfin.Api.Helpers
         private readonly TranscodingJobDto? _job;
         private readonly TranscodingJobHelper? _transcodingJobHelper;
         private readonly int _timeoutMs;
-        private readonly bool _allowAsyncFileRead;
         private int _bytesWritten;
         private bool _disposed;
 
@@ -34,17 +33,7 @@ namespace Jellyfin.Api.Helpers
             _transcodingJobHelper = transcodingJobHelper;
             _timeoutMs = timeoutMs;
 
-            var fileOptions = FileOptions.SequentialScan;
-            _allowAsyncFileRead = false;
-
-            // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
-            if (AsyncFile.UseAsyncIO)
-            {
-                fileOptions |= FileOptions.Asynchronous;
-                _allowAsyncFileRead = true;
-            }
-
-            _stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, fileOptions);
+            _stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan);
         }
 
         /// <summary>
@@ -57,7 +46,6 @@ namespace Jellyfin.Api.Helpers
             _job = null;
             _transcodingJobHelper = null;
             _timeoutMs = timeoutMs;
-            _allowAsyncFileRead = AsyncFile.UseAsyncIO;
             _stream = stream;
         }
 
@@ -103,15 +91,7 @@ namespace Jellyfin.Api.Helpers
             while (remainingBytesToRead > 0)
             {
                 cancellationToken.ThrowIfCancellationRequested();
-                int bytesRead;
-                if (_allowAsyncFileRead)
-                {
-                    bytesRead = await _stream.ReadAsync(buffer, newOffset, remainingBytesToRead, cancellationToken).ConfigureAwait(false);
-                }
-                else
-                {
-                    bytesRead = _stream.Read(buffer, newOffset, remainingBytesToRead);
-                }
+                int bytesRead = await _stream.ReadAsync(buffer, newOffset, remainingBytesToRead, cancellationToken).ConfigureAwait(false);
 
                 remainingBytesToRead -= bytesRead;
                 newOffset += bytesRead;

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

@@ -557,7 +557,7 @@ namespace Jellyfin.Api.Helpers
                 $"{logFilePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{state.Request.MediaSourceId}_{Guid.NewGuid().ToString()[..8]}.log");
 
             // FFmpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
-            Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
+            Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
 
             var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(request.Path + Environment.NewLine + Environment.NewLine + JsonSerializer.Serialize(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
             await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);

+ 1 - 1
Jellyfin.Api/Jellyfin.Api.csproj

@@ -6,7 +6,7 @@
   </PropertyGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 -->
     <NoWarn>AD0001</NoWarn>

+ 3 - 2
Jellyfin.Api/ModelBinders/NullableEnumModelBinder.cs

@@ -32,7 +32,8 @@ namespace Jellyfin.Api.ModelBinders
             {
                 try
                 {
-                    var convertedValue = converter.ConvertFromString(valueProviderResult.FirstValue);
+                    // REVIEW: This shouldn't be null here
+                    var convertedValue = converter.ConvertFromString(valueProviderResult.FirstValue!);
                     bindingContext.Result = ModelBindingResult.Success(convertedValue);
                 }
                 catch (FormatException e)
@@ -44,4 +45,4 @@ namespace Jellyfin.Api.ModelBinders
             return Task.CompletedTask;
         }
     }
-}
+}

+ 1 - 1
Jellyfin.Data/Jellyfin.Data.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <PublishRepositoryUrl>true</PublishRepositoryUrl>

+ 1 - 6
Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj

@@ -6,7 +6,7 @@
   </PropertyGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
   </PropertyGroup>
@@ -28,11 +28,6 @@
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
   </ItemGroup>
 
-  <ItemGroup>
-    <!-- Needed for https://github.com/dotnet/roslyn-analyzers/issues/4382 which is in the SDK yet -->
-    <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.3" PrivateAssets="All" />
-  </ItemGroup>
-
   <!-- Code analysers-->
   <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
     <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />

+ 1 - 1
Jellyfin.Networking/Jellyfin.Networking.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
   </PropertyGroup>

+ 1 - 1
Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
   </PropertyGroup>

+ 2 - 2
Jellyfin.Server/Configuration/CorsPolicyProvider.cs

@@ -23,7 +23,7 @@ namespace Jellyfin.Server.Configuration
         }
 
         /// <inheritdoc />
-        public Task<CorsPolicy> GetPolicyAsync(HttpContext context, string policyName)
+        public Task<CorsPolicy?> GetPolicyAsync(HttpContext context, string? policyName)
         {
             var corsHosts = _serverConfigurationManager.Configuration.CorsHosts;
             var builder = new CorsPolicyBuilder()
@@ -43,7 +43,7 @@ namespace Jellyfin.Server.Configuration
                     .AllowCredentials();
             }
 
-            return Task.FromResult(builder.Build());
+            return Task.FromResult<CorsPolicy?>(builder.Build());
         }
     }
 }

+ 4 - 4
Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs

@@ -55,8 +55,8 @@ namespace Jellyfin.Server.Infrastructure
             // This may or may not be fixed in .NET 6, but looks like it will not https://github.com/dotnet/aspnetcore/issues/34371
             if ((fileInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
             {
-                using Stream thisFileStream = File.OpenRead(path);
-                length = thisFileStream.Length;
+                using var fileHandle = File.OpenHandle(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+                length = RandomAccess.GetLength(fileHandle);
             }
 
             return new FileMetadata
@@ -68,7 +68,7 @@ namespace Jellyfin.Server.Infrastructure
         }
 
         /// <inheritdoc />
-        protected override Task WriteFileAsync(ActionContext context, PhysicalFileResult result, RangeItemHeaderValue range, long rangeLength)
+        protected override Task WriteFileAsync(ActionContext context, PhysicalFileResult result, RangeItemHeaderValue? range, long rangeLength)
         {
             if (context == null)
             {
@@ -132,7 +132,7 @@ namespace Jellyfin.Server.Infrastructure
                 FileAccess.Read,
                 FileShare.ReadWrite,
                 bufferSize: BufferSize,
-                options: (AsyncFile.UseAsyncIO ? FileOptions.Asynchronous : FileOptions.None) | FileOptions.SequentialScan);
+                options: FileOptions.Asynchronous | FileOptions.SequentialScan);
 
             fileStream.Seek(offset, SeekOrigin.Begin);
             await StreamCopyOperation

+ 1 - 1
Jellyfin.Server/Jellyfin.Server.csproj

@@ -8,7 +8,7 @@
   <PropertyGroup>
     <AssemblyName>jellyfin</AssemblyName>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <ServerGarbageCollection>false</ServerGarbageCollection>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>

+ 5 - 1
Jellyfin.Server/Middleware/QueryStringDecodingMiddleware.cs

@@ -27,7 +27,11 @@ namespace Jellyfin.Server.Middleware
         /// <returns>The async task.</returns>
         public async Task Invoke(HttpContext httpContext)
         {
-            httpContext.Features.Set<IQueryFeature>(new UrlDecodeQueryFeature(httpContext.Features.Get<IQueryFeature>()));
+            var feature = httpContext.Features.Get<IQueryFeature>();
+            if (feature != null)
+            {
+                httpContext.Features.Set<IQueryFeature>(new UrlDecodeQueryFeature(feature));
+            }
 
             await _next(httpContext).ConfigureAwait(false);
         }

+ 2 - 8
Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs

@@ -52,20 +52,14 @@ namespace Jellyfin.Server.Middleware
                     return;
                 }
 
-                // Unencode and re-parse querystring.
-                var unencodedKey = HttpUtility.UrlDecode(key);
-
-                if (string.Equals(unencodedKey, key, StringComparison.Ordinal))
+                if (!key.Contains('='))
                 {
-                    // Don't do anything if it's not encoded.
                     _store = value;
                     return;
                 }
 
                 var pairs = new Dictionary<string, StringValues>();
-                var queryString = unencodedKey.SpanSplit('&');
-
-                foreach (var pair in queryString)
+                foreach (var pair in key.SpanSplit('&'))
                 {
                     var i = pair.IndexOf('=');
                     if (i == -1)

+ 3 - 3
Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs

@@ -1,7 +1,6 @@
 using System;
 using System.IO;
 using Emby.Server.Implementations.Data;
-using Emby.Server.Implementations.Serialization;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
 using Jellyfin.Extensions.Json;
@@ -10,6 +9,7 @@ using Jellyfin.Server.Implementations.Users;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Users;
 using Microsoft.Extensions.Logging;
 using SQLitePCL.pretty;
@@ -27,7 +27,7 @@ namespace Jellyfin.Server.Migrations.Routines
         private readonly ILogger<MigrateUserDb> _logger;
         private readonly IServerApplicationPaths _paths;
         private readonly JellyfinDbProvider _provider;
-        private readonly MyXmlSerializer _xmlSerializer;
+        private readonly IXmlSerializer _xmlSerializer;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="MigrateUserDb"/> class.
@@ -40,7 +40,7 @@ namespace Jellyfin.Server.Migrations.Routines
             ILogger<MigrateUserDb> logger,
             IServerApplicationPaths paths,
             JellyfinDbProvider provider,
-            MyXmlSerializer xmlSerializer)
+            IXmlSerializer xmlSerializer)
         {
             _logger = logger;
             _paths = paths;

+ 1 - 1
Jellyfin.Server/Program.cs

@@ -547,7 +547,7 @@ namespace Jellyfin.Server
                 ?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'");
 
             // Copy the resource contents to the expected file path for the config file
-            await using Stream dst = new FileStream(configPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
+            await using Stream dst = new FileStream(configPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
             await resource.CopyToAsync(dst).ConfigureAwait(false);
         }
 

+ 1 - 1
MediaBrowser.Common/MediaBrowser.Common.csproj

@@ -29,7 +29,7 @@
   </ItemGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <PublishRepositoryUrl>true</PublishRepositoryUrl>

+ 1 - 1
MediaBrowser.Controller/Dlna/IDlnaManager.cs

@@ -74,6 +74,6 @@ namespace MediaBrowser.Controller.Dlna
         /// </summary>
         /// <param name="filename">The filename.</param>
         /// <returns>DlnaIconResponse.</returns>
-        ImageStream GetIcon(string filename);
+        ImageStream? GetIcon(string filename);
     }
 }

+ 1 - 1
MediaBrowser.Controller/Drawing/IImageProcessor.cs

@@ -58,7 +58,7 @@ namespace MediaBrowser.Controller.Drawing
         /// <returns>Guid.</returns>
         string GetImageCacheTag(BaseItem item, ItemImageInfo image);
 
-        string GetImageCacheTag(BaseItem item, ChapterInfo info);
+        string GetImageCacheTag(BaseItem item, ChapterInfo chapter);
 
         string? GetImageCacheTag(User user);
 

+ 7 - 2
MediaBrowser.Controller/Drawing/ImageStream.cs

@@ -8,11 +8,16 @@ namespace MediaBrowser.Controller.Drawing
 {
     public class ImageStream : IDisposable
     {
+        public ImageStream(Stream stream)
+        {
+            Stream = stream;
+        }
+
         /// <summary>
-        /// Gets or sets the stream.
+        /// Gets the stream.
         /// </summary>
         /// <value>The stream.</value>
-        public Stream? Stream { get; set; }
+        public Stream Stream { get; }
 
         /// <summary>
         /// Gets or sets the format.

+ 1 - 1
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -32,7 +32,7 @@
   </ItemGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <PublishRepositoryUrl>true</PublishRepositoryUrl>

+ 6 - 1
MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs

@@ -541,7 +541,12 @@ namespace MediaBrowser.Controller.MediaEncoding
                 return MimeType;
             }
 
-            return MimeTypes.GetMimeType(outputPath, enableStreamDefault);
+            if (enableStreamDefault)
+            {
+                return MimeTypes.GetMimeType(outputPath);
+            }
+
+            return MimeTypes.GetMimeType(outputPath, null);
         }
 
         public bool DeInterlace(string videoCodec, bool forceDeinterlaceIfSourceIsInterlaced)

+ 1 - 1
MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj

@@ -11,7 +11,7 @@
   </ItemGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
   </PropertyGroup>

+ 1 - 1
MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj

@@ -6,7 +6,7 @@
   </PropertyGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
   </PropertyGroup>

+ 1 - 1
MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs

@@ -38,7 +38,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             subRip.LoadSubtitle(subtitle, lines, "untitled");
             if (subRip.ErrorCount > 0)
             {
-                _logger.LogError("{ErrorCount} errors encountered while parsing subtitle.");
+                _logger.LogError("{ErrorCount} errors encountered while parsing subtitle", subRip.ErrorCount);
             }
 
             var trackInfo = new SubtitleTrackInfo();

+ 1 - 1
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -680,7 +680,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             if (!string.Equals(text, newText, StringComparison.Ordinal))
             {
                 // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
-                using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
+                using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
                 using (var writer = new StreamWriter(fileStream, encoding))
                 {
                     await writer.WriteAsync(newText.AsMemory(), cancellationToken).ConfigureAwait(false);

+ 2 - 9
MediaBrowser.Model/IO/AsyncFile.cs

@@ -8,20 +8,13 @@ namespace MediaBrowser.Model.IO
     /// </summary>
     public static class AsyncFile
     {
-        /// <summary>
-        /// Gets a value indicating whether we should use async IO on this platform.
-        /// <see href="https://github.com/dotnet/runtime/issues/16354" />.
-        /// </summary>
-        /// <returns>Returns <c>false</c> on Windows; otherwise <c>true</c>.</returns>
-        public static bool UseAsyncIO => !OperatingSystem.IsWindows();
-
         /// <summary>
         /// Opens an existing file for reading.
         /// </summary>
         /// <param name="path">The file to be opened for reading.</param>
         /// <returns>A read-only <see cref="FileStream" /> on the specified path.</returns>
         public static FileStream OpenRead(string path)
-            => new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, UseAsyncIO);
+            => new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
 
         /// <summary>
         /// Opens an existing file for writing.
@@ -29,6 +22,6 @@ namespace MediaBrowser.Model.IO
         /// <param name="path">The file to be opened for writing.</param>
         /// <returns>An unshared <see cref="FileStream" /> object on the specified path with Write access.</returns>
         public static FileStream OpenWrite(string path)
-            => new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, UseAsyncIO);
+            => new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
     }
 }

+ 1 - 1
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -14,7 +14,7 @@
   </PropertyGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <PublishRepositoryUrl>true</PublishRepositoryUrl>

+ 7 - 5
MediaBrowser.Model/Net/MimeTypes.cs

@@ -2,6 +2,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.IO;
 using System.Linq;
 using Jellyfin.Extensions;
@@ -164,15 +165,16 @@ namespace MediaBrowser.Model.Net
             return dict;
         }
 
-        public static string? GetMimeType(string path) => GetMimeType(path, true);
+        public static string GetMimeType(string path) => GetMimeType(path, "application/octet-stream");
 
         /// <summary>
         /// Gets the type of the MIME.
         /// </summary>
         /// <param name="filename">The filename to find the MIME type of.</param>
-        /// <param name="enableStreamDefault">Whether of not to return a default value if no fitting MIME type is found.</param>
-        /// <returns>The worrect MIME type for the given filename, or `null` if it wasn't found and <paramref name="enableStreamDefault"/> is false.</returns>
-        public static string? GetMimeType(string filename, bool enableStreamDefault)
+        /// <param name="defaultValue">The default value to return if no fitting MIME type is found.</param>
+        /// <returns>The correct MIME type for the given filename, or <paramref name="defaultValue"/> if it wasn't found.</returns>
+        [return: NotNullIfNotNullAttribute("defaultValue")]
+        public static string? GetMimeType(string filename, string? defaultValue = null)
         {
             if (filename.Length == 0)
             {
@@ -211,7 +213,7 @@ namespace MediaBrowser.Model.Net
                 return "application/octet-stream";
             }
 
-            return enableStreamDefault ? "application/octet-stream" : null;
+            return defaultValue;
         }
 
         public static string? ToExtension(string mimeType)

+ 1 - 1
MediaBrowser.Providers/Manager/ImageSaver.cs

@@ -264,7 +264,7 @@ namespace MediaBrowser.Providers.Manager
                 _fileSystem.SetAttributes(path, false, false);
 
                 // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
-                await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
+                await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
                 {
                     await source.CopyToAsync(fs, cancellationToken).ConfigureAwait(false);
                 }

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

@@ -163,7 +163,7 @@ namespace MediaBrowser.Providers.Manager
                                 {
                                     var mimeType = MimeTypes.GetMimeType(response.Path);
 
-                                    var stream = new FileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
+                                    var stream = new FileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
 
                                     await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
                                 }

+ 1 - 1
MediaBrowser.Providers/MediaBrowser.Providers.csproj

@@ -26,7 +26,7 @@
   </ItemGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>

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

@@ -173,7 +173,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
             using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false);
             await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
             // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
-            await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
+            await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
             await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
         }
 

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

@@ -155,7 +155,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
             Directory.CreateDirectory(Path.GetDirectoryName(path));
 
             // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
-            await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
+            await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
             await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
         }
 

+ 1 - 1
MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableInt32Converter.cs

@@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
             if (reader.TokenType == JsonTokenType.String)
             {
                 var str = reader.GetString();
-                if (str != null && str.Equals("N/A", StringComparison.OrdinalIgnoreCase))
+                if (str == null || str.Equals("N/A", StringComparison.OrdinalIgnoreCase))
                 {
                     return null;
                 }

+ 2 - 2
MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs

@@ -295,7 +295,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                     imdbParam));
 
             var rootObject = await GetDeserializedOmdbResponse<RootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
-            await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
+            await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
             await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
             return path;
@@ -335,7 +335,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                     seasonId));
 
             var rootObject = await GetDeserializedOmdbResponse<SeasonRootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
-            await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
+            await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
             await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
             return path;

+ 1 - 1
MediaBrowser.Providers/Studios/StudiosImageProvider.cs

@@ -146,7 +146,7 @@ namespace MediaBrowser.Providers.Studios
 
                 Directory.CreateDirectory(Path.GetDirectoryName(file));
                 await using var response = await httpClient.GetStreamAsync(url, cancellationToken).ConfigureAwait(false);
-                await using var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
+                await using var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
                 await response.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
             }
 

+ 1 - 1
MediaBrowser.Providers/Subtitles/SubtitleManager.cs

@@ -245,7 +245,7 @@ namespace MediaBrowser.Providers.Subtitles
                     Directory.CreateDirectory(Path.GetDirectoryName(savePath));
 
                     // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
-                    using var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None, FileStreamBufferSize, AsyncFile.UseAsyncIO);
+                    using var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None, FileStreamBufferSize, FileOptions.Asynchronous);
                     await stream.CopyToAsync(fs).ConfigureAwait(false);
 
                     return;

+ 1 - 1
MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj

@@ -15,7 +15,7 @@
   </ItemGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
   </PropertyGroup>

+ 3 - 2
RSSDP/DisposableManagedObjectBase.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Text;
 
 namespace Rssdp.Infrastructure
@@ -45,11 +46,11 @@ namespace Rssdp.Infrastructure
 
             const string ArgFormat = "{0}: {1}\r\n";
 
-            builder.AppendFormat("{0}\r\n", header);
+            builder.AppendFormat(CultureInfo.InvariantCulture, "{0}\r\n", header);
 
             foreach (var pair in values)
             {
-                builder.AppendFormat(ArgFormat, pair.Key, pair.Value);
+                builder.AppendFormat(CultureInfo.InvariantCulture, ArgFormat, pair.Key, pair.Value);
             }
 
             builder.Append("\r\n");

+ 1 - 1
RSSDP/RSSDP.csproj

@@ -11,7 +11,7 @@
   </ItemGroup>
 
   <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <AnalysisMode>AllDisabledByDefault</AnalysisMode>
     <Nullable>disable</Nullable>

+ 8 - 5
RSSDP/SsdpDevice.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Globalization;
 using Rssdp.Infrastructure;
 
 namespace Rssdp
@@ -134,11 +135,13 @@ namespace Rssdp
         {
             get
             {
-                return String.Format("urn:{0}:{3}:{1}:{2}",
-                this.DeviceTypeNamespace ?? String.Empty,
-                this.DeviceType ?? String.Empty,
-                this.DeviceVersion,
-                this.DeviceClass ?? "device");
+                return String.Format(
+                    CultureInfo.InvariantCulture,
+                    "urn:{0}:{3}:{1}:{2}",
+                    this.DeviceTypeNamespace ?? String.Empty,
+                    this.DeviceType ?? String.Empty,
+                    this.DeviceVersion,
+                    this.DeviceClass ?? "device");
             }
         }
 

+ 8 - 7
RSSDP/SsdpDevicePublisher.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Globalization;
 using System.Linq;
 using System.Net;
 using System.Threading;
@@ -233,7 +234,7 @@ namespace Rssdp.Infrastructure
         {
             if (String.IsNullOrEmpty(searchTarget))
             {
-                WriteTrace(String.Format("Invalid search request received From {0}, Target is null/empty.", remoteEndPoint.ToString()));
+                WriteTrace(String.Format(CultureInfo.InvariantCulture, "Invalid search request received From {0}, Target is null/empty.", remoteEndPoint.ToString()));
                 return;
             }
 
@@ -340,7 +341,7 @@ namespace Rssdp.Infrastructure
 
         private string GetUsn(string udn, string fullDeviceType)
         {
-            return String.Format("{0}::{1}", udn, fullDeviceType);
+            return String.Format(CultureInfo.InvariantCulture, "{0}::{1}", udn, fullDeviceType);
         }
 
         private async void SendSearchResponse(
@@ -363,7 +364,7 @@ namespace Rssdp.Infrastructure
             values["DATE"] = DateTime.UtcNow.ToString("r");
             values["CACHE-CONTROL"] = "max-age = " + rootDevice.CacheLifetime.TotalSeconds;
             values["ST"] = searchTarget;
-            values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
+            values["SERVER"] = string.Format(CultureInfo.InvariantCulture, "{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
             values["USN"] = uniqueServiceName;
             values["LOCATION"] = rootDevice.Location.ToString();
 
@@ -497,7 +498,7 @@ namespace Rssdp.Infrastructure
             values["DATE"] = DateTime.UtcNow.ToString("r");
             values["CACHE-CONTROL"] = "max-age = " + rootDevice.CacheLifetime.TotalSeconds;
             values["LOCATION"] = rootDevice.Location.ToString();
-            values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
+            values["SERVER"] = string.Format(CultureInfo.InvariantCulture, "{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
             values["NTS"] = "ssdp:alive";
             values["NT"] = notificationType;
             values["USN"] = uniqueServiceName;
@@ -522,7 +523,7 @@ namespace Rssdp.Infrastructure
             }
 
             tasks.Add(SendByeByeNotification(device, device.Udn, device.Udn, cancellationToken));
-            tasks.Add(SendByeByeNotification(device, String.Format("urn:{0}", device.FullDeviceType), GetUsn(device.Udn, device.FullDeviceType), cancellationToken));
+            tasks.Add(SendByeByeNotification(device, String.Format(CultureInfo.InvariantCulture, "urn:{0}", device.FullDeviceType), GetUsn(device.Udn, device.FullDeviceType), cancellationToken));
 
             foreach (var childDevice in device.Devices)
             {
@@ -542,7 +543,7 @@ namespace Rssdp.Infrastructure
             // If needed later for non-server devices, these headers will need to be dynamic
             values["HOST"] = "239.255.255.250:1900";
             values["DATE"] = DateTime.UtcNow.ToString("r");
-            values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
+            values["SERVER"] = string.Format(CultureInfo.InvariantCulture, "{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
             values["NTS"] = "ssdp:byebye";
             values["NT"] = notificationType;
             values["USN"] = uniqueServiceName;
@@ -550,7 +551,7 @@ namespace Rssdp.Infrastructure
             var message = BuildMessage(header, values);
 
             var sendCount = IsDisposed ? 1 : 3;
-            WriteTrace(String.Format("Sent byebye notification"), device);
+            WriteTrace(String.Format(CultureInfo.InvariantCulture, "Sent byebye notification"), device);
             return _CommsServer.SendMulticastMessage(message, sendCount, _sendOnlyMatchedHost ? device.ToRootDevice().Address : null, cancellationToken);
         }
 

+ 1 - 1
debian/control

@@ -3,7 +3,7 @@ Section: misc
 Priority: optional
 Maintainer: Jellyfin Team <team@jellyfin.org>
 Build-Depends:  debhelper (>= 9),
-                dotnet-sdk-5.0,
+                dotnet-sdk-6.0,
                 libc6-dev,
                 libcurl4-openssl-dev,
                 libfontconfig1-dev,

+ 5 - 5
deployment/Dockerfile.centos.amd64

@@ -2,7 +2,6 @@ FROM centos:7
 # Docker build arguments
 ARG SOURCE_DIR=/jellyfin
 ARG ARTIFACT_DIR=/dist
-ARG SDK_VERSION=5.0
 # Docker run environment
 ENV SOURCE_DIR=/jellyfin
 ENV ARTIFACT_DIR=/dist
@@ -11,12 +10,13 @@ ENV IS_DOCKER=YES
 # Prepare CentOS environment
 RUN yum update -yq \
   && yum install -yq epel-release \
-  && yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git
+  && 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 rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm \
-  && rpmdev-setuptree \
-  && yum install -yq dotnet-sdk-${SDK_VERSION}
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5fcb98bb-21e1-47a5-bb8e-bb25f41a3e52/04811d5d05b7e694f040d2a13c1aae4c/dotnet-sdk-6.0.100-rc.1.21463.6-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
 
 # Create symlinks and directories
 RUN ln -sf ${SOURCE_DIR}/deployment/build.centos.amd64 /build.sh \

+ 1 - 1
deployment/Dockerfile.debian.amd64

@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
+FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
 # Docker build arguments
 ARG SOURCE_DIR=/jellyfin
 ARG ARTIFACT_DIR=/dist

+ 5 - 5
deployment/Dockerfile.debian.arm64

@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
+FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
 # Docker build arguments
 ARG SOURCE_DIR=/jellyfin
 ARG ARTIFACT_DIR=/dist
@@ -18,16 +18,16 @@ RUN apt-get update -yqq \
 RUN dpkg --add-architecture arm64 \
   && apt-get update -yqq \
   && apt-get install -yqq --no-install-recommends cross-gcc-dev \
-  && TARGET_LIST="arm64" cross-gcc-gensource 8 \
-  && cd cross-gcc-packages-amd64/cross-gcc-8-arm64 \
+  && TARGET_LIST="arm64" cross-gcc-gensource 9 \
+  && cd cross-gcc-packages-amd64/cross-gcc-9-arm64 \
   && apt-get install -yqq --no-install-recommends \
-    gcc-8-source libstdc++-8-dev-arm64-cross \
+    gcc-9-source libstdc++-9-dev-arm64-cross \
     binutils-aarch64-linux-gnu bison flex libtool \
     gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev \
     systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip \
     libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 \
     libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 \
-    libfreetype6-dev:arm64 libssl-dev:arm64 liblttng-ust0:arm64 libstdc++-8-dev:arm64
+    libfreetype6-dev:arm64 libssl-dev:arm64 liblttng-ust0:arm64 libstdc++-9-dev:arm64
 
 # Link to build script
 RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.arm64 /build.sh

+ 5 - 5
deployment/Dockerfile.debian.armhf

@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
+FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
 # Docker build arguments
 ARG SOURCE_DIR=/jellyfin
 ARG ARTIFACT_DIR=/dist
@@ -18,17 +18,17 @@ RUN apt-get update -yqq \
 RUN dpkg --add-architecture armhf \
   && apt-get update -yqq \
   && apt-get install -yqq --no-install-recommends cross-gcc-dev \
-  && TARGET_LIST="armhf" cross-gcc-gensource 8 \
-  && cd cross-gcc-packages-amd64/cross-gcc-8-armhf \
+  && TARGET_LIST="armhf" cross-gcc-gensource 9 \
+  && cd cross-gcc-packages-amd64/cross-gcc-9-armhf \
   && apt-get install -yqq --no-install-recommends\
-    gcc-8-source libstdc++-8-dev-armhf-cross \
+    gcc-9-source libstdc++-9-dev-armhf-cross \
     binutils-aarch64-linux-gnu bison flex libtool gdb \
     sharutils netbase libmpc-dev libmpfr-dev libgmp-dev \
     systemtap-sdt-dev autogen expect chrpath zlib1g-dev \
     zip binutils-arm-linux-gnueabihf libc6-dev:armhf \
     linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf \
     libfontconfig1-dev:armhf libfreetype6-dev:armhf libssl-dev:armhf \
-    liblttng-ust0:armhf libstdc++-8-dev:armhf
+    liblttng-ust0:armhf libstdc++-9-dev:armhf
 
 # Link to build script
 RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.armhf /build.sh

+ 1 - 3
deployment/Dockerfile.docker.amd64

@@ -1,6 +1,4 @@
-ARG DOTNET_VERSION=5.0
-
-FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-buster-slim
+FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
 
 ARG SOURCE_DIR=/src
 ARG ARTIFACT_DIR=/jellyfin

+ 1 - 3
deployment/Dockerfile.docker.arm64

@@ -1,6 +1,4 @@
-ARG DOTNET_VERSION=5.0
-
-FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-buster-slim
+FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
 
 ARG SOURCE_DIR=/src
 ARG ARTIFACT_DIR=/jellyfin

+ 1 - 3
deployment/Dockerfile.docker.armhf

@@ -1,6 +1,4 @@
-ARG DOTNET_VERSION=5.0
-
-FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-buster-slim
+FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
 
 ARG SOURCE_DIR=/src
 ARG ARTIFACT_DIR=/jellyfin

+ 6 - 3
deployment/Dockerfile.fedora.amd64

@@ -2,7 +2,6 @@ FROM fedora:33
 # Docker build arguments
 ARG SOURCE_DIR=/jellyfin
 ARG ARTIFACT_DIR=/dist
-ARG SDK_VERSION=5.0
 # Docker run environment
 ENV SOURCE_DIR=/jellyfin
 ENV ARTIFACT_DIR=/dist
@@ -10,10 +9,14 @@ ENV IS_DOCKER=YES
 
 # Prepare Fedora environment
 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
+  && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget
 
 # Install DotNET SDK
-RUN dnf install -yq dotnet-sdk-${SDK_VERSION} dotnet-runtime-${SDK_VERSION}
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5fcb98bb-21e1-47a5-bb8e-bb25f41a3e52/04811d5d05b7e694f040d2a13c1aae4c/dotnet-sdk-6.0.100-rc.1.21463.6-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
+
 
 # Create symlinks and directories
 RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora.amd64 /build.sh \

+ 1 - 1
deployment/Dockerfile.linux.amd64

@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
+FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
 # Docker build arguments
 ARG SOURCE_DIR=/jellyfin
 ARG ARTIFACT_DIR=/dist

+ 1 - 1
deployment/Dockerfile.linux.amd64-musl

@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
+FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
 # Docker build arguments
 ARG SOURCE_DIR=/jellyfin
 ARG ARTIFACT_DIR=/dist

+ 1 - 1
deployment/Dockerfile.linux.arm64

@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
+FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
 # Docker build arguments
 ARG SOURCE_DIR=/jellyfin
 ARG ARTIFACT_DIR=/dist

+ 1 - 1
deployment/Dockerfile.linux.armhf

@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
+FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
 # Docker build arguments
 ARG SOURCE_DIR=/jellyfin
 ARG ARTIFACT_DIR=/dist

+ 1 - 1
deployment/Dockerfile.macos

@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
+FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
 # Docker build arguments
 ARG SOURCE_DIR=/jellyfin
 ARG ARTIFACT_DIR=/dist

Some files were not shown because too many files changed in this diff