Pārlūkot izejas kodu

Merge remote-tracking branch 'upstream/master' into integration-tests

Mark Monteiro 5 gadi atpakaļ
vecāks
revīzija
cd98938190
94 mainītis faili ar 1103 papildinājumiem un 1521 dzēšanām
  1. 9 9
      .ci/azure-pipelines-compat.yml
  2. 23 30
      .ci/azure-pipelines-main.yml
  3. 42 18
      .ci/azure-pipelines-test.yml
  4. 6 6
      .ci/azure-pipelines.yml
  5. 5 0
      DvdLib/DvdLib.csproj
  6. 1 0
      Emby.Dlna/ConfigurationExtension.cs
  7. 5 0
      Emby.Dlna/Emby.Dlna.csproj
  8. 6 0
      Emby.Drawing/Emby.Drawing.csproj
  9. 9 47
      Emby.Drawing/ImageProcessor.cs
  10. 5 0
      Emby.Naming/Emby.Naming.csproj
  11. 5 0
      Emby.Notifications/Emby.Notifications.csproj
  12. 6 0
      Emby.Photos/Emby.Photos.csproj
  13. 1 1
      Emby.Photos/PhotoProvider.cs
  14. 5 5
      Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
  15. 188 428
      Emby.Server.Implementations/ApplicationHost.cs
  16. 1 1
      Emby.Server.Implementations/ConfigurationOptions.cs
  17. 13 7
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  18. 3 3
      Emby.Server.Implementations/Devices/DeviceManager.cs
  19. 16 14
      Emby.Server.Implementations/Dto/DtoService.cs
  20. 5 0
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  21. 31 14
      Emby.Server.Implementations/EntryPoints/StartupWizard.cs
  22. 5 4
      Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
  23. 58 49
      Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
  24. 10 27
      Emby.Server.Implementations/HttpServer/Security/AuthService.cs
  25. 26 32
      Emby.Server.Implementations/IO/LibraryMonitor.cs
  26. 1 1
      Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
  27. 100 129
      Emby.Server.Implementations/Library/LibraryManager.cs
  28. 11 11
      Emby.Server.Implementations/Library/MediaSourceManager.cs
  29. 3 4
      Emby.Server.Implementations/Library/SearchEngine.cs
  30. 18 19
      Emby.Server.Implementations/Library/UserDataManager.cs
  31. 14 23
      Emby.Server.Implementations/Library/UserManager.cs
  32. 0 1
      Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
  33. 10 15
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  34. 1 1
      Emby.Server.Implementations/Localization/Core/fa.json
  35. 6 5
      Emby.Server.Implementations/Localization/Core/ja.json
  36. 3 3
      Emby.Server.Implementations/Localization/Core/nl.json
  37. 24 2
      Emby.Server.Implementations/Localization/Core/pt-PT.json
  38. 20 3
      Emby.Server.Implementations/Localization/Core/tr.json
  39. 28 6
      Emby.Server.Implementations/Localization/Core/zh-TW.json
  40. 0 3
      Emby.Server.Implementations/Localization/LocalizationManager.cs
  41. 3 17
      Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
  42. 2 2
      Emby.Server.Implementations/Security/AuthenticationRepository.cs
  43. 1 1
      Emby.Server.Implementations/Session/SessionManager.cs
  44. 35 42
      Emby.Server.Implementations/Updates/InstallationManager.cs
  45. 5 0
      Jellyfin.Api/Jellyfin.Api.csproj
  46. 6 0
      Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
  47. 5 8
      Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
  48. 28 21
      Jellyfin.Drawing.Skia/SkiaEncoder.cs
  49. 5 5
      Jellyfin.Drawing.Skia/StripCollageBuilder.cs
  50. 2 2
      Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs
  51. 22 5
      Jellyfin.Server/CoreAppHost.cs
  52. 5 0
      Jellyfin.Server/Jellyfin.Server.csproj
  53. 2 23
      Jellyfin.Server/Program.cs
  54. 8 1
      MediaBrowser.Api/Images/ImageService.cs
  55. 5 0
      MediaBrowser.Api/MediaBrowser.Api.csproj
  56. 1 43
      MediaBrowser.Api/PackageService.cs
  57. 1 1
      MediaBrowser.Api/UserService.cs
  58. 2 0
      MediaBrowser.Common/Extensions/ShuffleExtensions.cs
  59. 1 11
      MediaBrowser.Common/IApplicationHost.cs
  60. 5 0
      MediaBrowser.Common/MediaBrowser.Common.csproj
  61. 4 4
      MediaBrowser.Common/Plugins/BasePlugin.cs
  62. 9 13
      MediaBrowser.Common/Updates/IInstallationManager.cs
  63. 1 1
      MediaBrowser.Common/Updates/InstallationEventArgs.cs
  64. 12 6
      MediaBrowser.Controller/Authentication/AuthenticationException.cs
  65. 0 9
      MediaBrowser.Controller/Drawing/IImageProcessor.cs
  66. 5 0
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  67. 1 1
      MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
  68. 24 8
      MediaBrowser.Controller/Net/SecurityException.cs
  69. 5 0
      MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
  70. 4 13
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  71. 5 0
      MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
  72. 5 0
      MediaBrowser.Model/MediaBrowser.Model.csproj
  73. 5 5
      MediaBrowser.Model/Services/IHasRequestFilter.cs
  74. 0 2
      MediaBrowser.Model/System/SystemInfo.cs
  75. 0 29
      MediaBrowser.Model/Updates/CheckForUpdateResult.cs
  76. 3 15
      MediaBrowser.Model/Updates/InstallationInfo.cs
  77. 7 121
      MediaBrowser.Model/Updates/PackageInfo.cs
  78. 0 23
      MediaBrowser.Model/Updates/PackageTargetSystem.cs
  79. 0 23
      MediaBrowser.Model/Updates/PackageVersionClass.cs
  80. 0 96
      MediaBrowser.Model/Updates/PackageVersionInfo.cs
  81. 58 0
      MediaBrowser.Model/Updates/VersionInfo.cs
  82. 32 41
      MediaBrowser.Providers/Manager/ProviderManager.cs
  83. 5 0
      MediaBrowser.Providers/MediaBrowser.Providers.csproj
  84. 4 4
      MediaBrowser.Providers/Subtitles/SubtitleManager.cs
  85. 5 0
      MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
  86. 5 0
      MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
  87. 5 0
      RSSDP/RSSDP.csproj
  88. 5 0
      tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
  89. 5 0
      tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
  90. 5 0
      tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
  91. 5 0
      tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
  92. 5 0
      tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
  93. 5 0
      tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
  94. 2 4
      tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs

+ 9 - 9
.ci/azure-pipelines-compat.yml

@@ -1,13 +1,13 @@
 parameters:
 parameters:
-  - name: Packages
-    type: object
-    default: {}
-  - name: LinuxImage
-    type: string
-    default: "ubuntu-latest"
-  - name: DotNetSdkVersion
-    type: string
-    default: 3.1.100
+- name: Packages
+  type: object
+  default: {}
+- name: LinuxImage
+  type: string
+  default: "ubuntu-latest"
+- name: DotNetSdkVersion
+  type: string
+  default: 3.1.100
 
 
 jobs:
 jobs:
   - job: CompatibilityCheck
   - job: CompatibilityCheck

+ 23 - 30
.ci/azure-pipelines-main.yml

@@ -20,41 +20,34 @@ jobs:
         submodules: true
         submodules: true
         persistCredentials: true
         persistCredentials: true
 
 
-      - task: CmdLine@2
-        displayName: "Clone Web Branch"
-        condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+      - task: DownloadPipelineArtifact@2
+        displayName: "Download Web Branch"
+        condition: in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')
         inputs:
         inputs:
-          script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
+          path: '$(Agent.TempDirectory)'
+          artifact: 'jellyfin-web-production'
+          source: 'specific'
+          project: 'jellyfin'
+          pipeline: 'Jellyfin Web'
+          runBranch: variables['Build.SourceBranch']
 
 
-      - task: CmdLine@2
-        displayName: "Clone Web Target"
-        condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest'))
+      - task: DownloadPipelineArtifact@2
+        displayName: "Download Web Target"
+        condition: eq(variables['Build.Reason'], 'PullRequest')
         inputs:
         inputs:
-          script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
+          path: '$(Agent.TempDirectory)'
+          artifact: 'jellyfin-web-production'
+          source: 'specific'
+          project: 'jellyfin'
+          pipeline: 'Jellyfin Web'
+          runBranch: variables['System.PullRequest.TargetBranch']
 
 
-      - task: NodeTool@0
-        displayName: "Install Node"
-        condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+      - task: ExtractFiles@1
+        displayName: "Extract Web Client"
         inputs:
         inputs:
-          versionSpec: "12.x"
-
-      - task: CmdLine@2
-        displayName: "Build Web Client"
-        condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
-        inputs:
-          script: yarn install
-          workingDirectory: $(Agent.TempDirectory)/jellyfin-web
-
-      - task: CopyFiles@2
-        displayName: "Copy Web Client"
-        condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
-        inputs:
-          sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist
-          contents: "**"
-          targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web
-          cleanTargetFolder: true
-          overWrite: true
-          flattenFolders: false
+          archiveFilePatterns: '$(Agent.TempDirectory)/*.zip'
+          destinationFolder: '$(Build.SourcesDirectory)/MediaBrowser.WebDashboard'
+          cleanDestinationFolder: false
 
 
       - task: UseDotNet@2
       - task: UseDotNet@2
         displayName: "Update DotNet"
         displayName: "Update DotNet"

+ 42 - 18
.ci/azure-pipelines-test.yml

@@ -1,26 +1,25 @@
 parameters:
 parameters:
-  - name: ImageNames
-    type: object
-    default:
-      Linux: "ubuntu-latest"
-      Windows: "windows-latest"
-      macOS: "macos-latest"
-  - name: TestProjects
-    type: string
-    default: "tests/**/*Tests.csproj"
-  - name: DotNetSdkVersion
-    type: string
-    default: 3.1.100
+- name: ImageNames
+  type: object
+  default:
+    Linux: "ubuntu-latest"
+    Windows: "windows-latest"
+    macOS: "macos-latest"
+- name: TestProjects
+  type: string
+  default: "tests/**/*Tests.csproj"
+- name: DotNetSdkVersion
+  type: string
+  default: 3.1.100
 
 
 jobs:
 jobs:
-  - job: MainTest
-    displayName: Main Test
+  - job: Test
+    displayName: Test
     strategy:
     strategy:
       matrix:
       matrix:
         ${{ each imageName in parameters.ImageNames }}:
         ${{ each imageName in parameters.ImageNames }}:
           ${{ imageName.key }}:
           ${{ imageName.key }}:
             ImageName: ${{ imageName.value }}
             ImageName: ${{ imageName.value }}
-      maxParallel: 3
     pool:
     pool:
       vmImage: "$(ImageName)"
       vmImage: "$(ImageName)"
     steps:
     steps:
@@ -29,14 +28,30 @@ jobs:
         submodules: true
         submodules: true
         persistCredentials: false
         persistCredentials: false
 
 
+      # This is required for the SonarCloud analyzer
+      - task: UseDotNet@2
+        displayName: "Install .NET Core SDK 2.1"
+        condition: eq(variables['ImageName'], 'ubuntu-latest')
+        inputs:
+          packageType: sdk
+          version: '2.1.805'
+
       - task: UseDotNet@2
       - task: UseDotNet@2
         displayName: "Update DotNet"
         displayName: "Update DotNet"
         inputs:
         inputs:
           packageType: sdk
           packageType: sdk
           version: ${{ parameters.DotNetSdkVersion }}
           version: ${{ parameters.DotNetSdkVersion }}
 
 
+      - task: SonarCloudPrepare@1
+        displayName: 'Prepare analysis on SonarCloud'
+        condition: eq(variables['ImageName'], 'ubuntu-latest')
+        inputs:
+          SonarCloud: 'Sonarcloud for Jellyfin'
+          organization: 'jellyfin'
+          projectKey: 'jellyfin_jellyfin'
+
       - task: DotNetCoreCLI@2
       - task: DotNetCoreCLI@2
-        displayName: Run .NET Core CLI tests
+        displayName: 'Run CLI Tests'
         inputs:
         inputs:
           command: "test"
           command: "test"
           projects: ${{ parameters.TestProjects }}
           projects: ${{ parameters.TestProjects }}
@@ -45,9 +60,17 @@ jobs:
           testRunTitle: $(Agent.JobName)
           testRunTitle: $(Agent.JobName)
           workingDirectory: "$(Build.SourcesDirectory)"
           workingDirectory: "$(Build.SourcesDirectory)"
 
 
+      - task: SonarCloudAnalyze@1
+        displayName: 'Run Code Analysis'
+        condition: eq(variables['ImageName'], 'ubuntu-latest')
+
+      - task: SonarCloudPublish@1
+        displayName: 'Publish Quality Gate Result'
+        condition: eq(variables['ImageName'], 'ubuntu-latest')
+
       - task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4
       - task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4
         condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
         condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
-        displayName: ReportGenerator (merge)
+        displayName: 'Run ReportGenerator'
         inputs:
         inputs:
           reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml"
           reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml"
           targetdir: "$(Agent.TempDirectory)/merged/"
           targetdir: "$(Agent.TempDirectory)/merged/"
@@ -56,10 +79,11 @@ jobs:
       ## V2 is already in the repository but it does not work "wrong number of segments" YAML error.
       ## V2 is already in the repository but it does not work "wrong number of segments" YAML error.
       - task: PublishCodeCoverageResults@1
       - task: PublishCodeCoverageResults@1
         condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
         condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
-        displayName: Publish Code Coverage
+        displayName: 'Publish Code Coverage'
         inputs:
         inputs:
           codeCoverageTool: "cobertura"
           codeCoverageTool: "cobertura"
           #summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2
           #summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2
           summaryFileLocation: "$(Agent.TempDirectory)/merged/**.xml"
           summaryFileLocation: "$(Agent.TempDirectory)/merged/**.xml"
           pathToSources: $(Build.SourcesDirectory)
           pathToSources: $(Build.SourcesDirectory)
           failIfCoverageEmpty: true
           failIfCoverageEmpty: true
+

+ 6 - 6
.ci/azure-pipelines.yml

@@ -1,12 +1,12 @@
 name: $(Date:yyyyMMdd)$(Rev:.r)
 name: $(Date:yyyyMMdd)$(Rev:.r)
 
 
 variables:
 variables:
-  - name: TestProjects
-    value: "tests/**/*Tests.csproj"
-  - name: RestoreBuildProjects
-    value: "Jellyfin.Server/Jellyfin.Server.csproj"
-  - name: DotNetSdkVersion
-    value: 3.1.100
+- name: TestProjects
+  value: "tests/**/*Tests.csproj"
+- name: RestoreBuildProjects
+  value: "Jellyfin.Server/Jellyfin.Server.csproj"
+- name: DotNetSdkVersion
+  value: 3.1.100
 
 
 pr:
 pr:
   autoCancel: true
   autoCancel: true

+ 5 - 0
DvdLib/DvdLib.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{713F42B5-878E-499D-A878-E4C652B1D5E8}</ProjectGuid>
+  </PropertyGroup>
+
   <ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs" />
     <Compile Include="..\SharedVersion.cs" />
   </ItemGroup>
   </ItemGroup>

+ 1 - 0
Emby.Dlna/ConfigurationExtension.cs

@@ -1,3 +1,4 @@
+#nullable enable
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System.Collections.Generic;
 using System.Collections.Generic;

+ 5 - 0
Emby.Dlna/Emby.Dlna.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{805844AB-E92F-45E6-9D99-4F6D48D129A5}</ProjectGuid>
+  </PropertyGroup>
+
   <ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs" />
     <Compile Include="..\SharedVersion.cs" />
   </ItemGroup>
   </ItemGroup>

+ 6 - 0
Emby.Drawing/Emby.Drawing.csproj

@@ -1,10 +1,16 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{08FFF49B-F175-4807-A2B5-73B0EBD9F716}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netstandard2.1</TargetFramework>
     <TargetFramework>netstandard2.1</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <Nullable>enable</Nullable>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>

+ 9 - 47
Emby.Drawing/ImageProcessor.cs

@@ -8,7 +8,6 @@ using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
@@ -33,8 +32,7 @@ namespace Emby.Drawing
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly IServerApplicationPaths _appPaths;
         private readonly IServerApplicationPaths _appPaths;
         private readonly IImageEncoder _imageEncoder;
         private readonly IImageEncoder _imageEncoder;
-        private readonly Func<ILibraryManager> _libraryManager;
-        private readonly Func<IMediaEncoder> _mediaEncoder;
+        private readonly IMediaEncoder _mediaEncoder;
 
 
         private bool _disposed = false;
         private bool _disposed = false;
 
 
@@ -45,20 +43,17 @@ namespace Emby.Drawing
         /// <param name="appPaths">The server application paths.</param>
         /// <param name="appPaths">The server application paths.</param>
         /// <param name="fileSystem">The filesystem.</param>
         /// <param name="fileSystem">The filesystem.</param>
         /// <param name="imageEncoder">The image encoder.</param>
         /// <param name="imageEncoder">The image encoder.</param>
-        /// <param name="libraryManager">The library manager.</param>
         /// <param name="mediaEncoder">The media encoder.</param>
         /// <param name="mediaEncoder">The media encoder.</param>
         public ImageProcessor(
         public ImageProcessor(
             ILogger<ImageProcessor> logger,
             ILogger<ImageProcessor> logger,
             IServerApplicationPaths appPaths,
             IServerApplicationPaths appPaths,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
             IImageEncoder imageEncoder,
             IImageEncoder imageEncoder,
-            Func<ILibraryManager> libraryManager,
-            Func<IMediaEncoder> mediaEncoder)
+            IMediaEncoder mediaEncoder)
         {
         {
             _logger = logger;
             _logger = logger;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _imageEncoder = imageEncoder;
             _imageEncoder = imageEncoder;
-            _libraryManager = libraryManager;
             _mediaEncoder = mediaEncoder;
             _mediaEncoder = mediaEncoder;
             _appPaths = appPaths;
             _appPaths = appPaths;
         }
         }
@@ -121,26 +116,9 @@ namespace Emby.Drawing
         /// <inheritdoc />
         /// <inheritdoc />
         public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
         public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
         {
         {
-            if (options == null)
-            {
-                throw new ArgumentNullException(nameof(options));
-            }
-
-            var libraryManager = _libraryManager();
-
             ItemImageInfo originalImage = options.Image;
             ItemImageInfo originalImage = options.Image;
             BaseItem item = options.Item;
             BaseItem item = options.Item;
 
 
-            if (!originalImage.IsLocalFile)
-            {
-                if (item == null)
-                {
-                    item = libraryManager.GetItemById(options.ItemId);
-                }
-
-                originalImage = await libraryManager.ConvertImageToLocal(item, originalImage, options.ImageIndex).ConfigureAwait(false);
-            }
-
             string originalImagePath = originalImage.Path;
             string originalImagePath = originalImage.Path;
             DateTime dateModified = originalImage.DateModified;
             DateTime dateModified = originalImage.DateModified;
             ImageDimensions? originalImageSize = null;
             ImageDimensions? originalImageSize = null;
@@ -312,10 +290,6 @@ namespace Emby.Drawing
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info)
         public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info)
-            => GetImageDimensions(item, info, true);
-
-        /// <inheritdoc />
-        public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem)
         {
         {
             int width = info.Width;
             int width = info.Width;
             int height = info.Height;
             int height = info.Height;
@@ -332,11 +306,6 @@ namespace Emby.Drawing
             info.Width = size.Width;
             info.Width = size.Width;
             info.Height = size.Height;
             info.Height = size.Height;
 
 
-            if (updateItem)
-            {
-                _libraryManager().UpdateImages(item);
-            }
-
             return size;
             return size;
         }
         }
 
 
@@ -351,19 +320,12 @@ namespace Emby.Drawing
         /// <inheritdoc />
         /// <inheritdoc />
         public string GetImageCacheTag(BaseItem item, ChapterInfo chapter)
         public string GetImageCacheTag(BaseItem item, ChapterInfo chapter)
         {
         {
-            try
+            return GetImageCacheTag(item, new ItemImageInfo
             {
             {
-                return GetImageCacheTag(item, new ItemImageInfo
-                {
-                    Path = chapter.ImagePath,
-                    Type = ImageType.Chapter,
-                    DateModified = chapter.ImageDateModified
-                });
-            }
-            catch
-            {
-                return null;
-            }
+                Path = chapter.ImagePath,
+                Type = ImageType.Chapter,
+                DateModified = chapter.ImageDateModified
+            });
         }
         }
 
 
         private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
         private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
@@ -384,13 +346,13 @@ namespace Emby.Drawing
                 {
                 {
                     string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture);
                     string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture);
 
 
-                    string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png";
+                    string cacheExtension = _mediaEncoder.SupportsEncoder("libwebp") ? ".webp" : ".png";
                     var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);
                     var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);
 
 
                     var file = _fileSystem.GetFileInfo(outputPath);
                     var file = _fileSystem.GetFileInfo(outputPath);
                     if (!file.Exists)
                     if (!file.Exists)
                     {
                     {
-                        await _mediaEncoder().ConvertImage(originalImagePath, outputPath).ConfigureAwait(false);
+                        await _mediaEncoder.ConvertImage(originalImagePath, outputPath).ConfigureAwait(false);
                         dateModified = _fileSystem.GetLastWriteTimeUtc(outputPath);
                         dateModified = _fileSystem.GetLastWriteTimeUtc(outputPath);
                     }
                     }
                     else
                     else

+ 5 - 0
Emby.Naming/Emby.Naming.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netstandard2.1</TargetFramework>
     <TargetFramework>netstandard2.1</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>

+ 5 - 0
Emby.Notifications/Emby.Notifications.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{2E030C33-6923-4530-9E54-FA29FA6AD1A9}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netstandard2.1</TargetFramework>
     <TargetFramework>netstandard2.1</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>

+ 6 - 0
Emby.Photos/Emby.Photos.csproj

@@ -1,4 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
+
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{89AB4548-770D-41FD-A891-8DAFF44F452C}</ProjectGuid>
+  </PropertyGroup>
+
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />

+ 1 - 1
Emby.Photos/PhotoProvider.cs

@@ -160,7 +160,7 @@ namespace Emby.Photos
 
 
                 try
                 try
                 {
                 {
-                    var size = _imageProcessor.GetImageDimensions(item, img, false);
+                    var size = _imageProcessor.GetImageDimensions(item, img);
 
 
                     if (size.Width > 0 && size.Height > 0)
                     if (size.Width > 0 && size.Height > 0)
                     {
                     {

+ 5 - 5
Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs

@@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.Activity
             });
             });
         }
         }
 
 
-        private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, PackageVersionInfo)> e)
+        private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, VersionInfo)> e)
         {
         {
             CreateLogEntry(new ActivityLogEntry
             CreateLogEntry(new ActivityLogEntry
             {
             {
@@ -434,8 +434,8 @@ namespace Emby.Server.Implementations.Activity
                 ShortOverview = string.Format(
                 ShortOverview = string.Format(
                     CultureInfo.InvariantCulture,
                     CultureInfo.InvariantCulture,
                     _localization.GetLocalizedString("VersionNumber"),
                     _localization.GetLocalizedString("VersionNumber"),
-                    e.Argument.Item2.versionStr),
-                Overview = e.Argument.Item2.description
+                    e.Argument.Item2.version),
+                Overview = e.Argument.Item2.changelog
             });
             });
         }
         }
 
 
@@ -451,7 +451,7 @@ namespace Emby.Server.Implementations.Activity
             });
             });
         }
         }
 
 
-        private void OnPluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e)
+        private void OnPluginInstalled(object sender, GenericEventArgs<VersionInfo> e)
         {
         {
             CreateLogEntry(new ActivityLogEntry
             CreateLogEntry(new ActivityLogEntry
             {
             {
@@ -463,7 +463,7 @@ namespace Emby.Server.Implementations.Activity
                 ShortOverview = string.Format(
                 ShortOverview = string.Format(
                     CultureInfo.InvariantCulture,
                     CultureInfo.InvariantCulture,
                     _localization.GetLocalizedString("VersionNumber"),
                     _localization.GetLocalizedString("VersionNumber"),
-                    e.Argument.versionStr)
+                    e.Argument.version)
             });
             });
         }
         }
 
 

+ 188 - 428
Emby.Server.Implementations/ApplicationHost.cs

@@ -86,7 +86,6 @@ using MediaBrowser.Model.Activity;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Cryptography;
 using MediaBrowser.Model.Cryptography;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.MediaInfo;
@@ -104,7 +103,6 @@ using MediaBrowser.WebDashboard.Api;
 using MediaBrowser.XbmcMetadata.Providers;
 using MediaBrowser.XbmcMetadata.Providers;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http.Extensions;
 using Microsoft.AspNetCore.Http.Extensions;
-using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
 using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
@@ -121,14 +119,20 @@ namespace Emby.Server.Implementations
         /// </summary>
         /// </summary>
         private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" };
         private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" };
 
 
-        private SqliteUserRepository _userRepository;
-        private SqliteDisplayPreferencesRepository _displayPreferencesRepository;
+        private readonly IFileSystem _fileSystemManager;
+        private readonly INetworkManager _networkManager;
+        private readonly IXmlSerializer _xmlSerializer;
+        private readonly IStartupOptions _startupOptions;
+
+        private IMediaEncoder _mediaEncoder;
+        private ISessionManager _sessionManager;
+        private IHttpServer _httpServer;
+        private IHttpClient _httpClient;
 
 
         /// <summary>
         /// <summary>
         /// Gets a value indicating whether this instance can self restart.
         /// Gets a value indicating whether this instance can self restart.
         /// </summary>
         /// </summary>
-        /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
-        public abstract bool CanSelfRestart { get; }
+        public bool CanSelfRestart => _startupOptions.RestartPath != null;
 
 
         public virtual bool CanLaunchWebBrowser
         public virtual bool CanLaunchWebBrowser
         {
         {
@@ -139,7 +143,7 @@ namespace Emby.Server.Implementations
                     return false;
                     return false;
                 }
                 }
 
 
-                if (StartupOptions.IsService)
+                if (_startupOptions.IsService)
                 {
                 {
                     return false;
                     return false;
                 }
                 }
@@ -209,21 +213,6 @@ namespace Emby.Server.Implementations
         /// <value>The configuration manager.</value>
         /// <value>The configuration manager.</value>
         protected IConfigurationManager ConfigurationManager { get; set; }
         protected IConfigurationManager ConfigurationManager { get; set; }
 
 
-        public IFileSystem FileSystemManager { get; set; }
-
-        /// <inheritdoc />
-        public PackageVersionClass SystemUpdateLevel
-        {
-            get
-            {
-#if BETA
-                return PackageVersionClass.Beta;
-#else
-                return PackageVersionClass.Release;
-#endif
-            }
-        }
-
         /// <summary>
         /// <summary>
         /// Gets or sets the service provider.
         /// Gets or sets the service provider.
         /// </summary>
         /// </summary>
@@ -245,110 +234,6 @@ namespace Emby.Server.Implementations
         /// <value>The server configuration manager.</value>
         /// <value>The server configuration manager.</value>
         public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager;
         public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager;
 
 
-        /// <summary>
-        /// Gets or sets the user manager.
-        /// </summary>
-        /// <value>The user manager.</value>
-        public IUserManager UserManager { get; set; }
-
-        /// <summary>
-        /// Gets or sets the library manager.
-        /// </summary>
-        /// <value>The library manager.</value>
-        internal ILibraryManager LibraryManager { get; set; }
-
-        /// <summary>
-        /// Gets or sets the directory watchers.
-        /// </summary>
-        /// <value>The directory watchers.</value>
-        private ILibraryMonitor LibraryMonitor { get; set; }
-
-        /// <summary>
-        /// Gets or sets the provider manager.
-        /// </summary>
-        /// <value>The provider manager.</value>
-        private IProviderManager ProviderManager { get; set; }
-
-        /// <summary>
-        /// Gets or sets the HTTP server.
-        /// </summary>
-        /// <value>The HTTP server.</value>
-        private IHttpServer HttpServer { get; set; }
-
-        private IDtoService DtoService { get; set; }
-
-        public IImageProcessor ImageProcessor { get; set; }
-
-        /// <summary>
-        /// Gets or sets the media encoder.
-        /// </summary>
-        /// <value>The media encoder.</value>
-        private IMediaEncoder MediaEncoder { get; set; }
-
-        private ISubtitleEncoder SubtitleEncoder { get; set; }
-
-        private ISessionManager SessionManager { get; set; }
-
-        private ILiveTvManager LiveTvManager { get; set; }
-
-        public LocalizationManager LocalizationManager { get; set; }
-
-        private IEncodingManager EncodingManager { get; set; }
-
-        private IChannelManager ChannelManager { get; set; }
-
-        /// <summary>
-        /// Gets or sets the user data repository.
-        /// </summary>
-        /// <value>The user data repository.</value>
-        private IUserDataManager UserDataManager { get; set; }
-
-        internal SqliteItemRepository ItemRepository { get; set; }
-
-        private INotificationManager NotificationManager { get; set; }
-
-        private ISubtitleManager SubtitleManager { get; set; }
-
-        private IChapterManager ChapterManager { get; set; }
-
-        private IDeviceManager DeviceManager { get; set; }
-
-        internal IUserViewManager UserViewManager { get; set; }
-
-        private IAuthenticationRepository AuthenticationRepository { get; set; }
-
-        private ITVSeriesManager TVSeriesManager { get; set; }
-
-        private ICollectionManager CollectionManager { get; set; }
-
-        private IMediaSourceManager MediaSourceManager { get; set; }
-
-        /// <summary>
-        /// Gets the installation manager.
-        /// </summary>
-        /// <value>The installation manager.</value>
-        protected IInstallationManager InstallationManager { get; private set; }
-
-        protected IAuthService AuthService { get; private set; }
-
-        public IStartupOptions StartupOptions { get; }
-
-        internal IImageEncoder ImageEncoder { get; private set; }
-
-        protected readonly IXmlSerializer XmlSerializer;
-
-        protected ISocketFactory SocketFactory { get; private set; }
-
-        protected ITaskManager TaskManager { get; private set; }
-
-        public IHttpClient HttpClient { get; private set; }
-
-        protected INetworkManager NetworkManager { get; set; }
-
-        public IJsonSerializer JsonSerializer { get; private set; }
-
-        protected IIsoManager IsoManager { get; private set; }
-
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="ApplicationHost" /> class.
         /// Initializes a new instance of the <see cref="ApplicationHost" /> class.
         /// </summary>
         /// </summary>
@@ -357,29 +242,33 @@ namespace Emby.Server.Implementations
             ILoggerFactory loggerFactory,
             ILoggerFactory loggerFactory,
             IStartupOptions options,
             IStartupOptions options,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
-            IImageEncoder imageEncoder,
             INetworkManager networkManager)
             INetworkManager networkManager)
         {
         {
-            XmlSerializer = new MyXmlSerializer();
+            _xmlSerializer = new MyXmlSerializer();
 
 
-            NetworkManager = networkManager;
+            _networkManager = networkManager;
             networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
             networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
 
 
             ApplicationPaths = applicationPaths;
             ApplicationPaths = applicationPaths;
             LoggerFactory = loggerFactory;
             LoggerFactory = loggerFactory;
-            FileSystemManager = fileSystem;
+            _fileSystemManager = fileSystem;
 
 
-            ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, XmlSerializer, FileSystemManager);
+            ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager);
 
 
-            Logger = LoggerFactory.CreateLogger("App");
+            Logger = LoggerFactory.CreateLogger<ApplicationHost>();
 
 
-            StartupOptions = options;
-
-            ImageEncoder = imageEncoder;
+            _startupOptions = options;
 
 
             fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
             fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
 
 
-            NetworkManager.NetworkChanged += OnNetworkChanged;
+            _networkManager.NetworkChanged += OnNetworkChanged;
+
+            CertificateInfo = new CertificateInfo
+            {
+                Path = ServerConfigurationManager.Configuration.CertificatePath,
+                Password = ServerConfigurationManager.Configuration.CertificatePassword
+            };
+            Certificate = GetCertificate(CertificateInfo);
         }
         }
 
 
         public string ExpandVirtualPath(string path)
         public string ExpandVirtualPath(string path)
@@ -447,10 +336,7 @@ namespace Emby.Server.Implementations
             }
             }
         }
         }
 
 
-        /// <summary>
-        /// Gets the name.
-        /// </summary>
-        /// <value>The name.</value>
+        /// <inheritdoc/>
         public string Name => ApplicationProductName;
         public string Name => ApplicationProductName;
 
 
         /// <summary>
         /// <summary>
@@ -540,7 +426,7 @@ namespace Emby.Server.Implementations
 
 
             ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
             ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
 
 
-            MediaEncoder.SetFFmpegPath();
+            _mediaEncoder.SetFFmpegPath();
 
 
             Logger.LogInformation("ServerId: {0}", SystemId);
             Logger.LogInformation("ServerId: {0}", SystemId);
 
 
@@ -552,7 +438,7 @@ namespace Emby.Server.Implementations
             Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed);
             Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed);
 
 
             Logger.LogInformation("Core startup complete");
             Logger.LogInformation("Core startup complete");
-            HttpServer.GlobalResponse = null;
+            _httpServer.GlobalResponse = null;
 
 
             stopWatch.Restart();
             stopWatch.Restart();
             await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false);
             await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false);
@@ -576,7 +462,7 @@ namespace Emby.Server.Implementations
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
-        public async Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig)
+        public void Init(IServiceCollection serviceCollection)
         {
         {
             HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
             HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
             HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
             HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@@ -588,8 +474,6 @@ namespace Emby.Server.Implementations
                 HttpsPort = ServerConfiguration.DefaultHttpsPort;
                 HttpsPort = ServerConfiguration.DefaultHttpsPort;
             }
             }
 
 
-            JsonSerializer = new JsonSerializer();
-
             if (Plugins != null)
             if (Plugins != null)
             {
             {
                 var pluginBuilder = new StringBuilder();
                 var pluginBuilder = new StringBuilder();
@@ -609,7 +493,7 @@ namespace Emby.Server.Implementations
 
 
             DiscoverTypes();
             DiscoverTypes();
 
 
-            await RegisterServices(serviceCollection, startupConfig).ConfigureAwait(false);
+            RegisterServices(serviceCollection);
         }
         }
 
 
         public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next)
         public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next)
@@ -620,7 +504,7 @@ namespace Emby.Server.Implementations
                 return;
                 return;
             }
             }
 
 
-            await HttpServer.ProcessWebSocketRequest(context).ConfigureAwait(false);
+            await _httpServer.ProcessWebSocketRequest(context).ConfigureAwait(false);
         }
         }
 
 
         public async Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
         public async Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
@@ -636,14 +520,16 @@ namespace Emby.Server.Implementations
             var localPath = context.Request.Path.ToString();
             var localPath = context.Request.Path.ToString();
 
 
             var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger<WebSocketSharpRequest>());
             var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger<WebSocketSharpRequest>());
-            await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false);
+            await _httpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Registers services/resources with the service collection that will be available via DI.
         /// Registers services/resources with the service collection that will be available via DI.
         /// </summary>
         /// </summary>
-        protected async Task RegisterServices(IServiceCollection serviceCollection, IConfiguration startupConfig)
+        protected virtual void RegisterServices(IServiceCollection serviceCollection)
         {
         {
+            serviceCollection.AddSingleton(_startupOptions);
+
             serviceCollection.AddMemoryCache();
             serviceCollection.AddMemoryCache();
 
 
             serviceCollection.AddSingleton(ConfigurationManager);
             serviceCollection.AddSingleton(ConfigurationManager);
@@ -651,240 +537,169 @@ namespace Emby.Server.Implementations
 
 
             serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
             serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
 
 
-            serviceCollection.AddSingleton(JsonSerializer);
+            serviceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
 
 
-            // TODO: Support for injecting ILogger should be deprecated in favour of ILogger<T> and this removed
-            serviceCollection.AddSingleton<ILogger>(Logger);
+            // TODO: Remove support for injecting ILogger completely
+            serviceCollection.AddSingleton((provider) =>
+            {
+                Logger.LogWarning("Injecting ILogger directly is deprecated and should be replaced with ILogger<T>");
+                return Logger;
+            });
 
 
-            serviceCollection.AddSingleton(FileSystemManager);
+            serviceCollection.AddSingleton(_fileSystemManager);
             serviceCollection.AddSingleton<TvdbClientManager>();
             serviceCollection.AddSingleton<TvdbClientManager>();
 
 
-            HttpClient = new HttpClientManager.HttpClientManager(
-                ApplicationPaths,
-                LoggerFactory.CreateLogger<HttpClientManager.HttpClientManager>(),
-                FileSystemManager,
-                () => ApplicationUserAgent);
-            serviceCollection.AddSingleton(HttpClient);
+            serviceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>();
 
 
-            serviceCollection.AddSingleton(NetworkManager);
+            serviceCollection.AddSingleton(_networkManager);
 
 
-            IsoManager = new IsoManager();
-            serviceCollection.AddSingleton(IsoManager);
+            serviceCollection.AddSingleton<IIsoManager, IsoManager>();
 
 
-            TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LoggerFactory, FileSystemManager);
-            serviceCollection.AddSingleton(TaskManager);
+            serviceCollection.AddSingleton<ITaskManager, TaskManager>();
 
 
-            serviceCollection.AddSingleton(XmlSerializer);
+            serviceCollection.AddSingleton(_xmlSerializer);
 
 
-            serviceCollection.AddSingleton(typeof(IStreamHelper), typeof(StreamHelper));
+            serviceCollection.AddSingleton<IStreamHelper, StreamHelper>();
 
 
-            var cryptoProvider = new CryptographyProvider();
-            serviceCollection.AddSingleton<ICryptoProvider>(cryptoProvider);
+            serviceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
 
 
-            SocketFactory = new SocketFactory();
-            serviceCollection.AddSingleton(SocketFactory);
+            serviceCollection.AddSingleton<ISocketFactory, SocketFactory>();
 
 
-            serviceCollection.AddSingleton(typeof(IInstallationManager), typeof(InstallationManager));
+            serviceCollection.AddSingleton<IInstallationManager, InstallationManager>();
 
 
-            serviceCollection.AddSingleton(typeof(IZipClient), typeof(ZipClient));
+            serviceCollection.AddSingleton<IZipClient, ZipClient>();
 
 
-            serviceCollection.AddSingleton(typeof(IHttpResultFactory), typeof(HttpResultFactory));
+            serviceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>();
 
 
             serviceCollection.AddSingleton<IServerApplicationHost>(this);
             serviceCollection.AddSingleton<IServerApplicationHost>(this);
             serviceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
             serviceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
 
 
             serviceCollection.AddSingleton(ServerConfigurationManager);
             serviceCollection.AddSingleton(ServerConfigurationManager);
 
 
-            LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, LoggerFactory.CreateLogger<LocalizationManager>());
-            await LocalizationManager.LoadAll().ConfigureAwait(false);
-            serviceCollection.AddSingleton<ILocalizationManager>(LocalizationManager);
-
-            serviceCollection.AddSingleton<IBlurayExaminer>(new BdInfoExaminer(FileSystemManager));
+            serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
 
 
-            UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager);
-            serviceCollection.AddSingleton(UserDataManager);
+            serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
 
 
-            _displayPreferencesRepository = new SqliteDisplayPreferencesRepository(
-                LoggerFactory.CreateLogger<SqliteDisplayPreferencesRepository>(),
-                ApplicationPaths,
-                FileSystemManager);
-            serviceCollection.AddSingleton<IDisplayPreferencesRepository>(_displayPreferencesRepository);
+            serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
+            serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
 
 
-            ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, LoggerFactory.CreateLogger<SqliteItemRepository>(), LocalizationManager);
-            serviceCollection.AddSingleton<IItemRepository>(ItemRepository);
+            serviceCollection.AddSingleton<IDisplayPreferencesRepository, SqliteDisplayPreferencesRepository>();
 
 
-            AuthenticationRepository = GetAuthenticationRepository();
-            serviceCollection.AddSingleton(AuthenticationRepository);
+            serviceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
 
 
-            _userRepository = GetUserRepository();
+            serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
 
 
-            UserManager = new UserManager(
-                LoggerFactory.CreateLogger<UserManager>(),
-                _userRepository,
-                XmlSerializer,
-                NetworkManager,
-                () => ImageProcessor,
-                () => DtoService,
-                this,
-                JsonSerializer,
-                FileSystemManager,
-                cryptoProvider);
+            serviceCollection.AddSingleton<IUserRepository, SqliteUserRepository>();
 
 
-            serviceCollection.AddSingleton(UserManager);
+            // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
+            serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
+            serviceCollection.AddSingleton<IUserManager, UserManager>();
 
 
-            MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(
-                LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(),
-                ServerConfigurationManager,
-                FileSystemManager,
-                LocalizationManager,
-                () => SubtitleEncoder,
-                startupConfig,
-                StartupOptions.FFmpegPath);
-            serviceCollection.AddSingleton(MediaEncoder);
+            // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
+            // TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
+            serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
+            serviceCollection.AddSingleton<IMediaEncoder>(provider =>
+                ActivatorUtilities.CreateInstance<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(provider, _startupOptions.FFmpegPath ?? string.Empty));
 
 
-            LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager, MediaEncoder);
-            serviceCollection.AddSingleton(LibraryManager);
+            // TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
+            serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
+            serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
+            serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
+            serviceCollection.AddSingleton<ILibraryManager, LibraryManager>();
 
 
-            var musicManager = new MusicManager(LibraryManager);
-            serviceCollection.AddSingleton<IMusicManager>(musicManager);
+            serviceCollection.AddSingleton<IMusicManager, MusicManager>();
 
 
-            LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager);
-            serviceCollection.AddSingleton(LibraryMonitor);
+            serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
 
 
-            serviceCollection.AddSingleton<ISearchEngine>(new SearchEngine(LoggerFactory, LibraryManager, UserManager));
-
-            CertificateInfo = GetCertificateInfo(true);
-            Certificate = GetCertificate(CertificateInfo);
+            serviceCollection.AddSingleton<ISearchEngine, SearchEngine>();
 
 
             serviceCollection.AddSingleton<ServiceController>();
             serviceCollection.AddSingleton<ServiceController>();
             serviceCollection.AddSingleton<IHttpListener, WebSocketSharpListener>();
             serviceCollection.AddSingleton<IHttpListener, WebSocketSharpListener>();
             serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
             serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
 
 
-            ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger<ImageProcessor>(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
-            serviceCollection.AddSingleton(ImageProcessor);
-
-            TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager);
-            serviceCollection.AddSingleton(TVSeriesManager);
-
-            DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager);
-            serviceCollection.AddSingleton(DeviceManager);
-
-            MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder);
-            serviceCollection.AddSingleton(MediaSourceManager);
-
-            SubtitleManager = new SubtitleManager(LoggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager);
-            serviceCollection.AddSingleton(SubtitleManager);
-
-            ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LoggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer);
-            serviceCollection.AddSingleton(ProviderManager);
-
-            DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager);
-            serviceCollection.AddSingleton(DtoService);
-
-            ChannelManager = new ChannelManager(
-                UserManager,
-                DtoService,
-                LibraryManager,
-                LoggerFactory.CreateLogger<ChannelManager>(),
-                ServerConfigurationManager,
-                FileSystemManager,
-                UserDataManager,
-                JsonSerializer,
-                ProviderManager);
-            serviceCollection.AddSingleton(ChannelManager);
-
-            SessionManager = new SessionManager(
-                LoggerFactory.CreateLogger<SessionManager>(),
-                UserDataManager,
-                LibraryManager,
-                UserManager,
-                musicManager,
-                DtoService,
-                ImageProcessor,
-                this,
-                AuthenticationRepository,
-                DeviceManager,
-                MediaSourceManager);
-            serviceCollection.AddSingleton(SessionManager);
-
-            serviceCollection.AddSingleton<IDlnaManager>(
-                new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this));
-
-            CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LoggerFactory, ProviderManager);
-            serviceCollection.AddSingleton(CollectionManager);
-
-            serviceCollection.AddSingleton(typeof(IPlaylistManager), typeof(PlaylistManager));
-
-            LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager);
-            serviceCollection.AddSingleton(LiveTvManager);
-
-            UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager);
-            serviceCollection.AddSingleton(UserViewManager);
-
-            NotificationManager = new NotificationManager(
-                LoggerFactory.CreateLogger<NotificationManager>(),
-                UserManager,
-                ServerConfigurationManager);
-            serviceCollection.AddSingleton(NotificationManager);
-
-            serviceCollection.AddSingleton<IDeviceDiscovery>(new DeviceDiscovery(ServerConfigurationManager));
-
-            ChapterManager = new ChapterManager(ItemRepository);
-            serviceCollection.AddSingleton(ChapterManager);
-
-            EncodingManager = new MediaEncoder.EncodingManager(
-                LoggerFactory.CreateLogger<MediaEncoder.EncodingManager>(),
-                FileSystemManager,
-                MediaEncoder,
-                ChapterManager,
-                LibraryManager);
-            serviceCollection.AddSingleton(EncodingManager);
-
-            var activityLogRepo = GetActivityLogRepository();
-            serviceCollection.AddSingleton(activityLogRepo);
-            serviceCollection.AddSingleton<IActivityManager>(new ActivityManager(activityLogRepo, UserManager));
-
-            var authContext = new AuthorizationContext(AuthenticationRepository, UserManager);
-            serviceCollection.AddSingleton<IAuthorizationContext>(authContext);
-            serviceCollection.AddSingleton<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
-
-            AuthService = new AuthService(LoggerFactory.CreateLogger<AuthService>(), authContext, ServerConfigurationManager, SessionManager, NetworkManager);
-            serviceCollection.AddSingleton(AuthService);
-
-            SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(
-                LibraryManager,
-                LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>(),
-                ApplicationPaths,
-                FileSystemManager,
-                MediaEncoder,
-                HttpClient,
-                MediaSourceManager);
-            serviceCollection.AddSingleton(SubtitleEncoder);
-
-            serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager));
-            serviceCollection.AddSingleton<EncodingHelper>();
+            serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
 
 
-            serviceCollection.AddSingleton(typeof(IAttachmentExtractor), typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor));
+            serviceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
 
 
-            _displayPreferencesRepository.Initialize();
+            serviceCollection.AddSingleton<IDeviceManager, DeviceManager>();
 
 
-            var userDataRepo = new SqliteUserDataRepository(LoggerFactory.CreateLogger<SqliteUserDataRepository>(), ApplicationPaths);
+            serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
 
 
-            SetStaticProperties();
+            serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
+
+            serviceCollection.AddSingleton<IProviderManager, ProviderManager>();
+
+            // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
+            serviceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>));
+            serviceCollection.AddSingleton<IDtoService, DtoService>();
+
+            serviceCollection.AddSingleton<IChannelManager, ChannelManager>();
+
+            serviceCollection.AddSingleton<ISessionManager, SessionManager>();
 
 
-            ((UserManager)UserManager).Initialize();
+            serviceCollection.AddSingleton<IDlnaManager, DlnaManager>();
 
 
-            ((UserDataManager)UserDataManager).Repository = userDataRepo;
-            ItemRepository.Initialize(userDataRepo, UserManager);
-            ((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
+            serviceCollection.AddSingleton<ICollectionManager, CollectionManager>();
+
+            serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
+
+            serviceCollection.AddSingleton<LiveTvDtoService>();
+            serviceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
+
+            serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
+
+            serviceCollection.AddSingleton<INotificationManager, NotificationManager>();
+
+            serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
+
+            serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
+
+            serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
+
+            serviceCollection.AddSingleton<IActivityRepository, ActivityRepository>();
+            serviceCollection.AddSingleton<IActivityManager, ActivityManager>();
+
+            serviceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>();
+            serviceCollection.AddSingleton<ISessionContext, SessionContext>();
+
+            serviceCollection.AddSingleton<IAuthService, AuthService>();
+
+            serviceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
+
+            serviceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>();
+            serviceCollection.AddSingleton<EncodingHelper>();
+
+            serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Create services registered with the service container that need to be initialized at application startup.
         /// Create services registered with the service container that need to be initialized at application startup.
         /// </summary>
         /// </summary>
-        public void InitializeServices()
+        /// <returns>A task representing the service initialization operation.</returns>
+        public async Task InitializeServices()
         {
         {
-            HttpServer = Resolve<IHttpServer>();
+            var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>();
+            await localizationManager.LoadAll().ConfigureAwait(false);
+
+            _mediaEncoder = Resolve<IMediaEncoder>();
+            _sessionManager = Resolve<ISessionManager>();
+            _httpServer = Resolve<IHttpServer>();
+            _httpClient = Resolve<IHttpClient>();
+
+            ((SqliteDisplayPreferencesRepository)Resolve<IDisplayPreferencesRepository>()).Initialize();
+            ((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize();
+            ((SqliteUserRepository)Resolve<IUserRepository>()).Initialize();
+            ((ActivityRepository)Resolve<IActivityRepository>()).Initialize();
+
+            SetStaticProperties();
+
+            var userManager = (UserManager)Resolve<IUserManager>();
+            userManager.Initialize();
+
+            var userDataRepo = (SqliteUserDataRepository)Resolve<IUserDataRepository>();
+            ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, userManager);
+
+            FindParts();
         }
         }
 
 
         public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths)
         public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths)
@@ -953,75 +768,38 @@ namespace Emby.Server.Implementations
             }
             }
         }
         }
 
 
-        /// <summary>
-        /// Gets the user repository.
-        /// </summary>
-        /// <returns><see cref="Task{SqliteUserRepository}" />.</returns>
-        private SqliteUserRepository GetUserRepository()
-        {
-            var repo = new SqliteUserRepository(
-                LoggerFactory.CreateLogger<SqliteUserRepository>(),
-                ApplicationPaths);
-
-            repo.Initialize();
-
-            return repo;
-        }
-
-        private IAuthenticationRepository GetAuthenticationRepository()
-        {
-            var repo = new AuthenticationRepository(LoggerFactory, ServerConfigurationManager);
-
-            repo.Initialize();
-
-            return repo;
-        }
-
-        private IActivityRepository GetActivityLogRepository()
-        {
-            var repo = new ActivityRepository(LoggerFactory.CreateLogger<ActivityRepository>(), ServerConfigurationManager.ApplicationPaths, FileSystemManager);
-
-            repo.Initialize();
-
-            return repo;
-        }
-
         /// <summary>
         /// <summary>
         /// Dirty hacks.
         /// Dirty hacks.
         /// </summary>
         /// </summary>
         private void SetStaticProperties()
         private void SetStaticProperties()
         {
         {
-            ItemRepository.ImageProcessor = ImageProcessor;
-
             // For now there's no real way to inject these properly
             // For now there's no real way to inject these properly
-            BaseItem.Logger = LoggerFactory.CreateLogger("BaseItem");
+            BaseItem.Logger = Resolve<ILogger<BaseItem>>();
             BaseItem.ConfigurationManager = ServerConfigurationManager;
             BaseItem.ConfigurationManager = ServerConfigurationManager;
-            BaseItem.LibraryManager = LibraryManager;
-            BaseItem.ProviderManager = ProviderManager;
-            BaseItem.LocalizationManager = LocalizationManager;
-            BaseItem.ItemRepository = ItemRepository;
-            User.UserManager = UserManager;
-            BaseItem.FileSystem = FileSystemManager;
-            BaseItem.UserDataManager = UserDataManager;
-            BaseItem.ChannelManager = ChannelManager;
-            Video.LiveTvManager = LiveTvManager;
-            Folder.UserViewManager = UserViewManager;
-            UserView.TVSeriesManager = TVSeriesManager;
-            UserView.CollectionManager = CollectionManager;
-            BaseItem.MediaSourceManager = MediaSourceManager;
-            CollectionFolder.XmlSerializer = XmlSerializer;
-            CollectionFolder.JsonSerializer = JsonSerializer;
+            BaseItem.LibraryManager = Resolve<ILibraryManager>();
+            BaseItem.ProviderManager = Resolve<IProviderManager>();
+            BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
+            BaseItem.ItemRepository = Resolve<IItemRepository>();
+            User.UserManager = Resolve<IUserManager>();
+            BaseItem.FileSystem = _fileSystemManager;
+            BaseItem.UserDataManager = Resolve<IUserDataManager>();
+            BaseItem.ChannelManager = Resolve<IChannelManager>();
+            Video.LiveTvManager = Resolve<ILiveTvManager>();
+            Folder.UserViewManager = Resolve<IUserViewManager>();
+            UserView.TVSeriesManager = Resolve<ITVSeriesManager>();
+            UserView.CollectionManager = Resolve<ICollectionManager>();
+            BaseItem.MediaSourceManager = Resolve<IMediaSourceManager>();
+            CollectionFolder.XmlSerializer = _xmlSerializer;
+            CollectionFolder.JsonSerializer = Resolve<IJsonSerializer>();
             CollectionFolder.ApplicationHost = this;
             CollectionFolder.ApplicationHost = this;
-            AuthenticatedAttribute.AuthService = AuthService;
+            AuthenticatedAttribute.AuthService = Resolve<IAuthService>();
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Finds the parts.
+        /// Finds plugin components and register them with the appropriate services.
         /// </summary>
         /// </summary>
-        public void FindParts()
+        private void FindParts()
         {
         {
-            InstallationManager = ServiceProvider.GetService<IInstallationManager>();
-
             if (!ServerConfigurationManager.Configuration.IsPortAuthorized)
             if (!ServerConfigurationManager.Configuration.IsPortAuthorized)
             {
             {
                 ServerConfigurationManager.Configuration.IsPortAuthorized = true;
                 ServerConfigurationManager.Configuration.IsPortAuthorized = true;
@@ -1034,34 +812,34 @@ namespace Emby.Server.Implementations
                         .Where(i => i != null)
                         .Where(i => i != null)
                         .ToArray();
                         .ToArray();
 
 
-            HttpServer.Init(GetExportTypes<IService>(), GetExports<IWebSocketListener>(), GetUrlPrefixes());
+            _httpServer.Init(GetExportTypes<IService>(), GetExports<IWebSocketListener>(), GetUrlPrefixes());
 
 
-            LibraryManager.AddParts(
+            Resolve<ILibraryManager>().AddParts(
                 GetExports<IResolverIgnoreRule>(),
                 GetExports<IResolverIgnoreRule>(),
                 GetExports<IItemResolver>(),
                 GetExports<IItemResolver>(),
                 GetExports<IIntroProvider>(),
                 GetExports<IIntroProvider>(),
                 GetExports<IBaseItemComparer>(),
                 GetExports<IBaseItemComparer>(),
                 GetExports<ILibraryPostScanTask>());
                 GetExports<ILibraryPostScanTask>());
 
 
-            ProviderManager.AddParts(
+            Resolve<IProviderManager>().AddParts(
                 GetExports<IImageProvider>(),
                 GetExports<IImageProvider>(),
                 GetExports<IMetadataService>(),
                 GetExports<IMetadataService>(),
                 GetExports<IMetadataProvider>(),
                 GetExports<IMetadataProvider>(),
                 GetExports<IMetadataSaver>(),
                 GetExports<IMetadataSaver>(),
                 GetExports<IExternalId>());
                 GetExports<IExternalId>());
 
 
-            LiveTvManager.AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>());
+            Resolve<ILiveTvManager>().AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>());
 
 
-            SubtitleManager.AddParts(GetExports<ISubtitleProvider>());
+            Resolve<ISubtitleManager>().AddParts(GetExports<ISubtitleProvider>());
 
 
-            ChannelManager.AddParts(GetExports<IChannel>());
+            Resolve<IChannelManager>().AddParts(GetExports<IChannel>());
 
 
-            MediaSourceManager.AddParts(GetExports<IMediaSourceProvider>());
+            Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>());
 
 
-            NotificationManager.AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>());
-            UserManager.AddParts(GetExports<IAuthenticationProvider>(), GetExports<IPasswordResetProvider>());
+            Resolve<INotificationManager>().AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>());
+            Resolve<IUserManager>().AddParts(GetExports<IAuthenticationProvider>(), GetExports<IPasswordResetProvider>());
 
 
-            IsoManager.AddParts(GetExports<IIsoMounter>());
+            Resolve<IIsoManager>().AddParts(GetExports<IIsoMounter>());
         }
         }
 
 
         private IPlugin LoadPlugin(IPlugin plugin)
         private IPlugin LoadPlugin(IPlugin plugin)
@@ -1168,16 +946,6 @@ namespace Emby.Server.Implementations
             });
             });
         }
         }
 
 
-        private CertificateInfo GetCertificateInfo(bool generateCertificate)
-        {
-            // Custom cert
-            return new CertificateInfo
-            {
-                Path = ServerConfigurationManager.Configuration.CertificatePath,
-                Password = ServerConfigurationManager.Configuration.CertificatePassword
-            };
-        }
-
         /// <summary>
         /// <summary>
         /// Called when [configuration updated].
         /// Called when [configuration updated].
         /// </summary>
         /// </summary>
@@ -1204,14 +972,13 @@ namespace Emby.Server.Implementations
                 }
                 }
             }
             }
 
 
-            if (!HttpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase))
+            if (!_httpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase))
             {
             {
                 requiresRestart = true;
                 requiresRestart = true;
             }
             }
 
 
             var currentCertPath = CertificateInfo?.Path;
             var currentCertPath = CertificateInfo?.Path;
-            var newCertInfo = GetCertificateInfo(false);
-            var newCertPath = newCertInfo?.Path;
+            var newCertPath = ServerConfigurationManager.Configuration.CertificatePath;
 
 
             if (!string.Equals(currentCertPath, newCertPath, StringComparison.OrdinalIgnoreCase))
             if (!string.Equals(currentCertPath, newCertPath, StringComparison.OrdinalIgnoreCase))
             {
             {
@@ -1264,7 +1031,7 @@ namespace Emby.Server.Implementations
             {
             {
                 try
                 try
                 {
                 {
-                    await SessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false);
+                    await _sessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false);
                 }
                 }
                 catch (Exception ex)
                 catch (Exception ex)
                 {
                 {
@@ -1368,7 +1135,7 @@ namespace Emby.Server.Implementations
                 IsShuttingDown = IsShuttingDown,
                 IsShuttingDown = IsShuttingDown,
                 Version = ApplicationVersionString,
                 Version = ApplicationVersionString,
                 WebSocketPortNumber = HttpPort,
                 WebSocketPortNumber = HttpPort,
-                CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(),
+                CompletedInstallations = Resolve<IInstallationManager>().CompletedInstallations.ToArray(),
                 Id = SystemId,
                 Id = SystemId,
                 ProgramDataPath = ApplicationPaths.ProgramDataPath,
                 ProgramDataPath = ApplicationPaths.ProgramDataPath,
                 WebPath = ApplicationPaths.WebPath,
                 WebPath = ApplicationPaths.WebPath,
@@ -1388,15 +1155,14 @@ namespace Emby.Server.Implementations
                 ServerName = FriendlyName,
                 ServerName = FriendlyName,
                 LocalAddress = localAddress,
                 LocalAddress = localAddress,
                 SupportsLibraryMonitor = true,
                 SupportsLibraryMonitor = true,
-                EncoderLocation = MediaEncoder.EncoderLocation,
+                EncoderLocation = _mediaEncoder.EncoderLocation,
                 SystemArchitecture = RuntimeInformation.OSArchitecture,
                 SystemArchitecture = RuntimeInformation.OSArchitecture,
-                SystemUpdateLevel = SystemUpdateLevel,
-                PackageName = StartupOptions.PackageName
+                PackageName = _startupOptions.PackageName
             };
             };
         }
         }
 
 
         public IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo()
         public IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo()
-            => NetworkManager.GetMacAddresses()
+            => _networkManager.GetMacAddresses()
                 .Select(i => new WakeOnLanInfo(i))
                 .Select(i => new WakeOnLanInfo(i))
                 .ToList();
                 .ToList();
 
 
@@ -1508,7 +1274,7 @@ namespace Emby.Server.Implementations
 
 
             if (addresses.Count == 0)
             if (addresses.Count == 0)
             {
             {
-                addresses.AddRange(NetworkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
+                addresses.AddRange(_networkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
             }
             }
 
 
             var resultList = new List<IPAddress>();
             var resultList = new List<IPAddress>();
@@ -1575,7 +1341,7 @@ namespace Emby.Server.Implementations
 
 
             try
             try
             {
             {
-                using (var response = await HttpClient.SendAsync(
+                using (var response = await _httpClient.SendAsync(
                     new HttpRequestOptions
                     new HttpRequestOptions
                     {
                     {
                         Url = apiUrl,
                         Url = apiUrl,
@@ -1628,7 +1394,7 @@ namespace Emby.Server.Implementations
 
 
             try
             try
             {
             {
-                await SessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false);
+                await _sessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false);
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
@@ -1749,14 +1515,8 @@ namespace Emby.Server.Implementations
                         Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name);
                         Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name);
                     }
                     }
                 }
                 }
-
-                _userRepository?.Dispose();
-                _displayPreferencesRepository?.Dispose();
             }
             }
 
 
-            _userRepository = null;
-            _displayPreferencesRepository = null;
-
             _disposed = true;
             _disposed = true;
         }
         }
     }
     }

+ 1 - 1
Emby.Server.Implementations/ConfigurationOptions.cs

@@ -18,7 +18,7 @@ namespace Emby.Server.Implementations
         {
         {
             { HostWebClientKey, bool.TrueString },
             { HostWebClientKey, bool.TrueString },
             { HttpListenerHost.DefaultRedirectKey, "web/index.html" },
             { HttpListenerHost.DefaultRedirectKey, "web/index.html" },
-            { InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest.json" },
+            { InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest-stable.json" },
             { FfmpegProbeSizeKey, "1G" },
             { FfmpegProbeSizeKey, "1G" },
             { FfmpegAnalyzeDurationKey, "200M" },
             { FfmpegAnalyzeDurationKey, "200M" },
             { PlaylistsAllowDuplicatesKey, bool.TrueString }
             { PlaylistsAllowDuplicatesKey, bool.TrueString }

+ 13 - 7
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -39,12 +39,11 @@ namespace Emby.Server.Implementations.Data
     {
     {
         private const string ChaptersTableName = "Chapters2";
         private const string ChaptersTableName = "Chapters2";
 
 
-        /// <summary>
-        /// The _app paths
-        /// </summary>
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly IServerApplicationHost _appHost;
         private readonly IServerApplicationHost _appHost;
         private readonly ILocalizationManager _localization;
         private readonly ILocalizationManager _localization;
+        // TODO: Remove this dependency. GetImageCacheTag() is the only method used and it can be converted to a static helper method
+        private readonly IImageProcessor _imageProcessor;
 
 
         private readonly TypeMapper _typeMapper;
         private readonly TypeMapper _typeMapper;
         private readonly JsonSerializerOptions _jsonOptions;
         private readonly JsonSerializerOptions _jsonOptions;
@@ -71,7 +70,8 @@ namespace Emby.Server.Implementations.Data
             IServerConfigurationManager config,
             IServerConfigurationManager config,
             IServerApplicationHost appHost,
             IServerApplicationHost appHost,
             ILogger<SqliteItemRepository> logger,
             ILogger<SqliteItemRepository> logger,
-            ILocalizationManager localization)
+            ILocalizationManager localization,
+            IImageProcessor imageProcessor)
             : base(logger)
             : base(logger)
         {
         {
             if (config == null)
             if (config == null)
@@ -82,6 +82,7 @@ namespace Emby.Server.Implementations.Data
             _config = config;
             _config = config;
             _appHost = appHost;
             _appHost = appHost;
             _localization = localization;
             _localization = localization;
+            _imageProcessor = imageProcessor;
 
 
             _typeMapper = new TypeMapper();
             _typeMapper = new TypeMapper();
             _jsonOptions = JsonDefaults.GetOptions();
             _jsonOptions = JsonDefaults.GetOptions();
@@ -98,8 +99,6 @@ namespace Emby.Server.Implementations.Data
         /// <inheritdoc />
         /// <inheritdoc />
         protected override TempStoreMode TempStore => TempStoreMode.Memory;
         protected override TempStoreMode TempStore => TempStoreMode.Memory;
 
 
-        public IImageProcessor ImageProcessor { get; set; }
-
         /// <summary>
         /// <summary>
         /// Opens the connection to the database
         /// Opens the connection to the database
         /// </summary>
         /// </summary>
@@ -1991,7 +1990,14 @@ namespace Emby.Server.Implementations.Data
 
 
                 if (!string.IsNullOrEmpty(chapter.ImagePath))
                 if (!string.IsNullOrEmpty(chapter.ImagePath))
                 {
                 {
-                    chapter.ImageTag = ImageProcessor.GetImageCacheTag(item, chapter);
+                    try
+                    {
+                        chapter.ImageTag = _imageProcessor.GetImageCacheTag(item, chapter);
+                    }
+                    catch (Exception ex)
+                    {
+                        Logger.LogError(ex, "Failed to create image cache tag.");
+                    }
                 }
                 }
             }
             }
 
 

+ 3 - 3
Emby.Server.Implementations/Devices/DeviceManager.cs

@@ -38,10 +38,11 @@ namespace Emby.Server.Implementations.Devices
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
         private readonly ILocalizationManager _localizationManager;
         private readonly ILocalizationManager _localizationManager;
-
         private readonly IAuthenticationRepository _authRepo;
         private readonly IAuthenticationRepository _authRepo;
+        private readonly Dictionary<string, ClientCapabilities> _capabilitiesCache;
 
 
         public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
         public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
+
         public event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded;
         public event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded;
 
 
         private readonly object _cameraUploadSyncLock = new object();
         private readonly object _cameraUploadSyncLock = new object();
@@ -65,10 +66,9 @@ namespace Emby.Server.Implementations.Devices
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _localizationManager = localizationManager;
             _localizationManager = localizationManager;
             _authRepo = authRepo;
             _authRepo = authRepo;
+            _capabilitiesCache = new Dictionary<string, ClientCapabilities>(StringComparer.OrdinalIgnoreCase);
         }
         }
 
 
-
-        private Dictionary<string, ClientCapabilities> _capabilitiesCache = new Dictionary<string, ClientCapabilities>(StringComparer.OrdinalIgnoreCase);
         public void SaveCapabilities(string deviceId, ClientCapabilities capabilities)
         public void SaveCapabilities(string deviceId, ClientCapabilities capabilities)
         {
         {
             var path = Path.Combine(GetDevicePath(deviceId), "capabilities.json");
             var path = Path.Combine(GetDevicePath(deviceId), "capabilities.json");

+ 16 - 14
Emby.Server.Implementations/Dto/DtoService.cs

@@ -38,21 +38,23 @@ namespace Emby.Server.Implementations.Dto
         private readonly IProviderManager _providerManager;
         private readonly IProviderManager _providerManager;
 
 
         private readonly IApplicationHost _appHost;
         private readonly IApplicationHost _appHost;
-        private readonly Func<IMediaSourceManager> _mediaSourceManager;
-        private readonly Func<ILiveTvManager> _livetvManager;
+        private readonly IMediaSourceManager _mediaSourceManager;
+        private readonly Lazy<ILiveTvManager> _livetvManagerFactory;
+
+        private ILiveTvManager LivetvManager => _livetvManagerFactory.Value;
 
 
         public DtoService(
         public DtoService(
-            ILoggerFactory loggerFactory,
+            ILogger<DtoService> logger,
             ILibraryManager libraryManager,
             ILibraryManager libraryManager,
             IUserDataManager userDataRepository,
             IUserDataManager userDataRepository,
             IItemRepository itemRepo,
             IItemRepository itemRepo,
             IImageProcessor imageProcessor,
             IImageProcessor imageProcessor,
             IProviderManager providerManager,
             IProviderManager providerManager,
             IApplicationHost appHost,
             IApplicationHost appHost,
-            Func<IMediaSourceManager> mediaSourceManager,
-            Func<ILiveTvManager> livetvManager)
+            IMediaSourceManager mediaSourceManager,
+            Lazy<ILiveTvManager> livetvManagerFactory)
         {
         {
-            _logger = loggerFactory.CreateLogger(nameof(DtoService));
+            _logger = logger;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _userDataRepository = userDataRepository;
             _userDataRepository = userDataRepository;
             _itemRepo = itemRepo;
             _itemRepo = itemRepo;
@@ -60,7 +62,7 @@ namespace Emby.Server.Implementations.Dto
             _providerManager = providerManager;
             _providerManager = providerManager;
             _appHost = appHost;
             _appHost = appHost;
             _mediaSourceManager = mediaSourceManager;
             _mediaSourceManager = mediaSourceManager;
-            _livetvManager = livetvManager;
+            _livetvManagerFactory = livetvManagerFactory;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -125,12 +127,12 @@ namespace Emby.Server.Implementations.Dto
 
 
             if (programTuples.Count > 0)
             if (programTuples.Count > 0)
             {
             {
-                _livetvManager().AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult();
+                LivetvManager.AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult();
             }
             }
 
 
             if (channelTuples.Count > 0)
             if (channelTuples.Count > 0)
             {
             {
-                _livetvManager().AddChannelInfo(channelTuples, options, user);
+                LivetvManager.AddChannelInfo(channelTuples, options, user);
             }
             }
 
 
             return returnItems;
             return returnItems;
@@ -142,12 +144,12 @@ namespace Emby.Server.Implementations.Dto
             if (item is LiveTvChannel tvChannel)
             if (item is LiveTvChannel tvChannel)
             {
             {
                 var list = new List<(BaseItemDto, LiveTvChannel)>(1) { (dto, tvChannel) };
                 var list = new List<(BaseItemDto, LiveTvChannel)>(1) { (dto, tvChannel) };
-                _livetvManager().AddChannelInfo(list, options, user);
+                LivetvManager.AddChannelInfo(list, options, user);
             }
             }
             else if (item is LiveTvProgram)
             else if (item is LiveTvProgram)
             {
             {
                 var list = new List<(BaseItem, BaseItemDto)>(1) { (item, dto) };
                 var list = new List<(BaseItem, BaseItemDto)>(1) { (item, dto) };
-                var task = _livetvManager().AddInfoToProgramDto(list, options.Fields, user);
+                var task = LivetvManager.AddInfoToProgramDto(list, options.Fields, user);
                 Task.WaitAll(task);
                 Task.WaitAll(task);
             }
             }
 
 
@@ -223,7 +225,7 @@ namespace Emby.Server.Implementations.Dto
             if (item is IHasMediaSources
             if (item is IHasMediaSources
                 && options.ContainsField(ItemFields.MediaSources))
                 && options.ContainsField(ItemFields.MediaSources))
             {
             {
-                dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(item, true, user).ToArray();
+                dto.MediaSources = _mediaSourceManager.GetStaticMediaSources(item, true, user).ToArray();
 
 
                 NormalizeMediaSourceContainers(dto);
                 NormalizeMediaSourceContainers(dto);
             }
             }
@@ -254,7 +256,7 @@ namespace Emby.Server.Implementations.Dto
                 dto.Etag = item.GetEtag(user);
                 dto.Etag = item.GetEtag(user);
             }
             }
 
 
-            var liveTvManager = _livetvManager();
+            var liveTvManager = LivetvManager;
             var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path);
             var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path);
             if (activeRecording != null)
             if (activeRecording != null)
             {
             {
@@ -1045,7 +1047,7 @@ namespace Emby.Server.Implementations.Dto
                     }
                     }
                     else
                     else
                     {
                     {
-                        mediaStreams = _mediaSourceManager().GetStaticMediaSources(item, true)[0].MediaStreams.ToArray();
+                        mediaStreams = _mediaSourceManager.GetStaticMediaSources(item, true)[0].MediaStreams.ToArray();
                     }
                     }
 
 
                     dto.MediaStreams = mediaStreams;
                     dto.MediaStreams = mediaStreams;

+ 5 - 0
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{E383961B-9356-4D5D-8233-9A1079D03055}</ProjectGuid>
+  </PropertyGroup>
+
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" />
     <ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" />
     <ProjectReference Include="..\Emby.Notifications\Emby.Notifications.csproj" />
     <ProjectReference Include="..\Emby.Notifications\Emby.Notifications.csproj" />

+ 31 - 14
Emby.Server.Implementations/EntryPoints/StartupWizard.cs

@@ -16,46 +16,63 @@ namespace Emby.Server.Implementations.EntryPoints
         private readonly IServerApplicationHost _appHost;
         private readonly IServerApplicationHost _appHost;
         private readonly IConfiguration _appConfig;
         private readonly IConfiguration _appConfig;
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
+        private readonly IStartupOptions _startupOptions;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="StartupWizard"/> class.
         /// Initializes a new instance of the <see cref="StartupWizard"/> class.
         /// </summary>
         /// </summary>
         /// <param name="appHost">The application host.</param>
         /// <param name="appHost">The application host.</param>
+        /// <param name="appConfig">The application configuration.</param>
         /// <param name="config">The configuration manager.</param>
         /// <param name="config">The configuration manager.</param>
-        public StartupWizard(IServerApplicationHost appHost, IConfiguration appConfig, IServerConfigurationManager config)
+        /// <param name="startupOptions">The application startup options.</param>
+        public StartupWizard(
+            IServerApplicationHost appHost,
+            IConfiguration appConfig,
+            IServerConfigurationManager config,
+            IStartupOptions startupOptions)
         {
         {
             _appHost = appHost;
             _appHost = appHost;
             _appConfig = appConfig;
             _appConfig = appConfig;
             _config = config;
             _config = config;
+            _startupOptions = startupOptions;
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public Task RunAsync()
         public Task RunAsync()
+        {
+            Run();
+            return Task.CompletedTask;
+        }
+
+        private void Run()
         {
         {
             if (!_appHost.CanLaunchWebBrowser)
             if (!_appHost.CanLaunchWebBrowser)
             {
             {
-                return Task.CompletedTask;
+                return;
             }
             }
 
 
-            if (!_appConfig.HostWebClient())
+            // Always launch the startup wizard if possible when it has not been completed
+            if (!_config.Configuration.IsStartupWizardCompleted && _appConfig.HostWebClient())
             {
             {
-                BrowserLauncher.OpenSwaggerPage(_appHost);
+                BrowserLauncher.OpenWebApp(_appHost);
+                return;
+            }
+
+            // Do nothing if the web app is configured to not run automatically
+            if (!_config.Configuration.AutoRunWebApp || _startupOptions.NoAutoRunWebApp)
+            {
+                return;
             }
             }
-            else if (!_config.Configuration.IsStartupWizardCompleted)
+
+            // Launch the swagger page if the web client is not hosted, otherwise open the web client
+            if (_appConfig.HostWebClient())
             {
             {
                 BrowserLauncher.OpenWebApp(_appHost);
                 BrowserLauncher.OpenWebApp(_appHost);
             }
             }
-            else if (_config.Configuration.AutoRunWebApp)
+            else
             {
             {
-                var options = ((ApplicationHost)_appHost).StartupOptions;
-
-                if (!options.NoAutoRunWebApp)
-                {
-                    BrowserLauncher.OpenWebApp(_appHost);
-                }
+                BrowserLauncher.OpenSwaggerPage(_appHost);
             }
             }
-
-            return Task.CompletedTask;
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />

+ 5 - 4
Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs

@@ -6,6 +6,7 @@ using System.Linq;
 using System.Net;
 using System.Net;
 using System.Net.Http;
 using System.Net.Http;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Common;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
@@ -24,7 +25,7 @@ namespace Emby.Server.Implementations.HttpClientManager
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IApplicationPaths _appPaths;
         private readonly IApplicationPaths _appPaths;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
-        private readonly Func<string> _defaultUserAgentFn;
+        private readonly IApplicationHost _appHost;
 
 
         /// <summary>
         /// <summary>
         /// Holds a dictionary of http clients by host.  Use GetHttpClient(host) to retrieve or create a client for web requests.
         /// Holds a dictionary of http clients by host.  Use GetHttpClient(host) to retrieve or create a client for web requests.
@@ -40,12 +41,12 @@ namespace Emby.Server.Implementations.HttpClientManager
             IApplicationPaths appPaths,
             IApplicationPaths appPaths,
             ILogger<HttpClientManager> logger,
             ILogger<HttpClientManager> logger,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
-            Func<string> defaultUserAgentFn)
+            IApplicationHost appHost)
         {
         {
             _logger = logger ?? throw new ArgumentNullException(nameof(logger));
             _logger = logger ?? throw new ArgumentNullException(nameof(logger));
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _appPaths = appPaths ?? throw new ArgumentNullException(nameof(appPaths));
             _appPaths = appPaths ?? throw new ArgumentNullException(nameof(appPaths));
-            _defaultUserAgentFn = defaultUserAgentFn;
+            _appHost = appHost;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -91,7 +92,7 @@ namespace Emby.Server.Implementations.HttpClientManager
             if (options.EnableDefaultUserAgent
             if (options.EnableDefaultUserAgent
                 && !request.Headers.TryGetValues(HeaderNames.UserAgent, out _))
                 && !request.Headers.TryGetValues(HeaderNames.UserAgent, out _))
             {
             {
-                request.Headers.Add(HeaderNames.UserAgent, _defaultUserAgentFn());
+                request.Headers.Add(HeaderNames.UserAgent, _appHost.ApplicationUserAgent);
             }
             }
 
 
             switch (options.DecompressionMethod)
             switch (options.DecompressionMethod)

+ 58 - 49
Emby.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -14,6 +14,7 @@ using Emby.Server.Implementations.Services;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
+using MediaBrowser.Controller.Authentication;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Events;
@@ -230,7 +231,8 @@ namespace Emby.Server.Implementations.HttpServer
             switch (ex)
             switch (ex)
             {
             {
                 case ArgumentException _: return 400;
                 case ArgumentException _: return 400;
-                case SecurityException _: return 401;
+                case AuthenticationException _: return 401;
+                case SecurityException _: return 403;
                 case DirectoryNotFoundException _:
                 case DirectoryNotFoundException _:
                 case FileNotFoundException _:
                 case FileNotFoundException _:
                 case ResourceNotFoundException _: return 404;
                 case ResourceNotFoundException _: return 404;
@@ -239,55 +241,52 @@ namespace Emby.Server.Implementations.HttpServer
             }
             }
         }
         }
 
 
-        private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace, string urlToLog)
+        private async Task ErrorHandler(Exception ex, IRequest httpReq, int statusCode, string urlToLog)
         {
         {
-            try
-            {
-                ex = GetActualException(ex);
-
-                if (logExceptionStackTrace)
-                {
-                    _logger.LogError(ex, "Error processing request. URL: {Url}", urlToLog);
-                }
-                else
-                {
-                    _logger.LogError("Error processing request: {Message}. URL: {Url}", ex.Message.TrimEnd('.'), urlToLog);
-                }
-
-                var httpRes = httpReq.Response;
+            bool ignoreStackTrace =
+                ex is SocketException
+                || ex is IOException
+                || ex is OperationCanceledException
+                || ex is SecurityException
+                || ex is AuthenticationException
+                || ex is FileNotFoundException;
 
 
-                if (httpRes.HasStarted)
-                {
-                    return;
-                }
+            if (ignoreStackTrace)
+            {
+                _logger.LogError("Error processing request: {Message}. URL: {Url}", ex.Message.TrimEnd('.'), urlToLog);
+            }
+            else
+            {
+                _logger.LogError(ex, "Error processing request. URL: {Url}", urlToLog);
+            }
 
 
-                var statusCode = GetStatusCode(ex);
-                httpRes.StatusCode = statusCode;
+            var httpRes = httpReq.Response;
 
 
-                var errContent = NormalizeExceptionMessage(ex.Message);
-                httpRes.ContentType = "text/plain";
-                httpRes.ContentLength = errContent.Length;
-                await httpRes.WriteAsync(errContent).ConfigureAwait(false);
-            }
-            catch (Exception errorEx)
+            if (httpRes.HasStarted)
             {
             {
-                _logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response). URL: {Url}", urlToLog);
+                return;
             }
             }
+
+            httpRes.StatusCode = statusCode;
+
+            var errContent = NormalizeExceptionMessage(ex) ?? string.Empty;
+            httpRes.ContentType = "text/plain";
+            httpRes.ContentLength = errContent.Length;
+            await httpRes.WriteAsync(errContent).ConfigureAwait(false);
         }
         }
 
 
-        private string NormalizeExceptionMessage(string msg)
+        private string NormalizeExceptionMessage(Exception ex)
         {
         {
-            if (msg == null)
+            // Do not expose the exception message for AuthenticationException
+            if (ex is AuthenticationException)
             {
             {
-                return string.Empty;
+                return null;
             }
             }
 
 
             // Strip any information we don't want to reveal
             // Strip any information we don't want to reveal
-
-            msg = msg.Replace(_config.ApplicationPaths.ProgramSystemPath, string.Empty, StringComparison.OrdinalIgnoreCase);
-            msg = msg.Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase);
-
-            return msg;
+            return ex.Message
+                ?.Replace(_config.ApplicationPaths.ProgramSystemPath, string.Empty, StringComparison.OrdinalIgnoreCase)
+                .Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -536,22 +535,32 @@ namespace Emby.Server.Implementations.HttpServer
                     throw new FileNotFoundException();
                     throw new FileNotFoundException();
                 }
                 }
             }
             }
-            catch (Exception ex)
+            catch (Exception requestEx)
             {
             {
-                // Do not handle exceptions manually when in development mode
-                // The framework-defined development exception page will be returned instead
-                if (_hostEnvironment.IsDevelopment())
+                try
                 {
                 {
-                    throw;
+                    var requestInnerEx = GetActualException(requestEx);
+                    var statusCode = GetStatusCode(requestInnerEx);
+
+                    // Do not handle 500 server exceptions manually when in development mode
+                    // The framework-defined development exception page will be returned instead
+                    if (statusCode == 500 && _hostEnvironment.IsDevelopment())
+                    {
+                        throw;
+                    }
+
+                    await ErrorHandler(requestInnerEx, httpReq, statusCode, urlToLog).ConfigureAwait(false);
                 }
                 }
+                catch (Exception handlerException)
+                {
+                    var aggregateEx = new AggregateException("Error while handling request exception", requestEx, handlerException);
+                    _logger.LogError(aggregateEx, "Error while handling exception in response to {Url}", urlToLog);
 
 
-                bool ignoreStackTrace =
-                    ex is SocketException
-                    || ex is IOException
-                    || ex is OperationCanceledException
-                    || ex is SecurityException
-                    || ex is FileNotFoundException;
-                await ErrorHandler(ex, httpReq, !ignoreStackTrace, urlToLog).ConfigureAwait(false);
+                    if (_hostEnvironment.IsDevelopment())
+                    {
+                        throw aggregateEx;
+                    }
+                }
             }
             }
             finally
             finally
             {
             {

+ 10 - 27
Emby.Server.Implementations/HttpServer/Security/AuthService.cs

@@ -2,6 +2,7 @@
 
 
 using System;
 using System;
 using System.Linq;
 using System.Linq;
+using System.Security.Authentication;
 using Emby.Server.Implementations.SocketSharp;
 using Emby.Server.Implementations.SocketSharp;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
@@ -68,7 +69,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
 
 
             if (user == null && auth.UserId != Guid.Empty)
             if (user == null && auth.UserId != Guid.Empty)
             {
             {
-                throw new SecurityException("User with Id " + auth.UserId + " not found");
+                throw new AuthenticationException("User with Id " + auth.UserId + " not found");
             }
             }
 
 
             if (user != null)
             if (user != null)
@@ -108,18 +109,12 @@ namespace Emby.Server.Implementations.HttpServer.Security
         {
         {
             if (user.Policy.IsDisabled)
             if (user.Policy.IsDisabled)
             {
             {
-                throw new SecurityException("User account has been disabled.")
-                {
-                    SecurityExceptionType = SecurityExceptionType.Unauthenticated
-                };
+                throw new SecurityException("User account has been disabled.");
             }
             }
 
 
             if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp))
             if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp))
             {
             {
-                throw new SecurityException("User account has been disabled.")
-                {
-                    SecurityExceptionType = SecurityExceptionType.Unauthenticated
-                };
+                throw new SecurityException("User account has been disabled.");
             }
             }
 
 
             if (!user.Policy.IsAdministrator
             if (!user.Policy.IsAdministrator
@@ -128,10 +123,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
             {
             {
                 request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl");
                 request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl");
 
 
-                throw new SecurityException("This user account is not allowed access at this time.")
-                {
-                    SecurityExceptionType = SecurityExceptionType.ParentalControl
-                };
+                throw new SecurityException("This user account is not allowed access at this time.");
             }
             }
         }
         }
 
 
@@ -190,10 +182,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
             {
             {
                 if (user == null || !user.Policy.IsAdministrator)
                 if (user == null || !user.Policy.IsAdministrator)
                 {
                 {
-                    throw new SecurityException("User does not have admin access.")
-                    {
-                        SecurityExceptionType = SecurityExceptionType.Unauthenticated
-                    };
+                    throw new SecurityException("User does not have admin access.");
                 }
                 }
             }
             }
 
 
@@ -201,10 +190,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
             {
             {
                 if (user == null || !user.Policy.EnableContentDeletion)
                 if (user == null || !user.Policy.EnableContentDeletion)
                 {
                 {
-                    throw new SecurityException("User does not have delete access.")
-                    {
-                        SecurityExceptionType = SecurityExceptionType.Unauthenticated
-                    };
+                    throw new SecurityException("User does not have delete access.");
                 }
                 }
             }
             }
 
 
@@ -212,10 +198,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
             {
             {
                 if (user == null || !user.Policy.EnableContentDownloading)
                 if (user == null || !user.Policy.EnableContentDownloading)
                 {
                 {
-                    throw new SecurityException("User does not have download access.")
-                    {
-                        SecurityExceptionType = SecurityExceptionType.Unauthenticated
-                    };
+                    throw new SecurityException("User does not have download access.");
                 }
                 }
             }
             }
         }
         }
@@ -230,14 +213,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
         {
         {
             if (string.IsNullOrEmpty(token))
             if (string.IsNullOrEmpty(token))
             {
             {
-                throw new SecurityException("Access token is required.");
+                throw new AuthenticationException("Access token is required.");
             }
             }
 
 
             var info = GetTokenInfo(request);
             var info = GetTokenInfo(request);
 
 
             if (info == null)
             if (info == null)
             {
             {
-                throw new SecurityException("Access token is invalid or expired.");
+                throw new AuthenticationException("Access token is invalid or expired.");
             }
             }
 
 
             //if (!string.IsNullOrEmpty(info.UserId))
             //if (!string.IsNullOrEmpty(info.UserId))

+ 26 - 32
Emby.Server.Implementations/IO/LibraryMonitor.cs

@@ -17,6 +17,11 @@ namespace Emby.Server.Implementations.IO
 {
 {
     public class LibraryMonitor : ILibraryMonitor
     public class LibraryMonitor : ILibraryMonitor
     {
     {
+        private readonly ILogger _logger;
+        private readonly ILibraryManager _libraryManager;
+        private readonly IServerConfigurationManager _configurationManager;
+        private readonly IFileSystem _fileSystem;
+
         /// <summary>
         /// <summary>
         /// The file system watchers.
         /// The file system watchers.
         /// </summary>
         /// </summary>
@@ -113,34 +118,23 @@ namespace Emby.Server.Implementations.IO
                 }
                 }
                 catch (Exception ex)
                 catch (Exception ex)
                 {
                 {
-                    Logger.LogError(ex, "Error in ReportFileSystemChanged for {path}", path);
+                    _logger.LogError(ex, "Error in ReportFileSystemChanged for {path}", path);
                 }
                 }
             }
             }
         }
         }
 
 
-        /// <summary>
-        /// Gets or sets the logger.
-        /// </summary>
-        /// <value>The logger.</value>
-        private ILogger Logger { get; set; }
-
-        private ILibraryManager LibraryManager { get; set; }
-        private IServerConfigurationManager ConfigurationManager { get; set; }
-
-        private readonly IFileSystem _fileSystem;
-
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="LibraryMonitor" /> class.
         /// Initializes a new instance of the <see cref="LibraryMonitor" /> class.
         /// </summary>
         /// </summary>
         public LibraryMonitor(
         public LibraryMonitor(
-            ILoggerFactory loggerFactory,
+            ILogger<LibraryMonitor> logger,
             ILibraryManager libraryManager,
             ILibraryManager libraryManager,
             IServerConfigurationManager configurationManager,
             IServerConfigurationManager configurationManager,
             IFileSystem fileSystem)
             IFileSystem fileSystem)
         {
         {
-            LibraryManager = libraryManager;
-            Logger = loggerFactory.CreateLogger(GetType().Name);
-            ConfigurationManager = configurationManager;
+            _libraryManager = libraryManager;
+            _logger = logger;
+            _configurationManager = configurationManager;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
         }
         }
 
 
@@ -151,7 +145,7 @@ namespace Emby.Server.Implementations.IO
                 return false;
                 return false;
             }
             }
 
 
-            var options = LibraryManager.GetLibraryOptions(item);
+            var options = _libraryManager.GetLibraryOptions(item);
 
 
             if (options != null)
             if (options != null)
             {
             {
@@ -163,12 +157,12 @@ namespace Emby.Server.Implementations.IO
 
 
         public void Start()
         public void Start()
         {
         {
-            LibraryManager.ItemAdded += OnLibraryManagerItemAdded;
-            LibraryManager.ItemRemoved += OnLibraryManagerItemRemoved;
+            _libraryManager.ItemAdded += OnLibraryManagerItemAdded;
+            _libraryManager.ItemRemoved += OnLibraryManagerItemRemoved;
 
 
             var pathsToWatch = new List<string>();
             var pathsToWatch = new List<string>();
 
 
-            var paths = LibraryManager
+            var paths = _libraryManager
                 .RootFolder
                 .RootFolder
                 .Children
                 .Children
                 .Where(IsLibraryMonitorEnabled)
                 .Where(IsLibraryMonitorEnabled)
@@ -261,7 +255,7 @@ namespace Emby.Server.Implementations.IO
             if (!Directory.Exists(path))
             if (!Directory.Exists(path))
             {
             {
                 // Seeing a crash in the mono runtime due to an exception being thrown on a different thread
                 // Seeing a crash in the mono runtime due to an exception being thrown on a different thread
-                Logger.LogInformation("Skipping realtime monitor for {Path} because the path does not exist", path);
+                _logger.LogInformation("Skipping realtime monitor for {Path} because the path does not exist", path);
                 return;
                 return;
             }
             }
 
 
@@ -297,7 +291,7 @@ namespace Emby.Server.Implementations.IO
                     if (_fileSystemWatchers.TryAdd(path, newWatcher))
                     if (_fileSystemWatchers.TryAdd(path, newWatcher))
                     {
                     {
                         newWatcher.EnableRaisingEvents = true;
                         newWatcher.EnableRaisingEvents = true;
-                        Logger.LogInformation("Watching directory " + path);
+                        _logger.LogInformation("Watching directory " + path);
                     }
                     }
                     else
                     else
                     {
                     {
@@ -307,7 +301,7 @@ namespace Emby.Server.Implementations.IO
                 }
                 }
                 catch (Exception ex)
                 catch (Exception ex)
                 {
                 {
-                    Logger.LogError(ex, "Error watching path: {path}", path);
+                    _logger.LogError(ex, "Error watching path: {path}", path);
                 }
                 }
             });
             });
         }
         }
@@ -333,7 +327,7 @@ namespace Emby.Server.Implementations.IO
             {
             {
                 using (watcher)
                 using (watcher)
                 {
                 {
-                    Logger.LogInformation("Stopping directory watching for path {Path}", watcher.Path);
+                    _logger.LogInformation("Stopping directory watching for path {Path}", watcher.Path);
 
 
                     watcher.Created -= OnWatcherChanged;
                     watcher.Created -= OnWatcherChanged;
                     watcher.Deleted -= OnWatcherChanged;
                     watcher.Deleted -= OnWatcherChanged;
@@ -372,7 +366,7 @@ namespace Emby.Server.Implementations.IO
             var ex = e.GetException();
             var ex = e.GetException();
             var dw = (FileSystemWatcher)sender;
             var dw = (FileSystemWatcher)sender;
 
 
-            Logger.LogError(ex, "Error in Directory watcher for: {Path}", dw.Path);
+            _logger.LogError(ex, "Error in Directory watcher for: {Path}", dw.Path);
 
 
             DisposeWatcher(dw, true);
             DisposeWatcher(dw, true);
         }
         }
@@ -390,7 +384,7 @@ namespace Emby.Server.Implementations.IO
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
-                Logger.LogError(ex, "Exception in ReportFileSystemChanged. Path: {FullPath}", e.FullPath);
+                _logger.LogError(ex, "Exception in ReportFileSystemChanged. Path: {FullPath}", e.FullPath);
             }
             }
         }
         }
 
 
@@ -416,13 +410,13 @@ namespace Emby.Server.Implementations.IO
             {
             {
                 if (_fileSystem.AreEqual(i, path))
                 if (_fileSystem.AreEqual(i, path))
                 {
                 {
-                    Logger.LogDebug("Ignoring change to {Path}", path);
+                    _logger.LogDebug("Ignoring change to {Path}", path);
                     return true;
                     return true;
                 }
                 }
 
 
                 if (_fileSystem.ContainsSubPath(i, path))
                 if (_fileSystem.ContainsSubPath(i, path))
                 {
                 {
-                    Logger.LogDebug("Ignoring change to {Path}", path);
+                    _logger.LogDebug("Ignoring change to {Path}", path);
                     return true;
                     return true;
                 }
                 }
 
 
@@ -430,7 +424,7 @@ namespace Emby.Server.Implementations.IO
                 var parent = Path.GetDirectoryName(i);
                 var parent = Path.GetDirectoryName(i);
                 if (!string.IsNullOrEmpty(parent) && _fileSystem.AreEqual(parent, path))
                 if (!string.IsNullOrEmpty(parent) && _fileSystem.AreEqual(parent, path))
                 {
                 {
-                    Logger.LogDebug("Ignoring change to {Path}", path);
+                    _logger.LogDebug("Ignoring change to {Path}", path);
                     return true;
                     return true;
                 }
                 }
 
 
@@ -485,7 +479,7 @@ namespace Emby.Server.Implementations.IO
                     }
                     }
                 }
                 }
 
 
-                var newRefresher = new FileRefresher(path, ConfigurationManager, LibraryManager, Logger);
+                var newRefresher = new FileRefresher(path, _configurationManager, _libraryManager, _logger);
                 newRefresher.Completed += NewRefresher_Completed;
                 newRefresher.Completed += NewRefresher_Completed;
                 _activeRefreshers.Add(newRefresher);
                 _activeRefreshers.Add(newRefresher);
             }
             }
@@ -502,8 +496,8 @@ namespace Emby.Server.Implementations.IO
         /// </summary>
         /// </summary>
         public void Stop()
         public void Stop()
         {
         {
-            LibraryManager.ItemAdded -= OnLibraryManagerItemAdded;
-            LibraryManager.ItemRemoved -= OnLibraryManagerItemRemoved;
+            _libraryManager.ItemAdded -= OnLibraryManagerItemAdded;
+            _libraryManager.ItemRemoved -= OnLibraryManagerItemRemoved;
 
 
             foreach (var watcher in _fileSystemWatchers.Values.ToList())
             foreach (var watcher in _fileSystemWatchers.Values.ToList())
             {
             {

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

@@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.Library
         {
         {
             if (resolvedUser == null)
             if (resolvedUser == null)
             {
             {
-                throw new ArgumentNullException(nameof(resolvedUser));
+                throw new AuthenticationException($"Specified user does not exist.");
             }
             }
 
 
             bool success = false;
             bool success = false;

+ 100 - 129
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -54,9 +54,29 @@ namespace Emby.Server.Implementations.Library
     /// </summary>
     /// </summary>
     public class LibraryManager : ILibraryManager
     public class LibraryManager : ILibraryManager
     {
     {
+        private readonly ILogger _logger;
+        private readonly ITaskManager _taskManager;
+        private readonly IUserManager _userManager;
+        private readonly IUserDataManager _userDataRepository;
+        private readonly IServerConfigurationManager _configurationManager;
+        private readonly Lazy<ILibraryMonitor> _libraryMonitorFactory;
+        private readonly Lazy<IProviderManager> _providerManagerFactory;
+        private readonly Lazy<IUserViewManager> _userviewManagerFactory;
+        private readonly IServerApplicationHost _appHost;
+        private readonly IMediaEncoder _mediaEncoder;
+        private readonly IFileSystem _fileSystem;
+        private readonly IItemRepository _itemRepository;
+        private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
+
         private NamingOptions _namingOptions;
         private NamingOptions _namingOptions;
         private string[] _videoFileExtensions;
         private string[] _videoFileExtensions;
 
 
+        private ILibraryMonitor LibraryMonitor => _libraryMonitorFactory.Value;
+
+        private IProviderManager ProviderManager => _providerManagerFactory.Value;
+
+        private IUserViewManager UserViewManager => _userviewManagerFactory.Value;
+
         /// <summary>
         /// <summary>
         /// Gets or sets the postscan tasks.
         /// Gets or sets the postscan tasks.
         /// </summary>
         /// </summary>
@@ -89,12 +109,6 @@ namespace Emby.Server.Implementations.Library
         /// <value>The comparers.</value>
         /// <value>The comparers.</value>
         private IBaseItemComparer[] Comparers { get; set; }
         private IBaseItemComparer[] Comparers { get; set; }
 
 
-        /// <summary>
-        /// Gets or sets the active item repository
-        /// </summary>
-        /// <value>The item repository.</value>
-        public IItemRepository ItemRepository { get; set; }
-
         /// <summary>
         /// <summary>
         /// Occurs when [item added].
         /// Occurs when [item added].
         /// </summary>
         /// </summary>
@@ -110,90 +124,47 @@ namespace Emby.Server.Implementations.Library
         /// </summary>
         /// </summary>
         public event EventHandler<ItemChangeEventArgs> ItemRemoved;
         public event EventHandler<ItemChangeEventArgs> ItemRemoved;
 
 
-        /// <summary>
-        /// The _logger
-        /// </summary>
-        private readonly ILogger _logger;
-
-        /// <summary>
-        /// The _task manager
-        /// </summary>
-        private readonly ITaskManager _taskManager;
-
-        /// <summary>
-        /// The _user manager
-        /// </summary>
-        private readonly IUserManager _userManager;
-
-        /// <summary>
-        /// The _user data repository
-        /// </summary>
-        private readonly IUserDataManager _userDataRepository;
-
-        /// <summary>
-        /// Gets or sets the configuration manager.
-        /// </summary>
-        /// <value>The configuration manager.</value>
-        private IServerConfigurationManager ConfigurationManager { get; set; }
-
-        private readonly Func<ILibraryMonitor> _libraryMonitorFactory;
-        private readonly Func<IProviderManager> _providerManagerFactory;
-        private readonly Func<IUserViewManager> _userviewManager;
         public bool IsScanRunning { get; private set; }
         public bool IsScanRunning { get; private set; }
 
 
-        private IServerApplicationHost _appHost;
-        private readonly IMediaEncoder _mediaEncoder;
-
-        /// <summary>
-        /// The _library items cache
-        /// </summary>
-        private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
-
-        /// <summary>
-        /// Gets the library items cache.
-        /// </summary>
-        /// <value>The library items cache.</value>
-        private ConcurrentDictionary<Guid, BaseItem> LibraryItemsCache => _libraryItemsCache;
-
-        private readonly IFileSystem _fileSystem;
-
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="LibraryManager" /> class.
         /// Initializes a new instance of the <see cref="LibraryManager" /> class.
         /// </summary>
         /// </summary>
         /// <param name="appHost">The application host</param>
         /// <param name="appHost">The application host</param>
-        /// <param name="loggerFactory">The logger factory.</param>
+        /// <param name="logger">The logger.</param>
         /// <param name="taskManager">The task manager.</param>
         /// <param name="taskManager">The task manager.</param>
         /// <param name="userManager">The user manager.</param>
         /// <param name="userManager">The user manager.</param>
         /// <param name="configurationManager">The configuration manager.</param>
         /// <param name="configurationManager">The configuration manager.</param>
         /// <param name="userDataRepository">The user data repository.</param>
         /// <param name="userDataRepository">The user data repository.</param>
         public LibraryManager(
         public LibraryManager(
             IServerApplicationHost appHost,
             IServerApplicationHost appHost,
-            ILoggerFactory loggerFactory,
+            ILogger<LibraryManager> logger,
             ITaskManager taskManager,
             ITaskManager taskManager,
             IUserManager userManager,
             IUserManager userManager,
             IServerConfigurationManager configurationManager,
             IServerConfigurationManager configurationManager,
             IUserDataManager userDataRepository,
             IUserDataManager userDataRepository,
-            Func<ILibraryMonitor> libraryMonitorFactory,
+            Lazy<ILibraryMonitor> libraryMonitorFactory,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
-            Func<IProviderManager> providerManagerFactory,
-            Func<IUserViewManager> userviewManager,
-            IMediaEncoder mediaEncoder)
+            Lazy<IProviderManager> providerManagerFactory,
+            Lazy<IUserViewManager> userviewManagerFactory,
+            IMediaEncoder mediaEncoder,
+            IItemRepository itemRepository)
         {
         {
             _appHost = appHost;
             _appHost = appHost;
-            _logger = loggerFactory.CreateLogger(nameof(LibraryManager));
+            _logger = logger;
             _taskManager = taskManager;
             _taskManager = taskManager;
             _userManager = userManager;
             _userManager = userManager;
-            ConfigurationManager = configurationManager;
+            _configurationManager = configurationManager;
             _userDataRepository = userDataRepository;
             _userDataRepository = userDataRepository;
             _libraryMonitorFactory = libraryMonitorFactory;
             _libraryMonitorFactory = libraryMonitorFactory;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _providerManagerFactory = providerManagerFactory;
             _providerManagerFactory = providerManagerFactory;
-            _userviewManager = userviewManager;
+            _userviewManagerFactory = userviewManagerFactory;
             _mediaEncoder = mediaEncoder;
             _mediaEncoder = mediaEncoder;
+            _itemRepository = itemRepository;
 
 
             _libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
             _libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
 
 
-            ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated;
+            _configurationManager.ConfigurationUpdated += ConfigurationUpdated;
 
 
             RecordConfigurationValues(configurationManager.Configuration);
             RecordConfigurationValues(configurationManager.Configuration);
         }
         }
@@ -272,7 +243,7 @@ namespace Emby.Server.Implementations.Library
         /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
         /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
         private void ConfigurationUpdated(object sender, EventArgs e)
         private void ConfigurationUpdated(object sender, EventArgs e)
         {
         {
-            var config = ConfigurationManager.Configuration;
+            var config = _configurationManager.Configuration;
 
 
             var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted;
             var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted;
 
 
@@ -306,7 +277,7 @@ namespace Emby.Server.Implementations.Library
                 }
                 }
             }
             }
 
 
-            LibraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
+            _libraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
         }
         }
 
 
         public void DeleteItem(BaseItem item, DeleteOptions options)
         public void DeleteItem(BaseItem item, DeleteOptions options)
@@ -437,10 +408,10 @@ namespace Emby.Server.Implementations.Library
 
 
             item.SetParent(null);
             item.SetParent(null);
 
 
-            ItemRepository.DeleteItem(item.Id);
+            _itemRepository.DeleteItem(item.Id);
             foreach (var child in children)
             foreach (var child in children)
             {
             {
-                ItemRepository.DeleteItem(child.Id);
+                _itemRepository.DeleteItem(child.Id);
             }
             }
 
 
             _libraryItemsCache.TryRemove(item.Id, out BaseItem removed);
             _libraryItemsCache.TryRemove(item.Id, out BaseItem removed);
@@ -509,15 +480,15 @@ namespace Emby.Server.Implementations.Library
                 throw new ArgumentNullException(nameof(type));
                 throw new ArgumentNullException(nameof(type));
             }
             }
 
 
-            if (key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath, StringComparison.Ordinal))
+            if (key.StartsWith(_configurationManager.ApplicationPaths.ProgramDataPath, StringComparison.Ordinal))
             {
             {
                 // Try to normalize paths located underneath program-data in an attempt to make them more portable
                 // Try to normalize paths located underneath program-data in an attempt to make them more portable
-                key = key.Substring(ConfigurationManager.ApplicationPaths.ProgramDataPath.Length)
+                key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length)
                     .TrimStart(new[] { '/', '\\' })
                     .TrimStart(new[] { '/', '\\' })
                     .Replace("/", "\\");
                     .Replace("/", "\\");
             }
             }
 
 
-            if (forceCaseInsensitive || !ConfigurationManager.Configuration.EnableCaseSensitiveItemIds)
+            if (forceCaseInsensitive || !_configurationManager.Configuration.EnableCaseSensitiveItemIds)
             {
             {
                 key = key.ToLowerInvariant();
                 key = key.ToLowerInvariant();
             }
             }
@@ -550,7 +521,7 @@ namespace Emby.Server.Implementations.Library
                 collectionType = GetContentTypeOverride(fullPath, true);
                 collectionType = GetContentTypeOverride(fullPath, true);
             }
             }
 
 
-            var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
+            var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService)
             {
             {
                 Parent = parent,
                 Parent = parent,
                 Path = fullPath,
                 Path = fullPath,
@@ -720,7 +691,7 @@ namespace Emby.Server.Implementations.Library
         /// <exception cref="InvalidOperationException">Cannot create the root folder until plugins have loaded.</exception>
         /// <exception cref="InvalidOperationException">Cannot create the root folder until plugins have loaded.</exception>
         public AggregateFolder CreateRootFolder()
         public AggregateFolder CreateRootFolder()
         {
         {
-            var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath;
+            var rootFolderPath = _configurationManager.ApplicationPaths.RootFolderPath;
 
 
             Directory.CreateDirectory(rootFolderPath);
             Directory.CreateDirectory(rootFolderPath);
 
 
@@ -734,7 +705,7 @@ namespace Emby.Server.Implementations.Library
             }
             }
 
 
             // Add in the plug-in folders
             // Add in the plug-in folders
-            var path = Path.Combine(ConfigurationManager.ApplicationPaths.DataPath, "playlists");
+            var path = Path.Combine(_configurationManager.ApplicationPaths.DataPath, "playlists");
 
 
             Directory.CreateDirectory(path);
             Directory.CreateDirectory(path);
 
 
@@ -786,7 +757,7 @@ namespace Emby.Server.Implementations.Library
                 {
                 {
                     if (_userRootFolder == null)
                     if (_userRootFolder == null)
                     {
                     {
-                        var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+                        var userRootPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
 
 
                         _logger.LogDebug("Creating userRootPath at {path}", userRootPath);
                         _logger.LogDebug("Creating userRootPath at {path}", userRootPath);
                         Directory.CreateDirectory(userRootPath);
                         Directory.CreateDirectory(userRootPath);
@@ -980,7 +951,7 @@ namespace Emby.Server.Implementations.Library
               where T : BaseItem, new()
               where T : BaseItem, new()
         {
         {
             var path = getPathFn(name);
             var path = getPathFn(name);
-            var forceCaseInsensitiveId = ConfigurationManager.Configuration.EnableNormalizedItemByNameIds;
+            var forceCaseInsensitiveId = _configurationManager.Configuration.EnableNormalizedItemByNameIds;
             return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
             return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
         }
         }
 
 
@@ -994,7 +965,7 @@ namespace Emby.Server.Implementations.Library
         public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
         public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
         {
         {
             // Ensure the location is available.
             // Ensure the location is available.
-            Directory.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath);
+            Directory.CreateDirectory(_configurationManager.ApplicationPaths.PeoplePath);
 
 
             return new PeopleValidator(this, _logger, _fileSystem).ValidatePeople(cancellationToken, progress);
             return new PeopleValidator(this, _logger, _fileSystem).ValidatePeople(cancellationToken, progress);
         }
         }
@@ -1031,7 +1002,7 @@ namespace Emby.Server.Implementations.Library
         public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken)
         public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken)
         {
         {
             IsScanRunning = true;
             IsScanRunning = true;
-            _libraryMonitorFactory().Stop();
+            LibraryMonitor.Stop();
 
 
             try
             try
             {
             {
@@ -1039,7 +1010,7 @@ namespace Emby.Server.Implementations.Library
             }
             }
             finally
             finally
             {
             {
-                _libraryMonitorFactory().Start();
+                LibraryMonitor.Start();
                 IsScanRunning = false;
                 IsScanRunning = false;
             }
             }
         }
         }
@@ -1148,7 +1119,7 @@ namespace Emby.Server.Implementations.Library
                 progress.Report(percent * 100);
                 progress.Report(percent * 100);
             }
             }
 
 
-            ItemRepository.UpdateInheritedValues(cancellationToken);
+            _itemRepository.UpdateInheritedValues(cancellationToken);
 
 
             progress.Report(100);
             progress.Report(100);
         }
         }
@@ -1168,9 +1139,9 @@ namespace Emby.Server.Implementations.Library
             var topLibraryFolders = GetUserRootFolder().Children.ToList();
             var topLibraryFolders = GetUserRootFolder().Children.ToList();
 
 
             _logger.LogDebug("Getting refreshQueue");
             _logger.LogDebug("Getting refreshQueue");
-            var refreshQueue = includeRefreshState ? _providerManagerFactory().GetRefreshQueue() : null;
+            var refreshQueue = includeRefreshState ? ProviderManager.GetRefreshQueue() : null;
 
 
-            return _fileSystem.GetDirectoryPaths(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath)
+            return _fileSystem.GetDirectoryPaths(_configurationManager.ApplicationPaths.DefaultUserViewsPath)
                 .Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders, refreshQueue))
                 .Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders, refreshQueue))
                 .ToList();
                 .ToList();
         }
         }
@@ -1245,7 +1216,7 @@ namespace Emby.Server.Implementations.Library
                 throw new ArgumentException("Guid can't be empty", nameof(id));
                 throw new ArgumentException("Guid can't be empty", nameof(id));
             }
             }
 
 
-            if (LibraryItemsCache.TryGetValue(id, out BaseItem item))
+            if (_libraryItemsCache.TryGetValue(id, out BaseItem item))
             {
             {
                 return item;
                 return item;
             }
             }
@@ -1276,7 +1247,7 @@ namespace Emby.Server.Implementations.Library
                 AddUserToQuery(query, query.User, allowExternalContent);
                 AddUserToQuery(query, query.User, allowExternalContent);
             }
             }
 
 
-            return ItemRepository.GetItemList(query);
+            return _itemRepository.GetItemList(query);
         }
         }
 
 
         public List<BaseItem> GetItemList(InternalItemsQuery query)
         public List<BaseItem> GetItemList(InternalItemsQuery query)
@@ -1300,7 +1271,7 @@ namespace Emby.Server.Implementations.Library
                 AddUserToQuery(query, query.User);
                 AddUserToQuery(query, query.User);
             }
             }
 
 
-            return ItemRepository.GetCount(query);
+            return _itemRepository.GetCount(query);
         }
         }
 
 
         public List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
         public List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
@@ -1315,7 +1286,7 @@ namespace Emby.Server.Implementations.Library
                 }
                 }
             }
             }
 
 
-            return ItemRepository.GetItemList(query);
+            return _itemRepository.GetItemList(query);
         }
         }
 
 
         public QueryResult<BaseItem> QueryItems(InternalItemsQuery query)
         public QueryResult<BaseItem> QueryItems(InternalItemsQuery query)
@@ -1327,12 +1298,12 @@ namespace Emby.Server.Implementations.Library
 
 
             if (query.EnableTotalRecordCount)
             if (query.EnableTotalRecordCount)
             {
             {
-                return ItemRepository.GetItems(query);
+                return _itemRepository.GetItems(query);
             }
             }
 
 
             return new QueryResult<BaseItem>
             return new QueryResult<BaseItem>
             {
             {
-                Items = ItemRepository.GetItemList(query).ToArray()
+                Items = _itemRepository.GetItemList(query).ToArray()
             };
             };
         }
         }
 
 
@@ -1343,7 +1314,7 @@ namespace Emby.Server.Implementations.Library
                 AddUserToQuery(query, query.User);
                 AddUserToQuery(query, query.User);
             }
             }
 
 
-            return ItemRepository.GetItemIdsList(query);
+            return _itemRepository.GetItemIdsList(query);
         }
         }
 
 
         public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
         public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
@@ -1354,7 +1325,7 @@ namespace Emby.Server.Implementations.Library
             }
             }
 
 
             SetTopParentOrAncestorIds(query);
             SetTopParentOrAncestorIds(query);
-            return ItemRepository.GetStudios(query);
+            return _itemRepository.GetStudios(query);
         }
         }
 
 
         public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
         public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
@@ -1365,7 +1336,7 @@ namespace Emby.Server.Implementations.Library
             }
             }
 
 
             SetTopParentOrAncestorIds(query);
             SetTopParentOrAncestorIds(query);
-            return ItemRepository.GetGenres(query);
+            return _itemRepository.GetGenres(query);
         }
         }
 
 
         public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
         public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
@@ -1376,7 +1347,7 @@ namespace Emby.Server.Implementations.Library
             }
             }
 
 
             SetTopParentOrAncestorIds(query);
             SetTopParentOrAncestorIds(query);
-            return ItemRepository.GetMusicGenres(query);
+            return _itemRepository.GetMusicGenres(query);
         }
         }
 
 
         public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
         public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
@@ -1387,7 +1358,7 @@ namespace Emby.Server.Implementations.Library
             }
             }
 
 
             SetTopParentOrAncestorIds(query);
             SetTopParentOrAncestorIds(query);
-            return ItemRepository.GetAllArtists(query);
+            return _itemRepository.GetAllArtists(query);
         }
         }
 
 
         public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
         public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
@@ -1398,7 +1369,7 @@ namespace Emby.Server.Implementations.Library
             }
             }
 
 
             SetTopParentOrAncestorIds(query);
             SetTopParentOrAncestorIds(query);
-            return ItemRepository.GetArtists(query);
+            return _itemRepository.GetArtists(query);
         }
         }
 
 
         private void SetTopParentOrAncestorIds(InternalItemsQuery query)
         private void SetTopParentOrAncestorIds(InternalItemsQuery query)
@@ -1439,7 +1410,7 @@ namespace Emby.Server.Implementations.Library
             }
             }
 
 
             SetTopParentOrAncestorIds(query);
             SetTopParentOrAncestorIds(query);
-            return ItemRepository.GetAlbumArtists(query);
+            return _itemRepository.GetAlbumArtists(query);
         }
         }
 
 
         public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query)
         public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query)
@@ -1460,10 +1431,10 @@ namespace Emby.Server.Implementations.Library
 
 
             if (query.EnableTotalRecordCount)
             if (query.EnableTotalRecordCount)
             {
             {
-                return ItemRepository.GetItems(query);
+                return _itemRepository.GetItems(query);
             }
             }
 
 
-            var list = ItemRepository.GetItemList(query);
+            var list = _itemRepository.GetItemList(query);
 
 
             return new QueryResult<BaseItem>
             return new QueryResult<BaseItem>
             {
             {
@@ -1509,7 +1480,7 @@ namespace Emby.Server.Implementations.Library
                 string.IsNullOrEmpty(query.SeriesPresentationUniqueKey) &&
                 string.IsNullOrEmpty(query.SeriesPresentationUniqueKey) &&
                 query.ItemIds.Length == 0)
                 query.ItemIds.Length == 0)
             {
             {
-                var userViews = _userviewManager().GetUserViews(new UserViewQuery
+                var userViews = UserViewManager.GetUserViews(new UserViewQuery
                 {
                 {
                     UserId = user.Id,
                     UserId = user.Id,
                     IncludeHidden = true,
                     IncludeHidden = true,
@@ -1809,7 +1780,7 @@ namespace Emby.Server.Implementations.Library
             // Don't iterate multiple times
             // Don't iterate multiple times
             var itemsList = items.ToList();
             var itemsList = items.ToList();
 
 
-            ItemRepository.SaveItems(itemsList, cancellationToken);
+            _itemRepository.SaveItems(itemsList, cancellationToken);
 
 
             foreach (var item in itemsList)
             foreach (var item in itemsList)
             {
             {
@@ -1846,7 +1817,7 @@ namespace Emby.Server.Implementations.Library
 
 
         public void UpdateImages(BaseItem item)
         public void UpdateImages(BaseItem item)
         {
         {
-            ItemRepository.SaveImages(item);
+            _itemRepository.SaveImages(item);
 
 
             RegisterItem(item);
             RegisterItem(item);
         }
         }
@@ -1863,7 +1834,7 @@ namespace Emby.Server.Implementations.Library
             {
             {
                 if (item.IsFileProtocol)
                 if (item.IsFileProtocol)
                 {
                 {
-                    _providerManagerFactory().SaveMetadata(item, updateReason);
+                    ProviderManager.SaveMetadata(item, updateReason);
                 }
                 }
 
 
                 item.DateLastSaved = DateTime.UtcNow;
                 item.DateLastSaved = DateTime.UtcNow;
@@ -1871,7 +1842,7 @@ namespace Emby.Server.Implementations.Library
                 RegisterItem(item);
                 RegisterItem(item);
             }
             }
 
 
-            ItemRepository.SaveItems(itemsList, cancellationToken);
+            _itemRepository.SaveItems(itemsList, cancellationToken);
 
 
             if (ItemUpdated != null)
             if (ItemUpdated != null)
             {
             {
@@ -1947,7 +1918,7 @@ namespace Emby.Server.Implementations.Library
         /// <returns>BaseItem.</returns>
         /// <returns>BaseItem.</returns>
         public BaseItem RetrieveItem(Guid id)
         public BaseItem RetrieveItem(Guid id)
         {
         {
-            return ItemRepository.RetrieveItem(id);
+            return _itemRepository.RetrieveItem(id);
         }
         }
 
 
         public List<Folder> GetCollectionFolders(BaseItem item)
         public List<Folder> GetCollectionFolders(BaseItem item)
@@ -2066,7 +2037,7 @@ namespace Emby.Server.Implementations.Library
 
 
         private string GetContentTypeOverride(string path, bool inherit)
         private string GetContentTypeOverride(string path, bool inherit)
         {
         {
-            var nameValuePair = ConfigurationManager.Configuration.ContentTypes
+            var nameValuePair = _configurationManager.Configuration.ContentTypes
                                     .FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path)
                                     .FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path)
                                                          || (inherit && !string.IsNullOrEmpty(i.Name)
                                                          || (inherit && !string.IsNullOrEmpty(i.Name)
                                                                      && _fileSystem.ContainsSubPath(i.Name, path)));
                                                                      && _fileSystem.ContainsSubPath(i.Name, path)));
@@ -2115,7 +2086,7 @@ namespace Emby.Server.Implementations.Library
             string sortName)
             string sortName)
         {
         {
             var path = Path.Combine(
             var path = Path.Combine(
-                ConfigurationManager.ApplicationPaths.InternalMetadataPath,
+                _configurationManager.ApplicationPaths.InternalMetadataPath,
                 "views",
                 "views",
                 _fileSystem.GetValidFilename(viewType));
                 _fileSystem.GetValidFilename(viewType));
 
 
@@ -2147,7 +2118,7 @@ namespace Emby.Server.Implementations.Library
             if (refresh)
             if (refresh)
             {
             {
                 item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
                 item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
-                _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
+                ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
             }
             }
 
 
             return item;
             return item;
@@ -2165,7 +2136,7 @@ namespace Emby.Server.Implementations.Library
 
 
             var id = GetNewItemId(idValues, typeof(UserView));
             var id = GetNewItemId(idValues, typeof(UserView));
 
 
-            var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture));
+            var path = Path.Combine(_configurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture));
 
 
             var item = GetItemById(id) as UserView;
             var item = GetItemById(id) as UserView;
 
 
@@ -2202,7 +2173,7 @@ namespace Emby.Server.Implementations.Library
 
 
             if (refresh)
             if (refresh)
             {
             {
-                _providerManagerFactory().QueueRefresh(
+                ProviderManager.QueueRefresh(
                     item.Id,
                     item.Id,
                     new MetadataRefreshOptions(new DirectoryService(_fileSystem))
                     new MetadataRefreshOptions(new DirectoryService(_fileSystem))
                     {
                     {
@@ -2269,7 +2240,7 @@ namespace Emby.Server.Implementations.Library
 
 
             if (refresh)
             if (refresh)
             {
             {
-                _providerManagerFactory().QueueRefresh(
+                ProviderManager.QueueRefresh(
                     item.Id,
                     item.Id,
                     new MetadataRefreshOptions(new DirectoryService(_fileSystem))
                     new MetadataRefreshOptions(new DirectoryService(_fileSystem))
                     {
                     {
@@ -2303,7 +2274,7 @@ namespace Emby.Server.Implementations.Library
 
 
             var id = GetNewItemId(idValues, typeof(UserView));
             var id = GetNewItemId(idValues, typeof(UserView));
 
 
-            var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture));
+            var path = Path.Combine(_configurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture));
 
 
             var item = GetItemById(id) as UserView;
             var item = GetItemById(id) as UserView;
 
 
@@ -2346,7 +2317,7 @@ namespace Emby.Server.Implementations.Library
 
 
             if (refresh)
             if (refresh)
             {
             {
-                _providerManagerFactory().QueueRefresh(
+                ProviderManager.QueueRefresh(
                     item.Id,
                     item.Id,
                     new MetadataRefreshOptions(new DirectoryService(_fileSystem))
                     new MetadataRefreshOptions(new DirectoryService(_fileSystem))
                     {
                     {
@@ -2675,8 +2646,8 @@ namespace Emby.Server.Implementations.Library
                 }
                 }
             }
             }
 
 
-            var metadataPath = ConfigurationManager.Configuration.MetadataPath;
-            var metadataNetworkPath = ConfigurationManager.Configuration.MetadataNetworkPath;
+            var metadataPath = _configurationManager.Configuration.MetadataPath;
+            var metadataNetworkPath = _configurationManager.Configuration.MetadataNetworkPath;
 
 
             if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath))
             if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath))
             {
             {
@@ -2687,7 +2658,7 @@ namespace Emby.Server.Implementations.Library
                 }
                 }
             }
             }
 
 
-            foreach (var map in ConfigurationManager.Configuration.PathSubstitutions)
+            foreach (var map in _configurationManager.Configuration.PathSubstitutions)
             {
             {
                 if (!string.IsNullOrWhiteSpace(map.From))
                 if (!string.IsNullOrWhiteSpace(map.From))
                 {
                 {
@@ -2756,7 +2727,7 @@ namespace Emby.Server.Implementations.Library
 
 
         public List<PersonInfo> GetPeople(InternalPeopleQuery query)
         public List<PersonInfo> GetPeople(InternalPeopleQuery query)
         {
         {
-            return ItemRepository.GetPeople(query);
+            return _itemRepository.GetPeople(query);
         }
         }
 
 
         public List<PersonInfo> GetPeople(BaseItem item)
         public List<PersonInfo> GetPeople(BaseItem item)
@@ -2779,7 +2750,7 @@ namespace Emby.Server.Implementations.Library
 
 
         public List<Person> GetPeopleItems(InternalPeopleQuery query)
         public List<Person> GetPeopleItems(InternalPeopleQuery query)
         {
         {
-            return ItemRepository.GetPeopleNames(query).Select(i =>
+            return _itemRepository.GetPeopleNames(query).Select(i =>
             {
             {
                 try
                 try
                 {
                 {
@@ -2796,7 +2767,7 @@ namespace Emby.Server.Implementations.Library
 
 
         public List<string> GetPeopleNames(InternalPeopleQuery query)
         public List<string> GetPeopleNames(InternalPeopleQuery query)
         {
         {
-            return ItemRepository.GetPeopleNames(query);
+            return _itemRepository.GetPeopleNames(query);
         }
         }
 
 
         public void UpdatePeople(BaseItem item, List<PersonInfo> people)
         public void UpdatePeople(BaseItem item, List<PersonInfo> people)
@@ -2806,7 +2777,7 @@ namespace Emby.Server.Implementations.Library
                 return;
                 return;
             }
             }
 
 
-            ItemRepository.UpdatePeople(item.Id, people);
+            _itemRepository.UpdatePeople(item.Id, people);
         }
         }
 
 
         public async Task<ItemImageInfo> ConvertImageToLocal(BaseItem item, ItemImageInfo image, int imageIndex)
         public async Task<ItemImageInfo> ConvertImageToLocal(BaseItem item, ItemImageInfo image, int imageIndex)
@@ -2817,7 +2788,7 @@ namespace Emby.Server.Implementations.Library
                 {
                 {
                     _logger.LogDebug("ConvertImageToLocal item {0} - image url: {1}", item.Id, url);
                     _logger.LogDebug("ConvertImageToLocal item {0} - image url: {1}", item.Id, url);
 
 
-                    await _providerManagerFactory().SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
+                    await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
 
 
                     item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
                     item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
 
 
@@ -2850,7 +2821,7 @@ namespace Emby.Server.Implementations.Library
 
 
             name = _fileSystem.GetValidFilename(name);
             name = _fileSystem.GetValidFilename(name);
 
 
-            var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+            var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
 
 
             var virtualFolderPath = Path.Combine(rootFolderPath, name);
             var virtualFolderPath = Path.Combine(rootFolderPath, name);
             while (Directory.Exists(virtualFolderPath))
             while (Directory.Exists(virtualFolderPath))
@@ -2869,7 +2840,7 @@ namespace Emby.Server.Implementations.Library
                 }
                 }
             }
             }
 
 
-            _libraryMonitorFactory().Stop();
+            LibraryMonitor.Stop();
 
 
             try
             try
             {
             {
@@ -2904,7 +2875,7 @@ namespace Emby.Server.Implementations.Library
                 {
                 {
                     // Need to add a delay here or directory watchers may still pick up the changes
                     // Need to add a delay here or directory watchers may still pick up the changes
                     await Task.Delay(1000).ConfigureAwait(false);
                     await Task.Delay(1000).ConfigureAwait(false);
-                    _libraryMonitorFactory().Start();
+                    LibraryMonitor.Start();
                 }
                 }
             }
             }
         }
         }
@@ -2964,7 +2935,7 @@ namespace Emby.Server.Implementations.Library
                 throw new FileNotFoundException("The network path does not exist.");
                 throw new FileNotFoundException("The network path does not exist.");
             }
             }
 
 
-            var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+            var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
             var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
             var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
 
 
             var shortcutFilename = Path.GetFileNameWithoutExtension(path);
             var shortcutFilename = Path.GetFileNameWithoutExtension(path);
@@ -3007,7 +2978,7 @@ namespace Emby.Server.Implementations.Library
                 throw new FileNotFoundException("The network path does not exist.");
                 throw new FileNotFoundException("The network path does not exist.");
             }
             }
 
 
-            var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+            var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
             var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
             var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
 
 
             var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath);
             var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath);
@@ -3060,7 +3031,7 @@ namespace Emby.Server.Implementations.Library
                 throw new ArgumentNullException(nameof(name));
                 throw new ArgumentNullException(nameof(name));
             }
             }
 
 
-            var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+            var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
 
 
             var path = Path.Combine(rootFolderPath, name);
             var path = Path.Combine(rootFolderPath, name);
 
 
@@ -3069,7 +3040,7 @@ namespace Emby.Server.Implementations.Library
                 throw new FileNotFoundException("The media folder does not exist");
                 throw new FileNotFoundException("The media folder does not exist");
             }
             }
 
 
-            _libraryMonitorFactory().Stop();
+            LibraryMonitor.Stop();
 
 
             try
             try
             {
             {
@@ -3089,7 +3060,7 @@ namespace Emby.Server.Implementations.Library
                 {
                 {
                     // Need to add a delay here or directory watchers may still pick up the changes
                     // Need to add a delay here or directory watchers may still pick up the changes
                     await Task.Delay(1000).ConfigureAwait(false);
                     await Task.Delay(1000).ConfigureAwait(false);
-                    _libraryMonitorFactory().Start();
+                    LibraryMonitor.Start();
                 }
                 }
             }
             }
         }
         }
@@ -3103,7 +3074,7 @@ namespace Emby.Server.Implementations.Library
 
 
             var removeList = new List<NameValuePair>();
             var removeList = new List<NameValuePair>();
 
 
-            foreach (var contentType in ConfigurationManager.Configuration.ContentTypes)
+            foreach (var contentType in _configurationManager.Configuration.ContentTypes)
             {
             {
                 if (string.IsNullOrWhiteSpace(contentType.Name))
                 if (string.IsNullOrWhiteSpace(contentType.Name))
                 {
                 {
@@ -3118,11 +3089,11 @@ namespace Emby.Server.Implementations.Library
 
 
             if (removeList.Count > 0)
             if (removeList.Count > 0)
             {
             {
-                ConfigurationManager.Configuration.ContentTypes = ConfigurationManager.Configuration.ContentTypes
+                _configurationManager.Configuration.ContentTypes = _configurationManager.Configuration.ContentTypes
                     .Except(removeList)
                     .Except(removeList)
-                        .ToArray();
+                    .ToArray();
 
 
-                ConfigurationManager.SaveConfiguration();
+                _configurationManager.SaveConfiguration();
             }
             }
         }
         }
 
 
@@ -3133,7 +3104,7 @@ namespace Emby.Server.Implementations.Library
                 throw new ArgumentNullException(nameof(mediaPath));
                 throw new ArgumentNullException(nameof(mediaPath));
             }
             }
 
 
-            var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+            var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
             var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
             var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
 
 
             if (!Directory.Exists(virtualFolderPath))
             if (!Directory.Exists(virtualFolderPath))

+ 11 - 11
Emby.Server.Implementations/Library/MediaSourceManager.cs

@@ -33,13 +33,13 @@ namespace Emby.Server.Implementations.Library
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
-
-        private IMediaSourceProvider[] _providers;
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IUserDataManager _userDataManager;
         private readonly IUserDataManager _userDataManager;
-        private readonly Func<IMediaEncoder> _mediaEncoder;
-        private ILocalizationManager _localizationManager;
-        private IApplicationPaths _appPaths;
+        private readonly IMediaEncoder _mediaEncoder;
+        private readonly ILocalizationManager _localizationManager;
+        private readonly IApplicationPaths _appPaths;
+
+        private IMediaSourceProvider[] _providers;
 
 
         public MediaSourceManager(
         public MediaSourceManager(
             IItemRepository itemRepo,
             IItemRepository itemRepo,
@@ -47,16 +47,16 @@ namespace Emby.Server.Implementations.Library
             ILocalizationManager localizationManager,
             ILocalizationManager localizationManager,
             IUserManager userManager,
             IUserManager userManager,
             ILibraryManager libraryManager,
             ILibraryManager libraryManager,
-            ILoggerFactory loggerFactory,
+            ILogger<MediaSourceManager> logger,
             IJsonSerializer jsonSerializer,
             IJsonSerializer jsonSerializer,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
             IUserDataManager userDataManager,
             IUserDataManager userDataManager,
-            Func<IMediaEncoder> mediaEncoder)
+            IMediaEncoder mediaEncoder)
         {
         {
             _itemRepo = itemRepo;
             _itemRepo = itemRepo;
             _userManager = userManager;
             _userManager = userManager;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
-            _logger = loggerFactory.CreateLogger(nameof(MediaSourceManager));
+            _logger = logger;
             _jsonSerializer = jsonSerializer;
             _jsonSerializer = jsonSerializer;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _userDataManager = userDataManager;
             _userDataManager = userDataManager;
@@ -496,7 +496,7 @@ namespace Emby.Server.Implementations.Library
                     // hack - these two values were taken from LiveTVMediaSourceProvider
                     // hack - these two values were taken from LiveTVMediaSourceProvider
                     string cacheKey = request.OpenToken;
                     string cacheKey = request.OpenToken;
 
 
-                    await new LiveStreamHelper(_mediaEncoder(), _logger, _jsonSerializer, _appPaths)
+                    await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _appPaths)
                         .AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken)
                         .AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken)
                         .ConfigureAwait(false);
                         .ConfigureAwait(false);
                 }
                 }
@@ -621,7 +621,7 @@ namespace Emby.Server.Implementations.Library
 
 
             if (liveStreamInfo is IDirectStreamProvider)
             if (liveStreamInfo is IDirectStreamProvider)
             {
             {
-                var info = await _mediaEncoder().GetMediaInfo(new MediaInfoRequest
+                var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
                 {
                 {
                     MediaSource = mediaSource,
                     MediaSource = mediaSource,
                     ExtractChapters = false,
                     ExtractChapters = false,
@@ -674,7 +674,7 @@ namespace Emby.Server.Implementations.Library
                     mediaSource.AnalyzeDurationMs = 3000;
                     mediaSource.AnalyzeDurationMs = 3000;
                 }
                 }
 
 
-                mediaInfo = await _mediaEncoder().GetMediaInfo(new MediaInfoRequest
+                mediaInfo = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
                 {
                 {
                     MediaSource = mediaSource,
                     MediaSource = mediaSource,
                     MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
                     MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,

+ 3 - 4
Emby.Server.Implementations/Library/SearchEngine.cs

@@ -17,16 +17,15 @@ namespace Emby.Server.Implementations.Library
 {
 {
     public class SearchEngine : ISearchEngine
     public class SearchEngine : ISearchEngine
     {
     {
+        private readonly ILogger _logger;
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
         private readonly IUserManager _userManager;
         private readonly IUserManager _userManager;
-        private readonly ILogger _logger;
 
 
-        public SearchEngine(ILoggerFactory loggerFactory, ILibraryManager libraryManager, IUserManager userManager)
+        public SearchEngine(ILogger<SearchEngine> logger, ILibraryManager libraryManager, IUserManager userManager)
         {
         {
+            _logger = logger;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _userManager = userManager;
             _userManager = userManager;
-
-            _logger = loggerFactory.CreateLogger("SearchEngine");
         }
         }
 
 
         public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query)
         public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query)

+ 18 - 19
Emby.Server.Implementations/Library/UserDataManager.cs

@@ -28,25 +28,24 @@ namespace Emby.Server.Implementations.Library
 
 
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
-
-        private Func<IUserManager> _userManager;
-
-        public UserDataManager(ILoggerFactory loggerFactory, IServerConfigurationManager config, Func<IUserManager> userManager)
+        private readonly IUserManager _userManager;
+        private readonly IUserDataRepository _repository;
+
+        public UserDataManager(
+            ILogger<UserDataManager> logger,
+            IServerConfigurationManager config,
+            IUserManager userManager,
+            IUserDataRepository repository)
         {
         {
+            _logger = logger;
             _config = config;
             _config = config;
-            _logger = loggerFactory.CreateLogger(GetType().Name);
             _userManager = userManager;
             _userManager = userManager;
+            _repository = repository;
         }
         }
 
 
-        /// <summary>
-        /// Gets or sets the repository.
-        /// </summary>
-        /// <value>The repository.</value>
-        public IUserDataRepository Repository { get; set; }
-
         public void SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
         public void SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
         {
         {
-            var user = _userManager().GetUserById(userId);
+            var user = _userManager.GetUserById(userId);
 
 
             SaveUserData(user, item, userData, reason, cancellationToken);
             SaveUserData(user, item, userData, reason, cancellationToken);
         }
         }
@@ -71,7 +70,7 @@ namespace Emby.Server.Implementations.Library
 
 
             foreach (var key in keys)
             foreach (var key in keys)
             {
             {
-                Repository.SaveUserData(userId, key, userData, cancellationToken);
+                _repository.SaveUserData(userId, key, userData, cancellationToken);
             }
             }
 
 
             var cacheKey = GetCacheKey(userId, item.Id);
             var cacheKey = GetCacheKey(userId, item.Id);
@@ -96,9 +95,9 @@ namespace Emby.Server.Implementations.Library
         /// <returns></returns>
         /// <returns></returns>
         public void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken)
         public void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken)
         {
         {
-            var user = _userManager().GetUserById(userId);
+            var user = _userManager.GetUserById(userId);
 
 
-            Repository.SaveAllUserData(user.InternalId, userData, cancellationToken);
+            _repository.SaveAllUserData(user.InternalId, userData, cancellationToken);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -108,14 +107,14 @@ namespace Emby.Server.Implementations.Library
         /// <returns></returns>
         /// <returns></returns>
         public List<UserItemData> GetAllUserData(Guid userId)
         public List<UserItemData> GetAllUserData(Guid userId)
         {
         {
-            var user = _userManager().GetUserById(userId);
+            var user = _userManager.GetUserById(userId);
 
 
-            return Repository.GetAllUserData(user.InternalId);
+            return _repository.GetAllUserData(user.InternalId);
         }
         }
 
 
         public UserItemData GetUserData(Guid userId, Guid itemId, List<string> keys)
         public UserItemData GetUserData(Guid userId, Guid itemId, List<string> keys)
         {
         {
-            var user = _userManager().GetUserById(userId);
+            var user = _userManager.GetUserById(userId);
 
 
             return GetUserData(user, itemId, keys);
             return GetUserData(user, itemId, keys);
         }
         }
@@ -131,7 +130,7 @@ namespace Emby.Server.Implementations.Library
 
 
         private UserItemData GetUserDataInternal(long internalUserId, List<string> keys)
         private UserItemData GetUserDataInternal(long internalUserId, List<string> keys)
         {
         {
-            var userData = Repository.GetUserData(internalUserId, keys);
+            var userData = _repository.GetUserData(internalUserId, keys);
 
 
             if (userData != null)
             if (userData != null)
             {
             {

+ 14 - 23
Emby.Server.Implementations/Library/UserManager.cs

@@ -20,6 +20,7 @@ using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
@@ -44,22 +45,14 @@ namespace Emby.Server.Implementations.Library
     {
     {
         private readonly object _policySyncLock = new object();
         private readonly object _policySyncLock = new object();
         private readonly object _configSyncLock = new object();
         private readonly object _configSyncLock = new object();
-        /// <summary>
-        /// The logger.
-        /// </summary>
-        private readonly ILogger _logger;
 
 
-        /// <summary>
-        /// Gets the active user repository.
-        /// </summary>
-        /// <value>The user repository.</value>
+        private readonly ILogger _logger;
         private readonly IUserRepository _userRepository;
         private readonly IUserRepository _userRepository;
         private readonly IXmlSerializer _xmlSerializer;
         private readonly IXmlSerializer _xmlSerializer;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly INetworkManager _networkManager;
         private readonly INetworkManager _networkManager;
-
-        private readonly Func<IImageProcessor> _imageProcessorFactory;
-        private readonly Func<IDtoService> _dtoServiceFactory;
+        private readonly IImageProcessor _imageProcessor;
+        private readonly Lazy<IDtoService> _dtoServiceFactory;
         private readonly IServerApplicationHost _appHost;
         private readonly IServerApplicationHost _appHost;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly ICryptoProvider _cryptoProvider;
         private readonly ICryptoProvider _cryptoProvider;
@@ -74,13 +67,15 @@ namespace Emby.Server.Implementations.Library
         private IPasswordResetProvider[] _passwordResetProviders;
         private IPasswordResetProvider[] _passwordResetProviders;
         private DefaultPasswordResetProvider _defaultPasswordResetProvider;
         private DefaultPasswordResetProvider _defaultPasswordResetProvider;
 
 
+        private IDtoService DtoService => _dtoServiceFactory.Value;
+
         public UserManager(
         public UserManager(
             ILogger<UserManager> logger,
             ILogger<UserManager> logger,
             IUserRepository userRepository,
             IUserRepository userRepository,
             IXmlSerializer xmlSerializer,
             IXmlSerializer xmlSerializer,
             INetworkManager networkManager,
             INetworkManager networkManager,
-            Func<IImageProcessor> imageProcessorFactory,
-            Func<IDtoService> dtoServiceFactory,
+            IImageProcessor imageProcessor,
+            Lazy<IDtoService> dtoServiceFactory,
             IServerApplicationHost appHost,
             IServerApplicationHost appHost,
             IJsonSerializer jsonSerializer,
             IJsonSerializer jsonSerializer,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
@@ -90,7 +85,7 @@ namespace Emby.Server.Implementations.Library
             _userRepository = userRepository;
             _userRepository = userRepository;
             _xmlSerializer = xmlSerializer;
             _xmlSerializer = xmlSerializer;
             _networkManager = networkManager;
             _networkManager = networkManager;
-            _imageProcessorFactory = imageProcessorFactory;
+            _imageProcessor = imageProcessor;
             _dtoServiceFactory = dtoServiceFactory;
             _dtoServiceFactory = dtoServiceFactory;
             _appHost = appHost;
             _appHost = appHost;
             _jsonSerializer = jsonSerializer;
             _jsonSerializer = jsonSerializer;
@@ -327,23 +322,19 @@ namespace Emby.Server.Implementations.Library
             if (user.Policy.IsDisabled)
             if (user.Policy.IsDisabled)
             {
             {
                 _logger.LogInformation("Authentication request for {UserName} has been denied because this account is currently disabled (IP: {IP}).", username, remoteEndPoint);
                 _logger.LogInformation("Authentication request for {UserName} has been denied because this account is currently disabled (IP: {IP}).", username, remoteEndPoint);
-                throw new AuthenticationException(
-                    string.Format(
-                        CultureInfo.InvariantCulture,
-                        "The {0} account is currently disabled. Please consult with your administrator.",
-                        user.Name));
+                throw new SecurityException($"The {user.Name} account is currently disabled. Please consult with your administrator.");
             }
             }
 
 
             if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
             if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
             {
             {
                 _logger.LogInformation("Authentication request for {UserName} forbidden: remote access disabled and user not in local network (IP: {IP}).", username, remoteEndPoint);
                 _logger.LogInformation("Authentication request for {UserName} forbidden: remote access disabled and user not in local network (IP: {IP}).", username, remoteEndPoint);
-                throw new AuthenticationException("Forbidden.");
+                throw new SecurityException("Forbidden.");
             }
             }
 
 
             if (!user.IsParentalScheduleAllowed())
             if (!user.IsParentalScheduleAllowed())
             {
             {
                 _logger.LogInformation("Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).", username, remoteEndPoint);
                 _logger.LogInformation("Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).", username, remoteEndPoint);
-                throw new AuthenticationException("User is not allowed access at this time.");
+                throw new SecurityException("User is not allowed access at this time.");
             }
             }
 
 
             // Update LastActivityDate and LastLoginDate, then save
             // Update LastActivityDate and LastLoginDate, then save
@@ -605,7 +596,7 @@ namespace Emby.Server.Implementations.Library
 
 
                 try
                 try
                 {
                 {
-                    _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user);
+                    DtoService.AttachPrimaryImageAspectRatio(dto, user);
                 }
                 }
                 catch (Exception ex)
                 catch (Exception ex)
                 {
                 {
@@ -630,7 +621,7 @@ namespace Emby.Server.Implementations.Library
         {
         {
             try
             try
             {
             {
-                return _imageProcessorFactory().GetImageCacheTag(item, image);
+                return _imageProcessor.GetImageCacheTag(item, image);
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {

+ 0 - 1
Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs

@@ -28,7 +28,6 @@ namespace Emby.Server.Implementations.LiveTv
 
 
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IImageProcessor _imageProcessor;
         private readonly IImageProcessor _imageProcessor;
-
         private readonly IDtoService _dtoService;
         private readonly IDtoService _dtoService;
         private readonly IApplicationHost _appHost;
         private readonly IApplicationHost _appHost;
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;

+ 10 - 15
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -49,29 +49,24 @@ namespace Emby.Server.Implementations.LiveTv
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IItemRepository _itemRepo;
         private readonly IItemRepository _itemRepo;
         private readonly IUserManager _userManager;
         private readonly IUserManager _userManager;
+        private readonly IDtoService _dtoService;
         private readonly IUserDataManager _userDataManager;
         private readonly IUserDataManager _userDataManager;
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
         private readonly ITaskManager _taskManager;
         private readonly ITaskManager _taskManager;
-        private readonly IJsonSerializer _jsonSerializer;
-        private readonly Func<IChannelManager> _channelManager;
-
-        private readonly IDtoService _dtoService;
         private readonly ILocalizationManager _localization;
         private readonly ILocalizationManager _localization;
-
+        private readonly IJsonSerializer _jsonSerializer;
+        private readonly IFileSystem _fileSystem;
+        private readonly IChannelManager _channelManager;
         private readonly LiveTvDtoService _tvDtoService;
         private readonly LiveTvDtoService _tvDtoService;
 
 
         private ILiveTvService[] _services = Array.Empty<ILiveTvService>();
         private ILiveTvService[] _services = Array.Empty<ILiveTvService>();
-
         private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>();
         private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>();
         private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
         private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
-        private readonly IFileSystem _fileSystem;
 
 
         public LiveTvManager(
         public LiveTvManager(
-            IServerApplicationHost appHost,
             IServerConfigurationManager config,
             IServerConfigurationManager config,
-            ILoggerFactory loggerFactory,
+            ILogger<LiveTvManager> logger,
             IItemRepository itemRepo,
             IItemRepository itemRepo,
-            IImageProcessor imageProcessor,
             IUserDataManager userDataManager,
             IUserDataManager userDataManager,
             IDtoService dtoService,
             IDtoService dtoService,
             IUserManager userManager,
             IUserManager userManager,
@@ -80,10 +75,11 @@ namespace Emby.Server.Implementations.LiveTv
             ILocalizationManager localization,
             ILocalizationManager localization,
             IJsonSerializer jsonSerializer,
             IJsonSerializer jsonSerializer,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
-            Func<IChannelManager> channelManager)
+            IChannelManager channelManager,
+            LiveTvDtoService liveTvDtoService)
         {
         {
             _config = config;
             _config = config;
-            _logger = loggerFactory.CreateLogger(nameof(LiveTvManager));
+            _logger = logger;
             _itemRepo = itemRepo;
             _itemRepo = itemRepo;
             _userManager = userManager;
             _userManager = userManager;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
@@ -94,8 +90,7 @@ namespace Emby.Server.Implementations.LiveTv
             _dtoService = dtoService;
             _dtoService = dtoService;
             _userDataManager = userDataManager;
             _userDataManager = userDataManager;
             _channelManager = channelManager;
             _channelManager = channelManager;
-
-            _tvDtoService = new LiveTvDtoService(dtoService, imageProcessor, loggerFactory.CreateLogger<LiveTvDtoService>(), appHost, _libraryManager);
+            _tvDtoService = liveTvDtoService;
         }
         }
 
 
         public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
         public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
@@ -2496,7 +2491,7 @@ namespace Emby.Server.Implementations.LiveTv
                 .OrderBy(i => i.SortName)
                 .OrderBy(i => i.SortName)
                 .ToList();
                 .ToList();
 
 
-            folders.AddRange(_channelManager().GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery
+            folders.AddRange(_channelManager.GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery
             {
             {
                 UserId = user.Id,
                 UserId = user.Id,
                 IsRecordingsFolder = true,
                 IsRecordingsFolder = true,

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

@@ -23,7 +23,7 @@
     "HeaderFavoriteEpisodes": "قسمت‌های مورد علاقه",
     "HeaderFavoriteEpisodes": "قسمت‌های مورد علاقه",
     "HeaderFavoriteShows": "سریال‌های مورد علاقه",
     "HeaderFavoriteShows": "سریال‌های مورد علاقه",
     "HeaderFavoriteSongs": "آهنگ‌های مورد علاقه",
     "HeaderFavoriteSongs": "آهنگ‌های مورد علاقه",
-    "HeaderLiveTV": "تلویزیون زنده",
+    "HeaderLiveTV": "پخش زنده",
     "HeaderNextUp": "قسمت بعدی",
     "HeaderNextUp": "قسمت بعدی",
     "HeaderRecordingGroups": "گروه‌های ضبط",
     "HeaderRecordingGroups": "گروه‌های ضبط",
     "HomeVideos": "ویدیوهای خانگی",
     "HomeVideos": "ویدیوهای خانگی",

+ 6 - 5
Emby.Server.Implementations/Localization/Core/ja.json

@@ -104,13 +104,14 @@
     "TasksMaintenanceCategory": "メンテナンス",
     "TasksMaintenanceCategory": "メンテナンス",
     "TaskRefreshChannelsDescription": "ネットチャンネルの情報をリフレッシュします。",
     "TaskRefreshChannelsDescription": "ネットチャンネルの情報をリフレッシュします。",
     "TaskRefreshChannels": "チャンネルのリフレッシュ",
     "TaskRefreshChannels": "チャンネルのリフレッシュ",
-    "TaskCleanTranscodeDescription": "一日以上前のトランスコードを消去します。",
-    "TaskCleanTranscode": "トランスコード用のディレクトリの掃除",
+    "TaskCleanTranscodeDescription": "1日以上経過したトランスコードファイルを削除します。",
+    "TaskCleanTranscode": "トランスコードディレクトリの削除",
     "TaskUpdatePluginsDescription": "自動更新可能なプラグインのアップデートをダウンロードしてインストールします。",
     "TaskUpdatePluginsDescription": "自動更新可能なプラグインのアップデートをダウンロードしてインストールします。",
     "TaskUpdatePlugins": "プラグインの更新",
     "TaskUpdatePlugins": "プラグインの更新",
-    "TaskRefreshPeopleDescription": "メディアライブラリで俳優や監督のメタデータをリフレッシュします。",
-    "TaskRefreshPeople": "俳優や監督のデータのリフレッシュ",
+    "TaskRefreshPeopleDescription": "メディアライブラリで俳優や監督のメタデータを更新します。",
+    "TaskRefreshPeople": "俳優や監督のデータの更新",
     "TaskDownloadMissingSubtitlesDescription": "メタデータ構成に基づいて、欠落している字幕をインターネットで検索します。",
     "TaskDownloadMissingSubtitlesDescription": "メタデータ構成に基づいて、欠落している字幕をインターネットで検索します。",
     "TaskRefreshChapterImagesDescription": "チャプターのあるビデオのサムネイルを作成します。",
     "TaskRefreshChapterImagesDescription": "チャプターのあるビデオのサムネイルを作成します。",
-    "TaskRefreshChapterImages": "チャプター画像を抽出する"
+    "TaskRefreshChapterImages": "チャプター画像を抽出する",
+    "TaskDownloadMissingSubtitles": "不足している字幕をダウンロードする"
 }
 }

+ 3 - 3
Emby.Server.Implementations/Localization/Core/nl.json

@@ -1,11 +1,11 @@
 {
 {
     "Albums": "Albums",
     "Albums": "Albums",
     "AppDeviceValues": "App: {0}, Apparaat: {1}",
     "AppDeviceValues": "App: {0}, Apparaat: {1}",
-    "Application": "Applicatie",
+    "Application": "Programma",
     "Artists": "Artiesten",
     "Artists": "Artiesten",
-    "AuthenticationSucceededWithUserName": "{0} succesvol geauthenticeerd",
+    "AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd",
     "Books": "Boeken",
     "Books": "Boeken",
-    "CameraImageUploadedFrom": "Er is een nieuwe foto toegevoegd van {0}",
+    "CameraImageUploadedFrom": "Er is een nieuwe afbeelding toegevoegd via {0}",
     "Channels": "Kanalen",
     "Channels": "Kanalen",
     "ChapterNameValue": "Hoofdstuk {0}",
     "ChapterNameValue": "Hoofdstuk {0}",
     "Collections": "Verzamelingen",
     "Collections": "Verzamelingen",

+ 24 - 2
Emby.Server.Implementations/Localization/Core/pt-PT.json

@@ -26,7 +26,7 @@
     "HeaderLiveTV": "TV em Direto",
     "HeaderLiveTV": "TV em Direto",
     "HeaderNextUp": "A Seguir",
     "HeaderNextUp": "A Seguir",
     "HeaderRecordingGroups": "Grupos de Gravação",
     "HeaderRecordingGroups": "Grupos de Gravação",
-    "HomeVideos": "Home videos",
+    "HomeVideos": "Videos caseiros",
     "Inherit": "Herdar",
     "Inherit": "Herdar",
     "ItemAddedWithName": "{0} foi adicionado à biblioteca",
     "ItemAddedWithName": "{0} foi adicionado à biblioteca",
     "ItemRemovedWithName": "{0} foi removido da biblioteca",
     "ItemRemovedWithName": "{0} foi removido da biblioteca",
@@ -92,5 +92,27 @@
     "UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}",
     "UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}",
     "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia",
     "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia",
     "ValueSpecialEpisodeName": "Especial - {0}",
     "ValueSpecialEpisodeName": "Especial - {0}",
-    "VersionNumber": "Versão {0}"
+    "VersionNumber": "Versão {0}",
+    "TaskDownloadMissingSubtitlesDescription": "Procurar na internet por legendas em falta baseado na configuração de metadados.",
+    "TaskDownloadMissingSubtitles": "Fazer download de legendas em falta",
+    "TaskRefreshChannelsDescription": "Atualizar informação sobre canais da Internet.",
+    "TaskRefreshChannels": "Atualizar Canais",
+    "TaskCleanTranscodeDescription": "Apagar ficheiros de transcode com mais de um dia.",
+    "TaskCleanTranscode": "Limpar a Diretoria de Transcode",
+    "TaskUpdatePluginsDescription": "Faz o download e instala updates para os plugins que estão configurados para atualizar automaticamente.",
+    "TaskUpdatePlugins": "Atualizar Plugins",
+    "TaskRefreshPeopleDescription": "Atualizar metadados para atores e diretores na biblioteca.",
+    "TaskRefreshPeople": "Atualizar Pessoas",
+    "TaskCleanLogsDescription": "Apagar ficheiros de log que têm mais de {0} dias.",
+    "TaskCleanLogs": "Limpar a Diretoria de Logs",
+    "TaskRefreshLibraryDescription": "Scannear a biblioteca de música para novos ficheiros e atualizar os metadados.",
+    "TaskRefreshLibrary": "Scannear Biblioteca de Música",
+    "TaskRefreshChapterImagesDescription": "Criar thumbnails para os vídeos que têm capítulos.",
+    "TaskRefreshChapterImages": "Extrair Imagens dos Capítulos",
+    "TaskCleanCacheDescription": "Apagar ficheiros em cache que já não são necessários.",
+    "TaskCleanCache": "Limpar Cache",
+    "TasksChannelsCategory": "Canais da Internet",
+    "TasksApplicationCategory": "Aplicação",
+    "TasksLibraryCategory": "Biblioteca",
+    "TasksMaintenanceCategory": "Manutenção"
 }
 }

+ 20 - 3
Emby.Server.Implementations/Localization/Core/tr.json

@@ -50,7 +50,7 @@
     "NotificationOptionAudioPlayback": "Ses çalma başladı",
     "NotificationOptionAudioPlayback": "Ses çalma başladı",
     "NotificationOptionAudioPlaybackStopped": "Ses çalma durduruldu",
     "NotificationOptionAudioPlaybackStopped": "Ses çalma durduruldu",
     "NotificationOptionCameraImageUploaded": "Kamera fotoğrafı yüklendi",
     "NotificationOptionCameraImageUploaded": "Kamera fotoğrafı yüklendi",
-    "NotificationOptionInstallationFailed": "Yükleme başarısız oldu",
+    "NotificationOptionInstallationFailed": "Kurulum hatası",
     "NotificationOptionNewLibraryContent": "Yeni içerik eklendi",
     "NotificationOptionNewLibraryContent": "Yeni içerik eklendi",
     "NotificationOptionPluginError": "Eklenti hatası",
     "NotificationOptionPluginError": "Eklenti hatası",
     "NotificationOptionPluginInstalled": "Eklenti yüklendi",
     "NotificationOptionPluginInstalled": "Eklenti yüklendi",
@@ -95,7 +95,24 @@
     "VersionNumber": "Versiyon {0}",
     "VersionNumber": "Versiyon {0}",
     "TaskCleanCache": "Geçici dosya klasörünü temizle",
     "TaskCleanCache": "Geçici dosya klasörünü temizle",
     "TasksChannelsCategory": "İnternet kanalları",
     "TasksChannelsCategory": "İnternet kanalları",
-    "TasksApplicationCategory": "Yazılım",
+    "TasksApplicationCategory": "Uygulama",
     "TasksLibraryCategory": "Kütüphane",
     "TasksLibraryCategory": "Kütüphane",
-    "TasksMaintenanceCategory": "Onarım"
+    "TasksMaintenanceCategory": "Onarım",
+    "TaskRefreshPeopleDescription": "Medya kütüphanenizdeki videoların oyuncu ve yönetmen bilgilerini günceller.",
+    "TaskDownloadMissingSubtitlesDescription": "Metadata ayarlarını baz alarak eksik altyazıları internette arar.",
+    "TaskDownloadMissingSubtitles": "Eksik altyazıları indir",
+    "TaskRefreshChannelsDescription": "Internet kanal bilgilerini yenile.",
+    "TaskRefreshChannels": "Kanalları Yenile",
+    "TaskCleanTranscodeDescription": "Bir günü dolmuş dönüştürme bilgisi içeren dosyaları siler.",
+    "TaskCleanTranscode": "Dönüşüm Dizinini Temizle",
+    "TaskUpdatePluginsDescription": "Otomatik güncellenmeye ayarlanmış eklentilerin güncellemelerini indirir ve kurar.",
+    "TaskUpdatePlugins": "Eklentileri Güncelle",
+    "TaskRefreshPeople": "Kullanıcıları Yenile",
+    "TaskCleanLogsDescription": "{0} günden eski log dosyalarını siler.",
+    "TaskCleanLogs": "Log Dizinini Temizle",
+    "TaskRefreshLibraryDescription": "Medya kütüphanenize eklenen yeni dosyaları arar ve bilgileri yeniler.",
+    "TaskRefreshLibrary": "Medya Kütüphanesini Tara",
+    "TaskRefreshChapterImagesDescription": "Sahnelere ayrılmış videolar için küçük resimler oluştur.",
+    "TaskRefreshChapterImages": "Bölüm Resimlerini Çıkar",
+    "TaskCleanCacheDescription": "Sistem tarafından artık ihtiyaç duyulmayan önbellek dosyalarını siler."
 }
 }

+ 28 - 6
Emby.Server.Implementations/Localization/Core/zh-TW.json

@@ -50,10 +50,10 @@
     "NotificationOptionCameraImageUploaded": "相機相片已上傳",
     "NotificationOptionCameraImageUploaded": "相機相片已上傳",
     "NotificationOptionInstallationFailed": "安裝失敗",
     "NotificationOptionInstallationFailed": "安裝失敗",
     "NotificationOptionNewLibraryContent": "已新增新內容",
     "NotificationOptionNewLibraryContent": "已新增新內容",
-    "NotificationOptionPluginError": "擴充元件錯誤",
-    "NotificationOptionPluginInstalled": "擴充元件已安裝",
-    "NotificationOptionPluginUninstalled": "擴充元件已移除",
-    "NotificationOptionPluginUpdateInstalled": "已更新擴充元件",
+    "NotificationOptionPluginError": "插件安裝錯誤",
+    "NotificationOptionPluginInstalled": "件已安裝",
+    "NotificationOptionPluginUninstalled": "件已移除",
+    "NotificationOptionPluginUpdateInstalled": "插件已更新",
     "NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
     "NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
     "NotificationOptionTaskFailed": "排程任務失敗",
     "NotificationOptionTaskFailed": "排程任務失敗",
     "NotificationOptionUserLockedOut": "使用者已鎖定",
     "NotificationOptionUserLockedOut": "使用者已鎖定",
@@ -61,7 +61,7 @@
     "NotificationOptionVideoPlaybackStopped": "影片停止播放",
     "NotificationOptionVideoPlaybackStopped": "影片停止播放",
     "Photos": "相片",
     "Photos": "相片",
     "Playlists": "播放清單",
     "Playlists": "播放清單",
-    "Plugin": "外掛",
+    "Plugin": "插件",
     "PluginInstalledWithName": "{0} 已安裝",
     "PluginInstalledWithName": "{0} 已安裝",
     "PluginUninstalledWithName": "{0} 已移除",
     "PluginUninstalledWithName": "{0} 已移除",
     "PluginUpdatedWithName": "{0} 已更新",
     "PluginUpdatedWithName": "{0} 已更新",
@@ -91,5 +91,27 @@
     "VersionNumber": "版本 {0}",
     "VersionNumber": "版本 {0}",
     "HeaderRecordingGroups": "錄製組",
     "HeaderRecordingGroups": "錄製組",
     "Inherit": "繼承",
     "Inherit": "繼承",
-    "SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕"
+    "SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕",
+    "TaskDownloadMissingSubtitlesDescription": "在網路上透過描述資料搜尋遺失的字幕。",
+    "TaskDownloadMissingSubtitles": "下載遺失的字幕",
+    "TaskRefreshChannels": "重新整理頻道",
+    "TaskUpdatePlugins": "更新插件",
+    "TaskRefreshPeople": "重新整理人員",
+    "TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔案。",
+    "TaskCleanLogs": "清空紀錄資料夾",
+    "TaskRefreshLibraryDescription": "掃描媒體庫內新的檔案並重新整理描述資料。",
+    "TaskRefreshLibrary": "掃描媒體庫",
+    "TaskRefreshChapterImages": "擷取章節圖片",
+    "TaskCleanCacheDescription": "刪除系統長時間不需要的快取。",
+    "TaskCleanCache": "清除快取資料夾",
+    "TasksLibraryCategory": "媒體庫",
+    "TaskRefreshChannelsDescription": "重新整理網絡頻道資料。",
+    "TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。",
+    "TaskCleanTranscode": "清除轉碼資料夾",
+    "TaskUpdatePluginsDescription": "下載並安裝配置為自動更新的插件的更新。",
+    "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的中繼資料。",
+    "TaskRefreshChapterImagesDescription": "為有章節的視頻創建縮圖。",
+    "TasksChannelsCategory": "網絡頻道",
+    "TasksApplicationCategory": "應用程式",
+    "TasksMaintenanceCategory": "維修"
 }
 }

+ 0 - 3
Emby.Server.Implementations/Localization/LocalizationManager.cs

@@ -23,9 +23,6 @@ namespace Emby.Server.Implementations.Localization
         private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly;
         private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly;
         private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
         private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
 
 
-        /// <summary>
-        /// The _configuration manager.
-        /// </summary>
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly ILogger _logger;
         private readonly ILogger _logger;

+ 3 - 17
Emby.Server.Implementations/ScheduledTasks/TaskManager.cs

@@ -32,22 +32,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
         private readonly ConcurrentQueue<Tuple<Type, TaskOptions>> _taskQueue =
         private readonly ConcurrentQueue<Tuple<Type, TaskOptions>> _taskQueue =
             new ConcurrentQueue<Tuple<Type, TaskOptions>>();
             new ConcurrentQueue<Tuple<Type, TaskOptions>>();
 
 
-        /// <summary>
-        /// Gets or sets the json serializer.
-        /// </summary>
-        /// <value>The json serializer.</value>
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IJsonSerializer _jsonSerializer;
-
-        /// <summary>
-        /// Gets or sets the application paths.
-        /// </summary>
-        /// <value>The application paths.</value>
         private readonly IApplicationPaths _applicationPaths;
         private readonly IApplicationPaths _applicationPaths;
-
-        /// <summary>
-        /// Gets the logger.
-        /// </summary>
-        /// <value>The logger.</value>
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
 
 
@@ -56,17 +42,17 @@ namespace Emby.Server.Implementations.ScheduledTasks
         /// </summary>
         /// </summary>
         /// <param name="applicationPaths">The application paths.</param>
         /// <param name="applicationPaths">The application paths.</param>
         /// <param name="jsonSerializer">The json serializer.</param>
         /// <param name="jsonSerializer">The json serializer.</param>
-        /// <param name="loggerFactory">The logger factory.</param>
+        /// <param name="logger">The logger.</param>
         /// <param name="fileSystem">The filesystem manager.</param>
         /// <param name="fileSystem">The filesystem manager.</param>
         public TaskManager(
         public TaskManager(
             IApplicationPaths applicationPaths,
             IApplicationPaths applicationPaths,
             IJsonSerializer jsonSerializer,
             IJsonSerializer jsonSerializer,
-            ILoggerFactory loggerFactory,
+            ILogger<TaskManager> logger,
             IFileSystem fileSystem)
             IFileSystem fileSystem)
         {
         {
             _applicationPaths = applicationPaths;
             _applicationPaths = applicationPaths;
             _jsonSerializer = jsonSerializer;
             _jsonSerializer = jsonSerializer;
-            _logger = loggerFactory.CreateLogger(nameof(TaskManager));
+            _logger = logger;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
 
 
             ScheduledTasks = Array.Empty<IScheduledTaskWorker>();
             ScheduledTasks = Array.Empty<IScheduledTaskWorker>();

+ 2 - 2
Emby.Server.Implementations/Security/AuthenticationRepository.cs

@@ -15,8 +15,8 @@ namespace Emby.Server.Implementations.Security
 {
 {
     public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository
     public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository
     {
     {
-        public AuthenticationRepository(ILoggerFactory loggerFactory, IServerConfigurationManager config)
-            : base(loggerFactory.CreateLogger(nameof(AuthenticationRepository)))
+        public AuthenticationRepository(ILogger<AuthenticationRepository> logger, IServerConfigurationManager config)
+            : base(logger)
         {
         {
             DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "authentication.db");
             DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "authentication.db");
         }
         }

+ 1 - 1
Emby.Server.Implementations/Session/SessionManager.cs

@@ -1414,7 +1414,7 @@ namespace Emby.Server.Implementations.Session
             if (user == null)
             if (user == null)
             {
             {
                 AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request));
                 AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request));
-                throw new SecurityException("Invalid username or password entered.");
+                throw new AuthenticationException("Invalid username or password entered.");
             }
             }
 
 
             if (!string.IsNullOrEmpty(request.DeviceId)
             if (!string.IsNullOrEmpty(request.DeviceId)

+ 35 - 42
Emby.Server.Implementations/Updates/InstallationManager.cs

@@ -26,7 +26,7 @@ using Microsoft.Extensions.Logging;
 namespace Emby.Server.Implementations.Updates
 namespace Emby.Server.Implementations.Updates
 {
 {
     /// <summary>
     /// <summary>
-    /// Manages all install, uninstall and update operations (both plugins and system).
+    /// Manages all install, uninstall, and update operations for the system and individual plugins.
     /// </summary>
     /// </summary>
     public class InstallationManager : IInstallationManager
     public class InstallationManager : IInstallationManager
     {
     {
@@ -36,7 +36,7 @@ namespace Emby.Server.Implementations.Updates
         public const string PluginManifestUrlKey = "InstallationManager:PluginManifestUrl";
         public const string PluginManifestUrlKey = "InstallationManager:PluginManifestUrl";
 
 
         /// <summary>
         /// <summary>
-        /// The _logger.
+        /// The logger.
         /// </summary>
         /// </summary>
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IApplicationPaths _appPaths;
         private readonly IApplicationPaths _appPaths;
@@ -112,10 +112,10 @@ namespace Emby.Server.Implementations.Updates
         public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
         public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public event EventHandler<GenericEventArgs<(IPlugin, PackageVersionInfo)>> PluginUpdated;
+        public event EventHandler<GenericEventArgs<(IPlugin, VersionInfo)>> PluginUpdated;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled;
+        public event EventHandler<GenericEventArgs<VersionInfo>> PluginInstalled;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public IEnumerable<InstallationInfo> CompletedInstallations => _completedInstallationsInternal;
         public IEnumerable<InstallationInfo> CompletedInstallations => _completedInstallationsInternal;
@@ -183,61 +183,56 @@ namespace Emby.Server.Implementations.Updates
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public IEnumerable<PackageVersionInfo> GetCompatibleVersions(
-            IEnumerable<PackageVersionInfo> availableVersions,
-            Version minVersion = null,
-            PackageVersionClass classification = PackageVersionClass.Release)
+        public IEnumerable<VersionInfo> GetCompatibleVersions(
+            IEnumerable<VersionInfo> availableVersions,
+            Version minVersion = null)
         {
         {
             var appVer = _applicationHost.ApplicationVersion;
             var appVer = _applicationHost.ApplicationVersion;
             availableVersions = availableVersions
             availableVersions = availableVersions
-                .Where(x => x.classification == classification
-                    && Version.Parse(x.requiredVersionStr) <= appVer);
+                .Where(x => Version.Parse(x.targetAbi) <= appVer);
 
 
             if (minVersion != null)
             if (minVersion != null)
             {
             {
-                availableVersions = availableVersions.Where(x => x.Version >= minVersion);
+                availableVersions = availableVersions.Where(x => x.version >= minVersion);
             }
             }
 
 
-            return availableVersions.OrderByDescending(x => x.Version);
+            return availableVersions.OrderByDescending(x => x.version);
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public IEnumerable<PackageVersionInfo> GetCompatibleVersions(
+        public IEnumerable<VersionInfo> GetCompatibleVersions(
             IEnumerable<PackageInfo> availablePackages,
             IEnumerable<PackageInfo> availablePackages,
             string name = null,
             string name = null,
             Guid guid = default,
             Guid guid = default,
-            Version minVersion = null,
-            PackageVersionClass classification = PackageVersionClass.Release)
+            Version minVersion = null)
         {
         {
             var package = FilterPackages(availablePackages, name, guid).FirstOrDefault();
             var package = FilterPackages(availablePackages, name, guid).FirstOrDefault();
 
 
-            // Package not found.
+            // Package not found in repository
             if (package == null)
             if (package == null)
             {
             {
-                return Enumerable.Empty<PackageVersionInfo>();
+                return Enumerable.Empty<VersionInfo>();
             }
             }
 
 
             return GetCompatibleVersions(
             return GetCompatibleVersions(
                 package.versions,
                 package.versions,
-                minVersion,
-                classification);
+                minVersion);
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public async Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default)
+        public async Task<IEnumerable<VersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default)
         {
         {
             var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false);
             var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false);
             return GetAvailablePluginUpdates(catalog);
             return GetAvailablePluginUpdates(catalog);
         }
         }
 
 
-        private IEnumerable<PackageVersionInfo> GetAvailablePluginUpdates(IReadOnlyList<PackageInfo> pluginCatalog)
+        private IEnumerable<VersionInfo> GetAvailablePluginUpdates(IReadOnlyList<PackageInfo> pluginCatalog)
         {
         {
             foreach (var plugin in _applicationHost.Plugins)
             foreach (var plugin in _applicationHost.Plugins)
             {
             {
-                var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version, _applicationHost.SystemUpdateLevel);
-                var version = compatibleversions.FirstOrDefault(y => y.Version > plugin.Version);
-                if (version != null
-                    && !CompletedInstallations.Any(x => string.Equals(x.AssemblyGuid, version.guid, StringComparison.OrdinalIgnoreCase)))
+                var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version);
+                var version = compatibleversions.FirstOrDefault(y => y.version > plugin.Version);
+                if (version != null && !CompletedInstallations.Any(x => string.Equals(x.Guid, version.guid, StringComparison.OrdinalIgnoreCase)))
                 {
                 {
                     yield return version;
                     yield return version;
                 }
                 }
@@ -245,7 +240,7 @@ namespace Emby.Server.Implementations.Updates
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public async Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken)
+        public async Task InstallPackage(VersionInfo package, CancellationToken cancellationToken)
         {
         {
             if (package == null)
             if (package == null)
             {
             {
@@ -254,11 +249,9 @@ namespace Emby.Server.Implementations.Updates
 
 
             var installationInfo = new InstallationInfo
             var installationInfo = new InstallationInfo
             {
             {
-                Id = Guid.NewGuid(),
+                Guid = package.guid,
                 Name = package.name,
                 Name = package.name,
-                AssemblyGuid = package.guid,
-                UpdateClass = package.classification,
-                Version = package.versionStr
+                Version = package.version.ToString()
             };
             };
 
 
             var innerCancellationTokenSource = new CancellationTokenSource();
             var innerCancellationTokenSource = new CancellationTokenSource();
@@ -276,7 +269,7 @@ namespace Emby.Server.Implementations.Updates
             var installationEventArgs = new InstallationEventArgs
             var installationEventArgs = new InstallationEventArgs
             {
             {
                 InstallationInfo = installationInfo,
                 InstallationInfo = installationInfo,
-                PackageVersionInfo = package
+                VersionInfo = package
             };
             };
 
 
             PackageInstalling?.Invoke(this, installationEventArgs);
             PackageInstalling?.Invoke(this, installationEventArgs);
@@ -301,7 +294,7 @@ namespace Emby.Server.Implementations.Updates
                     _currentInstallations.Remove(tuple);
                     _currentInstallations.Remove(tuple);
                 }
                 }
 
 
-                _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.versionStr);
+                _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.version);
 
 
                 PackageInstallationCancelled?.Invoke(this, installationEventArgs);
                 PackageInstallationCancelled?.Invoke(this, installationEventArgs);
 
 
@@ -337,7 +330,7 @@ namespace Emby.Server.Implementations.Updates
         /// <param name="package">The package.</param>
         /// <param name="package">The package.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns><see cref="Task" />.</returns>
         /// <returns><see cref="Task" />.</returns>
-        private async Task InstallPackageInternal(PackageVersionInfo package, CancellationToken cancellationToken)
+        private async Task InstallPackageInternal(VersionInfo package, CancellationToken cancellationToken)
         {
         {
             // Set last update time if we were installed before
             // Set last update time if we were installed before
             IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
             IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
@@ -349,26 +342,26 @@ namespace Emby.Server.Implementations.Updates
             // Do plugin-specific processing
             // Do plugin-specific processing
             if (plugin == null)
             if (plugin == null)
             {
             {
-                _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
+                _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.version);
 
 
-                PluginInstalled?.Invoke(this, new GenericEventArgs<PackageVersionInfo>(package));
+                PluginInstalled?.Invoke(this, new GenericEventArgs<VersionInfo>(package));
             }
             }
             else
             else
             {
             {
-                _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
+                _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.version);
 
 
-                PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, PackageVersionInfo)>((plugin, package)));
+                PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, VersionInfo)>((plugin, package)));
             }
             }
 
 
             _applicationHost.NotifyPendingRestart();
             _applicationHost.NotifyPendingRestart();
         }
         }
 
 
-        private async Task PerformPackageInstallation(PackageVersionInfo package, CancellationToken cancellationToken)
+        private async Task PerformPackageInstallation(VersionInfo package, CancellationToken cancellationToken)
         {
         {
-            var extension = Path.GetExtension(package.targetFilename);
+            var extension = Path.GetExtension(package.filename);
             if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase))
             if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase))
             {
             {
-                _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename);
+                _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.filename);
                 return;
                 return;
             }
             }
 
 
@@ -415,7 +408,7 @@ namespace Emby.Server.Implementations.Updates
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Uninstalls a plugin
+        /// Uninstalls a plugin.
         /// </summary>
         /// </summary>
         /// <param name="plugin">The plugin.</param>
         /// <param name="plugin">The plugin.</param>
         public void UninstallPlugin(IPlugin plugin)
         public void UninstallPlugin(IPlugin plugin)
@@ -473,7 +466,7 @@ namespace Emby.Server.Implementations.Updates
         {
         {
             lock (_currentInstallationsLock)
             lock (_currentInstallationsLock)
             {
             {
-                var install = _currentInstallations.Find(x => x.info.Id == id);
+                var install = _currentInstallations.Find(x => x.info.Guid == id.ToString());
                 if (install == default((InstallationInfo, CancellationTokenSource)))
                 if (install == default((InstallationInfo, CancellationTokenSource)))
                 {
                 {
                     return false;
                     return false;

+ 5 - 0
Jellyfin.Api/Jellyfin.Api.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{DFBEFB4C-DA19-4143-98B7-27320C7F7163}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netstandard2.1</TargetFramework>
     <TargetFramework>netstandard2.1</TargetFramework>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>

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

@@ -1,10 +1,16 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{154872D9-6C12-4007-96E3-8F70A58386CE}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netstandard2.1</TargetFramework>
     <TargetFramework>netstandard2.1</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <Nullable>enable</Nullable>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>

+ 5 - 8
Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs

@@ -26,7 +26,7 @@ namespace Jellyfin.Drawing.Skia
             {
             {
                 paint.Color = SKColor.Parse("#CC00A4DC");
                 paint.Color = SKColor.Parse("#CC00A4DC");
                 paint.Style = SKPaintStyle.Fill;
                 paint.Style = SKPaintStyle.Fill;
-                canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint);
+                canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint);
             }
             }
 
 
             using (var paint = new SKPaint())
             using (var paint = new SKPaint())
@@ -39,16 +39,13 @@ namespace Jellyfin.Drawing.Skia
 
 
                 // or:
                 // or:
                 // var emojiChar = 0x1F680;
                 // var emojiChar = 0x1F680;
-                var text = "✔️";
-                var emojiChar = StringUtilities.GetUnicodeCharacterCode(text, SKTextEncoding.Utf32);
+                const string Text = "✔️";
+                var emojiChar = StringUtilities.GetUnicodeCharacterCode(Text, SKTextEncoding.Utf32);
 
 
                 // ask the font manager for a font with that character
                 // ask the font manager for a font with that character
-                var fontManager = SKFontManager.Default;
-                var emojiTypeface = fontManager.MatchCharacter(emojiChar);
+                paint.Typeface = SKFontManager.Default.MatchCharacter(emojiChar);
 
 
-                paint.Typeface = emojiTypeface;
-
-                canvas.DrawText(text, (float)x - 20, OffsetFromTopRightCorner + 12, paint);
+                canvas.DrawText(Text, (float)x - 20, OffsetFromTopRightCorner + 12, paint);
             }
             }
         }
         }
     }
     }

+ 28 - 21
Jellyfin.Drawing.Skia/SkiaEncoder.cs

@@ -78,12 +78,21 @@ namespace Jellyfin.Drawing.Skia
             => new HashSet<ImageFormat>() { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png };
             => new HashSet<ImageFormat>() { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png };
 
 
         /// <summary>
         /// <summary>
-        /// Test to determine if the native lib is available.
+        /// Check if the native lib is available.
         /// </summary>
         /// </summary>
-        public static void TestSkia()
+        /// <returns>True if the native lib is available, otherwise false.</returns>
+        public static bool IsNativeLibAvailable()
         {
         {
-            // test an operation that requires the native library
-            SKPMColor.PreMultiply(SKColors.Black);
+            try
+            {
+                // test an operation that requires the native library
+                SKPMColor.PreMultiply(SKColors.Black);
+                return true;
+            }
+            catch (Exception)
+            {
+                return false;
+            }
         }
         }
 
 
         private static bool IsTransparent(SKColor color)
         private static bool IsTransparent(SKColor color)
@@ -205,11 +214,6 @@ namespace Jellyfin.Drawing.Skia
         /// <exception cref="SkiaCodecException">The file at the specified path could not be used to generate a codec.</exception>
         /// <exception cref="SkiaCodecException">The file at the specified path could not be used to generate a codec.</exception>
         public ImageDimensions GetImageSize(string path)
         public ImageDimensions GetImageSize(string path)
         {
         {
-            if (path == null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
-
             if (!File.Exists(path))
             if (!File.Exists(path))
             {
             {
                 throw new FileNotFoundException("File not found", path);
                 throw new FileNotFoundException("File not found", path);
@@ -297,7 +301,7 @@ namespace Jellyfin.Drawing.Skia
         /// <param name="orientation">The orientation of the image.</param>
         /// <param name="orientation">The orientation of the image.</param>
         /// <param name="origin">The detected origin of the image.</param>
         /// <param name="origin">The detected origin of the image.</param>
         /// <returns>The resulting bitmap of the image.</returns>
         /// <returns>The resulting bitmap of the image.</returns>
-        internal SKBitmap Decode(string path, bool forceCleanBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin)
+        internal SKBitmap? Decode(string path, bool forceCleanBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin)
         {
         {
             if (!File.Exists(path))
             if (!File.Exists(path))
             {
             {
@@ -348,12 +352,17 @@ namespace Jellyfin.Drawing.Skia
             return resultBitmap;
             return resultBitmap;
         }
         }
 
 
-        private SKBitmap GetBitmap(string path, bool cropWhitespace, bool forceAnalyzeBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin)
+        private SKBitmap? GetBitmap(string path, bool cropWhitespace, bool forceAnalyzeBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin)
         {
         {
             if (cropWhitespace)
             if (cropWhitespace)
             {
             {
                 using (var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin))
                 using (var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin))
                 {
                 {
+                    if (bitmap == null)
+                    {
+                        return null;
+                    }
+
                     return CropWhiteSpace(bitmap);
                     return CropWhiteSpace(bitmap);
                 }
                 }
             }
             }
@@ -361,13 +370,11 @@ namespace Jellyfin.Drawing.Skia
             return Decode(path, forceAnalyzeBitmap, orientation, out origin);
             return Decode(path, forceAnalyzeBitmap, orientation, out origin);
         }
         }
 
 
-        private SKBitmap GetBitmap(string path, bool cropWhitespace, bool autoOrient, ImageOrientation? orientation)
+        private SKBitmap? GetBitmap(string path, bool cropWhitespace, bool autoOrient, ImageOrientation? orientation)
         {
         {
-            SKEncodedOrigin origin;
-
             if (autoOrient)
             if (autoOrient)
             {
             {
-                var bitmap = GetBitmap(path, cropWhitespace, true, orientation, out origin);
+                var bitmap = GetBitmap(path, cropWhitespace, true, orientation, out var origin);
 
 
                 if (bitmap != null && origin != SKEncodedOrigin.TopLeft)
                 if (bitmap != null && origin != SKEncodedOrigin.TopLeft)
                 {
                 {
@@ -380,7 +387,7 @@ namespace Jellyfin.Drawing.Skia
                 return bitmap;
                 return bitmap;
             }
             }
 
 
-            return GetBitmap(path, cropWhitespace, false, orientation, out origin);
+            return GetBitmap(path, cropWhitespace, false, orientation, out _);
         }
         }
 
 
         private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin)
         private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin)
@@ -517,14 +524,14 @@ namespace Jellyfin.Drawing.Skia
         /// <inheritdoc/>
         /// <inheritdoc/>
         public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
         public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
         {
         {
-            if (string.IsNullOrWhiteSpace(inputPath))
+            if (inputPath.Length == 0)
             {
             {
-                throw new ArgumentNullException(nameof(inputPath));
+                throw new ArgumentException("String can't be empty.", nameof(inputPath));
             }
             }
 
 
-            if (string.IsNullOrWhiteSpace(inputPath))
+            if (outputPath.Length == 0)
             {
             {
-                throw new ArgumentNullException(nameof(outputPath));
+                throw new ArgumentException("String can't be empty.", nameof(outputPath));
             }
             }
 
 
             var skiaOutputFormat = GetImageFormat(selectedOutputFormat);
             var skiaOutputFormat = GetImageFormat(selectedOutputFormat);
@@ -538,7 +545,7 @@ namespace Jellyfin.Drawing.Skia
             {
             {
                 if (bitmap == null)
                 if (bitmap == null)
                 {
                 {
-                    throw new ArgumentOutOfRangeException($"Skia unable to read image {inputPath}");
+                    throw new InvalidDataException($"Skia unable to read image {inputPath}");
                 }
                 }
 
 
                 var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height);
                 var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height);

+ 5 - 5
Jellyfin.Drawing.Skia/StripCollageBuilder.cs

@@ -120,13 +120,13 @@ namespace Jellyfin.Drawing.Skia
                         }
                         }
 
 
                         // resize to the same aspect as the original
                         // resize to the same aspect as the original
-                        int iWidth = (int)Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
+                        int iWidth = Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
                         using (var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
                         using (var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
                         {
                         {
                             currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High);
                             currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High);
 
 
                             // crop image
                             // crop image
-                            int ix = (int)Math.Abs((iWidth - iSlice) / 2);
+                            int ix = Math.Abs((iWidth - iSlice) / 2);
                             using (var image = SKImage.FromBitmap(resizeBitmap))
                             using (var image = SKImage.FromBitmap(resizeBitmap))
                             using (var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight)))
                             using (var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight)))
                             {
                             {
@@ -141,10 +141,10 @@ namespace Jellyfin.Drawing.Skia
             return bitmap;
             return bitmap;
         }
         }
 
 
-        private SKBitmap GetNextValidImage(string[] paths, int currentIndex, out int newIndex)
+        private SKBitmap? GetNextValidImage(string[] paths, int currentIndex, out int newIndex)
         {
         {
             var imagesTested = new Dictionary<int, int>();
             var imagesTested = new Dictionary<int, int>();
-            SKBitmap bitmap = null;
+            SKBitmap? bitmap = null;
 
 
             while (imagesTested.Count < paths.Length)
             while (imagesTested.Count < paths.Length)
             {
             {
@@ -153,7 +153,7 @@ namespace Jellyfin.Drawing.Skia
                     currentIndex = 0;
                     currentIndex = 0;
                 }
                 }
 
 
-                bitmap = _skiaEncoder.Decode(paths[currentIndex], false, null, out var origin);
+                bitmap = _skiaEncoder.Decode(paths[currentIndex], false, null, out _);
 
 
                 imagesTested[currentIndex] = 0;
                 imagesTested[currentIndex] = 0;
 
 

+ 2 - 2
Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs

@@ -32,7 +32,7 @@ namespace Jellyfin.Drawing.Skia
             {
             {
                 paint.Color = SKColor.Parse("#CC00A4DC");
                 paint.Color = SKColor.Parse("#CC00A4DC");
                 paint.Style = SKPaintStyle.Fill;
                 paint.Style = SKPaintStyle.Fill;
-                canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint);
+                canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint);
             }
             }
 
 
             using (var paint = new SKPaint())
             using (var paint = new SKPaint())
@@ -61,7 +61,7 @@ namespace Jellyfin.Drawing.Skia
                     paint.TextSize = 18;
                     paint.TextSize = 18;
                 }
                 }
 
 
-                canvas.DrawText(text, (float)x, y, paint);
+                canvas.DrawText(text, x, y, paint);
             }
             }
         }
         }
     }
     }

+ 22 - 5
Jellyfin.Server/CoreAppHost.cs

@@ -1,9 +1,13 @@
+using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Reflection;
 using System.Reflection;
+using Emby.Drawing;
 using Emby.Server.Implementations;
 using Emby.Server.Implementations;
+using Jellyfin.Drawing.Skia;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
+using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Jellyfin.Server
 namespace Jellyfin.Server
@@ -20,27 +24,40 @@ namespace Jellyfin.Server
         /// <param name="loggerFactory">The <see cref="ILoggerFactory" /> to be used by the <see cref="CoreAppHost" />.</param>
         /// <param name="loggerFactory">The <see cref="ILoggerFactory" /> to be used by the <see cref="CoreAppHost" />.</param>
         /// <param name="options">The <see cref="StartupOptions" /> to be used by the <see cref="CoreAppHost" />.</param>
         /// <param name="options">The <see cref="StartupOptions" /> to be used by the <see cref="CoreAppHost" />.</param>
         /// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
         /// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
-        /// <param name="imageEncoder">The <see cref="IImageEncoder" /> to be used by the <see cref="CoreAppHost" />.</param>
         /// <param name="networkManager">The <see cref="INetworkManager" /> to be used by the <see cref="CoreAppHost" />.</param>
         /// <param name="networkManager">The <see cref="INetworkManager" /> to be used by the <see cref="CoreAppHost" />.</param>
         public CoreAppHost(
         public CoreAppHost(
             ServerApplicationPaths applicationPaths,
             ServerApplicationPaths applicationPaths,
             ILoggerFactory loggerFactory,
             ILoggerFactory loggerFactory,
             StartupOptions options,
             StartupOptions options,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
-            IImageEncoder imageEncoder,
             INetworkManager networkManager)
             INetworkManager networkManager)
             : base(
             : base(
                 applicationPaths,
                 applicationPaths,
                 loggerFactory,
                 loggerFactory,
                 options,
                 options,
                 fileSystem,
                 fileSystem,
-                imageEncoder,
                 networkManager)
                 networkManager)
         {
         {
         }
         }
 
 
-        /// <inheritdoc />
-        public override bool CanSelfRestart => StartupOptions.RestartPath != null;
+        /// <inheritdoc/>
+        protected override void RegisterServices(IServiceCollection serviceCollection)
+        {
+            // Register an image encoder
+            bool useSkiaEncoder = SkiaEncoder.IsNativeLibAvailable();
+            Type imageEncoderType = useSkiaEncoder
+                ? typeof(SkiaEncoder)
+                : typeof(NullImageEncoder);
+            serviceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType);
+
+            // Log a warning if the Skia encoder could not be used
+            if (!useSkiaEncoder)
+            {
+                Logger.LogWarning($"Skia not available. Will fallback to {nameof(NullImageEncoder)}.");
+            }
+
+            base.RegisterServices(serviceCollection);
+        }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         protected override void RestartInternal() => Program.Restart();
         protected override void RestartInternal() => Program.Restart();

+ 5 - 0
Jellyfin.Server/Jellyfin.Server.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{07E39F42-A2C6-4B32-AF8C-725F957A73FF}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <AssemblyName>jellyfin</AssemblyName>
     <AssemblyName>jellyfin</AssemblyName>
     <OutputType>Exe</OutputType>
     <OutputType>Exe</OutputType>

+ 2 - 23
Jellyfin.Server/Program.cs

@@ -168,7 +168,6 @@ namespace Jellyfin.Server
                 _loggerFactory,
                 _loggerFactory,
                 options,
                 options,
                 new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
                 new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
-                GetImageEncoder(appPaths),
                 new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()));
                 new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()));
 
 
             try
             try
@@ -188,14 +187,13 @@ namespace Jellyfin.Server
                 }
                 }
 
 
                 ServiceCollection serviceCollection = new ServiceCollection();
                 ServiceCollection serviceCollection = new ServiceCollection();
-                await appHost.InitAsync(serviceCollection, startupConfig).ConfigureAwait(false);
+                appHost.Init(serviceCollection);
 
 
                 var webHost = new WebHostBuilder().ConfigureWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build();
                 var webHost = new WebHostBuilder().ConfigureWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build();
 
 
                 // Re-use the web host service provider in the app host since ASP.NET doesn't allow a custom service collection.
                 // Re-use the web host service provider in the app host since ASP.NET doesn't allow a custom service collection.
                 appHost.ServiceProvider = webHost.Services;
                 appHost.ServiceProvider = webHost.Services;
-                appHost.InitializeServices();
-                appHost.FindParts();
+                await appHost.InitializeServices().ConfigureAwait(false);
                 Migrations.MigrationRunner.Run(appHost, _loggerFactory);
                 Migrations.MigrationRunner.Run(appHost, _loggerFactory);
 
 
                 try
                 try
@@ -598,25 +596,6 @@ namespace Jellyfin.Server
             }
             }
         }
         }
 
 
-        private static IImageEncoder GetImageEncoder(IApplicationPaths appPaths)
-        {
-            try
-            {
-                // Test if the native lib is available
-                SkiaEncoder.TestSkia();
-
-                return new SkiaEncoder(
-                    _loggerFactory.CreateLogger<SkiaEncoder>(),
-                    appPaths);
-            }
-            catch (Exception ex)
-            {
-                _logger.LogWarning(ex, $"Skia not available. Will fallback to {nameof(NullImageEncoder)}.");
-            }
-
-            return new NullImageEncoder();
-        }
-
         private static void StartNewInstance(StartupOptions options)
         private static void StartNewInstance(StartupOptions options)
         {
         {
             _logger.LogInformation("Starting new instance");
             _logger.LogInformation("Starting new instance");

+ 8 - 1
MediaBrowser.Api/Images/ImageService.cs

@@ -332,7 +332,8 @@ namespace MediaBrowser.Api.Images
                     var fileInfo = _fileSystem.GetFileInfo(info.Path);
                     var fileInfo = _fileSystem.GetFileInfo(info.Path);
                     length = fileInfo.Length;
                     length = fileInfo.Length;
 
 
-                    ImageDimensions size = _imageProcessor.GetImageDimensions(item, info, true);
+                    ImageDimensions size = _imageProcessor.GetImageDimensions(item, info);
+                    _libraryManager.UpdateImages(item);
                     width = size.Width;
                     width = size.Width;
                     height = size.Height;
                     height = size.Height;
 
 
@@ -606,6 +607,12 @@ namespace MediaBrowser.Api.Images
             IDictionary<string, string> headers,
             IDictionary<string, string> headers,
             bool isHeadRequest)
             bool isHeadRequest)
         {
         {
+            if (!image.IsLocalFile)
+            {
+                item ??= _libraryManager.GetItemById(itemId);
+                image = await _libraryManager.ConvertImageToLocal(item, image, request.Index ?? 0).ConfigureAwait(false);
+            }
+
             var options = new ImageProcessingOptions
             var options = new ImageProcessingOptions
             {
             {
                 CropWhiteSpace = cropwhitespace,
                 CropWhiteSpace = cropwhitespace,

+ 5 - 0
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{4FD51AC5-2C16-4308-A993-C3A84F3B4582}</ProjectGuid>
+  </PropertyGroup>
+
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />

+ 1 - 43
MediaBrowser.Api/PackageService.cs

@@ -42,23 +42,6 @@ namespace MediaBrowser.Api
     [Authenticated]
     [Authenticated]
     public class GetPackages : IReturn<PackageInfo[]>
     public class GetPackages : IReturn<PackageInfo[]>
     {
     {
-        /// <summary>
-        /// Gets or sets the name.
-        /// </summary>
-        /// <value>The name.</value>
-        [ApiMember(Name = "PackageType", Description = "Optional package type filter (System/UserInstalled)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public string PackageType { get; set; }
-
-        [ApiMember(Name = "TargetSystems", Description = "Optional. Filter by target system type. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
-        public string TargetSystems { get; set; }
-
-        [ApiMember(Name = "IsPremium", Description = "Optional. Filter by premium status", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
-        public bool? IsPremium { get; set; }
-
-        [ApiMember(Name = "IsAdult", Description = "Optional. Filter by package that contain adult content.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
-        public bool? IsAdult { get; set; }
-
-        public bool? IsAppStoreEnabled { get; set; }
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -88,13 +71,6 @@ namespace MediaBrowser.Api
         /// <value>The version.</value>
         /// <value>The version.</value>
         [ApiMember(Name = "Version", Description = "Optional version. Defaults to latest version.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
         [ApiMember(Name = "Version", Description = "Optional version. Defaults to latest version.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
         public string Version { get; set; }
         public string Version { get; set; }
-
-        /// <summary>
-        /// Gets or sets the update class.
-        /// </summary>
-        /// <value>The update class.</value>
-        [ApiMember(Name = "UpdateClass", Description = "Optional update class (Dev, Beta, Release). Defaults to Release.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
-        public PackageVersionClass UpdateClass { get; set; }
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -154,23 +130,6 @@ namespace MediaBrowser.Api
         {
         {
             IEnumerable<PackageInfo> packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false);
             IEnumerable<PackageInfo> packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false);
 
 
-            if (!string.IsNullOrEmpty(request.TargetSystems))
-            {
-                var apps = request.TargetSystems.Split(',').Select(i => (PackageTargetSystem)Enum.Parse(typeof(PackageTargetSystem), i, true));
-
-                packages = packages.Where(p => apps.Contains(p.targetSystem));
-            }
-
-            if (request.IsAdult.HasValue)
-            {
-                packages = packages.Where(p => p.adult == request.IsAdult.Value);
-            }
-
-            if (request.IsAppStoreEnabled.HasValue)
-            {
-                packages = packages.Where(p => p.enableInAppStore == request.IsAppStoreEnabled.Value);
-            }
-
             return ToOptimizedResult(packages.ToArray());
             return ToOptimizedResult(packages.ToArray());
         }
         }
 
 
@@ -186,8 +145,7 @@ namespace MediaBrowser.Api
                     packages,
                     packages,
                     request.Name,
                     request.Name,
                     string.IsNullOrEmpty(request.AssemblyGuid) ? Guid.Empty : Guid.Parse(request.AssemblyGuid),
                     string.IsNullOrEmpty(request.AssemblyGuid) ? Guid.Empty : Guid.Parse(request.AssemblyGuid),
-                    string.IsNullOrEmpty(request.Version) ? null : Version.Parse(request.Version),
-                    request.UpdateClass).FirstOrDefault();
+                    string.IsNullOrEmpty(request.Version) ? null : Version.Parse(request.Version)).FirstOrDefault();
 
 
             if (package == null)
             if (package == null)
             {
             {

+ 1 - 1
MediaBrowser.Api/UserService.cs

@@ -426,7 +426,7 @@ namespace MediaBrowser.Api
             catch (SecurityException e)
             catch (SecurityException e)
             {
             {
                 // rethrow adding IP address to message
                 // rethrow adding IP address to message
-                throw new SecurityException($"[{Request.RemoteIp}] {e.Message}");
+                throw new SecurityException($"[{Request.RemoteIp}] {e.Message}", e);
             }
             }
         }
         }
 
 

+ 2 - 0
MediaBrowser.Common/Extensions/ShuffleExtensions.cs

@@ -1,3 +1,5 @@
+#nullable enable
+
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 
 

+ 1 - 11
MediaBrowser.Common/IApplicationHost.cs

@@ -2,8 +2,6 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Common.Plugins;
-using MediaBrowser.Model.Updates;
-using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
 
 
 namespace MediaBrowser.Common
 namespace MediaBrowser.Common
@@ -48,12 +46,6 @@ namespace MediaBrowser.Common
         /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
         /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
         bool CanSelfRestart { get; }
         bool CanSelfRestart { get; }
 
 
-        /// <summary>
-        /// Gets the version class of the system.
-        /// </summary>
-        /// <value><see cref="PackageVersionClass.Release" /> or <see cref="PackageVersionClass.Beta" />.</value>
-        PackageVersionClass SystemUpdateLevel { get; }
-
         /// <summary>
         /// <summary>
         /// Gets the application version.
         /// Gets the application version.
         /// </summary>
         /// </summary>
@@ -125,9 +117,7 @@ namespace MediaBrowser.Common
         /// Initializes this instance.
         /// Initializes this instance.
         /// </summary>
         /// </summary>
         /// <param name="serviceCollection">The service collection.</param>
         /// <param name="serviceCollection">The service collection.</param>
-        /// <param name="startupConfig">The configuration to use for initialization.</param>
-        /// <returns>A task.</returns>
-        Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig);
+        void Init(IServiceCollection serviceCollection);
 
 
         /// <summary>
         /// <summary>
         /// Creates the instance.
         /// Creates the instance.

+ 5 - 0
MediaBrowser.Common/MediaBrowser.Common.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <Authors>Jellyfin Contributors</Authors>
     <Authors>Jellyfin Contributors</Authors>
     <PackageId>Jellyfin.Common</PackageId>
     <PackageId>Jellyfin.Common</PackageId>

+ 4 - 4
MediaBrowser.Common/Plugins/BasePlugin.cs

@@ -67,7 +67,7 @@ namespace MediaBrowser.Common.Plugins
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Called when just before the plugin is uninstalled from the server.
+        /// Called just before the plugin is uninstalled from the server.
         /// </summary>
         /// </summary>
         public virtual void OnUninstalling()
         public virtual void OnUninstalling()
         {
         {
@@ -101,7 +101,7 @@ namespace MediaBrowser.Common.Plugins
         private readonly object _configurationSyncLock = new object();
         private readonly object _configurationSyncLock = new object();
 
 
         /// <summary>
         /// <summary>
-        /// The save lock.
+        /// The configuration save lock.
         /// </summary>
         /// </summary>
         private readonly object _configurationSaveLock = new object();
         private readonly object _configurationSaveLock = new object();
 
 
@@ -148,7 +148,7 @@ namespace MediaBrowser.Common.Plugins
         protected string AssemblyFileName => Path.GetFileName(AssemblyFilePath);
         protected string AssemblyFileName => Path.GetFileName(AssemblyFilePath);
 
 
         /// <summary>
         /// <summary>
-        /// Gets or sets the plugin's configuration.
+        /// Gets or sets the plugin configuration.
         /// </summary>
         /// </summary>
         /// <value>The configuration.</value>
         /// <value>The configuration.</value>
         public TConfigurationType Configuration
         public TConfigurationType Configuration
@@ -186,7 +186,7 @@ namespace MediaBrowser.Common.Plugins
         public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName);
         public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName);
 
 
         /// <summary>
         /// <summary>
-        /// Gets the plugin's configuration.
+        /// Gets the plugin configuration.
         /// </summary>
         /// </summary>
         /// <value>The configuration.</value>
         /// <value>The configuration.</value>
         BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration;
         BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration;

+ 9 - 13
MediaBrowser.Common/Updates/IInstallationManager.cs

@@ -28,12 +28,12 @@ namespace MediaBrowser.Common.Updates
         /// <summary>
         /// <summary>
         /// Occurs when a plugin is updated.
         /// Occurs when a plugin is updated.
         /// </summary>
         /// </summary>
-        event EventHandler<GenericEventArgs<(IPlugin, PackageVersionInfo)>> PluginUpdated;
+        event EventHandler<GenericEventArgs<(IPlugin, VersionInfo)>> PluginUpdated;
 
 
         /// <summary>
         /// <summary>
         /// Occurs when a plugin is installed.
         /// Occurs when a plugin is installed.
         /// </summary>
         /// </summary>
-        event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled;
+        event EventHandler<GenericEventArgs<VersionInfo>> PluginInstalled;
 
 
         /// <summary>
         /// <summary>
         /// Gets the completed installations.
         /// Gets the completed installations.
@@ -64,12 +64,10 @@ namespace MediaBrowser.Common.Updates
         /// </summary>
         /// </summary>
         /// <param name="availableVersions">The available version of the plugin.</param>
         /// <param name="availableVersions">The available version of the plugin.</param>
         /// <param name="minVersion">The minimum required version of the plugin.</param>
         /// <param name="minVersion">The minimum required version of the plugin.</param>
-        /// <param name="classification">The classification of updates.</param>
         /// <returns>All compatible versions ordered from newest to oldest.</returns>
         /// <returns>All compatible versions ordered from newest to oldest.</returns>
-        IEnumerable<PackageVersionInfo> GetCompatibleVersions(
-            IEnumerable<PackageVersionInfo> availableVersions,
-            Version minVersion = null,
-            PackageVersionClass classification = PackageVersionClass.Release);
+        IEnumerable<VersionInfo> GetCompatibleVersions(
+            IEnumerable<VersionInfo> availableVersions,
+            Version minVersion = null);
 
 
         /// <summary>
         /// <summary>
         /// Returns all compatible versions ordered from newest to oldest.
         /// Returns all compatible versions ordered from newest to oldest.
@@ -78,21 +76,19 @@ namespace MediaBrowser.Common.Updates
         /// <param name="name">The name.</param>
         /// <param name="name">The name.</param>
         /// <param name="guid">The guid of the plugin.</param>
         /// <param name="guid">The guid of the plugin.</param>
         /// <param name="minVersion">The minimum required version of the plugin.</param>
         /// <param name="minVersion">The minimum required version of the plugin.</param>
-        /// <param name="classification">The classification.</param>
         /// <returns>All compatible versions ordered from newest to oldest.</returns>
         /// <returns>All compatible versions ordered from newest to oldest.</returns>
-        IEnumerable<PackageVersionInfo> GetCompatibleVersions(
+        IEnumerable<VersionInfo> GetCompatibleVersions(
             IEnumerable<PackageInfo> availablePackages,
             IEnumerable<PackageInfo> availablePackages,
             string name = null,
             string name = null,
             Guid guid = default,
             Guid guid = default,
-            Version minVersion = null,
-            PackageVersionClass classification = PackageVersionClass.Release);
+            Version minVersion = null);
 
 
         /// <summary>
         /// <summary>
         /// Returns the available plugin updates.
         /// Returns the available plugin updates.
         /// </summary>
         /// </summary>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>The available plugin updates.</returns>
         /// <returns>The available plugin updates.</returns>
-        Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default);
+        Task<IEnumerable<VersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default);
 
 
         /// <summary>
         /// <summary>
         /// Installs the package.
         /// Installs the package.
@@ -100,7 +96,7 @@ namespace MediaBrowser.Common.Updates
         /// <param name="package">The package.</param>
         /// <param name="package">The package.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns><see cref="Task" />.</returns>
         /// <returns><see cref="Task" />.</returns>
-        Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken = default);
+        Task InstallPackage(VersionInfo package, CancellationToken cancellationToken = default);
 
 
         /// <summary>
         /// <summary>
         /// Uninstalls a plugin.
         /// Uninstalls a plugin.

+ 1 - 1
MediaBrowser.Common/Updates/InstallationEventArgs.cs

@@ -8,6 +8,6 @@ namespace MediaBrowser.Common.Updates
     {
     {
         public InstallationInfo InstallationInfo { get; set; }
         public InstallationInfo InstallationInfo { get; set; }
 
 
-        public PackageVersionInfo PackageVersionInfo { get; set; }
+        public VersionInfo VersionInfo { get; set; }
     }
     }
 }
 }

+ 12 - 6
MediaBrowser.Controller/Authentication/AuthenticationException.cs

@@ -7,23 +7,29 @@ namespace MediaBrowser.Controller.Authentication
     /// </summary>
     /// </summary>
     public class AuthenticationException : Exception
     public class AuthenticationException : Exception
     {
     {
-        /// <inheritdoc />
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AuthenticationException"/> class.
+        /// </summary>
         public AuthenticationException() : base()
         public AuthenticationException() : base()
         {
         {
-
         }
         }
 
 
-        /// <inheritdoc />
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AuthenticationException"/> class.
+        /// </summary>
+        /// <param name="message">The message that describes the error.</param>
         public AuthenticationException(string message) : base(message)
         public AuthenticationException(string message) : base(message)
         {
         {
-
         }
         }
 
 
-        /// <inheritdoc />
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AuthenticationException"/> class.
+        /// </summary>
+        /// <param name="message">The message that describes the error.</param>
+        /// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
         public AuthenticationException(string message, Exception innerException)
         public AuthenticationException(string message, Exception innerException)
             : base(message, innerException)
             : base(message, innerException)
         {
         {
-
         }
         }
     }
     }
 }
 }

+ 0 - 9
MediaBrowser.Controller/Drawing/IImageProcessor.cs

@@ -40,15 +40,6 @@ namespace MediaBrowser.Controller.Drawing
         /// <returns>ImageDimensions</returns>
         /// <returns>ImageDimensions</returns>
         ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info);
         ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info);
 
 
-        /// <summary>
-        /// Gets the dimensions of the image.
-        /// </summary>
-        /// <param name="item">The base item.</param>
-        /// <param name="info">The information.</param>
-        /// <param name="updateItem">Whether or not the item info should be updated.</param>
-        /// <returns>ImageDimensions</returns>
-        ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem);
-
         /// <summary>
         /// <summary>
         /// Gets the image cache tag.
         /// Gets the image cache tag.
         /// </summary>
         /// </summary>

+ 5 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <Authors>Jellyfin Contributors</Authors>
     <Authors>Jellyfin Contributors</Authors>
     <PackageId>Jellyfin.Controller</PackageId>
     <PackageId>Jellyfin.Controller</PackageId>

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

@@ -459,7 +459,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             {
             {
                 var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions);
                 var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions);
                 var outputVideoCodec = GetVideoEncoder(state, encodingOptions);
                 var outputVideoCodec = GetVideoEncoder(state, encodingOptions);
-				
+
                 var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
                 var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
 
 
                 if (!hasTextSubs)
                 if (!hasTextSubs)

+ 24 - 8
MediaBrowser.Controller/Net/SecurityException.cs

@@ -2,20 +2,36 @@ using System;
 
 
 namespace MediaBrowser.Controller.Net
 namespace MediaBrowser.Controller.Net
 {
 {
+    /// <summary>
+    /// The exception that is thrown when a user is authenticated, but not authorized to access a requested resource.
+    /// </summary>
     public class SecurityException : Exception
     public class SecurityException : Exception
     {
     {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SecurityException"/> class.
+        /// </summary>
+        public SecurityException()
+            : base()
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SecurityException"/> class.
+        /// </summary>
+        /// <param name="message">The message that describes the error.</param>
         public SecurityException(string message)
         public SecurityException(string message)
             : base(message)
             : base(message)
         {
         {
-
         }
         }
 
 
-        public SecurityExceptionType SecurityExceptionType { get; set; }
-    }
-
-    public enum SecurityExceptionType
-    {
-        Unauthenticated = 0,
-        ParentalControl = 1
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SecurityException"/> class.
+        /// </summary>
+        /// <param name="message">The message that describes the error</param>
+        /// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
+        public SecurityException(string message, Exception innerException)
+            : base(message, innerException)
+        {
+        }
     }
     }
 }
 }

+ 5 - 0
MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}</ProjectGuid>
+  </PropertyGroup>
+
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />

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

@@ -19,7 +19,6 @@ using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.System;
 using MediaBrowser.Model.System;
-using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 using System.Diagnostics;
 using System.Diagnostics;
 
 
@@ -39,8 +38,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly ILocalizationManager _localization;
         private readonly ILocalizationManager _localization;
-        private readonly Func<ISubtitleEncoder> _subtitleEncoder;
-        private readonly IConfiguration _configuration;
+        private readonly Lazy<EncodingHelper> _encodingHelperFactory;
         private readonly string _startupOptionFFmpegPath;
         private readonly string _startupOptionFFmpegPath;
 
 
         private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2);
         private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2);
@@ -48,8 +46,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
         private readonly object _runningProcessesLock = new object();
         private readonly object _runningProcessesLock = new object();
         private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
         private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
 
 
-        private EncodingHelper _encodingHelper;
-
         private string _ffmpegPath;
         private string _ffmpegPath;
         private string _ffprobePath;
         private string _ffprobePath;
 
 
@@ -58,23 +54,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
             IServerConfigurationManager configurationManager,
             IServerConfigurationManager configurationManager,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
             ILocalizationManager localization,
             ILocalizationManager localization,
-            Func<ISubtitleEncoder> subtitleEncoder,
-            IConfiguration configuration,
+            Lazy<EncodingHelper> encodingHelperFactory,
             string startupOptionsFFmpegPath)
             string startupOptionsFFmpegPath)
         {
         {
             _logger = logger;
             _logger = logger;
             _configurationManager = configurationManager;
             _configurationManager = configurationManager;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _localization = localization;
             _localization = localization;
+            _encodingHelperFactory = encodingHelperFactory;
             _startupOptionFFmpegPath = startupOptionsFFmpegPath;
             _startupOptionFFmpegPath = startupOptionsFFmpegPath;
-            _subtitleEncoder = subtitleEncoder;
-            _configuration = configuration;
         }
         }
 
 
-        private EncodingHelper EncodingHelper
-            => LazyInitializer.EnsureInitialized(
-                ref _encodingHelper,
-                () => new EncodingHelper(this, _fileSystem, _subtitleEncoder(), _configuration));
+        private EncodingHelper EncodingHelper => _encodingHelperFactory.Value;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public string EncoderPath => _ffmpegPath;
         public string EncoderPath => _ffmpegPath;

+ 5 - 0
MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{960295EE-4AF4-4440-A525-B4C295B01A61}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netstandard2.1</TargetFramework>
     <TargetFramework>netstandard2.1</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>

+ 5 - 0
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <Authors>Jellyfin Contributors</Authors>
     <Authors>Jellyfin Contributors</Authors>
     <PackageId>Jellyfin.Model</PackageId>
     <PackageId>Jellyfin.Model</PackageId>

+ 5 - 5
MediaBrowser.Model/Services/IHasRequestFilter.cs

@@ -8,17 +8,17 @@ namespace MediaBrowser.Model.Services
     {
     {
         /// <summary>
         /// <summary>
         /// Order in which Request Filters are executed.
         /// Order in which Request Filters are executed.
-        /// &lt;0 Executed before global request filters
-        /// &gt;0 Executed after global request filters
+        /// &lt;0 Executed before global request filters.
+        /// &gt;0 Executed after global request filters.
         /// </summary>
         /// </summary>
         int Priority { get; }
         int Priority { get; }
 
 
         /// <summary>
         /// <summary>
         /// The request filter is executed before the service.
         /// The request filter is executed before the service.
         /// </summary>
         /// </summary>
-        /// <param name="req">The http request wrapper</param>
-        /// <param name="res">The http response wrapper</param>
-        /// <param name="requestDto">The request DTO</param>
+        /// <param name="req">The http request wrapper.</param>
+        /// <param name="res">The http response wrapper.</param>
+        /// <param name="requestDto">The request DTO.</param>
         void RequestFilter(IRequest req, HttpResponse res, object requestDto);
         void RequestFilter(IRequest req, HttpResponse res, object requestDto);
     }
     }
 }
 }

+ 0 - 2
MediaBrowser.Model/System/SystemInfo.cs

@@ -26,8 +26,6 @@ namespace MediaBrowser.Model.System
     /// </summary>
     /// </summary>
     public class SystemInfo : PublicSystemInfo
     public class SystemInfo : PublicSystemInfo
     {
     {
-        public PackageVersionClass SystemUpdateLevel { get; set; }
-
         /// <summary>
         /// <summary>
         /// Gets or sets the display name of the operating system.
         /// Gets or sets the display name of the operating system.
         /// </summary>
         /// </summary>

+ 0 - 29
MediaBrowser.Model/Updates/CheckForUpdateResult.cs

@@ -1,29 +0,0 @@
-namespace MediaBrowser.Model.Updates
-{
-    /// <summary>
-    /// Class CheckForUpdateResult.
-    /// </summary>
-    public class CheckForUpdateResult
-    {
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is update available.
-        /// </summary>
-        /// <value><c>true</c> if this instance is update available; otherwise, <c>false</c>.</value>
-        public bool IsUpdateAvailable { get; set; }
-
-        /// <summary>
-        /// Gets or sets the available version.
-        /// </summary>
-        /// <value>The available version.</value>
-        public string AvailableVersion
-        {
-            get => Package != null ? Package.versionStr : "0.0.0.1";
-            set { } // need this for the serializer
-        }
-
-        /// <summary>
-        /// Get or sets package information for an available update
-        /// </summary>
-        public PackageVersionInfo Package { get; set; }
-    }
-}

+ 3 - 15
MediaBrowser.Model/Updates/InstallationInfo.cs

@@ -8,10 +8,10 @@ namespace MediaBrowser.Model.Updates
     public class InstallationInfo
     public class InstallationInfo
     {
     {
         /// <summary>
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets or sets the guid.
         /// </summary>
         /// </summary>
-        /// <value>The id.</value>
-        public Guid Id { get; set; }
+        /// <value>The guid.</value>
+        public string Guid { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets the name.
         /// Gets or sets the name.
@@ -19,22 +19,10 @@ namespace MediaBrowser.Model.Updates
         /// <value>The name.</value>
         /// <value>The name.</value>
         public string Name { get; set; }
         public string Name { get; set; }
 
 
-        /// <summary>
-        /// Gets or sets the assembly guid.
-        /// </summary>
-        /// <value>The guid of the assembly.</value>
-        public string AssemblyGuid { get; set; }
-
         /// <summary>
         /// <summary>
         /// Gets or sets the version.
         /// Gets or sets the version.
         /// </summary>
         /// </summary>
         /// <value>The version.</value>
         /// <value>The version.</value>
         public string Version { get; set; }
         public string Version { get; set; }
-
-        /// <summary>
-        /// Gets or sets the update class.
-        /// </summary>
-        /// <value>The update class.</value>
-        public PackageVersionClass UpdateClass { get; set; }
     }
     }
 }
 }

+ 7 - 121
MediaBrowser.Model/Updates/PackageInfo.cs

@@ -8,12 +8,6 @@ namespace MediaBrowser.Model.Updates
     /// </summary>
     /// </summary>
     public class PackageInfo
     public class PackageInfo
     {
     {
-        /// <summary>
-        /// The internal id of this package.
-        /// </summary>
-        /// <value>The id.</value>
-        public string id { get; set; }
-
         /// <summary>
         /// <summary>
         /// Gets or sets the name.
         /// Gets or sets the name.
         /// </summary>
         /// </summary>
@@ -21,59 +15,17 @@ namespace MediaBrowser.Model.Updates
         public string name { get; set; }
         public string name { get; set; }
 
 
         /// <summary>
         /// <summary>
-        /// Gets or sets the short description.
+        /// Gets or sets a long description of the plugin containing features or helpful explanations.
         /// </summary>
         /// </summary>
-        /// <value>The short description.</value>
-        public string shortDescription { get; set; }
+        /// <value>The description.</value>
+        public string description { get; set; }
 
 
         /// <summary>
         /// <summary>
-        /// Gets or sets the overview.
+        /// Gets or sets a short overview of what the plugin does.
         /// </summary>
         /// </summary>
         /// <value>The overview.</value>
         /// <value>The overview.</value>
         public string overview { get; set; }
         public string overview { get; set; }
 
 
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is premium.
-        /// </summary>
-        /// <value><c>true</c> if this instance is premium; otherwise, <c>false</c>.</value>
-        public bool isPremium { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is adult only content.
-        /// </summary>
-        /// <value><c>true</c> if this instance is adult; otherwise, <c>false</c>.</value>
-        public bool adult { get; set; }
-
-        /// <summary>
-        /// Gets or sets the rich desc URL.
-        /// </summary>
-        /// <value>The rich desc URL.</value>
-        public string richDescUrl { get; set; }
-
-        /// <summary>
-        /// Gets or sets the thumb image.
-        /// </summary>
-        /// <value>The thumb image.</value>
-        public string thumbImage { get; set; }
-
-        /// <summary>
-        /// Gets or sets the preview image.
-        /// </summary>
-        /// <value>The preview image.</value>
-        public string previewImage { get; set; }
-
-        /// <summary>
-        /// Gets or sets the type.
-        /// </summary>
-        /// <value>The type.</value>
-        public string type { get; set; }
-
-        /// <summary>
-        /// Gets or sets the target filename.
-        /// </summary>
-        /// <value>The target filename.</value>
-        public string targetFilename { get; set; }
-
         /// <summary>
         /// <summary>
         /// Gets or sets the owner.
         /// Gets or sets the owner.
         /// </summary>
         /// </summary>
@@ -87,90 +39,24 @@ namespace MediaBrowser.Model.Updates
         public string category { get; set; }
         public string category { get; set; }
 
 
         /// <summary>
         /// <summary>
-        /// Gets or sets the catalog tile color.
-        /// </summary>
-        /// <value>The owner.</value>
-        public string tileColor { get; set; }
-
-        /// <summary>
-        /// Gets or sets the feature id of this package (if premium).
-        /// </summary>
-        /// <value>The feature id.</value>
-        public string featureId { get; set; }
-
-        /// <summary>
-        /// Gets or sets the registration info for this package (if premium).
-        /// </summary>
-        /// <value>The registration info.</value>
-        public string regInfo { get; set; }
-
-        /// <summary>
-        /// Gets or sets the price for this package (if premium).
-        /// </summary>
-        /// <value>The price.</value>
-        public float price { get; set; }
-
-        /// <summary>
-        /// Gets or sets the target system for this plug-in (Server, MBTheater, MBClassic).
-        /// </summary>
-        /// <value>The target system.</value>
-        public PackageTargetSystem targetSystem { get; set; }
-
-        /// <summary>
-        /// The guid of the assembly associated with this package (if a plug-in).
+        /// The guid of the assembly associated with this plugin.
         /// This is used to identify the proper item for automatic updates.
         /// This is used to identify the proper item for automatic updates.
         /// </summary>
         /// </summary>
         /// <value>The name.</value>
         /// <value>The name.</value>
         public string guid { get; set; }
         public string guid { get; set; }
 
 
-        /// <summary>
-        /// Gets or sets the total number of ratings for this package.
-        /// </summary>
-        /// <value>The total ratings.</value>
-        public int? totalRatings { get; set; }
-
-        /// <summary>
-        /// Gets or sets the average rating for this package .
-        /// </summary>
-        /// <value>The rating.</value>
-        public float avgRating { get; set; }
-
-        /// <summary>
-        /// Gets or sets whether or not this package is registered.
-        /// </summary>
-        /// <value>True if registered.</value>
-        public bool isRegistered { get; set; }
-
-        /// <summary>
-        /// Gets or sets the expiration date for this package.
-        /// </summary>
-        /// <value>Expiration Date.</value>
-        public DateTime expDate { get; set; }
-
         /// <summary>
         /// <summary>
         /// Gets or sets the versions.
         /// Gets or sets the versions.
         /// </summary>
         /// </summary>
         /// <value>The versions.</value>
         /// <value>The versions.</value>
-        public IReadOnlyList<PackageVersionInfo> versions { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether [enable in application store].
-        /// </summary>
-        /// <value><c>true</c> if [enable in application store]; otherwise, <c>false</c>.</value>
-        public bool enableInAppStore { get; set; }
-
-        /// <summary>
-        /// Gets or sets the installs.
-        /// </summary>
-        /// <value>The installs.</value>
-        public int installs { get; set; }
+        public IReadOnlyList<VersionInfo> versions { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="PackageInfo"/> class.
         /// Initializes a new instance of the <see cref="PackageInfo"/> class.
         /// </summary>
         /// </summary>
         public PackageInfo()
         public PackageInfo()
         {
         {
-            versions = Array.Empty<PackageVersionInfo>();
+            versions = Array.Empty<VersionInfo>();
         }
         }
     }
     }
 }
 }

+ 0 - 23
MediaBrowser.Model/Updates/PackageTargetSystem.cs

@@ -1,23 +0,0 @@
-namespace MediaBrowser.Model.Updates
-{
-    /// <summary>
-    /// Enum PackageType.
-    /// </summary>
-    public enum PackageTargetSystem
-    {
-        /// <summary>
-        /// Server.
-        /// </summary>
-        Server,
-
-        /// <summary>
-        /// MB Theater.
-        /// </summary>
-        MBTheater,
-
-        /// <summary>
-        /// MB Classic.
-        /// </summary>
-        MBClassic
-    }
-}

+ 0 - 23
MediaBrowser.Model/Updates/PackageVersionClass.cs

@@ -1,23 +0,0 @@
-namespace MediaBrowser.Model.Updates
-{
-    /// <summary>
-    /// Enum PackageVersionClass.
-    /// </summary>
-    public enum PackageVersionClass
-    {
-        /// <summary>
-        /// The release.
-        /// </summary>
-        Release = 0,
-
-        /// <summary>
-        /// The beta.
-        /// </summary>
-        Beta = 1,
-
-        /// <summary>
-        /// The dev.
-        /// </summary>
-        Dev = 2
-    }
-}

+ 0 - 96
MediaBrowser.Model/Updates/PackageVersionInfo.cs

@@ -1,96 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Model.Updates
-{
-    /// <summary>
-    /// Class PackageVersionInfo.
-    /// </summary>
-    public class PackageVersionInfo
-    {
-        /// <summary>
-        /// Gets or sets the name.
-        /// </summary>
-        /// <value>The name.</value>
-        public string name { get; set; }
-
-        /// <summary>
-        /// Gets or sets the guid.
-        /// </summary>
-        /// <value>The guid.</value>
-        public string guid { get; set; }
-
-        /// <summary>
-        /// Gets or sets the version STR.
-        /// </summary>
-        /// <value>The version STR.</value>
-        public string versionStr { get; set; }
-
-        /// <summary>
-        /// The _version
-        /// </summary>
-        private Version _version;
-
-        /// <summary>
-        /// Gets or sets the version.
-        /// Had to make this an interpreted property since Protobuf can't handle Version
-        /// </summary>
-        /// <value>The version.</value>
-        [JsonIgnore]
-        public Version Version
-        {
-            get
-            {
-                if (_version == null)
-                {
-                    var ver = versionStr;
-                    _version = new Version(string.IsNullOrEmpty(ver) ? "0.0.0.1" : ver);
-                }
-
-                return _version;
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets the classification.
-        /// </summary>
-        /// <value>The classification.</value>
-        public PackageVersionClass classification { get; set; }
-
-        /// <summary>
-        /// Gets or sets the description.
-        /// </summary>
-        /// <value>The description.</value>
-        public string description { get; set; }
-
-        /// <summary>
-        /// Gets or sets the required version STR.
-        /// </summary>
-        /// <value>The required version STR.</value>
-        public string requiredVersionStr { get; set; }
-
-        /// <summary>
-        /// Gets or sets the source URL.
-        /// </summary>
-        /// <value>The source URL.</value>
-        public string sourceUrl { get; set; }
-
-        /// <summary>
-        /// Gets or sets the source URL.
-        /// </summary>
-        /// <value>The source URL.</value>
-        public string checksum { get; set; }
-
-        /// <summary>
-        /// Gets or sets the target filename.
-        /// </summary>
-        /// <value>The target filename.</value>
-        public string targetFilename { get; set; }
-
-        public string infoUrl { get; set; }
-
-        public string runtimes { get; set; }
-    }
-}

+ 58 - 0
MediaBrowser.Model/Updates/VersionInfo.cs

@@ -0,0 +1,58 @@
+using System;
+
+namespace MediaBrowser.Model.Updates
+{
+    /// <summary>
+    /// Class PackageVersionInfo.
+    /// </summary>
+    public class VersionInfo
+    {
+        /// <summary>
+        /// Gets or sets the name.
+        /// </summary>
+        /// <value>The name.</value>
+        public string name { get; set; }
+
+        /// <summary>
+        /// Gets or sets the guid.
+        /// </summary>
+        /// <value>The guid.</value>
+        public string guid { get; set; }
+
+        /// <summary>
+        /// Gets or sets the version.
+        /// </summary>
+        /// <value>The version.</value>
+        public Version version { get; set; }
+
+        /// <summary>
+        /// Gets or sets the changelog for this version.
+        /// </summary>
+        /// <value>The changelog.</value>
+        public string changelog { get; set; }
+
+        /// <summary>
+        /// Gets or sets the ABI that this version was built against.
+        /// </summary>
+        /// <value>The target ABI version.</value>
+        public string targetAbi { get; set; }
+
+        /// <summary>
+        /// Gets or sets the source URL.
+        /// </summary>
+        /// <value>The source URL.</value>
+        public string sourceUrl { get; set; }
+
+        /// <summary>
+        /// Gets or sets a checksum for the binary.
+        /// </summary>
+        /// <value>The checksum.</value>
+        public string checksum { get; set; }
+
+        /// <summary>
+        /// Gets or sets the target filename for the downloaded binary.
+        /// </summary>
+        /// <value>The target filename.</value>
+        public string filename { get; set; }
+    }
+}

+ 32 - 41
MediaBrowser.Providers/Manager/ProviderManager.cs

@@ -36,60 +36,51 @@ namespace MediaBrowser.Providers.Manager
     /// </summary>
     /// </summary>
     public class ProviderManager : IProviderManager, IDisposable
     public class ProviderManager : IProviderManager, IDisposable
     {
     {
-        /// <summary>
-        /// The _logger
-        /// </summary>
         private readonly ILogger _logger;
         private readonly ILogger _logger;
-
-        /// <summary>
-        /// The _HTTP client
-        /// </summary>
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
-
-        /// <summary>
-        /// The _directory watchers
-        /// </summary>
         private readonly ILibraryMonitor _libraryMonitor;
         private readonly ILibraryMonitor _libraryMonitor;
-
-        /// <summary>
-        /// Gets or sets the configuration manager.
-        /// </summary>
-        /// <value>The configuration manager.</value>
-        private IServerConfigurationManager ConfigurationManager { get; set; }
+        private readonly IFileSystem _fileSystem;
+        private readonly IServerApplicationPaths _appPaths;
+        private readonly IJsonSerializer _json;
+        private readonly ILibraryManager _libraryManager;
+        private readonly ISubtitleManager _subtitleManager;
+        private readonly IServerConfigurationManager _configurationManager;
 
 
         private IImageProvider[] ImageProviders { get; set; }
         private IImageProvider[] ImageProviders { get; set; }
 
 
-        private readonly IFileSystem _fileSystem;
-
         private IMetadataService[] _metadataServices = { };
         private IMetadataService[] _metadataServices = { };
         private IMetadataProvider[] _metadataProviders = { };
         private IMetadataProvider[] _metadataProviders = { };
         private IEnumerable<IMetadataSaver> _savers;
         private IEnumerable<IMetadataSaver> _savers;
-        private readonly IServerApplicationPaths _appPaths;
-        private readonly IJsonSerializer _json;
 
 
         private IExternalId[] _externalIds;
         private IExternalId[] _externalIds;
 
 
-        private readonly Func<ILibraryManager> _libraryManagerFactory;
         private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
         private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
 
 
         public event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted;
         public event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted;
         public event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted;
         public event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted;
         public event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress;
         public event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress;
 
 
-        private ISubtitleManager _subtitleManager;
-
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="ProviderManager" /> class.
         /// Initializes a new instance of the <see cref="ProviderManager" /> class.
         /// </summary>
         /// </summary>
-        public ProviderManager(IHttpClient httpClient, ISubtitleManager subtitleManager, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILoggerFactory loggerFactory, IFileSystem fileSystem, IServerApplicationPaths appPaths, Func<ILibraryManager> libraryManagerFactory, IJsonSerializer json)
+        public ProviderManager(
+            IHttpClient httpClient,
+            ISubtitleManager subtitleManager,
+            IServerConfigurationManager configurationManager,
+            ILibraryMonitor libraryMonitor,
+            ILogger<ProviderManager> logger,
+            IFileSystem fileSystem,
+            IServerApplicationPaths appPaths,
+            ILibraryManager libraryManager,
+            IJsonSerializer json)
         {
         {
-            _logger = loggerFactory.CreateLogger("ProviderManager");
+            _logger = logger;
             _httpClient = httpClient;
             _httpClient = httpClient;
-            ConfigurationManager = configurationManager;
+            _configurationManager = configurationManager;
             _libraryMonitor = libraryMonitor;
             _libraryMonitor = libraryMonitor;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _appPaths = appPaths;
             _appPaths = appPaths;
-            _libraryManagerFactory = libraryManagerFactory;
+            _libraryManager = libraryManager;
             _json = json;
             _json = json;
             _subtitleManager = subtitleManager;
             _subtitleManager = subtitleManager;
         }
         }
@@ -176,7 +167,7 @@ namespace MediaBrowser.Providers.Manager
 
 
         public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
         public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
         {
         {
-            return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken);
+            return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken);
         }
         }
 
 
         public Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
         public Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
@@ -188,7 +179,7 @@ namespace MediaBrowser.Providers.Manager
 
 
             var fileStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, true);
             var fileStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, true);
 
 
-            return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
+            return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
         }
         }
 
 
         public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, RemoteImageQuery query, CancellationToken cancellationToken)
         public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, RemoteImageQuery query, CancellationToken cancellationToken)
@@ -273,7 +264,7 @@ namespace MediaBrowser.Providers.Manager
 
 
         public IEnumerable<IImageProvider> GetImageProviders(BaseItem item, ImageRefreshOptions refreshOptions)
         public IEnumerable<IImageProvider> GetImageProviders(BaseItem item, ImageRefreshOptions refreshOptions)
         {
         {
-            return GetImageProviders(item, _libraryManagerFactory().GetLibraryOptions(item), GetMetadataOptions(item), refreshOptions, false);
+            return GetImageProviders(item, _libraryManager.GetLibraryOptions(item), GetMetadataOptions(item), refreshOptions, false);
         }
         }
 
 
         private IEnumerable<IImageProvider> GetImageProviders(BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled)
         private IEnumerable<IImageProvider> GetImageProviders(BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled)
@@ -328,7 +319,7 @@ namespace MediaBrowser.Providers.Manager
         private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(BaseItem item, bool includeDisabled)
         private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(BaseItem item, bool includeDisabled)
         {
         {
             var options = GetMetadataOptions(item);
             var options = GetMetadataOptions(item);
-            var libraryOptions = _libraryManagerFactory().GetLibraryOptions(item);
+            var libraryOptions = _libraryManager.GetLibraryOptions(item);
 
 
             return GetImageProviders(item, libraryOptions, options,
             return GetImageProviders(item, libraryOptions, options,
                     new ImageRefreshOptions(
                     new ImageRefreshOptions(
@@ -593,7 +584,7 @@ namespace MediaBrowser.Providers.Manager
         {
         {
             var type = item.GetType().Name;
             var type = item.GetType().Name;
 
 
-            return ConfigurationManager.Configuration.MetadataOptions
+            return _configurationManager.Configuration.MetadataOptions
                 .FirstOrDefault(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) ??
                 .FirstOrDefault(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) ??
                 new MetadataOptions();
                 new MetadataOptions();
         }
         }
@@ -623,7 +614,7 @@ namespace MediaBrowser.Providers.Manager
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         private void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable<IMetadataSaver> savers)
         private void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable<IMetadataSaver> savers)
         {
         {
-            var libraryOptions = _libraryManagerFactory().GetLibraryOptions(item);
+            var libraryOptions = _libraryManager.GetLibraryOptions(item);
 
 
             foreach (var saver in savers.Where(i => IsSaverEnabledForItem(i, item, libraryOptions, updateType, false)))
             foreach (var saver in savers.Where(i => IsSaverEnabledForItem(i, item, libraryOptions, updateType, false)))
             {
             {
@@ -743,7 +734,7 @@ namespace MediaBrowser.Providers.Manager
 
 
             if (!searchInfo.ItemId.Equals(Guid.Empty))
             if (!searchInfo.ItemId.Equals(Guid.Empty))
             {
             {
-                referenceItem = _libraryManagerFactory().GetItemById(searchInfo.ItemId);
+                referenceItem = _libraryManager.GetItemById(searchInfo.ItemId);
             }
             }
 
 
             return GetRemoteSearchResults<TItemType, TLookupType>(searchInfo, referenceItem, cancellationToken);
             return GetRemoteSearchResults<TItemType, TLookupType>(searchInfo, referenceItem, cancellationToken);
@@ -771,7 +762,7 @@ namespace MediaBrowser.Providers.Manager
             }
             }
             else
             else
             {
             {
-                libraryOptions = _libraryManagerFactory().GetLibraryOptions(referenceItem);
+                libraryOptions = _libraryManager.GetLibraryOptions(referenceItem);
             }
             }
 
 
             var options = GetMetadataOptions(referenceItem);
             var options = GetMetadataOptions(referenceItem);
@@ -786,11 +777,11 @@ namespace MediaBrowser.Providers.Manager
 
 
             if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataLanguage))
             if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataLanguage))
             {
             {
-                searchInfo.SearchInfo.MetadataLanguage = ConfigurationManager.Configuration.PreferredMetadataLanguage;
+                searchInfo.SearchInfo.MetadataLanguage = _configurationManager.Configuration.PreferredMetadataLanguage;
             }
             }
             if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataCountryCode))
             if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataCountryCode))
             {
             {
-                searchInfo.SearchInfo.MetadataCountryCode = ConfigurationManager.Configuration.MetadataCountryCode;
+                searchInfo.SearchInfo.MetadataCountryCode = _configurationManager.Configuration.MetadataCountryCode;
             }
             }
 
 
             var resultList = new List<RemoteSearchResult>();
             var resultList = new List<RemoteSearchResult>();
@@ -967,7 +958,7 @@ namespace MediaBrowser.Providers.Manager
         public void OnRefreshProgress(BaseItem item, double progress)
         public void OnRefreshProgress(BaseItem item, double progress)
         {
         {
             var id = item.Id;
             var id = item.Id;
-            _logger.LogInformation("OnRefreshProgress {0} {1}", id.ToString("N", CultureInfo.InvariantCulture), progress);
+            _logger.LogDebug("OnRefreshProgress {0} {1}", id.ToString("N", CultureInfo.InvariantCulture), progress);
 
 
             // TODO: Need to hunt down the conditions for this happening
             // TODO: Need to hunt down the conditions for this happening
             _activeRefreshes.AddOrUpdate(
             _activeRefreshes.AddOrUpdate(
@@ -1010,7 +1001,7 @@ namespace MediaBrowser.Providers.Manager
 
 
         private async Task StartProcessingRefreshQueue()
         private async Task StartProcessingRefreshQueue()
         {
         {
-            var libraryManager = _libraryManagerFactory();
+            var libraryManager = _libraryManager;
 
 
             if (_disposed)
             if (_disposed)
             {
             {
@@ -1088,7 +1079,7 @@ namespace MediaBrowser.Providers.Manager
 
 
         private async Task RefreshArtist(MusicArtist item, MetadataRefreshOptions options, CancellationToken cancellationToken)
         private async Task RefreshArtist(MusicArtist item, MetadataRefreshOptions options, CancellationToken cancellationToken)
         {
         {
-            var albums = _libraryManagerFactory()
+            var albums = _libraryManager
                 .GetItemList(new InternalItemsQuery
                 .GetItemList(new InternalItemsQuery
                 {
                 {
                     IncludeItemTypes = new[] { nameof(MusicAlbum) },
                     IncludeItemTypes = new[] { nameof(MusicAlbum) },

+ 5 - 0
MediaBrowser.Providers/MediaBrowser.Providers.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{442B5058-DCAF-4263-BB6A-F21E31120A1B}</ProjectGuid>
+  </PropertyGroup>
+
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />

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

@@ -25,22 +25,22 @@ namespace MediaBrowser.Providers.Subtitles
 {
 {
     public class SubtitleManager : ISubtitleManager
     public class SubtitleManager : ISubtitleManager
     {
     {
-        private ISubtitleProvider[] _subtitleProviders;
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly ILibraryMonitor _monitor;
         private readonly ILibraryMonitor _monitor;
         private readonly IMediaSourceManager _mediaSourceManager;
         private readonly IMediaSourceManager _mediaSourceManager;
+        private readonly ILocalizationManager _localization;
 
 
-        private ILocalizationManager _localization;
+        private ISubtitleProvider[] _subtitleProviders;
 
 
         public SubtitleManager(
         public SubtitleManager(
-            ILoggerFactory loggerFactory,
+            ILogger<SubtitleManager> logger,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
             ILibraryMonitor monitor,
             ILibraryMonitor monitor,
             IMediaSourceManager mediaSourceManager,
             IMediaSourceManager mediaSourceManager,
             ILocalizationManager localizationManager)
             ILocalizationManager localizationManager)
         {
         {
-            _logger = loggerFactory.CreateLogger(nameof(SubtitleManager));
+            _logger = logger;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _monitor = monitor;
             _monitor = monitor;
             _mediaSourceManager = mediaSourceManager;
             _mediaSourceManager = mediaSourceManager;

+ 5 - 0
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{5624B7B5-B5A7-41D8-9F10-CC5611109619}</ProjectGuid>
+  </PropertyGroup>
+
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />

+ 5 - 0
MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{23499896-B135-4527-8574-C26E926EA99E}</ProjectGuid>
+  </PropertyGroup>
+
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />

+ 5 - 0
RSSDP/RSSDP.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{21002819-C39A-4D3E-BE83-2A276A77FB1F}</ProjectGuid>
+  </PropertyGroup>
+
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />

+ 5 - 0
tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <IsPackable>false</IsPackable>
     <IsPackable>false</IsPackable>

+ 5 - 0
tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{DF194677-DFD3-42AF-9F75-D44D5A416478}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <IsPackable>false</IsPackable>
     <IsPackable>false</IsPackable>

+ 5 - 0
tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{462584F7-5023-4019-9EAC-B98CA458C0A0}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <IsPackable>false</IsPackable>
     <IsPackable>false</IsPackable>

+ 5 - 0
tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{28464062-0939-4AA7-9F7B-24DDDA61A7C0}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <IsPackable>false</IsPackable>
     <IsPackable>false</IsPackable>

+ 5 - 0
tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{3998657B-1CCC-49DD-A19F-275DC8495F57}</ProjectGuid>
+  </PropertyGroup>
+
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <IsPackable>false</IsPackable>
     <IsPackable>false</IsPackable>

+ 5 - 0
tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj

@@ -1,5 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
+  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+  <PropertyGroup>
+    <ProjectGuid>{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}</ProjectGuid>
+  </PropertyGroup>
+
     <PropertyGroup>
     <PropertyGroup>
       <TargetFramework>netcoreapp3.1</TargetFramework>
       <TargetFramework>netcoreapp3.1</TargetFramework>
       <IsPackable>false</IsPackable>
       <IsPackable>false</IsPackable>

+ 2 - 4
tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs

@@ -82,11 +82,10 @@ namespace MediaBrowser.Api.Tests
                 loggerFactory,
                 loggerFactory,
                 commandLineOpts,
                 commandLineOpts,
                 new ManagedFileSystem(loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
                 new ManagedFileSystem(loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
-                new SkiaEncoder(loggerFactory.CreateLogger<SkiaEncoder>(), appPaths),
                 new NetworkManager(loggerFactory.CreateLogger<NetworkManager>()));
                 new NetworkManager(loggerFactory.CreateLogger<NetworkManager>()));
             _appHosts.Add(appHost);
             _appHosts.Add(appHost);
             var serviceCollection = new ServiceCollection();
             var serviceCollection = new ServiceCollection();
-            appHost.InitAsync(serviceCollection, startupConfig).Wait();
+            appHost.Init(serviceCollection);
 
 
             // Configure the web host builder
             // Configure the web host builder
             Program.ConfigureWebHostBuilder(builder, appHost, serviceCollection, commandLineOpts, startupConfig, appPaths);
             Program.ConfigureWebHostBuilder(builder, appHost, serviceCollection, commandLineOpts, startupConfig, appPaths);
@@ -101,8 +100,7 @@ namespace MediaBrowser.Api.Tests
             // Finish initializing the app host
             // Finish initializing the app host
             var appHost = (CoreAppHost)testServer.Services.GetRequiredService<IApplicationHost>();
             var appHost = (CoreAppHost)testServer.Services.GetRequiredService<IApplicationHost>();
             appHost.ServiceProvider = testServer.Services;
             appHost.ServiceProvider = testServer.Services;
-            appHost.InitializeServices();
-            appHost.FindParts();
+            appHost.InitializeServices().Wait();
             appHost.RunStartupTasksAsync().Wait();
             appHost.RunStartupTasksAsync().Wait();
 
 
             return testServer;
             return testServer;