Sfoglia il codice sorgente

Merge remote-tracking branch 'upstream/master' into baseitemkind-fixes

Cody Robibero 3 anni fa
parent
commit
398ca85944
100 ha cambiato i file con 407 aggiunte e 517 eliminazioni
  1. 1 1
      .ci/azure-pipelines-abi.yml
  2. 1 1
      .ci/azure-pipelines-main.yml
  3. 1 1
      .ci/azure-pipelines-test.yml
  4. 1 1
      .ci/azure-pipelines.yml
  5. 1 0
      .github/ISSUE_TEMPLATE/bug_report.md
  6. 5 1
      .github/stale.yml
  7. 7 5
      .github/workflows/automation.yml
  8. 14 0
      Directory.Build.props
  9. 2 1
      DvdLib/DvdLib.csproj
  10. 1 3
      Emby.Dlna/Configuration/DlnaOptions.cs
  11. 1 3
      Emby.Dlna/ContentDirectory/ContentDirectoryService.cs
  12. 3 3
      Emby.Dlna/ControlResponse.cs
  13. 1 1
      Emby.Dlna/DlnaManager.cs
  14. 1 6
      Emby.Dlna/Emby.Dlna.csproj
  15. 3 3
      Emby.Dlna/EventSubscriptionResponse.cs
  16. 3 15
      Emby.Dlna/Eventing/DlnaEventManager.cs
  17. 8 5
      Emby.Dlna/Main/DlnaEntryPoint.cs
  18. 4 17
      Emby.Dlna/PlayTo/Device.cs
  19. 7 3
      Emby.Dlna/PlayTo/MediaChangedEventArgs.cs
  20. 5 2
      Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
  21. 5 2
      Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
  22. 5 2
      Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
  23. 2 6
      Emby.Dlna/Service/BaseControlHandler.cs
  24. 1 5
      Emby.Dlna/Service/ControlErrorHandler.cs
  25. 1 6
      Emby.Drawing/Emby.Drawing.csproj
  26. 1 1
      Emby.Naming/Audio/AudioFileParser.cs
  27. 8 8
      Emby.Naming/Common/NamingOptions.cs
  28. 1 3
      Emby.Naming/Emby.Naming.csproj
  29. 1 1
      Emby.Naming/Video/VideoResolver.cs
  30. 0 4
      Emby.Notifications/Emby.Notifications.csproj
  31. 0 21
      Emby.Notifications/NotificationEntryPoint.cs
  32. 0 4
      Emby.Photos/Emby.Photos.csproj
  33. 11 39
      Emby.Server.Implementations/ApplicationHost.cs
  34. 1 1
      Emby.Server.Implementations/Channels/ChannelManager.cs
  35. 4 6
      Emby.Server.Implementations/Collections/CollectionManager.cs
  36. 3 1
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  37. 7 6
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  38. 1 1
      Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
  39. 1 1
      Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
  40. 4 5
      Emby.Server.Implementations/IO/ManagedFileSystem.cs
  41. 4 2
      Emby.Server.Implementations/Library/LibraryManager.cs
  42. 1 1
      Emby.Server.Implementations/Library/LiveStreamHelper.cs
  43. 1 1
      Emby.Server.Implementations/Library/MediaSourceManager.cs
  44. 1 1
      Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
  45. 1 1
      Emby.Server.Implementations/Library/SearchEngine.cs
  46. 2 2
      Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
  47. 1 1
      Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
  48. 2 2
      Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
  49. 2 1
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
  50. 1 0
      Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
  51. 3 1
      Emby.Server.Implementations/Localization/Core/ar.json
  52. 10 8
      Emby.Server.Implementations/Localization/Core/bg-BG.json
  53. 3 1
      Emby.Server.Implementations/Localization/Core/ca.json
  54. 3 1
      Emby.Server.Implementations/Localization/Core/cs.json
  55. 3 1
      Emby.Server.Implementations/Localization/Core/da.json
  56. 5 3
      Emby.Server.Implementations/Localization/Core/de.json
  57. 3 1
      Emby.Server.Implementations/Localization/Core/en-GB.json
  58. 3 1
      Emby.Server.Implementations/Localization/Core/es-AR.json
  59. 1 1
      Emby.Server.Implementations/Localization/Core/es.json
  60. 3 1
      Emby.Server.Implementations/Localization/Core/es_419.json
  61. 3 1
      Emby.Server.Implementations/Localization/Core/fi.json
  62. 3 1
      Emby.Server.Implementations/Localization/Core/fr.json
  63. 30 1
      Emby.Server.Implementations/Localization/Core/gl.json
  64. 3 1
      Emby.Server.Implementations/Localization/Core/hu.json
  65. 3 1
      Emby.Server.Implementations/Localization/Core/it.json
  66. 3 1
      Emby.Server.Implementations/Localization/Core/ja.json
  67. 3 1
      Emby.Server.Implementations/Localization/Core/kk.json
  68. 3 1
      Emby.Server.Implementations/Localization/Core/ko.json
  69. 3 1
      Emby.Server.Implementations/Localization/Core/lv.json
  70. 3 1
      Emby.Server.Implementations/Localization/Core/ml.json
  71. 10 2
      Emby.Server.Implementations/Localization/Core/ms.json
  72. 2 1
      Emby.Server.Implementations/Localization/Core/nb.json
  73. 10 8
      Emby.Server.Implementations/Localization/Core/nl.json
  74. 2 1
      Emby.Server.Implementations/Localization/Core/pl.json
  75. 1 1
      Emby.Server.Implementations/Localization/Core/pt.json
  76. 3 1
      Emby.Server.Implementations/Localization/Core/ru.json
  77. 3 1
      Emby.Server.Implementations/Localization/Core/sk.json
  78. 5 3
      Emby.Server.Implementations/Localization/Core/sl-SI.json
  79. 19 12
      Emby.Server.Implementations/Localization/Core/sq.json
  80. 3 1
      Emby.Server.Implementations/Localization/Core/ta.json
  81. 3 1
      Emby.Server.Implementations/Localization/Core/vi.json
  82. 3 1
      Emby.Server.Implementations/Localization/Core/zh-CN.json
  83. 2 2
      Emby.Server.Implementations/Localization/LocalizationManager.cs
  84. 1 1
      Emby.Server.Implementations/Playlists/PlaylistManager.cs
  85. 2 2
      Emby.Server.Implementations/Plugins/PluginManager.cs
  86. 51 111
      Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs
  87. 1 1
      Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
  88. 1 1
      Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
  89. 1 0
      Emby.Server.Implementations/Session/SessionManager.cs
  90. 2 1
      Emby.Server.Implementations/Sorting/StudioComparer.cs
  91. 1 1
      Emby.Server.Implementations/Updates/InstallationManager.cs
  92. 1 1
      Jellyfin.Api/BaseJellyfinApiController.cs
  93. 1 1
      Jellyfin.Api/Controllers/ConfigurationController.cs
  94. 2 2
      Jellyfin.Api/Controllers/DynamicHlsController.cs
  95. 10 47
      Jellyfin.Api/Controllers/InstantMixController.cs
  96. 6 16
      Jellyfin.Api/Controllers/MediaInfoController.cs
  97. 1 1
      Jellyfin.Api/Controllers/PluginsController.cs
  98. 21 58
      Jellyfin.Api/Controllers/QuickConnectController.cs
  99. 1 1
      Jellyfin.Api/Controllers/TvShowsController.cs
  100. 1 1
      Jellyfin.Api/Controllers/UserLibraryController.cs

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

@@ -7,7 +7,7 @@ parameters:
   default: "ubuntu-latest"
   default: "ubuntu-latest"
 - name: DotNetSdkVersion
 - name: DotNetSdkVersion
   type: string
   type: string
-  default: 5.0.103
+  default: 5.0.302
 
 
 jobs:
 jobs:
   - job: CompatibilityCheck
   - job: CompatibilityCheck

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

@@ -1,7 +1,7 @@
 parameters:
 parameters:
   LinuxImage: 'ubuntu-latest'
   LinuxImage: 'ubuntu-latest'
   RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj'
   RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj'
-  DotNetSdkVersion: 5.0.103
+  DotNetSdkVersion: 5.0.302
 
 
 jobs:
 jobs:
   - job: Build
   - job: Build

+ 1 - 1
.ci/azure-pipelines-test.yml

@@ -10,7 +10,7 @@ parameters:
   default: "tests/**/*Tests.csproj"
   default: "tests/**/*Tests.csproj"
 - name: DotNetSdkVersion
 - name: DotNetSdkVersion
   type: string
   type: string
-  default: 5.0.103
+  default: 5.0.302
 
 
 jobs:
 jobs:
   - job: Test
   - job: Test

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

@@ -6,7 +6,7 @@ variables:
 - name: RestoreBuildProjects
 - name: RestoreBuildProjects
   value: 'Jellyfin.Server/Jellyfin.Server.csproj'
   value: 'Jellyfin.Server/Jellyfin.Server.csproj'
 - name: DotNetSdkVersion
 - name: DotNetSdkVersion
-  value: 5.0.103
+  value: 5.0.302
 
 
 pr:
 pr:
   autoCancel: true
   autoCancel: true

+ 1 - 0
.github/ISSUE_TEMPLATE/bug_report.md

@@ -17,6 +17,7 @@ assignees: ''
  - Browser: [e.g. Firefox 72, Chrome 80, Safari 13]
  - Browser: [e.g. Firefox 72, Chrome 80, Safari 13]
  - Jellyfin Version: [e.g. 10.4.3, nightly 20191231]
  - Jellyfin Version: [e.g. 10.4.3, nightly 20191231]
  - Playback: [Direct Play, Remux, Direct Stream, Transcode] 
  - Playback: [Direct Play, Remux, Direct Stream, Transcode] 
+ - Hardware Acceleration: [e.g. none, VAAPI, NVENC, etc.]
  - Installed Plugins: [e.g. none, Fanart, Anime, etc.]
  - Installed Plugins: [e.g. none, Fanart, Anime, etc.]
  - Reverse Proxy: [e.g. none, nginx, apache, etc.]
  - Reverse Proxy: [e.g. none, nginx, apache, etc.]
  - Base URL: [e.g. none, yes: /example]
  - Base URL: [e.g. none, yes: /example]

+ 5 - 1
.github/stale.yml

@@ -17,9 +17,13 @@ staleLabel: stale
 # Comment to post when marking an issue as stale. Set to `false` to disable
 # Comment to post when marking an issue as stale. Set to `false` to disable
 markComment: >
 markComment: >
   This issue has gone 120 days without comment. To avoid abandoned issues, it will be closed in 21 days if there are no new comments.
   This issue has gone 120 days without comment. To avoid abandoned issues, it will be closed in 21 days if there are no new comments.
-  
+
   If you're the original submitter of this issue, please comment confirming if this issue still affects you in the latest release or nightlies, or close the issue if it has been fixed. If you're another user also affected by this bug, please comment confirming so. Either action will remove the stale label.
   If you're the original submitter of this issue, please comment confirming if this issue still affects you in the latest release or nightlies, or close the issue if it has been fixed. If you're another user also affected by this bug, please comment confirming so. Either action will remove the stale label.
 
 
   This bot exists to prevent issues from becoming stale and forgotten. Jellyfin is always moving forward, and bugs are often fixed as side effects of other changes. We therefore ask that bug report authors remain vigilant about their issues to ensure they are closed if fixed, or re-confirmed - perhaps with fresh logs or reproduction examples - regularly. If you have any questions you can reach us on [Matrix or Social Media](https://docs.jellyfin.org/general/getting-help.html).
   This bot exists to prevent issues from becoming stale and forgotten. Jellyfin is always moving forward, and bugs are often fixed as side effects of other changes. We therefore ask that bug report authors remain vigilant about their issues to ensure they are closed if fixed, or re-confirmed - perhaps with fresh logs or reproduction examples - regularly. If you have any questions you can reach us on [Matrix or Social Media](https://docs.jellyfin.org/general/getting-help.html).
 # Comment to post when closing a stale issue. Set to `false` to disable
 # Comment to post when closing a stale issue. Set to `false` to disable
 closeComment: false
 closeComment: false
+
+# Disable automatic closing of pull requests
+pulls:
+  daysUntilClose: false

+ 7 - 5
.github/workflows/automation.yml

@@ -11,6 +11,7 @@ jobs:
   label:
   label:
     name: Labeling
     name: Labeling
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
+    if: ${{ github.repository == 'jellyfin/jellyfin' }}
     steps:
     steps:
       - name: Apply label
       - name: Apply label
         uses: eps1lon/actions-label-merge-conflict@v2.0.1
         uses: eps1lon/actions-label-merge-conflict@v2.0.1
@@ -22,9 +23,10 @@ jobs:
   project:
   project:
     name: Project board
     name: Project board
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
+    if: ${{ github.repository == 'jellyfin/jellyfin' }}
     steps:
     steps:
       - name: Remove from 'Current Release' project
       - name: Remove from 'Current Release' project
-        uses: alex-page/github-project-automation-plus@v0.7.1
+        uses: alex-page/github-project-automation-plus@v0.8.1
         if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport')
         if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport')
         continue-on-error: true
         continue-on-error: true
         with:
         with:
@@ -33,7 +35,7 @@ jobs:
           repo-token: ${{ secrets.JF_BOT_TOKEN }}
           repo-token: ${{ secrets.JF_BOT_TOKEN }}
 
 
       - name: Add to 'Release Next' project
       - name: Add to 'Release Next' project
-        uses: alex-page/github-project-automation-plus@v0.7.1
+        uses: alex-page/github-project-automation-plus@v0.8.1
         if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened'
         if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened'
         continue-on-error: true
         continue-on-error: true
         with:
         with:
@@ -42,7 +44,7 @@ jobs:
           repo-token: ${{ secrets.JF_BOT_TOKEN }}
           repo-token: ${{ secrets.JF_BOT_TOKEN }}
 
 
       - name: Add to 'Current Release' project
       - name: Add to 'Current Release' project
-        uses: alex-page/github-project-automation-plus@v0.7.1
+        uses: alex-page/github-project-automation-plus@v0.8.1
         if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport')
         if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport')
         continue-on-error: true
         continue-on-error: true
         with:
         with:
@@ -56,7 +58,7 @@ jobs:
         run: echo "::set-output name=number::$(curl -s ${{ github.event.issue.comments_url }} | jq '.[] | select(.author_association == "MEMBER") | .author_association' | wc -l)"
         run: echo "::set-output name=number::$(curl -s ${{ github.event.issue.comments_url }} | jq '.[] | select(.author_association == "MEMBER") | .author_association' | wc -l)"
 
 
       - name: Move issue to needs triage
       - name: Move issue to needs triage
-        uses: alex-page/github-project-automation-plus@v0.7.1
+        uses: alex-page/github-project-automation-plus@v0.8.1
         if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1
         if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1
         continue-on-error: true
         continue-on-error: true
         with:
         with:
@@ -65,7 +67,7 @@ jobs:
           repo-token: ${{ secrets.JF_BOT_TOKEN }}
           repo-token: ${{ secrets.JF_BOT_TOKEN }}
 
 
       - name: Add issue to triage project
       - name: Add issue to triage project
-        uses: alex-page/github-project-automation-plus@v0.7.1
+        uses: alex-page/github-project-automation-plus@v0.8.1
         if: github.event.issue.pull_request == '' && github.event.action == 'opened'
         if: github.event.issue.pull_request == '' && github.event.action == 'opened'
         continue-on-error: true
         continue-on-error: true
         with:
         with:

+ 14 - 0
Directory.Build.props

@@ -0,0 +1,14 @@
+<Project>
+  <!-- Sets defaults for all projects in the repo -->
+
+  <PropertyGroup>
+    <Nullable>enable</Nullable>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)/jellyfin.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+    <AnalysisMode>AllEnabledByDefault</AnalysisMode>
+  </PropertyGroup>
+
+</Project>

+ 2 - 1
DvdLib/DvdLib.csproj

@@ -13,7 +13,8 @@
     <TargetFramework>net5.0</TargetFramework>
     <TargetFramework>net5.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <AnalysisMode>AllDisabledByDefault</AnalysisMode>
+    <Nullable>disable</Nullable>
   </PropertyGroup>
   </PropertyGroup>
 
 
 </Project>
 </Project>

+ 1 - 3
Emby.Dlna/Configuration/DlnaOptions.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 namespace Emby.Dlna.Configuration
 namespace Emby.Dlna.Configuration
@@ -74,7 +72,7 @@ namespace Emby.Dlna.Configuration
         /// <summary>
         /// <summary>
         /// Gets or sets the default user account that the dlna server uses.
         /// Gets or sets the default user account that the dlna server uses.
         /// </summary>
         /// </summary>
-        public string DefaultUserId { get; set; }
+        public string? DefaultUserId { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets a value indicating whether playTo device profiles should be created.
         /// Gets or sets a value indicating whether playTo device profiles should be created.

+ 1 - 3
Emby.Dlna/ContentDirectory/ContentDirectoryService.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;
@@ -140,7 +138,7 @@ namespace Emby.Dlna.ContentDirectory
         /// </summary>
         /// </summary>
         /// <param name="profile">The <see cref="DeviceProfile"/>.</param>
         /// <param name="profile">The <see cref="DeviceProfile"/>.</param>
         /// <returns>The <see cref="User"/>.</returns>
         /// <returns>The <see cref="User"/>.</returns>
-        private User GetUser(DeviceProfile profile)
+        private User? GetUser(DeviceProfile profile)
         {
         {
             if (!string.IsNullOrEmpty(profile.UserId))
             if (!string.IsNullOrEmpty(profile.UserId))
             {
             {

+ 3 - 3
Emby.Dlna/ControlResponse.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System.Collections.Generic;
 using System.Collections.Generic;
@@ -8,9 +6,11 @@ namespace Emby.Dlna
 {
 {
     public class ControlResponse
     public class ControlResponse
     {
     {
-        public ControlResponse()
+        public ControlResponse(string xml, bool isSuccessful)
         {
         {
             Headers = new Dictionary<string, string>();
             Headers = new Dictionary<string, string>();
+            Xml = xml;
+            IsSuccessful = isSuccessful;
         }
         }
 
 
         public IDictionary<string, string> Headers { get; }
         public IDictionary<string, string> Headers { get; }

+ 1 - 1
Emby.Dlna/DlnaManager.cs

@@ -14,9 +14,9 @@ using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Emby.Dlna.Profiles;
 using Emby.Dlna.Profiles;
 using Emby.Dlna.Server;
 using Emby.Dlna.Server;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Json;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Drawing;

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

@@ -20,8 +20,7 @@
     <TargetFramework>net5.0</TargetFramework>
     <TargetFramework>net5.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-    <Nullable>enable</Nullable>
+    <AnalysisMode>AllDisabledByDefault</AnalysisMode>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <!-- Code Analyzers-->
   <!-- Code Analyzers-->
@@ -31,10 +30,6 @@
     <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
     <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
   </ItemGroup>
   </ItemGroup>
 
 
-  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
-  </PropertyGroup>
-
   <ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Images\logo120.jpg" />
     <EmbeddedResource Include="Images\logo120.jpg" />
     <EmbeddedResource Include="Images\logo120.png" />
     <EmbeddedResource Include="Images\logo120.png" />

+ 3 - 3
Emby.Dlna/EventSubscriptionResponse.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System.Collections.Generic;
 using System.Collections.Generic;
@@ -8,8 +6,10 @@ namespace Emby.Dlna
 {
 {
     public class EventSubscriptionResponse
     public class EventSubscriptionResponse
     {
     {
-        public EventSubscriptionResponse()
+        public EventSubscriptionResponse(string content, string contentType)
         {
         {
+            Content = content;
+            ContentType = contentType;
             Headers = new Dictionary<string, string>();
             Headers = new Dictionary<string, string>();
         }
         }
 
 

+ 3 - 15
Emby.Dlna/Eventing/DlnaEventManager.cs

@@ -51,11 +51,7 @@ namespace Emby.Dlna.Eventing
                 return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds);
                 return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds);
             }
             }
 
 
-            return new EventSubscriptionResponse
-            {
-                Content = string.Empty,
-                ContentType = "text/plain"
-            };
+            return new EventSubscriptionResponse(string.Empty, "text/plain");
         }
         }
 
 
         public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
         public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
@@ -103,20 +99,12 @@ namespace Emby.Dlna.Eventing
 
 
             _subscriptions.TryRemove(subscriptionId, out _);
             _subscriptions.TryRemove(subscriptionId, out _);
 
 
-            return new EventSubscriptionResponse
-            {
-                Content = string.Empty,
-                ContentType = "text/plain"
-            };
+            return new EventSubscriptionResponse(string.Empty, "text/plain");
         }
         }
 
 
         private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, string requestedTimeoutString, int timeoutSeconds)
         private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, string requestedTimeoutString, int timeoutSeconds)
         {
         {
-            var response = new EventSubscriptionResponse
-            {
-                Content = string.Empty,
-                ContentType = "text/plain"
-            };
+            var response = new EventSubscriptionResponse(string.Empty, "text/plain");
 
 
             response.Headers["SID"] = subscriptionId;
             response.Headers["SID"] = subscriptionId;
             response.Headers["TIMEOUT"] = string.IsNullOrEmpty(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(_usCulture)) : requestedTimeoutString;
             response.Headers["TIMEOUT"] = string.IsNullOrEmpty(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(_usCulture)) : requestedTimeoutString;

+ 8 - 5
Emby.Dlna/Main/DlnaEntryPoint.cs

@@ -27,11 +27,9 @@ using MediaBrowser.Controller.TV;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Net;
-using MediaBrowser.Model.System;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 using Rssdp;
 using Rssdp;
 using Rssdp.Infrastructure;
 using Rssdp.Infrastructure;
-using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
 
 
 namespace Emby.Dlna.Main
 namespace Emby.Dlna.Main
 {
 {
@@ -204,8 +202,8 @@ namespace Emby.Dlna.Main
             {
             {
                 if (_communicationsServer == null)
                 if (_communicationsServer == null)
                 {
                 {
-                    var enableMultiSocketBinding = OperatingSystem.Id == OperatingSystemId.Windows ||
-                                                   OperatingSystem.Id == OperatingSystemId.Linux;
+                    var enableMultiSocketBinding = OperatingSystem.IsWindows() ||
+                                                   OperatingSystem.IsLinux();
 
 
                     _communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger, enableMultiSocketBinding)
                     _communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger, enableMultiSocketBinding)
                     {
                     {
@@ -268,7 +266,12 @@ namespace Emby.Dlna.Main
 
 
             try
             try
             {
             {
-                _publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost)
+                _publisher = new SsdpDevicePublisher(
+                    _communicationsServer,
+                    _networkManager,
+                    MediaBrowser.Common.System.OperatingSystem.Name,
+                    Environment.OSVersion.VersionString,
+                    _config.GetDlnaConfiguration().SendOnlyMatchedHost)
                 {
                 {
                     LogFunction = LogMessage,
                     LogFunction = LogMessage,
                     SupportPnpRootDevice = false
                     SupportPnpRootDevice = false

+ 4 - 17
Emby.Dlna/PlayTo/Device.cs

@@ -1260,10 +1260,7 @@ namespace Emby.Dlna.PlayTo
                 return;
                 return;
             }
             }
 
 
-            PlaybackStart?.Invoke(this, new PlaybackStartEventArgs
-            {
-                MediaInfo = mediaInfo
-            });
+            PlaybackStart?.Invoke(this, new PlaybackStartEventArgs(mediaInfo));
         }
         }
 
 
         private void OnPlaybackProgress(UBaseObject mediaInfo)
         private void OnPlaybackProgress(UBaseObject mediaInfo)
@@ -1273,27 +1270,17 @@ namespace Emby.Dlna.PlayTo
                 return;
                 return;
             }
             }
 
 
-            PlaybackProgress?.Invoke(this, new PlaybackProgressEventArgs
-            {
-                MediaInfo = mediaInfo
-            });
+            PlaybackProgress?.Invoke(this, new PlaybackProgressEventArgs(mediaInfo));
         }
         }
 
 
         private void OnPlaybackStop(UBaseObject mediaInfo)
         private void OnPlaybackStop(UBaseObject mediaInfo)
         {
         {
-            PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs
-            {
-                MediaInfo = mediaInfo
-            });
+            PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs(mediaInfo));
         }
         }
 
 
         private void OnMediaChanged(UBaseObject old, UBaseObject newMedia)
         private void OnMediaChanged(UBaseObject old, UBaseObject newMedia)
         {
         {
-            MediaChanged?.Invoke(this, new MediaChangedEventArgs
-            {
-                OldMediaInfo = old,
-                NewMediaInfo = newMedia
-            });
+            MediaChanged?.Invoke(this, new MediaChangedEventArgs(old, newMedia));
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />

+ 7 - 3
Emby.Dlna/PlayTo/MediaChangedEventArgs.cs

@@ -1,6 +1,4 @@
-#nullable disable
-
-#pragma warning disable CS1591
+#pragma warning disable CS1591
 
 
 using System;
 using System;
 
 
@@ -8,6 +6,12 @@ namespace Emby.Dlna.PlayTo
 {
 {
     public class MediaChangedEventArgs : EventArgs
     public class MediaChangedEventArgs : EventArgs
     {
     {
+        public MediaChangedEventArgs(UBaseObject oldMediaInfo, UBaseObject newMediaInfo)
+        {
+            OldMediaInfo = oldMediaInfo;
+            NewMediaInfo = newMediaInfo;
+        }
+
         public UBaseObject OldMediaInfo { get; set; }
         public UBaseObject OldMediaInfo { get; set; }
 
 
         public UBaseObject NewMediaInfo { get; set; }
         public UBaseObject NewMediaInfo { get; set; }

+ 5 - 2
Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;
@@ -8,6 +6,11 @@ namespace Emby.Dlna.PlayTo
 {
 {
     public class PlaybackProgressEventArgs : EventArgs
     public class PlaybackProgressEventArgs : EventArgs
     {
     {
+        public PlaybackProgressEventArgs(UBaseObject mediaInfo)
+        {
+            MediaInfo = mediaInfo;
+        }
+
         public UBaseObject MediaInfo { get; set; }
         public UBaseObject MediaInfo { get; set; }
     }
     }
 }
 }

+ 5 - 2
Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;
@@ -8,6 +6,11 @@ namespace Emby.Dlna.PlayTo
 {
 {
     public class PlaybackStartEventArgs : EventArgs
     public class PlaybackStartEventArgs : EventArgs
     {
     {
+        public PlaybackStartEventArgs(UBaseObject mediaInfo)
+        {
+            MediaInfo = mediaInfo;
+        }
+
         public UBaseObject MediaInfo { get; set; }
         public UBaseObject MediaInfo { get; set; }
     }
     }
 }
 }

+ 5 - 2
Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;
@@ -8,6 +6,11 @@ namespace Emby.Dlna.PlayTo
 {
 {
     public class PlaybackStoppedEventArgs : EventArgs
     public class PlaybackStoppedEventArgs : EventArgs
     {
     {
+        public PlaybackStoppedEventArgs(UBaseObject mediaInfo)
+        {
+            MediaInfo = mediaInfo;
+        }
+
         public UBaseObject MediaInfo { get; set; }
         public UBaseObject MediaInfo { get; set; }
     }
     }
 }
 }

+ 2 - 6
Emby.Dlna/Service/BaseControlHandler.cs

@@ -6,9 +6,9 @@ using System.IO;
 using System.Text;
 using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using System.Xml;
 using System.Xml;
+using Diacritics.Extensions;
 using Emby.Dlna.Didl;
 using Emby.Dlna.Didl;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Extensions;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Emby.Dlna.Service
 namespace Emby.Dlna.Service
@@ -95,11 +95,7 @@ namespace Emby.Dlna.Service
 
 
             var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=", StringComparison.Ordinal);
             var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=", StringComparison.Ordinal);
 
 
-            var controlResponse = new ControlResponse
-            {
-                Xml = xml,
-                IsSuccessful = true
-            };
+            var controlResponse = new ControlResponse(xml, true);
 
 
             controlResponse.Headers.Add("EXT", string.Empty);
             controlResponse.Headers.Add("EXT", string.Empty);
 
 

+ 1 - 5
Emby.Dlna/Service/ControlErrorHandler.cs

@@ -46,11 +46,7 @@ namespace Emby.Dlna.Service
                 writer.WriteEndDocument();
                 writer.WriteEndDocument();
             }
             }
 
 
-            return new ControlResponse
-            {
-                Xml = builder.ToString(),
-                IsSuccessful = false
-            };
+            return new ControlResponse(builder.ToString(), false);
         }
         }
     }
     }
 }
 }

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

@@ -9,8 +9,7 @@
     <TargetFramework>net5.0</TargetFramework>
     <TargetFramework>net5.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-    <Nullable>enable</Nullable>
+    <AnalysisMode>AllDisabledByDefault</AnalysisMode>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
@@ -30,8 +29,4 @@
     <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
     <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
   </ItemGroup>
   </ItemGroup>
 
 
-  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
-  </PropertyGroup>
-
 </Project>
 </Project>

+ 1 - 1
Emby.Naming/Audio/AudioFileParser.cs

@@ -1,7 +1,7 @@
 using System;
 using System;
 using System.IO;
 using System.IO;
 using Emby.Naming.Common;
 using Emby.Naming.Common;
-using MediaBrowser.Common.Extensions;
+using Jellyfin.Extensions;
 
 
 namespace Emby.Naming.Audio
 namespace Emby.Naming.Audio
 {
 {

+ 8 - 8
Emby.Naming/Common/NamingOptions.cs

@@ -137,7 +137,7 @@ namespace Emby.Naming.Common
 
 
             CleanStrings = new[]
             CleanStrings = new[]
             {
             {
-                @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
+                @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
                 @"(\[.*\])"
                 @"(\[.*\])"
             };
             };
 
 
@@ -277,7 +277,7 @@ namespace Emby.Naming.Common
                     IsNamed = true
                     IsNamed = true
                 },
                 },
 
 
-                new EpisodeExpression("[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$")
+                new EpisodeExpression(@"[\\\/\._ \[\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\.[1-9])(?![0-9]))?)([^\\\/]*)$")
                 {
                 {
                     SupportsAbsoluteEpisodeNumbers = true
                     SupportsAbsoluteEpisodeNumbers = true
                 },
                 },
@@ -305,6 +305,12 @@ namespace Emby.Naming.Common
 
 
                 // *** End Kodi Standard Naming
                 // *** End Kodi Standard Naming
 
 
+                // "Episode 16", "Episode 16 - Title"
+                new EpisodeExpression(@"[Ee]pisode (?<epnumber>[0-9]+)(-(?<endingepnumber>[0-9]+))?[^\\\/]*$")
+                {
+                    IsNamed = true
+                },
+
                 new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>[0-9]+)[xX](?<epnumber>[0-9]+)[^\\\/]*$")
                 new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>[0-9]+)[xX](?<epnumber>[0-9]+)[^\\\/]*$")
                 {
                 {
                     IsNamed = true
                     IsNamed = true
@@ -362,12 +368,6 @@ namespace Emby.Naming.Common
                     IsOptimistic = true,
                     IsOptimistic = true,
                     IsNamed = true
                     IsNamed = true
                 },
                 },
-                // "Episode 16", "Episode 16 - Title"
-                new EpisodeExpression(@".*[\\\/][^\\\/]* (?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/]*$")
-                {
-                    IsOptimistic = true,
-                    IsNamed = true
-                }
             };
             };
 
 
             EpisodeWithoutSeasonExpressions = new[]
             EpisodeWithoutSeasonExpressions = new[]

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

@@ -9,12 +9,11 @@
     <TargetFramework>net5.0</TargetFramework>
     <TargetFramework>net5.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
     <PublishRepositoryUrl>true</PublishRepositoryUrl>
     <PublishRepositoryUrl>true</PublishRepositoryUrl>
     <EmbedUntrackedSources>true</EmbedUntrackedSources>
     <EmbedUntrackedSources>true</EmbedUntrackedSources>
     <IncludeSymbols>true</IncludeSymbols>
     <IncludeSymbols>true</IncludeSymbols>
     <SymbolPackageFormat>snupkg</SymbolPackageFormat>
     <SymbolPackageFormat>snupkg</SymbolPackageFormat>
-    <Nullable>enable</Nullable>
+    <AnalysisMode>AllDisabledByDefault</AnalysisMode>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <PropertyGroup Condition=" '$(Stability)'=='Unstable'">
   <PropertyGroup Condition=" '$(Stability)'=='Unstable'">
@@ -51,7 +50,6 @@
   </ItemGroup>
   </ItemGroup>
 
 
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
   </PropertyGroup>
   </PropertyGroup>
 
 
 </Project>
 </Project>

+ 1 - 1
Emby.Naming/Video/VideoResolver.cs

@@ -2,7 +2,7 @@ using System;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 using System.IO;
 using System.IO;
 using Emby.Naming.Common;
 using Emby.Naming.Common;
-using MediaBrowser.Common.Extensions;
+using Jellyfin.Extensions;
 
 
 namespace Emby.Naming.Video
 namespace Emby.Naming.Video
 {
 {

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

@@ -9,10 +9,6 @@
     <TargetFramework>net5.0</TargetFramework>
     <TargetFramework>net5.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-    <Nullable>enable</Nullable>
-    <AnalysisMode>AllEnabledByDefault</AnalysisMode>
-    <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>

+ 0 - 21
Emby.Notifications/NotificationEntryPoint.cs

@@ -77,7 +77,6 @@ namespace Emby.Notifications
         {
         {
             _libraryManager.ItemAdded += OnLibraryManagerItemAdded;
             _libraryManager.ItemAdded += OnLibraryManagerItemAdded;
             _appHost.HasPendingRestartChanged += OnAppHostHasPendingRestartChanged;
             _appHost.HasPendingRestartChanged += OnAppHostHasPendingRestartChanged;
-            _appHost.HasUpdateAvailableChanged += OnAppHostHasUpdateAvailableChanged;
             _activityManager.EntryCreated += OnActivityManagerEntryCreated;
             _activityManager.EntryCreated += OnActivityManagerEntryCreated;
 
 
             return Task.CompletedTask;
             return Task.CompletedTask;
@@ -132,25 +131,6 @@ namespace Emby.Notifications
             return _config.GetConfiguration<NotificationOptions>("notifications");
             return _config.GetConfiguration<NotificationOptions>("notifications");
         }
         }
 
 
-        private async void OnAppHostHasUpdateAvailableChanged(object? sender, EventArgs e)
-        {
-            if (!_appHost.HasUpdateAvailable)
-            {
-                return;
-            }
-
-            var type = NotificationType.ApplicationUpdateAvailable.ToString();
-
-            var notification = new NotificationRequest
-            {
-                Description = "Please see jellyfin.org for details.",
-                NotificationType = type,
-                Name = _localization.GetLocalizedString("NewVersionIsAvailable")
-            };
-
-            await SendNotification(notification, null).ConfigureAwait(false);
-        }
-
         private void OnLibraryManagerItemAdded(object? sender, ItemChangeEventArgs e)
         private void OnLibraryManagerItemAdded(object? sender, ItemChangeEventArgs e)
         {
         {
             if (!FilterItem(e.Item))
             if (!FilterItem(e.Item))
@@ -325,7 +305,6 @@ namespace Emby.Notifications
 
 
             _libraryManager.ItemAdded -= OnLibraryManagerItemAdded;
             _libraryManager.ItemAdded -= OnLibraryManagerItemAdded;
             _appHost.HasPendingRestartChanged -= OnAppHostHasPendingRestartChanged;
             _appHost.HasPendingRestartChanged -= OnAppHostHasPendingRestartChanged;
-            _appHost.HasUpdateAvailableChanged -= OnAppHostHasUpdateAvailableChanged;
             _activityManager.EntryCreated -= OnActivityManagerEntryCreated;
             _activityManager.EntryCreated -= OnActivityManagerEntryCreated;
 
 
             _disposed = true;
             _disposed = true;

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

@@ -22,10 +22,6 @@
     <TargetFramework>net5.0</TargetFramework>
     <TargetFramework>net5.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-    <Nullable>enable</Nullable>
-    <AnalysisMode>AllEnabledByDefault</AnalysisMode>
-    <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <!-- Code Analyzers-->
   <!-- Code Analyzers-->

+ 11 - 39
Emby.Server.Implementations/ApplicationHost.cs

@@ -103,7 +103,6 @@ using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 using Prometheus.DotNetRuntime;
 using Prometheus.DotNetRuntime;
-using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
 using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager;
 using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager;
 
 
 namespace Emby.Server.Implementations
 namespace Emby.Server.Implementations
@@ -150,13 +149,7 @@ namespace Emby.Server.Implementations
                     return false;
                     return false;
                 }
                 }
 
 
-                if (OperatingSystem.Id == OperatingSystemId.Windows
-                    || OperatingSystem.Id == OperatingSystemId.Darwin)
-                {
-                    return true;
-                }
-
-                return false;
+                return OperatingSystem.IsWindows() || OperatingSystem.IsMacOS();
             }
             }
         }
         }
 
 
@@ -721,7 +714,7 @@ namespace Emby.Server.Implementations
 
 
             logger.LogInformation("Environment Variables: {EnvVars}", relevantEnvVars);
             logger.LogInformation("Environment Variables: {EnvVars}", relevantEnvVars);
             logger.LogInformation("Arguments: {Args}", commandLineArgs);
             logger.LogInformation("Arguments: {Args}", commandLineArgs);
-            logger.LogInformation("Operating system: {OS}", OperatingSystem.Name);
+            logger.LogInformation("Operating system: {OS}", MediaBrowser.Common.System.OperatingSystem.Name);
             logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture);
             logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture);
             logger.LogInformation("64-Bit Process: {Is64Bit}", Environment.Is64BitProcess);
             logger.LogInformation("64-Bit Process: {Is64Bit}", Environment.Is64BitProcess);
             logger.LogInformation("User Interactive: {IsUserInteractive}", Environment.UserInteractive);
             logger.LogInformation("User Interactive: {IsUserInteractive}", Environment.UserInteractive);
@@ -1098,11 +1091,10 @@ namespace Emby.Server.Implementations
                 ItemsByNamePath = ApplicationPaths.InternalMetadataPath,
                 ItemsByNamePath = ApplicationPaths.InternalMetadataPath,
                 InternalMetadataPath = ApplicationPaths.InternalMetadataPath,
                 InternalMetadataPath = ApplicationPaths.InternalMetadataPath,
                 CachePath = ApplicationPaths.CachePath,
                 CachePath = ApplicationPaths.CachePath,
-                OperatingSystem = OperatingSystem.Id.ToString(),
-                OperatingSystemDisplayName = OperatingSystem.Name,
+                OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(),
+                OperatingSystemDisplayName = MediaBrowser.Common.System.OperatingSystem.Name,
                 CanSelfRestart = CanSelfRestart,
                 CanSelfRestart = CanSelfRestart,
                 CanLaunchWebBrowser = CanLaunchWebBrowser,
                 CanLaunchWebBrowser = CanLaunchWebBrowser,
-                HasUpdateAvailable = HasUpdateAvailable,
                 TranscodingTempPath = ConfigurationManager.GetTranscodePath(),
                 TranscodingTempPath = ConfigurationManager.GetTranscodePath(),
                 ServerName = FriendlyName,
                 ServerName = FriendlyName,
                 LocalAddress = GetSmartApiUrl(source),
                 LocalAddress = GetSmartApiUrl(source),
@@ -1118,16 +1110,16 @@ namespace Emby.Server.Implementations
                 .Select(i => new WakeOnLanInfo(i))
                 .Select(i => new WakeOnLanInfo(i))
                 .ToList();
                 .ToList();
 
 
-        public PublicSystemInfo GetPublicSystemInfo(IPAddress source)
+        public PublicSystemInfo GetPublicSystemInfo(IPAddress address)
         {
         {
             return new PublicSystemInfo
             return new PublicSystemInfo
             {
             {
                 Version = ApplicationVersionString,
                 Version = ApplicationVersionString,
                 ProductName = ApplicationProductName,
                 ProductName = ApplicationProductName,
                 Id = SystemId,
                 Id = SystemId,
-                OperatingSystem = OperatingSystem.Id.ToString(),
+                OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(),
                 ServerName = FriendlyName,
                 ServerName = FriendlyName,
-                LocalAddress = GetSmartApiUrl(source),
+                LocalAddress = GetSmartApiUrl(address),
                 StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted
                 StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted
             };
             };
         }
         }
@@ -1136,7 +1128,7 @@ namespace Emby.Server.Implementations
         public bool ListenWithHttps => Certificate != null && ConfigurationManager.GetNetworkConfiguration().EnableHttps;
         public bool ListenWithHttps => Certificate != null && ConfigurationManager.GetNetworkConfiguration().EnableHttps;
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
-        public string GetSmartApiUrl(IPAddress ipAddress, int? port = null)
+        public string GetSmartApiUrl(IPAddress remoteAddr, int? port = null)
         {
         {
             // Published server ends with a /
             // Published server ends with a /
             if (!string.IsNullOrEmpty(PublishedServerUrl))
             if (!string.IsNullOrEmpty(PublishedServerUrl))
@@ -1145,7 +1137,7 @@ namespace Emby.Server.Implementations
                 return PublishedServerUrl.Trim('/');
                 return PublishedServerUrl.Trim('/');
             }
             }
 
 
-            string smart = NetManager.GetBindInterface(ipAddress, out port);
+            string smart = NetManager.GetBindInterface(remoteAddr, out port);
             // If the smartAPI doesn't start with http then treat it as a host or ip.
             // If the smartAPI doesn't start with http then treat it as a host or ip.
             if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
             if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
             {
             {
@@ -1208,14 +1200,14 @@ namespace Emby.Server.Implementations
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
-        public string GetLocalApiUrl(string host, string scheme = null, int? port = null)
+        public string GetLocalApiUrl(string hostname, string scheme = null, int? port = null)
         {
         {
             // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
             // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
             // not. For consistency, always trim the trailing slash.
             // not. For consistency, always trim the trailing slash.
             return new UriBuilder
             return new UriBuilder
             {
             {
                 Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp),
                 Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp),
-                Host = host,
+                Host = hostname,
                 Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort),
                 Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort),
                 Path = ConfigurationManager.GetNetworkConfiguration().BaseUrl
                 Path = ConfigurationManager.GetNetworkConfiguration().BaseUrl
             }.ToString().TrimEnd('/');
             }.ToString().TrimEnd('/');
@@ -1252,26 +1244,6 @@ namespace Emby.Server.Implementations
 
 
         protected abstract void ShutdownInternal();
         protected abstract void ShutdownInternal();
 
 
-        public event EventHandler HasUpdateAvailableChanged;
-
-        private bool _hasUpdateAvailable;
-
-        public bool HasUpdateAvailable
-        {
-            get => _hasUpdateAvailable;
-            set
-            {
-                var fireEvent = value && !_hasUpdateAvailable;
-
-                _hasUpdateAvailable = value;
-
-                if (fireEvent)
-                {
-                    HasUpdateAvailableChanged?.Invoke(this, EventArgs.Empty);
-                }
-            }
-        }
-
         public IEnumerable<Assembly> GetApiPluginAssemblies()
         public IEnumerable<Assembly> GetApiPluginAssemblies()
         {
         {
             var assemblies = _allConcreteTypes
             var assemblies = _allConcreteTypes

+ 1 - 1
Emby.Server.Implementations/Channels/ChannelManager.cs

@@ -11,7 +11,7 @@ using System.Threading.Tasks;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
 using Jellyfin.Data.Enums;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Json;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;

+ 4 - 6
Emby.Server.Implementations/Collections/CollectionManager.cs

@@ -82,12 +82,10 @@ namespace Emby.Server.Implementations.Collections
 
 
         internal async Task<Folder> EnsureLibraryFolder(string path, bool createIfNeeded)
         internal async Task<Folder> EnsureLibraryFolder(string path, bool createIfNeeded)
         {
         {
-            var existingFolders = FindFolders(path)
-                .ToList();
-
-            if (existingFolders.Count > 0)
+            var existingFolder = FindFolders(path).FirstOrDefault();
+            if (existingFolder != null)
             {
             {
-                return existingFolders[0];
+                return existingFolder;
             }
             }
 
 
             if (!createIfNeeded)
             if (!createIfNeeded)
@@ -164,7 +162,7 @@ namespace Emby.Server.Implementations.Collections
                     DateCreated = DateTime.UtcNow
                     DateCreated = DateTime.UtcNow
                 };
                 };
 
 
-                parentFolder.AddChild(collection, CancellationToken.None);
+                parentFolder.AddChild(collection);
 
 
                 if (options.ItemIdList.Count > 0)
                 if (options.ItemIdList.Count > 0)
                 {
                 {

+ 3 - 1
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -11,10 +11,12 @@ using System.Linq;
 using System.Text;
 using System.Text;
 using System.Text.Json;
 using System.Text.Json;
 using System.Threading;
 using System.Threading;
+using Diacritics.Extensions;
 using Emby.Server.Implementations.Playlists;
 using Emby.Server.Implementations.Playlists;
 using Jellyfin.Data.Enums;
 using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Json;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;

+ 7 - 6
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -24,11 +24,11 @@
 
 
   <ItemGroup>
   <ItemGroup>
     <PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
     <PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
     <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
     <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
     <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
     <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
-    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.7" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.8" />
     <PackageReference Include="Mono.Nat" Version="3.0.1" />
     <PackageReference Include="Mono.Nat" Version="3.0.1" />
     <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.1.0" />
     <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.1.0" />
     <PackageReference Include="sharpcompress" Version="0.28.3" />
     <PackageReference Include="sharpcompress" Version="0.28.3" />
@@ -44,12 +44,13 @@
     <TargetFramework>net5.0</TargetFramework>
     <TargetFramework>net5.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    <TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'">true</TreatWarningsAsErrors>
-    <Nullable>enable</Nullable>
     <!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 -->
     <!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 -->
     <NoWarn>AD0001</NoWarn>
     <NoWarn>AD0001</NoWarn>
-    <AnalysisMode Condition=" '$(Configuration)' == 'Debug' ">AllEnabledByDefault</AnalysisMode>
-    <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
+    <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
+  </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(Configuration)' == 'Release'">
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <!-- Code Analyzers-->
   <!-- Code Analyzers-->

+ 1 - 1
Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs

@@ -3,7 +3,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Net;
 using System.Net;
-using MediaBrowser.Common.Extensions;
+using Jellyfin.Extensions;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Security;
 using MediaBrowser.Controller.Security;

+ 1 - 1
Emby.Server.Implementations/HttpServer/WebSocketConnection.cs

@@ -7,7 +7,7 @@ using System.Text;
 using System.Text.Json;
 using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using MediaBrowser.Common.Json;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Session;
 using MediaBrowser.Model.Session;

+ 4 - 5
Emby.Server.Implementations/IO/ManagedFileSystem.cs

@@ -6,12 +6,11 @@ using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
+using Jellyfin.Extensions;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.System;
 using MediaBrowser.Model.System;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
-using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
 
 
 namespace Emby.Server.Implementations.IO
 namespace Emby.Server.Implementations.IO
 {
 {
@@ -24,7 +23,7 @@ namespace Emby.Server.Implementations.IO
 
 
         private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
         private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
         private readonly string _tempPath;
         private readonly string _tempPath;
-        private static readonly bool _isEnvironmentCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+        private static readonly bool _isEnvironmentCaseInsensitive = OperatingSystem.IsWindows();
 
 
         public ManagedFileSystem(
         public ManagedFileSystem(
             ILogger<ManagedFileSystem> logger,
             ILogger<ManagedFileSystem> logger,
@@ -402,7 +401,7 @@ namespace Emby.Server.Implementations.IO
 
 
         public virtual void SetHidden(string path, bool isHidden)
         public virtual void SetHidden(string path, bool isHidden)
         {
         {
-            if (OperatingSystem.Id != OperatingSystemId.Windows)
+            if (!OperatingSystem.IsWindows())
             {
             {
                 return;
                 return;
             }
             }
@@ -426,7 +425,7 @@ namespace Emby.Server.Implementations.IO
 
 
         public virtual void SetAttributes(string path, bool isHidden, bool isReadOnly)
         public virtual void SetAttributes(string path, bool isHidden, bool isReadOnly)
         {
         {
-            if (OperatingSystem.Id != OperatingSystemId.Windows)
+            if (!OperatingSystem.IsWindows())
             {
             {
                 return;
                 return;
             }
             }

+ 4 - 2
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -21,6 +21,7 @@ using Emby.Server.Implementations.Playlists;
 using Emby.Server.Implementations.ScheduledTasks;
 using Emby.Server.Implementations.ScheduledTasks;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
 using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
@@ -2539,9 +2540,10 @@ namespace Emby.Server.Implementations.Library
             {
             {
                 episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming);
                 episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming);
                 // Resolve from parent folder if it's not the Season folder
                 // Resolve from parent folder if it's not the Season folder
-                if (episodeInfo == null && episode.Parent.GetType() == typeof(Folder))
+                var parent = episode.GetParent();
+                if (episodeInfo == null && parent.GetType() == typeof(Folder))
                 {
                 {
-                    episodeInfo = resolver.Resolve(episode.Parent.Path, true, null, null, isAbsoluteNaming);
+                    episodeInfo = resolver.Resolve(parent.Path, true, null, null, isAbsoluteNaming);
                     if (episodeInfo != null)
                     if (episodeInfo != null)
                     {
                     {
                         // add the container
                         // add the container

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

@@ -12,7 +12,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Json;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;

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

@@ -15,7 +15,7 @@ using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
 using Jellyfin.Data.Enums;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Json;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.MediaEncoding;

+ 1 - 1
Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -6,7 +6,7 @@ using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
 using Emby.Naming.Video;
 using Emby.Naming.Video;
-using MediaBrowser.Common.Extensions;
+using Jellyfin.Extensions;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.Movies;

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

@@ -5,12 +5,12 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using Diacritics.Extensions;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
 using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Extensions;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Search;
 using MediaBrowser.Model.Search;

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

@@ -11,9 +11,9 @@ using System.Text;
 using System.Text.Json;
 using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using Jellyfin.Extensions;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Json;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;

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

@@ -7,7 +7,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Text.Json;
 using System.Text.Json;
-using MediaBrowser.Common.Json;
+using Jellyfin.Extensions.Json;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Emby.Server.Implementations.LiveTv.EmbyTV
 namespace Emby.Server.Implementations.LiveTv.EmbyTV

+ 2 - 2
Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -15,7 +15,7 @@ using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common;
 using MediaBrowser.Common;
-using MediaBrowser.Common.Json;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Model.Cryptography;
 using MediaBrowser.Model.Cryptography;
@@ -789,7 +789,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             {
             {
                 var channelNumber = GetChannelNumber(channel);
                 var channelNumber = GetChannelNumber(channel);
 
 
-                var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase)) 
+                var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase))
                     ?? new ScheduleDirect.Station
                     ?? new ScheduleDirect.Station
                     {
                     {
                         stationID = channel.stationID
                         stationID = channel.stationID

+ 2 - 1
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs

@@ -12,8 +12,9 @@ using System.Net.Http;
 using System.Text.Json;
 using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using Jellyfin.Extensions;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Json;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;

+ 1 - 0
Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs

@@ -10,6 +10,7 @@ using System.Net.Http;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using Jellyfin.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;

+ 3 - 1
Emby.Server.Implementations/Localization/Core/ar.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "حذف سجل الأنشطة",
     "TaskCleanActivityLog": "حذف سجل الأنشطة",
     "Default": "الإعدادات الافتراضية",
     "Default": "الإعدادات الافتراضية",
     "Undefined": "غير معرف",
     "Undefined": "غير معرف",
-    "Forced": "ملحقة"
+    "Forced": "ملحقة",
+    "TaskOptimizeDatabaseDescription": "يضغط قاعدة البيانات ويقتطع المساحة الحرة. تشغيل هذه المهمة بعد فحص المكتبة أو إجراء تغييرات أخرى تشير ضمنًا إلى أن تعديلات قاعدة البيانات قد تؤدي إلى تحسين الأداء.",
+    "TaskOptimizeDatabase": "تحسين قاعدة البيانات"
 }
 }

+ 10 - 8
Emby.Server.Implementations/Localization/Core/bg-BG.json

@@ -8,7 +8,7 @@
     "CameraImageUploadedFrom": "Нова снимка от камера беше качена от {0}",
     "CameraImageUploadedFrom": "Нова снимка от камера беше качена от {0}",
     "Channels": "Канали",
     "Channels": "Канали",
     "ChapterNameValue": "Глава {0}",
     "ChapterNameValue": "Глава {0}",
-    "Collections": "Поредици",
+    "Collections": "Колекции",
     "DeviceOfflineWithName": "{0} се разкачи",
     "DeviceOfflineWithName": "{0} се разкачи",
     "DeviceOnlineWithName": "{0} е свързан",
     "DeviceOnlineWithName": "{0} е свързан",
     "FailedLoginAttemptWithUserName": "Неуспешен опит за влизане от {0}",
     "FailedLoginAttemptWithUserName": "Неуспешен опит за влизане от {0}",
@@ -29,13 +29,13 @@
     "Inherit": "Наследяване",
     "Inherit": "Наследяване",
     "ItemAddedWithName": "{0} е добавено към библиотеката",
     "ItemAddedWithName": "{0} е добавено към библиотеката",
     "ItemRemovedWithName": "{0} е премахнато от библиотеката",
     "ItemRemovedWithName": "{0} е премахнато от библиотеката",
-    "LabelIpAddressValue": "ИП адрес: {0}",
-    "LabelRunningTimeValue": "Стартирано от: {0}",
+    "LabelIpAddressValue": "IP адрес: {0}",
+    "LabelRunningTimeValue": "Продължителност: {0}",
     "Latest": "Последни",
     "Latest": "Последни",
-    "MessageApplicationUpdated": "Сървърът е обновен",
-    "MessageApplicationUpdatedTo": "Сървърът е обновен до {0}",
-    "MessageNamedServerConfigurationUpdatedWithValue": "Секцията {0} от сървърната конфигурация се актуализира",
-    "MessageServerConfigurationUpdated": "Конфигурацията на сървъра се актуализира",
+    "MessageApplicationUpdated": "Сървърът беше обновен",
+    "MessageApplicationUpdatedTo": "Сървърът беше обновен до {0}",
+    "MessageNamedServerConfigurationUpdatedWithValue": "Секцията {0} от сървърната конфигурация беше актуализирана",
+    "MessageServerConfigurationUpdated": "Конфигурацията на сървъра беше актуализирана",
     "MixedContent": "Смесено съдържание",
     "MixedContent": "Смесено съдържание",
     "Movies": "Филми",
     "Movies": "Филми",
     "Music": "Музика",
     "Music": "Музика",
@@ -118,5 +118,7 @@
     "Forced": "Принудително",
     "Forced": "Принудително",
     "Default": "По подразбиране",
     "Default": "По подразбиране",
     "TaskCleanActivityLogDescription": "Изтрива записите в дневника с активност по стари от конфигурираната възраст.",
     "TaskCleanActivityLogDescription": "Изтрива записите в дневника с активност по стари от конфигурираната възраст.",
-    "TaskCleanActivityLog": "Изчисти дневника с активност"
+    "TaskCleanActivityLog": "Изчисти дневника с активност",
+    "TaskOptimizeDatabaseDescription": "Прави базата данни по-компактна и освобождава място. Пускането на тази задача след сканиране на библиотеката или правене на други промени, свързани с модификации на базата данни, може да подобри производителността.",
+    "TaskOptimizeDatabase": "Оптимизирай базата данни"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/ca.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "Buidar Registre d'Activitat",
     "TaskCleanActivityLog": "Buidar Registre d'Activitat",
     "Undefined": "Indefinit",
     "Undefined": "Indefinit",
     "Forced": "Forçat",
     "Forced": "Forçat",
-    "Default": "Defecto"
+    "Default": "Defecto",
+    "TaskOptimizeDatabaseDescription": "Compacta la base de dades i trunca l'espai lliure. Executar aquesta tasca després d’escanejar la biblioteca o fer altres canvis que impliquin modificacions a la base de dades pot millorar el rendiment.",
+    "TaskOptimizeDatabase": "Optimitzar la base de dades"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/cs.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "Smazat záznam aktivity",
     "TaskCleanActivityLog": "Smazat záznam aktivity",
     "Undefined": "Nedefinované",
     "Undefined": "Nedefinované",
     "Forced": "Vynucené",
     "Forced": "Vynucené",
-    "Default": "Výchozí"
+    "Default": "Výchozí",
+    "TaskOptimizeDatabaseDescription": "Zmenší databázi a odstraní prázdné místo. Spuštění této úlohy po skenování knihovny či jiných změnách databáze může zlepšit výkon.",
+    "TaskOptimizeDatabase": "Optimalizovat databázi"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/da.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "Ryd Aktivitetslog",
     "TaskCleanActivityLog": "Ryd Aktivitetslog",
     "Undefined": "Udefineret",
     "Undefined": "Udefineret",
     "Forced": "Tvunget",
     "Forced": "Tvunget",
-    "Default": "Standard"
+    "Default": "Standard",
+    "TaskOptimizeDatabaseDescription": "Kompakter database og forkorter fri plads. Ved at køre denne proces efter at scanne biblioteket eller efter at ændre noget som kunne have indflydelse på databasen, kan forbedre ydeevne.",
+    "TaskOptimizeDatabase": "Optimér database"
 }
 }

+ 5 - 3
Emby.Server.Implementations/Localization/Core/de.json

@@ -3,7 +3,7 @@
     "AppDeviceValues": "App: {0}, Gerät: {1}",
     "AppDeviceValues": "App: {0}, Gerät: {1}",
     "Application": "Anwendung",
     "Application": "Anwendung",
     "Artists": "Interpreten",
     "Artists": "Interpreten",
-    "AuthenticationSucceededWithUserName": "{0} wurde angemeldet",
+    "AuthenticationSucceededWithUserName": "{0} erfolgreich authentifiziert",
     "Books": "Bücher",
     "Books": "Bücher",
     "CameraImageUploadedFrom": "Ein neues Kamerafoto wurde von {0} hochgeladen",
     "CameraImageUploadedFrom": "Ein neues Kamerafoto wurde von {0} hochgeladen",
     "Channels": "Kanäle",
     "Channels": "Kanäle",
@@ -16,7 +16,7 @@
     "Folders": "Verzeichnisse",
     "Folders": "Verzeichnisse",
     "Genres": "Genres",
     "Genres": "Genres",
     "HeaderAlbumArtists": "Album-Interpreten",
     "HeaderAlbumArtists": "Album-Interpreten",
-    "HeaderContinueWatching": "Fortsetzen",
+    "HeaderContinueWatching": "Weiterschauen",
     "HeaderFavoriteAlbums": "Lieblingsalben",
     "HeaderFavoriteAlbums": "Lieblingsalben",
     "HeaderFavoriteArtists": "Lieblings-Interpreten",
     "HeaderFavoriteArtists": "Lieblings-Interpreten",
     "HeaderFavoriteEpisodes": "Lieblingsepisoden",
     "HeaderFavoriteEpisodes": "Lieblingsepisoden",
@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "Aktivitätsprotokoll aufräumen",
     "TaskCleanActivityLog": "Aktivitätsprotokoll aufräumen",
     "Undefined": "Undefiniert",
     "Undefined": "Undefiniert",
     "Forced": "Erzwungen",
     "Forced": "Erzwungen",
-    "Default": "Standard"
+    "Default": "Standard",
+    "TaskOptimizeDatabaseDescription": "Komprimiert die Datenbank und trimmt den freien Speicherplatz. Die Ausführung dieser Aufgabe nach dem Scannen der Bibliothek oder nach anderen Änderungen, die Datenbankänderungen implizieren, kann die Leistung verbessern.",
+    "TaskOptimizeDatabase": "Datenbank optimieren"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/en-GB.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "Clean Activity Log",
     "TaskCleanActivityLog": "Clean Activity Log",
     "Undefined": "Undefined",
     "Undefined": "Undefined",
     "Forced": "Forced",
     "Forced": "Forced",
-    "Default": "Default"
+    "Default": "Default",
+    "TaskOptimizeDatabaseDescription": "Compacts database and truncates free space. Running this task after scanning the library or doing other changes that imply database modifications might improve performance.",
+    "TaskOptimizeDatabase": "Optimise database"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/es-AR.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "Borrar log de actividades",
     "TaskCleanActivityLog": "Borrar log de actividades",
     "Undefined": "Indefinido",
     "Undefined": "Indefinido",
     "Forced": "Forzado",
     "Forced": "Forzado",
-    "Default": "Por Defecto"
+    "Default": "Por Defecto",
+    "TaskOptimizeDatabaseDescription": "Compacta la base de datos y restaura el espacio libre. Ejecutar esta tarea después de actualizar las librerías o realizar otros cambios que impliquen modificar las bases de datos puede mejorar la performance.",
+    "TaskOptimizeDatabase": "Optimización de base de datos"
 }
 }

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

@@ -70,7 +70,7 @@
     "ScheduledTaskFailedWithName": "{0} falló",
     "ScheduledTaskFailedWithName": "{0} falló",
     "ScheduledTaskStartedWithName": "{0} iniciada",
     "ScheduledTaskStartedWithName": "{0} iniciada",
     "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
     "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
-    "Shows": "Series de Televisión",
+    "Shows": "Series",
     "Songs": "Canciones",
     "Songs": "Canciones",
     "StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
     "StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
     "SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}",
     "SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}",

+ 3 - 1
Emby.Server.Implementations/Localization/Core/es_419.json

@@ -117,5 +117,7 @@
     "TaskCleanActivityLog": "Limpiar Registro de Actividades",
     "TaskCleanActivityLog": "Limpiar Registro de Actividades",
     "Undefined": "Sin definir",
     "Undefined": "Sin definir",
     "Forced": "Forzado",
     "Forced": "Forzado",
-    "Default": "Por Defecto"
+    "Default": "Por Defecto",
+    "TaskOptimizeDatabaseDescription": "Compacta la base de datos y restaura el espacio libre. Ejecutar esta tarea después de actualizar las librerías o realizar otros cambios que impliquen modificar las bases de datos puede mejorar la performance.",
+    "TaskOptimizeDatabase": "Optimización de base de datos"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/fi.json

@@ -117,5 +117,7 @@
     "Default": "Oletus",
     "Default": "Oletus",
     "TaskCleanActivityLogDescription": "Poistaa määritettyä ikää vanhemmat tapahtumat toimintahistoriasta.",
     "TaskCleanActivityLogDescription": "Poistaa määritettyä ikää vanhemmat tapahtumat toimintahistoriasta.",
     "TaskCleanActivityLog": "Tyhjennä toimintahistoria",
     "TaskCleanActivityLog": "Tyhjennä toimintahistoria",
-    "Undefined": "Määrittelemätön"
+    "Undefined": "Määrittelemätön",
+    "TaskOptimizeDatabaseDescription": "Tiivistää ja puhdistaa tietokannan. Tämän toiminnon suorittaminen kirjastojen skannauksen tai muiden tietokantaan liittyvien muutoksien jälkeen voi parantaa suorituskykyä.",
+    "TaskOptimizeDatabase": "Optimoi tietokanta"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/fr.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "Nettoyer le journal d'activité",
     "TaskCleanActivityLog": "Nettoyer le journal d'activité",
     "Undefined": "Non défini",
     "Undefined": "Non défini",
     "Forced": "Forcé",
     "Forced": "Forcé",
-    "Default": "Par défaut"
+    "Default": "Par défaut",
+    "TaskOptimizeDatabaseDescription": "Réduit les espaces vides/inutiles et compacte la base de données. Utiliser cette fonction après une mise à jour de la bibliothèque ou toute autre modification de la base de données peut améliorer les performances du serveur.",
+    "TaskOptimizeDatabase": "Optimiser la base de données"
 }
 }

+ 30 - 1
Emby.Server.Implementations/Localization/Core/gl.json

@@ -88,5 +88,34 @@
     "NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo parada",
     "NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo parada",
     "NotificationOptionVideoPlayback": "Reproducción de vídeo iniciada",
     "NotificationOptionVideoPlayback": "Reproducción de vídeo iniciada",
     "NotificationOptionUserLockedOut": "Usuario bloqueado",
     "NotificationOptionUserLockedOut": "Usuario bloqueado",
-    "NotificationOptionTaskFailed": "Falla na tarefa axendada"
+    "NotificationOptionTaskFailed": "Falla na tarefa axendada",
+    "TaskCleanTranscodeDescription": "Borra os arquivos de transcode anteriores a un día.",
+    "TaskCleanTranscode": "Limpar Directorio de Transcode",
+    "UserStoppedPlayingItemWithValues": "{0} rematou de reproducir {1} en {2}",
+    "UserStartedPlayingItemWithValues": "{0} está reproducindo {1} en {2}",
+    "TaskDownloadMissingSubtitlesDescription": "Busca en internet por subtítulos que faltan baseado na configuración de metadatos.",
+    "TaskDownloadMissingSubtitles": "Descargar subtítulos que faltan",
+    "TaskRefreshChannelsDescription": "Refresca a información do canle de internet.",
+    "TaskRefreshChannels": "Refrescar Canles",
+    "TaskUpdatePluginsDescription": "Descarga e instala actualizacións para plugins que están configurados para actualizarse automáticamente.",
+    "TaskRefreshPeopleDescription": "Actualiza os metadatos dos actores e directores na túa libraría multimedia.",
+    "TaskRefreshPeople": "Refrescar Persoas",
+    "TaskCleanLogsDescription": "Borra arquivos de rexistro que son mais antigos que {0} días.",
+    "TaskRefreshLibraryDescription": "Escanea a tua libraría multimedia buscando novos arquivos e refrescando os metadatos.",
+    "TaskRefreshLibrary": "Escanear Libraría Multimedia",
+    "TaskRefreshChapterImagesDescription": "Crea previsualizacións para videos que teñen capítulos.",
+    "TaskRefreshChapterImages": "Extraer Imaxes dos Capítulos",
+    "TaskCleanCacheDescription": "Borra ficheiros da caché que xa non son necesarios para o sistema.",
+    "TaskCleanCache": "Limpa Directorio de Caché",
+    "TaskCleanActivityLogDescription": "Borra as entradas no rexistro de actividade anteriores á data configurada.",
+    "TasksApplicationCategory": "Aplicación",
+    "ValueSpecialEpisodeName": "Especial - {0}",
+    "ValueHasBeenAddedToLibrary": "{0} foi engadido a túa libraría multimedia",
+    "TasksLibraryCategory": "Libraría",
+    "TasksMaintenanceCategory": "Mantemento",
+    "VersionNumber": "Versión {0}",
+    "UserPolicyUpdatedWithName": "A política de usuario foi actualizada para {0}",
+    "UserPasswordChangedWithName": "Cambiouse o contrasinal para o usuario {0}",
+    "UserOnlineFromDevice": "{0} está en liña desde {1}",
+    "UserOfflineFromDevice": "{0} desconectouse desde {1}"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/hu.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "Tevékenységnapló törlése",
     "TaskCleanActivityLog": "Tevékenységnapló törlése",
     "Undefined": "Meghatározatlan",
     "Undefined": "Meghatározatlan",
     "Forced": "Kényszerített",
     "Forced": "Kényszerített",
-    "Default": "Alapértelmezett"
+    "Default": "Alapértelmezett",
+    "TaskOptimizeDatabaseDescription": "Tömöríti az adatbázist és csonkolja a szabad helyet. A feladat futtatása a könyvtár beolvasása után, vagy egyéb, adatbázis-módosítást igénylő változtatások végrehajtása javíthatja a teljesítményt.",
+    "TaskOptimizeDatabase": "Adatbázis optimalizálása"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/it.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLogDescription": "Elimina gli inserimenti nel registro delle attività più vecchie dell’età configurata.",
     "TaskCleanActivityLogDescription": "Elimina gli inserimenti nel registro delle attività più vecchie dell’età configurata.",
     "Undefined": "Non Definito",
     "Undefined": "Non Definito",
     "Forced": "Forzato",
     "Forced": "Forzato",
-    "Default": "Predefinito"
+    "Default": "Predefinito",
+    "TaskOptimizeDatabaseDescription": "Compatta Database e tronca spazi liberi. Eseguire questa azione dopo la scansione o dopo aver fatto altri cambiamenti inerenti il database potrebbe aumentarne la performance.",
+    "TaskOptimizeDatabase": "Ottimizza Database"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/ja.json

@@ -117,5 +117,7 @@
     "TaskCleanActivityLog": "アクティビティの履歴を消去",
     "TaskCleanActivityLog": "アクティビティの履歴を消去",
     "Undefined": "未定義",
     "Undefined": "未定義",
     "Forced": "強制",
     "Forced": "強制",
-    "Default": "デフォルト"
+    "Default": "デフォルト",
+    "TaskOptimizeDatabaseDescription": "データベースをコンパクトにして、空き領域を切り詰めます。メディアライブラリのスキャン後でこのタスクを実行するとパフォーマンスが向上する可能性があります。",
+    "TaskOptimizeDatabase": "データベースの最適化"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/kk.json

@@ -118,5 +118,7 @@
     "TaskRefreshLibraryDescription": "Tasyğyşhanadağy jaña faildardy skanerleidі jäne metaderekterdı jañğyrtady.",
     "TaskRefreshLibraryDescription": "Tasyğyşhanadağy jaña faildardy skanerleidі jäne metaderekterdı jañğyrtady.",
     "TaskRefreshChapterImagesDescription": "Sahnalary bar beineler üşın nobailar jasaidy.",
     "TaskRefreshChapterImagesDescription": "Sahnalary bar beineler üşın nobailar jasaidy.",
     "TaskCleanCacheDescription": "Jüiede qajet emes keştelgen faildardy joiady.",
     "TaskCleanCacheDescription": "Jüiede qajet emes keştelgen faildardy joiady.",
-    "TaskCleanActivityLogDescription": "Äreket jūrnalyndağy teñşelgen jasynan asqan jazbalary joiady."
+    "TaskCleanActivityLogDescription": "Äreket jūrnalyndağy teñşelgen jasynan asqan jazbalary joiady.",
+    "TaskOptimizeDatabaseDescription": "Derekqordy qysyp, bos oryndy qysqartady. Būl tapsyrmany tasyğyşhanany skanerlegennen keiın nemese derekqorğa meñzeitın basqa özgertuler ıstelgennen keiın oryndau önımdılıktı damytuy mümkın.",
+    "TaskOptimizeDatabase": "Derekqordy oñtailandyru"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/ko.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "활동내역청소",
     "TaskCleanActivityLog": "활동내역청소",
     "Undefined": "일치하지 않음",
     "Undefined": "일치하지 않음",
     "Forced": "강제하기",
     "Forced": "강제하기",
-    "Default": "기본 설정"
+    "Default": "기본 설정",
+    "TaskOptimizeDatabaseDescription": "데이터베이스를 압축하고 사용 가능한 공간을 늘립니다. 라이브러리를 검색한 후 이 작업을 실행하거나 데이터베이스 수정같은 비슷한 작업을 수행하면 성능이 향상될 수 있습니다.",
+    "TaskOptimizeDatabase": "데이터베이스 최적화"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/lv.json

@@ -117,5 +117,7 @@
     "TaskCleanActivityLogDescription": "Nodzēš darbību žurnāla ierakstus, kuri ir vecāki par doto vecumu.",
     "TaskCleanActivityLogDescription": "Nodzēš darbību žurnāla ierakstus, kuri ir vecāki par doto vecumu.",
     "TaskCleanActivityLog": "Notīrīt Darbību Žurnālu",
     "TaskCleanActivityLog": "Notīrīt Darbību Žurnālu",
     "Undefined": "Nenoteikts",
     "Undefined": "Nenoteikts",
-    "Default": "Noklusējums"
+    "Default": "Noklusējums",
+    "TaskOptimizeDatabaseDescription": "Saspiež datubāzi un atbrīvo atmiņu. Uzdevum palaišana pēc bibliotēku skenēšanas vai citām, ar datubāzi saistītām, izmaiņām iespējams uzlabos ātrdarbību.",
+    "TaskOptimizeDatabase": "Optimizēt datubāzi"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/ml.json

@@ -117,5 +117,7 @@
     "Favorites": "പ്രിയങ്കരങ്ങൾ",
     "Favorites": "പ്രിയങ്കരങ്ങൾ",
     "Books": "പുസ്തകങ്ങൾ",
     "Books": "പുസ്തകങ്ങൾ",
     "Genres": "വിഭാഗങ്ങൾ",
     "Genres": "വിഭാഗങ്ങൾ",
-    "Channels": "ചാനലുകൾ"
+    "Channels": "ചാനലുകൾ",
+    "TaskOptimizeDatabaseDescription": "ഡാറ്റാബേസ് ചുരുക്കുകയും സ്വതന്ത്ര ഇടം വെട്ടിച്ചുരുക്കുകയും ചെയ്യുന്നു. ലൈബ്രറി സ്‌കാൻ ചെയ്‌തതിനുശേഷം അല്ലെങ്കിൽ ഡാറ്റാബേസ് പരിഷ്‌ക്കരണങ്ങളെ സൂചിപ്പിക്കുന്ന മറ്റ് മാറ്റങ്ങൾ ചെയ്‌തതിന് ശേഷം ഈ ടാസ്‌ക് പ്രവർത്തിപ്പിക്കുന്നത് പ്രകടനം മെച്ചപ്പെടുത്തും.",
+    "TaskOptimizeDatabase": "ഡാറ്റാബേസ് ഒപ്റ്റിമൈസ് ചെയ്യുക"
 }
 }

+ 10 - 2
Emby.Server.Implementations/Localization/Core/ms.json

@@ -5,7 +5,7 @@
     "Artists": "Artis",
     "Artists": "Artis",
     "AuthenticationSucceededWithUserName": "{0} berjaya disahkan",
     "AuthenticationSucceededWithUserName": "{0} berjaya disahkan",
     "Books": "Buku-buku",
     "Books": "Buku-buku",
-    "CameraImageUploadedFrom": "Ada gambar dari kamera yang baru dimuat naik melalui {0}",
+    "CameraImageUploadedFrom": "Gambar baharu telah dimuat naik melalui {0}",
     "Channels": "Saluran",
     "Channels": "Saluran",
     "ChapterNameValue": "Bab {0}",
     "ChapterNameValue": "Bab {0}",
     "Collections": "Koleksi",
     "Collections": "Koleksi",
@@ -101,5 +101,13 @@
     "Forced": "Paksa",
     "Forced": "Paksa",
     "Default": "Asal",
     "Default": "Asal",
     "TaskCleanCache": "Bersihkan Direktori Cache",
     "TaskCleanCache": "Bersihkan Direktori Cache",
-    "TaskCleanActivityLogDescription": "Padamkan entri log aktiviti yang lebih tua daripada usia yang dikonfigurasi."
+    "TaskCleanActivityLogDescription": "Padamkan entri log aktiviti yang lebih tua daripada usia yang dikonfigurasi.",
+    "TaskRefreshPeople": "Segarkan Orang",
+    "TaskCleanLogsDescription": "Padamkan fail log yang berumur lebih dari {0} hari.",
+    "TaskCleanLogs": "Bersihkan Direktotri Log",
+    "TaskRefreshLibraryDescription": "Imbas perpustakaan media untuk mencari fail-fail baru dan menyegarkan metadata.",
+    "TaskRefreshLibrary": "Imbas Perpustakaan Media",
+    "TaskRefreshChapterImagesDescription": "Membuat gambaran kecil untuk video yang mempunyai bab.",
+    "TaskRefreshChapterImages": "Ekstrak Gambar-gambar Bab",
+    "TaskCleanCacheDescription": "Menghapuskan fail cache yang tidak lagi diperlukan oleh sistem."
 }
 }

+ 2 - 1
Emby.Server.Implementations/Localization/Core/nb.json

@@ -118,5 +118,6 @@
     "Undefined": "Udefinert",
     "Undefined": "Udefinert",
     "Forced": "Tvunget",
     "Forced": "Tvunget",
     "Default": "Standard",
     "Default": "Standard",
-    "TaskCleanActivityLogDescription": "Sletter oppføringer i aktivitetsloggen som er eldre enn den konfigurerte alderen."
+    "TaskCleanActivityLogDescription": "Sletter oppføringer i aktivitetsloggen som er eldre enn den konfigurerte alderen.",
+    "TaskOptimizeDatabase": "Optimiser database"
 }
 }

+ 10 - 8
Emby.Server.Implementations/Localization/Core/nl.json

@@ -1,7 +1,7 @@
 {
 {
     "Albums": "Albums",
     "Albums": "Albums",
     "AppDeviceValues": "App: {0}, Apparaat: {1}",
     "AppDeviceValues": "App: {0}, Apparaat: {1}",
-    "Application": "Applicatie",
+    "Application": "Toepassing",
     "Artists": "Artiesten",
     "Artists": "Artiesten",
     "AuthenticationSucceededWithUserName": "{0} is succesvol geauthenticeerd",
     "AuthenticationSucceededWithUserName": "{0} is succesvol geauthenticeerd",
     "Books": "Boeken",
     "Books": "Boeken",
@@ -25,7 +25,7 @@
     "HeaderLiveTV": "Live TV",
     "HeaderLiveTV": "Live TV",
     "HeaderNextUp": "Volgende",
     "HeaderNextUp": "Volgende",
     "HeaderRecordingGroups": "Opnamegroepen",
     "HeaderRecordingGroups": "Opnamegroepen",
-    "HomeVideos": "Home video's",
+    "HomeVideos": "Thuis video's",
     "Inherit": "Erven",
     "Inherit": "Erven",
     "ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek",
     "ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek",
     "ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek",
     "ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek",
@@ -92,11 +92,11 @@
     "ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek",
     "ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek",
     "ValueSpecialEpisodeName": "Speciaal - {0}",
     "ValueSpecialEpisodeName": "Speciaal - {0}",
     "VersionNumber": "Versie {0}",
     "VersionNumber": "Versie {0}",
-    "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar missende ondertitels gebaseerd op metadata configuratie.",
-    "TaskDownloadMissingSubtitles": "Download missende ondertitels",
+    "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar missende ondertiteling gebaseerd op metadata configuratie.",
+    "TaskDownloadMissingSubtitles": "Download missende ondertiteling",
     "TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.",
     "TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.",
     "TaskRefreshChannels": "Vernieuw Kanalen",
     "TaskRefreshChannels": "Vernieuw Kanalen",
-    "TaskCleanTranscodeDescription": "Verwijder transcode bestanden ouder dan 1 dag.",
+    "TaskCleanTranscodeDescription": "Verwijdert transcode bestanden ouder dan 1 dag.",
     "TaskCleanLogs": "Log Folder Opschonen",
     "TaskCleanLogs": "Log Folder Opschonen",
     "TaskCleanTranscode": "Transcode Folder Opschonen",
     "TaskCleanTranscode": "Transcode Folder Opschonen",
     "TaskUpdatePluginsDescription": "Download en installeert updates voor plugins waar automatisch updaten aan staat.",
     "TaskUpdatePluginsDescription": "Download en installeert updates voor plugins waar automatisch updaten aan staat.",
@@ -108,15 +108,17 @@
     "TaskRefreshLibrary": "Scan Media Bibliotheek",
     "TaskRefreshLibrary": "Scan Media Bibliotheek",
     "TaskRefreshChapterImagesDescription": "Maakt thumbnails aan voor videos met hoofdstukken.",
     "TaskRefreshChapterImagesDescription": "Maakt thumbnails aan voor videos met hoofdstukken.",
     "TaskRefreshChapterImages": "Hoofdstukafbeeldingen Uitpakken",
     "TaskRefreshChapterImages": "Hoofdstukafbeeldingen Uitpakken",
-    "TaskCleanCacheDescription": "Verwijder gecachte bestanden die het systeem niet langer nodig heeft.",
+    "TaskCleanCacheDescription": "Verwijdert gecachte bestanden die het systeem niet langer nodig heeft.",
     "TaskCleanCache": "Cache Folder Opschonen",
     "TaskCleanCache": "Cache Folder Opschonen",
     "TasksChannelsCategory": "Internet Kanalen",
     "TasksChannelsCategory": "Internet Kanalen",
     "TasksApplicationCategory": "Applicatie",
     "TasksApplicationCategory": "Applicatie",
     "TasksLibraryCategory": "Bibliotheek",
     "TasksLibraryCategory": "Bibliotheek",
     "TasksMaintenanceCategory": "Onderhoud",
     "TasksMaintenanceCategory": "Onderhoud",
-    "TaskCleanActivityLogDescription": "Verwijder activiteiten logs ouder dan de ingestelde tijd.",
+    "TaskCleanActivityLogDescription": "Verwijdert activiteiten logs ouder dan de ingestelde tijd.",
     "TaskCleanActivityLog": "Leeg activiteiten logboek",
     "TaskCleanActivityLog": "Leeg activiteiten logboek",
     "Undefined": "Niet gedefinieerd",
     "Undefined": "Niet gedefinieerd",
     "Forced": "Geforceerd",
     "Forced": "Geforceerd",
-    "Default": "Standaard"
+    "Default": "Standaard",
+    "TaskOptimizeDatabaseDescription": "Comprimeert de database en trimt vrije ruimte. Het uitvoeren van deze taak kan de prestaties verbeteren, na het scannen van de bibliotheek of andere aanpassingen die invloed hebben op de database.",
+    "TaskOptimizeDatabase": "Database optimaliseren"
 }
 }

+ 2 - 1
Emby.Server.Implementations/Localization/Core/pl.json

@@ -118,5 +118,6 @@
     "TaskCleanActivityLog": "Czyść dziennik aktywności",
     "TaskCleanActivityLog": "Czyść dziennik aktywności",
     "Undefined": "Nieustalony",
     "Undefined": "Nieustalony",
     "Forced": "Wymuszony",
     "Forced": "Wymuszony",
-    "Default": "Domyślne"
+    "Default": "Domyślne",
+    "TaskOptimizeDatabase": "Optymalizuj bazę danych"
 }
 }

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

@@ -61,7 +61,7 @@
     "NameSeasonUnknown": "Temporada Desconhecida",
     "NameSeasonUnknown": "Temporada Desconhecida",
     "NameSeasonNumber": "Temporada {0}",
     "NameSeasonNumber": "Temporada {0}",
     "NameInstallFailed": "Falha na instalação de {0}",
     "NameInstallFailed": "Falha na instalação de {0}",
-    "MusicVideos": "Videoclips",
+    "MusicVideos": "Videoclipes",
     "Music": "Música",
     "Music": "Música",
     "MixedContent": "Conteúdo Misto",
     "MixedContent": "Conteúdo Misto",
     "MessageServerConfigurationUpdated": "A configuração do servidor foi actualizada",
     "MessageServerConfigurationUpdated": "A configuração do servidor foi actualizada",

+ 3 - 1
Emby.Server.Implementations/Localization/Core/ru.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "Очистить журнал активности",
     "TaskCleanActivityLog": "Очистить журнал активности",
     "Undefined": "Не определено",
     "Undefined": "Не определено",
     "Forced": "Форсир-ые",
     "Forced": "Форсир-ые",
-    "Default": "По умолчанию"
+    "Default": "По умолчанию",
+    "TaskOptimizeDatabaseDescription": "Сжимает базу данных и обрезает свободное место. Выполнение этой задачи после сканирования библиотеки или внесения других изменений, предполагающих модификации базы данных, может повысить производительность.",
+    "TaskOptimizeDatabase": "Оптимизировать базу данных"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/sk.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "Vyčistiť log aktivít",
     "TaskCleanActivityLog": "Vyčistiť log aktivít",
     "Undefined": "Nedefinované",
     "Undefined": "Nedefinované",
     "Forced": "Vynútené",
     "Forced": "Vynútené",
-    "Default": "Predvolené"
+    "Default": "Predvolené",
+    "TaskOptimizeDatabaseDescription": "Zmenší databázu a odstráni prázdne miesto. Spustenie tejto úlohy po skenovaní knižnice alebo po iných zmenách zahŕňajúcich úpravy databáze môže zlepšiť výkon.",
+    "TaskOptimizeDatabase": "Optimalizovať databázu"
 }
 }

+ 5 - 3
Emby.Server.Implementations/Localization/Core/sl-SI.json

@@ -16,7 +16,7 @@
     "Folders": "Mape",
     "Folders": "Mape",
     "Genres": "Zvrsti",
     "Genres": "Zvrsti",
     "HeaderAlbumArtists": "Izvajalci albuma",
     "HeaderAlbumArtists": "Izvajalci albuma",
-    "HeaderContinueWatching": "Nadaljuj z ogledom",
+    "HeaderContinueWatching": "Nadaljuj ogled",
     "HeaderFavoriteAlbums": "Priljubljeni albumi",
     "HeaderFavoriteAlbums": "Priljubljeni albumi",
     "HeaderFavoriteArtists": "Priljubljeni izvajalci",
     "HeaderFavoriteArtists": "Priljubljeni izvajalci",
     "HeaderFavoriteEpisodes": "Priljubljene epizode",
     "HeaderFavoriteEpisodes": "Priljubljene epizode",
@@ -90,7 +90,7 @@
     "UserStartedPlayingItemWithValues": "{0} predvaja {1} na {2}",
     "UserStartedPlayingItemWithValues": "{0} predvaja {1} na {2}",
     "UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}",
     "UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}",
     "ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici",
     "ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici",
-    "ValueSpecialEpisodeName": "Posebna - {0}",
+    "ValueSpecialEpisodeName": "Bonus - {0}",
     "VersionNumber": "Različica {0}",
     "VersionNumber": "Različica {0}",
     "TaskDownloadMissingSubtitles": "Prenesi manjkajoče podnapise",
     "TaskDownloadMissingSubtitles": "Prenesi manjkajoče podnapise",
     "TaskRefreshChannelsDescription": "Osveži podatke spletnih kanalov.",
     "TaskRefreshChannelsDescription": "Osveži podatke spletnih kanalov.",
@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "Počisti dnevnik aktivnosti",
     "TaskCleanActivityLog": "Počisti dnevnik aktivnosti",
     "Undefined": "Nedoločen",
     "Undefined": "Nedoločen",
     "Forced": "Prisilno",
     "Forced": "Prisilno",
-    "Default": "Privzeto"
+    "Default": "Privzeto",
+    "TaskOptimizeDatabaseDescription": "Stisne bazo podatkov in uredi prazen prostor. Zagon tega opravila po iskanju predstavnosti ali drugih spremembah ki vplivajo na bazo podatkov lahko izboljša hitrost delovanja.",
+    "TaskOptimizeDatabase": "Optimiziraj bazo podatkov"
 }
 }

+ 19 - 12
Emby.Server.Implementations/Localization/Core/sq.json

@@ -42,8 +42,8 @@
     "Sync": "Sinkronizo",
     "Sync": "Sinkronizo",
     "SubtitleDownloadFailureFromForItem": "Titrat deshtuan të shkarkohen nga {0} për {1}",
     "SubtitleDownloadFailureFromForItem": "Titrat deshtuan të shkarkohen nga {0} për {1}",
     "StartupEmbyServerIsLoading": "Serveri Jellyfin po ngarkohet. Ju lutemi provoni përseri pas pak.",
     "StartupEmbyServerIsLoading": "Serveri Jellyfin po ngarkohet. Ju lutemi provoni përseri pas pak.",
-    "Songs": "Këngë",
-    "Shows": "Seriale",
+    "Songs": "Këngët",
+    "Shows": "Serialet",
     "ServerNameNeedsToBeRestarted": "{0} duhet të ristartoj",
     "ServerNameNeedsToBeRestarted": "{0} duhet të ristartoj",
     "ScheduledTaskStartedWithName": "{0} filloi",
     "ScheduledTaskStartedWithName": "{0} filloi",
     "ScheduledTaskFailedWithName": "{0} dështoi",
     "ScheduledTaskFailedWithName": "{0} dështoi",
@@ -74,9 +74,9 @@
     "NameSeasonUnknown": "Sezon i panjohur",
     "NameSeasonUnknown": "Sezon i panjohur",
     "NameSeasonNumber": "Sezoni {0}",
     "NameSeasonNumber": "Sezoni {0}",
     "NameInstallFailed": "Instalimi i {0} dështoi",
     "NameInstallFailed": "Instalimi i {0} dështoi",
-    "MusicVideos": "Video muzikore",
+    "MusicVideos": "Videot muzikore",
     "Music": "Muzikë",
     "Music": "Muzikë",
-    "Movies": "Filma",
+    "Movies": "Filmat",
     "MixedContent": "Përmbajtje e përzier",
     "MixedContent": "Përmbajtje e përzier",
     "MessageServerConfigurationUpdated": "Konfigurimet e serverit u përditësuan",
     "MessageServerConfigurationUpdated": "Konfigurimet e serverit u përditësuan",
     "MessageNamedServerConfigurationUpdatedWithValue": "Seksioni i konfigurimit të serverit {0} u përditësua",
     "MessageNamedServerConfigurationUpdatedWithValue": "Seksioni i konfigurimit të serverit {0} u përditësua",
@@ -97,20 +97,27 @@
     "HeaderFavoriteAlbums": "Albumet e preferuar",
     "HeaderFavoriteAlbums": "Albumet e preferuar",
     "HeaderContinueWatching": "Vazhdo të shikosh",
     "HeaderContinueWatching": "Vazhdo të shikosh",
     "HeaderAlbumArtists": "Artistët e albumeve",
     "HeaderAlbumArtists": "Artistët e albumeve",
-    "Genres": "Zhanre",
-    "Folders": "Dosje",
-    "Favorites": "Të preferuara",
+    "Genres": "Zhanret",
+    "Folders": "Skedarët",
+    "Favorites": "Të preferuarat",
     "FailedLoginAttemptWithUserName": "Përpjekja për hyrje dështoi nga {0}",
     "FailedLoginAttemptWithUserName": "Përpjekja për hyrje dështoi nga {0}",
     "DeviceOnlineWithName": "{0} u lidh",
     "DeviceOnlineWithName": "{0} u lidh",
     "DeviceOfflineWithName": "{0} u shkëput",
     "DeviceOfflineWithName": "{0} u shkëput",
-    "Collections": "Koleksione",
+    "Collections": "Koleksionet",
     "ChapterNameValue": "Kapituj",
     "ChapterNameValue": "Kapituj",
-    "Channels": "Kanale",
+    "Channels": "Kanalet",
     "CameraImageUploadedFrom": "Një foto e re nga kamera u ngarkua nga {0}",
     "CameraImageUploadedFrom": "Një foto e re nga kamera u ngarkua nga {0}",
-    "Books": "Libra",
+    "Books": "Librat",
     "AuthenticationSucceededWithUserName": "{0} u identifikua me sukses",
     "AuthenticationSucceededWithUserName": "{0} u identifikua me sukses",
-    "Artists": "Artistë",
+    "Artists": "Artistët",
     "Application": "Aplikacioni",
     "Application": "Aplikacioni",
     "AppDeviceValues": "Aplikacioni: {0}, Pajisja: {1}",
     "AppDeviceValues": "Aplikacioni: {0}, Pajisja: {1}",
-    "Albums": "Albume"
+    "Albums": "Albumet",
+    "TaskCleanActivityLogDescription": "Pastro të dhënat mbi aktivitetin më të vjetra sesa koha e përcaktuar.",
+    "TaskCleanActivityLog": "Pastro të dhënat mbi aktivitetin",
+    "Undefined": "I papërcaktuar",
+    "Forced": "I detyruar",
+    "Default": "Parazgjedhur",
+    "TaskOptimizeDatabaseDescription": "Kompakton bazën e të dhënave dhe shkurton hapësirën e lirë. Drejtimi i kësaj detyre pasi skanoni bibliotekën ose bëni ndryshime të tjera që nënkuptojnë modifikime të bazës së të dhënave mund të përmirësojë performancën.",
+    "TaskOptimizeDatabase": "Optimizo databazën"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/ta.json

@@ -117,5 +117,7 @@
     "TaskCleanActivityLog": "செயல்பாட்டு பதிவை அழி",
     "TaskCleanActivityLog": "செயல்பாட்டு பதிவை அழி",
     "Undefined": "வரையறுக்கப்படாத",
     "Undefined": "வரையறுக்கப்படாத",
     "Forced": "கட்டாயப்படுத்தப்பட்டது",
     "Forced": "கட்டாயப்படுத்தப்பட்டது",
-    "Default": "இயல்புநிலை"
+    "Default": "இயல்புநிலை",
+    "TaskOptimizeDatabaseDescription": "தரவுத்தளத்தை சுருக்கி, இலவச இடத்தை குறைக்கிறது. நூலகத்தை ஸ்கேன் செய்தபின் அல்லது தரவுத்தள மாற்றங்களைக் குறிக்கும் பிற மாற்றங்களைச் செய்தபின் இந்த பணியை இயக்குவது செயல்திறனை மேம்படுத்தக்கூடும்.",
+    "TaskOptimizeDatabase": "தரவுத்தளத்தை மேம்படுத்தவும்"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/vi.json

@@ -117,5 +117,7 @@
     "TaskCleanActivityLog": "Xóa Nhật Ký Hoạt Động",
     "TaskCleanActivityLog": "Xóa Nhật Ký Hoạt Động",
     "Undefined": "Không Xác Định",
     "Undefined": "Không Xác Định",
     "Forced": "Bắt Buộc",
     "Forced": "Bắt Buộc",
-    "Default": "Mặc Định"
+    "Default": "Mặc Định",
+    "TaskOptimizeDatabaseDescription": "Thu gọn cơ sở dữ liệu và cắt bớt dung lượng trống. Chạy tác vụ này sau khi quét thư viện hoặc thực hiện các thay đổi khác ngụ ý sửa đổi cơ sở dữ liệu có thể cải thiện hiệu suất.",
+    "TaskOptimizeDatabase": "Tối ưu hóa cơ sở dữ liệu"
 }
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/zh-CN.json

@@ -118,5 +118,7 @@
     "TaskCleanActivityLogDescription": "删除早于设置时间的活动日志条目。",
     "TaskCleanActivityLogDescription": "删除早于设置时间的活动日志条目。",
     "Undefined": "未定义",
     "Undefined": "未定义",
     "Forced": "强制的",
     "Forced": "强制的",
-    "Default": "默认"
+    "Default": "默认",
+    "TaskOptimizeDatabaseDescription": "压缩数据库并优化可用空间,在扫描库或执行其他数据库修改后运行此任务可能会提高性能。",
+    "TaskOptimizeDatabase": "优化数据库"
 }
 }

+ 2 - 2
Emby.Server.Implementations/Localization/LocalizationManager.cs

@@ -8,8 +8,8 @@ using System.IO;
 using System.Reflection;
 using System.Reflection;
 using System.Text.Json;
 using System.Text.Json;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Json;
+using Jellyfin.Extensions;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;

+ 1 - 1
Emby.Server.Implementations/Playlists/PlaylistManager.cs

@@ -147,7 +147,7 @@ namespace Emby.Server.Implementations.Playlists
 
 
                 playlist.SetMediaType(options.MediaType);
                 playlist.SetMediaType(options.MediaType);
 
 
-                parentFolder.AddChild(playlist, CancellationToken.None);
+                parentFolder.AddChild(playlist);
 
 
                 await playlist.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { ForceSave = true }, CancellationToken.None)
                 await playlist.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { ForceSave = true }, CancellationToken.None)
                     .ConfigureAwait(false);
                     .ConfigureAwait(false);

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

@@ -10,8 +10,8 @@ using System.Text.Json;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common;
 using MediaBrowser.Common;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Json;
-using MediaBrowser.Common.Json.Converters;
+using Jellyfin.Extensions.Json;
+using Jellyfin.Extensions.Json.Converters;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Configuration;

+ 51 - 111
Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 using System;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
 using System.Globalization;
 using System.Globalization;
@@ -9,7 +7,6 @@ using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Authentication;
 using MediaBrowser.Controller.Authentication;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.QuickConnect;
 using MediaBrowser.Controller.QuickConnect;
 using MediaBrowser.Controller.Security;
 using MediaBrowser.Controller.Security;
 using MediaBrowser.Model.QuickConnect;
 using MediaBrowser.Model.QuickConnect;
@@ -22,14 +19,28 @@ namespace Emby.Server.Implementations.QuickConnect
     /// </summary>
     /// </summary>
     public class QuickConnectManager : IQuickConnect, IDisposable
     public class QuickConnectManager : IQuickConnect, IDisposable
     {
     {
-        private readonly RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider();
-        private readonly ConcurrentDictionary<string, QuickConnectResult> _currentRequests = new ConcurrentDictionary<string, QuickConnectResult>();
+        /// <summary>
+        /// The name of internal access tokens.
+        /// </summary>
+        private const string TokenName = "QuickConnect";
+
+        /// <summary>
+        /// The length of user facing codes.
+        /// </summary>
+        private const int CodeLength = 6;
+
+        /// <summary>
+        /// The time (in minutes) that the quick connect token is valid.
+        /// </summary>
+        private const int Timeout = 10;
+
+        private readonly RNGCryptoServiceProvider _rng = new();
+        private readonly ConcurrentDictionary<string, QuickConnectResult> _currentRequests = new();
 
 
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly ILogger<QuickConnectManager> _logger;
         private readonly ILogger<QuickConnectManager> _logger;
-        private readonly IAuthenticationRepository _authenticationRepository;
-        private readonly IAuthorizationContext _authContext;
         private readonly IServerApplicationHost _appHost;
         private readonly IServerApplicationHost _appHost;
+        private readonly IAuthenticationRepository _authenticationRepository;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="QuickConnectManager"/> class.
         /// Initializes a new instance of the <see cref="QuickConnectManager"/> class.
@@ -38,86 +49,42 @@ namespace Emby.Server.Implementations.QuickConnect
         /// <param name="config">Configuration.</param>
         /// <param name="config">Configuration.</param>
         /// <param name="logger">Logger.</param>
         /// <param name="logger">Logger.</param>
         /// <param name="appHost">Application host.</param>
         /// <param name="appHost">Application host.</param>
-        /// <param name="authContext">Authentication context.</param>
         /// <param name="authenticationRepository">Authentication repository.</param>
         /// <param name="authenticationRepository">Authentication repository.</param>
         public QuickConnectManager(
         public QuickConnectManager(
             IServerConfigurationManager config,
             IServerConfigurationManager config,
             ILogger<QuickConnectManager> logger,
             ILogger<QuickConnectManager> logger,
             IServerApplicationHost appHost,
             IServerApplicationHost appHost,
-            IAuthorizationContext authContext,
             IAuthenticationRepository authenticationRepository)
             IAuthenticationRepository authenticationRepository)
         {
         {
             _config = config;
             _config = config;
             _logger = logger;
             _logger = logger;
             _appHost = appHost;
             _appHost = appHost;
-            _authContext = authContext;
             _authenticationRepository = authenticationRepository;
             _authenticationRepository = authenticationRepository;
-
-            ReloadConfiguration();
         }
         }
 
 
-        /// <inheritdoc/>
-        public int CodeLength { get; set; } = 6;
-
-        /// <inheritdoc/>
-        public string TokenName { get; set; } = "QuickConnect";
-
-        /// <inheritdoc/>
-        public QuickConnectState State { get; private set; } = QuickConnectState.Unavailable;
+        /// <inheritdoc />
+        public bool IsEnabled => _config.Configuration.QuickConnectAvailable;
 
 
-        /// <inheritdoc/>
-        public int Timeout { get; set; } = 5;
-
-        private DateTime DateActivated { get; set; }
-
-        /// <inheritdoc/>
-        public void AssertActive()
+        /// <summary>
+        /// Assert that quick connect is currently active and throws an exception if it is not.
+        /// </summary>
+        private void AssertActive()
         {
         {
-            if (State != QuickConnectState.Active)
+            if (!IsEnabled)
             {
             {
-                throw new ArgumentException("Quick connect is not active on this server");
+                throw new AuthenticationException("Quick connect is not active on this server");
             }
             }
         }
         }
 
 
-        /// <inheritdoc/>
-        public void Activate()
-        {
-            DateActivated = DateTime.UtcNow;
-            SetState(QuickConnectState.Active);
-        }
-
-        /// <inheritdoc/>
-        public void SetState(QuickConnectState newState)
-        {
-            _logger.LogDebug("Changed quick connect state from {State} to {newState}", State, newState);
-
-            ExpireRequests(true);
-
-            State = newState;
-            _config.Configuration.QuickConnectAvailable = newState == QuickConnectState.Available || newState == QuickConnectState.Active;
-            _config.SaveConfiguration();
-
-            _logger.LogDebug("Configuration saved");
-        }
-
         /// <inheritdoc/>
         /// <inheritdoc/>
         public QuickConnectResult TryConnect()
         public QuickConnectResult TryConnect()
         {
         {
+            AssertActive();
             ExpireRequests();
             ExpireRequests();
 
 
-            if (State != QuickConnectState.Active)
-            {
-                _logger.LogDebug("Refusing quick connect initiation request, current state is {State}", State);
-                throw new AuthenticationException("Quick connect is not active on this server");
-            }
-
+            var secret = GenerateSecureRandom();
             var code = GenerateCode();
             var code = GenerateCode();
-            var result = new QuickConnectResult()
-            {
-                Secret = GenerateSecureRandom(),
-                DateAdded = DateTime.UtcNow,
-                Code = code
-            };
+            var result = new QuickConnectResult(secret, code, DateTime.UtcNow);
 
 
             _currentRequests[code] = result;
             _currentRequests[code] = result;
             return result;
             return result;
@@ -126,12 +93,12 @@ namespace Emby.Server.Implementations.QuickConnect
         /// <inheritdoc/>
         /// <inheritdoc/>
         public QuickConnectResult CheckRequestStatus(string secret)
         public QuickConnectResult CheckRequestStatus(string secret)
         {
         {
-            ExpireRequests();
             AssertActive();
             AssertActive();
+            ExpireRequests();
 
 
             string code = _currentRequests.Where(x => x.Value.Secret == secret).Select(x => x.Value.Code).DefaultIfEmpty(string.Empty).First();
             string code = _currentRequests.Where(x => x.Value.Secret == secret).Select(x => x.Value.Code).DefaultIfEmpty(string.Empty).First();
 
 
-            if (!_currentRequests.TryGetValue(code, out QuickConnectResult result))
+            if (!_currentRequests.TryGetValue(code, out QuickConnectResult? result))
             {
             {
                 throw new ResourceNotFoundException("Unable to find request with provided secret");
                 throw new ResourceNotFoundException("Unable to find request with provided secret");
             }
             }
@@ -139,8 +106,11 @@ namespace Emby.Server.Implementations.QuickConnect
             return result;
             return result;
         }
         }
 
 
-        /// <inheritdoc/>
-        public string GenerateCode()
+        /// <summary>
+        /// Generates a short code to display to the user to uniquely identify this request.
+        /// </summary>
+        /// <returns>A short, unique alphanumeric string.</returns>
+        private string GenerateCode()
         {
         {
             Span<byte> raw = stackalloc byte[4];
             Span<byte> raw = stackalloc byte[4];
 
 
@@ -161,10 +131,10 @@ namespace Emby.Server.Implementations.QuickConnect
         /// <inheritdoc/>
         /// <inheritdoc/>
         public bool AuthorizeRequest(Guid userId, string code)
         public bool AuthorizeRequest(Guid userId, string code)
         {
         {
-            ExpireRequests();
             AssertActive();
             AssertActive();
+            ExpireRequests();
 
 
-            if (!_currentRequests.TryGetValue(code, out QuickConnectResult result))
+            if (!_currentRequests.TryGetValue(code, out QuickConnectResult? result))
             {
             {
                 throw new ResourceNotFoundException("Unable to find request");
                 throw new ResourceNotFoundException("Unable to find request");
             }
             }
@@ -174,16 +144,16 @@ namespace Emby.Server.Implementations.QuickConnect
                 throw new InvalidOperationException("Request is already authorized");
                 throw new InvalidOperationException("Request is already authorized");
             }
             }
 
 
-            result.Authentication = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
+            var token = Guid.NewGuid();
+            result.Authentication = token;
 
 
             // Change the time on the request so it expires one minute into the future. It can't expire immediately as otherwise some clients wouldn't ever see that they have been authenticated.
             // Change the time on the request so it expires one minute into the future. It can't expire immediately as otherwise some clients wouldn't ever see that they have been authenticated.
-            var added = result.DateAdded ?? DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(Timeout));
-            result.DateAdded = added.Subtract(TimeSpan.FromMinutes(Timeout - 1));
+            result.DateAdded = DateTime.Now.Add(TimeSpan.FromMinutes(1));
 
 
             _authenticationRepository.Create(new AuthenticationInfo
             _authenticationRepository.Create(new AuthenticationInfo
             {
             {
                 AppName = TokenName,
                 AppName = TokenName,
-                AccessToken = result.Authentication,
+                AccessToken = token.ToString("N", CultureInfo.InvariantCulture),
                 DateCreated = DateTime.UtcNow,
                 DateCreated = DateTime.UtcNow,
                 DeviceId = _appHost.SystemId,
                 DeviceId = _appHost.SystemId,
                 DeviceName = _appHost.FriendlyName,
                 DeviceName = _appHost.FriendlyName,
@@ -196,28 +166,6 @@ namespace Emby.Server.Implementations.QuickConnect
             return true;
             return true;
         }
         }
 
 
-        /// <inheritdoc/>
-        public int DeleteAllDevices(Guid user)
-        {
-            var raw = _authenticationRepository.Get(new AuthenticationInfoQuery()
-            {
-                DeviceId = _appHost.SystemId,
-                UserId = user
-            });
-
-            var tokens = raw.Items.Where(x => x.AppName.StartsWith(TokenName, StringComparison.Ordinal));
-
-            var removed = 0;
-            foreach (var token in tokens)
-            {
-                _authenticationRepository.Delete(token);
-                _logger.LogDebug("Deleted token {AccessToken}", token.AccessToken);
-                removed++;
-            }
-
-            return removed;
-        }
-
         /// <summary>
         /// <summary>
         /// Dispose.
         /// Dispose.
         /// </summary>
         /// </summary>
@@ -235,7 +183,7 @@ namespace Emby.Server.Implementations.QuickConnect
         {
         {
             if (disposing)
             if (disposing)
             {
             {
-                _rng?.Dispose();
+                _rng.Dispose();
             }
             }
         }
         }
 
 
@@ -247,22 +195,19 @@ namespace Emby.Server.Implementations.QuickConnect
             return Convert.ToHexString(bytes);
             return Convert.ToHexString(bytes);
         }
         }
 
 
-        /// <inheritdoc/>
-        public void ExpireRequests(bool expireAll = false)
+        /// <summary>
+        /// Expire quick connect requests that are over the time limit. If <paramref name="expireAll"/> is true, all requests are unconditionally expired.
+        /// </summary>
+        /// <param name="expireAll">If true, all requests will be expired.</param>
+        private void ExpireRequests(bool expireAll = false)
         {
         {
-            // Check if quick connect should be deactivated
-            if (State == QuickConnectState.Active && DateTime.UtcNow > DateActivated.AddMinutes(Timeout) && !expireAll)
-            {
-                _logger.LogDebug("Quick connect time expired, deactivating");
-                SetState(QuickConnectState.Available);
-                expireAll = true;
-            }
+            // All requests before this timestamp have expired
+            var minTime = DateTime.UtcNow.AddMinutes(-Timeout);
 
 
             // Expire stale connection requests
             // Expire stale connection requests
             foreach (var (_, currentRequest) in _currentRequests)
             foreach (var (_, currentRequest) in _currentRequests)
             {
             {
-                var added = currentRequest.DateAdded ?? DateTime.UnixEpoch;
-                if (expireAll || DateTime.UtcNow > added.AddMinutes(Timeout))
+                if (expireAll || currentRequest.DateAdded < minTime)
                 {
                 {
                     var code = currentRequest.Code;
                     var code = currentRequest.Code;
                     _logger.LogDebug("Removing expired request {Code}", code);
                     _logger.LogDebug("Removing expired request {Code}", code);
@@ -274,10 +219,5 @@ namespace Emby.Server.Implementations.QuickConnect
                 }
                 }
             }
             }
         }
         }
-
-        private void ReloadConfiguration()
-        {
-            State = _config.Configuration.QuickConnectAvailable ? QuickConnectState.Available : QuickConnectState.Unavailable;
-        }
     }
     }
 }
 }

+ 1 - 1
Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs

@@ -12,7 +12,7 @@ using System.Threading.Tasks;
 using Jellyfin.Data.Events;
 using Jellyfin.Data.Events;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Json;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Model.Tasks;
 using MediaBrowser.Model.Tasks;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;

+ 1 - 1
Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs

@@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
                 throw new Exception($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
                 throw new Exception($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
             }
             }
 
 
-            var startDate = DateTime.UtcNow.AddDays(retentionDays.Value * -1);
+            var startDate = DateTime.UtcNow.AddDays(-retentionDays.Value);
             return _activityManager.CleanAsync(startDate);
             return _activityManager.CleanAsync(startDate);
         }
         }
 
 

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

@@ -12,6 +12,7 @@ using System.Threading.Tasks;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
 using Jellyfin.Data.Enums;
 using Jellyfin.Data.Events;
 using Jellyfin.Data.Events;
+using Jellyfin.Extensions;
 using MediaBrowser.Common.Events;
 using MediaBrowser.Common.Events;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;

+ 2 - 1
Emby.Server.Implementations/Sorting/StudioComparer.cs

@@ -4,6 +4,7 @@
 
 
 using System;
 using System;
 using System.Linq;
 using System.Linq;
+using Jellyfin.Extensions;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Sorting;
 using MediaBrowser.Controller.Sorting;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
@@ -30,7 +31,7 @@ namespace Emby.Server.Implementations.Sorting
                 throw new ArgumentNullException(nameof(y));
                 throw new ArgumentNullException(nameof(y));
             }
             }
 
 
-            return AlphanumComparator.CompareValues(x.Studios.FirstOrDefault() ?? string.Empty, y.Studios.FirstOrDefault() ?? string.Empty);
+            return AlphanumericComparator.CompareValues(x.Studios.FirstOrDefault(), y.Studios.FirstOrDefault());
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 1 - 1
Emby.Server.Implementations/Updates/InstallationManager.cs

@@ -11,7 +11,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Jellyfin.Data.Events;
 using Jellyfin.Data.Events;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Json;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Common.Updates;
 using MediaBrowser.Common.Updates;

+ 1 - 1
Jellyfin.Api/BaseJellyfinApiController.cs

@@ -1,5 +1,5 @@
 using System.Net.Mime;
 using System.Net.Mime;
-using MediaBrowser.Common.Json;
+using Jellyfin.Extensions.Json;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc;
 
 
 namespace Jellyfin.Api
 namespace Jellyfin.Api

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

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
 using Jellyfin.Api.Attributes;
 using Jellyfin.Api.Constants;
 using Jellyfin.Api.Constants;
 using Jellyfin.Api.Models.ConfigurationDtos;
 using Jellyfin.Api.Models.ConfigurationDtos;
-using MediaBrowser.Common.Json;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Configuration;

+ 2 - 2
Jellyfin.Api/Controllers/DynamicHlsController.cs

@@ -1380,7 +1380,7 @@ namespace Jellyfin.Api.Controllers
             }
             }
             else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase))
             else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase))
             {
             {
-                var outputFmp4HeaderArg = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) switch
+                var outputFmp4HeaderArg = OperatingSystem.IsWindows() switch
                 {
                 {
                     // on Windows, the path of fmp4 header file needs to be configured
                     // on Windows, the path of fmp4 header file needs to be configured
                     true => " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\"",
                     true => " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\"",
@@ -1496,7 +1496,7 @@ namespace Jellyfin.Api.Controllers
                 args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture);
                 args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture);
             }
             }
 
 
-            args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions, true);
+            args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions);
 
 
             return args;
             return args;
         }
         }

+ 10 - 47
Jellyfin.Api/Controllers/InstantMixController.cs

@@ -228,42 +228,6 @@ namespace Jellyfin.Api.Controllers
             return GetResult(items, user, limit, dtoOptions);
             return GetResult(items, user, limit, dtoOptions);
         }
         }
 
 
-        /// <summary>
-        /// Creates an instant playlist based on a given genre.
-        /// </summary>
-        /// <param name="id">The item id.</param>
-        /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
-        /// <param name="limit">Optional. The maximum number of records to return.</param>
-        /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
-        /// <param name="enableImages">Optional. Include image information in output.</param>
-        /// <param name="enableUserData">Optional. Include user data.</param>
-        /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
-        /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
-        /// <response code="200">Instant playlist returned.</response>
-        /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
-        [HttpGet("MusicGenres/{id}/InstantMix")]
-        [ProducesResponseType(StatusCodes.Status200OK)]
-        public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenreById(
-            [FromRoute, Required] Guid id,
-            [FromQuery] Guid? userId,
-            [FromQuery] int? limit,
-            [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
-            [FromQuery] bool? enableImages,
-            [FromQuery] bool? enableUserData,
-            [FromQuery] int? imageTypeLimit,
-            [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
-        {
-            var item = _libraryManager.GetItemById(id);
-            var user = userId.HasValue && !userId.Equals(Guid.Empty)
-                ? _userManager.GetUserById(userId.Value)
-                : null;
-            var dtoOptions = new DtoOptions { Fields = fields }
-                .AddClientFields(Request)
-                .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes!);
-            var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
-            return GetResult(items, user, limit, dtoOptions);
-        }
-
         /// <summary>
         /// <summary>
         /// Creates an instant playlist based on a given item.
         /// Creates an instant playlist based on a given item.
         /// </summary>
         /// </summary>
@@ -352,8 +316,7 @@ namespace Jellyfin.Api.Controllers
         /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
         /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
         [HttpGet("MusicGenres/InstantMix")]
         [HttpGet("MusicGenres/InstantMix")]
         [ProducesResponseType(StatusCodes.Status200OK)]
         [ProducesResponseType(StatusCodes.Status200OK)]
-        [Obsolete("Use GetInstantMixFromMusicGenres instead")]
-        public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenreById2(
+        public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenreById(
             [FromQuery, Required] Guid id,
             [FromQuery, Required] Guid id,
             [FromQuery] Guid? userId,
             [FromQuery] Guid? userId,
             [FromQuery] int? limit,
             [FromQuery] int? limit,
@@ -363,15 +326,15 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? imageTypeLimit,
             [FromQuery] int? imageTypeLimit,
             [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
             [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
         {
         {
-            return GetInstantMixFromMusicGenreById(
-                id,
-                userId,
-                limit,
-                fields,
-                enableImages,
-                enableUserData,
-                imageTypeLimit,
-                enableImageTypes);
+            var item = _libraryManager.GetItemById(id);
+            var user = userId.HasValue && !userId.Equals(Guid.Empty)
+                ? _userManager.GetUserById(userId.Value)
+                : null;
+            var dtoOptions = new DtoOptions { Fields = fields }
+                .AddClientFields(Request)
+                .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes!);
+            var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
+            return GetResult(items, user, limit, dtoOptions);
         }
         }
 
 
         private QueryResult<BaseItemDto> GetResult(List<BaseItem> items, User? user, int? limit, DtoOptions dtoOptions)
         private QueryResult<BaseItemDto> GetResult(List<BaseItem> items, User? user, int? limit, DtoOptions dtoOptions)

+ 6 - 16
Jellyfin.Api/Controllers/MediaInfoController.cs

@@ -161,6 +161,11 @@ namespace Jellyfin.Api.Controllers
                     liveStreamId)
                     liveStreamId)
                 .ConfigureAwait(false);
                 .ConfigureAwait(false);
 
 
+            if (info.ErrorCode != null)
+            {
+                return info;
+            }
+
             if (profile != null)
             if (profile != null)
             {
             {
                 // set device specific data
                 // set device specific data
@@ -302,27 +307,12 @@ namespace Jellyfin.Api.Controllers
         /// </summary>
         /// </summary>
         /// <param name="size">The bitrate. Defaults to 102400.</param>
         /// <param name="size">The bitrate. Defaults to 102400.</param>
         /// <response code="200">Test buffer returned.</response>
         /// <response code="200">Test buffer returned.</response>
-        /// <response code="400">Size has to be a numer between 0 and 10,000,000.</response>
         /// <returns>A <see cref="FileResult"/> with specified bitrate.</returns>
         /// <returns>A <see cref="FileResult"/> with specified bitrate.</returns>
         [HttpGet("Playback/BitrateTest")]
         [HttpGet("Playback/BitrateTest")]
         [ProducesResponseType(StatusCodes.Status200OK)]
         [ProducesResponseType(StatusCodes.Status200OK)]
-        [ProducesResponseType(StatusCodes.Status400BadRequest)]
-        [Produces(MediaTypeNames.Application.Octet)]
         [ProducesFile(MediaTypeNames.Application.Octet)]
         [ProducesFile(MediaTypeNames.Application.Octet)]
-        public ActionResult GetBitrateTestBytes([FromQuery] int size = 102400)
+        public ActionResult GetBitrateTestBytes([FromQuery][Range(1, 100_000_000, ErrorMessage = "The requested size must be greater than or equal to {1} and less than or equal to {2}")] int size = 102400)
         {
         {
-            const int MaxSize = 10_000_000;
-
-            if (size <= 0)
-            {
-                return BadRequest($"The requested size ({size}) is equal to or smaller than 0.");
-            }
-
-            if (size > MaxSize)
-            {
-                return BadRequest($"The requested size ({size}) is larger than the max allowed value ({MaxSize}).");
-            }
-
             byte[] buffer = ArrayPool<byte>.Shared.Rent(size);
             byte[] buffer = ArrayPool<byte>.Shared.Rent(size);
             try
             try
             {
             {

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

@@ -8,8 +8,8 @@ using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
 using Jellyfin.Api.Attributes;
 using Jellyfin.Api.Constants;
 using Jellyfin.Api.Constants;
 using Jellyfin.Api.Models.PluginDtos;
 using Jellyfin.Api.Models.PluginDtos;
+using Jellyfin.Extensions.Json;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Json;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Common.Updates;
 using MediaBrowser.Common.Updates;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Net;

+ 21 - 58
Jellyfin.Api/Controllers/QuickConnectController.cs

@@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations;
 using Jellyfin.Api.Constants;
 using Jellyfin.Api.Constants;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.Helpers;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Authentication;
 using MediaBrowser.Controller.QuickConnect;
 using MediaBrowser.Controller.QuickConnect;
 using MediaBrowser.Model.QuickConnect;
 using MediaBrowser.Model.QuickConnect;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Authorization;
@@ -30,13 +31,12 @@ namespace Jellyfin.Api.Controllers
         /// Gets the current quick connect state.
         /// Gets the current quick connect state.
         /// </summary>
         /// </summary>
         /// <response code="200">Quick connect state returned.</response>
         /// <response code="200">Quick connect state returned.</response>
-        /// <returns>The current <see cref="QuickConnectState"/>.</returns>
-        [HttpGet("Status")]
+        /// <returns>Whether Quick Connect is enabled on the server or not.</returns>
+        [HttpGet("Enabled")]
         [ProducesResponseType(StatusCodes.Status200OK)]
         [ProducesResponseType(StatusCodes.Status200OK)]
-        public ActionResult<QuickConnectState> GetStatus()
+        public ActionResult<bool> GetEnabled()
         {
         {
-            _quickConnect.ExpireRequests();
-            return _quickConnect.State;
+            return _quickConnect.IsEnabled;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -49,7 +49,14 @@ namespace Jellyfin.Api.Controllers
         [ProducesResponseType(StatusCodes.Status200OK)]
         [ProducesResponseType(StatusCodes.Status200OK)]
         public ActionResult<QuickConnectResult> Initiate()
         public ActionResult<QuickConnectResult> Initiate()
         {
         {
-            return _quickConnect.TryConnect();
+            try
+            {
+                return _quickConnect.TryConnect();
+            }
+            catch (AuthenticationException)
+            {
+                return Unauthorized("Quick connect is disabled");
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -72,42 +79,10 @@ namespace Jellyfin.Api.Controllers
             {
             {
                 return NotFound("Unknown secret");
                 return NotFound("Unknown secret");
             }
             }
-        }
-
-        /// <summary>
-        /// Temporarily activates quick connect for five minutes.
-        /// </summary>
-        /// <response code="204">Quick connect has been temporarily activated.</response>
-        /// <response code="403">Quick connect is unavailable on this server.</response>
-        /// <returns>An <see cref="NoContentResult"/> on success.</returns>
-        [HttpPost("Activate")]
-        [Authorize(Policy = Policies.DefaultAuthorization)]
-        [ProducesResponseType(StatusCodes.Status204NoContent)]
-        [ProducesResponseType(StatusCodes.Status403Forbidden)]
-        public ActionResult Activate()
-        {
-            if (_quickConnect.State == QuickConnectState.Unavailable)
+            catch (AuthenticationException)
             {
             {
-                return StatusCode(StatusCodes.Status403Forbidden, "Quick connect is unavailable");
+                return Unauthorized("Quick connect is disabled");
             }
             }
-
-            _quickConnect.Activate();
-            return NoContent();
-        }
-
-        /// <summary>
-        /// Enables or disables quick connect.
-        /// </summary>
-        /// <param name="status">New <see cref="QuickConnectState"/>.</param>
-        /// <response code="204">Quick connect state set successfully.</response>
-        /// <returns>An <see cref="NoContentResult"/> on success.</returns>
-        [HttpPost("Available")]
-        [Authorize(Policy = Policies.RequiresElevation)]
-        [ProducesResponseType(StatusCodes.Status204NoContent)]
-        public ActionResult Available([FromQuery] QuickConnectState status = QuickConnectState.Available)
-        {
-            _quickConnect.SetState(status);
-            return NoContent();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -129,26 +104,14 @@ namespace Jellyfin.Api.Controllers
                 return StatusCode(StatusCodes.Status403Forbidden, "Unknown user id");
                 return StatusCode(StatusCodes.Status403Forbidden, "Unknown user id");
             }
             }
 
 
-            return _quickConnect.AuthorizeRequest(userId.Value, code);
-        }
-
-        /// <summary>
-        /// Deauthorize all quick connect devices for the current user.
-        /// </summary>
-        /// <response code="200">All quick connect devices were deleted.</response>
-        /// <returns>The number of devices that were deleted.</returns>
-        [HttpPost("Deauthorize")]
-        [Authorize(Policy = Policies.DefaultAuthorization)]
-        [ProducesResponseType(StatusCodes.Status200OK)]
-        public ActionResult<int> Deauthorize()
-        {
-            var userId = ClaimHelpers.GetUserId(Request.HttpContext.User);
-            if (!userId.HasValue)
+            try
             {
             {
-                return 0;
+                return _quickConnect.AuthorizeRequest(userId.Value, code);
+            }
+            catch (AuthenticationException)
+            {
+                return Unauthorized("Quick connect is disabled");
             }
             }
-
-            return _quickConnect.DeleteAllDevices(userId.Value);
         }
         }
     }
     }
 }
 }

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

@@ -6,7 +6,7 @@ using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Enums;
 using Jellyfin.Data.Enums;
-using MediaBrowser.Common.Extensions;
+using Jellyfin.Extensions;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Entities.TV;

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

@@ -9,7 +9,7 @@ using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Enums;
 using Jellyfin.Data.Enums;
-using MediaBrowser.Common.Extensions;
+using Jellyfin.Extensions;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Audio;

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