Browse Source

Merge branch 'jellyfin:master' into whitelist-dlls

AmbulantRex 2 years ago
parent
commit
4a0b135b7a
87 changed files with 532 additions and 582 deletions
  1. 4 4
      .github/workflows/codeql-analysis.yml
  2. 7 7
      .github/workflows/commands.yml
  3. 4 4
      .github/workflows/openapi.yml
  4. 8 8
      Directory.Packages.props
  5. 0 1
      Emby.Dlna/Main/DlnaEntryPoint.cs
  6. 6 6
      Emby.Dlna/PlayTo/TransportCommands.cs
  7. 1 13
      Emby.Server.Implementations/Data/BaseSqliteRepository.cs
  8. 68 74
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  9. 1 4
      Emby.Server.Implementations/Dto/DtoService.cs
  10. 8 4
      Emby.Server.Implementations/Library/LibraryManager.cs
  11. 2 2
      Emby.Server.Implementations/Library/MediaSourceManager.cs
  12. 2 1
      Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
  13. 3 6
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  14. 4 5
      Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
  15. 1 1
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
  16. 1 2
      Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
  17. 22 16
      Emby.Server.Implementations/Localization/Core/bn.json
  18. 52 52
      Emby.Server.Implementations/Localization/Core/da.json
  19. 21 1
      Emby.Server.Implementations/Localization/Core/hi.json
  20. 22 20
      Emby.Server.Implementations/Localization/Core/pa.json
  21. 2 1
      Emby.Server.Implementations/Localization/Core/pt.json
  22. 2 2
      Emby.Server.Implementations/Localization/Core/ru.json
  23. 46 46
      Emby.Server.Implementations/Localization/Core/zh-HK.json
  24. 3 3
      Emby.Server.Implementations/Localization/Core/zh-TW.json
  25. 6 6
      Emby.Server.Implementations/Localization/LocalizationManager.cs
  26. 1 1
      Emby.Server.Implementations/Plugins/PluginManager.cs
  27. 2 2
      Emby.Server.Implementations/Session/SessionManager.cs
  28. 1 3
      Emby.Server.Implementations/Session/WebSocketController.cs
  29. 4 8
      Emby.Server.Implementations/SyncPlay/Group.cs
  30. 2 4
      Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
  31. 1 3
      Jellyfin.Api/Controllers/DynamicHlsController.cs
  32. 0 1
      Jellyfin.Api/Controllers/FilterController.cs
  33. 1 1
      Jellyfin.Api/Controllers/PluginsController.cs
  34. 0 2
      Jellyfin.Api/Controllers/QuickConnectController.cs
  35. 0 1
      Jellyfin.Api/Controllers/SearchController.cs
  36. 2 4
      Jellyfin.Api/Controllers/SubtitleController.cs
  37. 0 1
      Jellyfin.Api/Controllers/UniversalAudioController.cs
  38. 7 8
      Jellyfin.Networking/Manager/NetworkManager.cs
  39. 0 1
      Jellyfin.Server.Implementations/Security/AuthorizationContext.cs
  40. 0 1
      Jellyfin.Server/CoreAppHost.cs
  41. 1 3
      Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
  42. 0 1
      Jellyfin.Server/Startup.cs
  43. 1 0
      Jellyfin.sln.DotSettings
  44. 1 1
      MediaBrowser.Common/Plugins/BasePluginOfT.cs
  45. 6 8
      MediaBrowser.Controller/Entities/BaseItem.cs
  46. 2 4
      MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
  47. 0 2
      MediaBrowser.Controller/Lyrics/LyricMetadata.cs
  48. 100 101
      MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
  49. 0 1
      MediaBrowser.Controller/Session/ISessionManager.cs
  50. 0 1
      MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
  51. 3 5
      MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs
  52. 11 18
      MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs
  53. 1 4
      MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
  54. 1 1
      MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
  55. 4 8
      MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
  56. 1 2
      MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs
  57. 4 4
      MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
  58. 3 5
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
  59. 0 1
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  60. 2 1
      MediaBrowser.Model/Cryptography/PasswordHash.cs
  61. 16 8
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  62. 1 1
      MediaBrowser.Model/Dlna/StreamInfo.cs
  63. 4 2
      MediaBrowser.Model/MediaInfo/AudioCodec.cs
  64. 0 1
      MediaBrowser.Providers/Lyric/TxtLyricProvider.cs
  65. 1 4
      MediaBrowser.Providers/Manager/MetadataService.cs
  66. 1 4
      MediaBrowser.Providers/Manager/ProviderManager.cs
  67. 1 4
      MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
  68. 2 4
      RSSDP/HttpParserBase.cs
  69. 1 2
      RSSDP/HttpRequestParser.cs
  70. 1 2
      RSSDP/HttpResponseParser.cs
  71. 2 4
      RSSDP/SsdpDevice.cs
  72. 4 8
      RSSDP/SsdpDeviceLocator.cs
  73. 5 9
      RSSDP/SsdpDevicePublisher.cs
  74. 1 1
      deployment/Dockerfile.centos.amd64
  75. 1 1
      deployment/Dockerfile.fedora.amd64
  76. 1 1
      deployment/Dockerfile.ubuntu.amd64
  77. 1 1
      deployment/Dockerfile.ubuntu.arm64
  78. 1 1
      deployment/Dockerfile.ubuntu.armhf
  79. 14 4
      src/Jellyfin.Drawing.Skia/SkiaEncoder.cs
  80. 10 5
      src/Jellyfin.Extensions/AlphanumericComparator.cs
  81. 0 1
      src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs
  82. 0 2
      src/Jellyfin.Extensions/StringExtensions.cs
  83. 0 1
      tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs
  84. 0 1
      tests/Jellyfin.Api.Tests/Controllers/ImageControllerTests.cs
  85. 3 3
      tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs
  86. 2 5
      tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs
  87. 1 1
      tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs

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

@@ -20,18 +20,18 @@ jobs:
 
     steps:
     - name: Checkout repository
-      uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
+      uses: actions/checkout@83b7061638ee4956cf7545a6f7efe594e5ad0247 # v3.5.1
     - name: Setup .NET
       uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # v3.0.3
       with:
         dotnet-version: '7.0.x'
 
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@04df1262e6247151b5ac09cd2c303ac36ad3f62b # v2.2.9
+      uses: github/codeql-action/init@d186a2a36cc67bfa1b860e6170d37fb9634742c7 # v2.2.11
       with:
         languages: ${{ matrix.language }}
         queries: +security-extended
     - name: Autobuild
-      uses: github/codeql-action/autobuild@04df1262e6247151b5ac09cd2c303ac36ad3f62b # v2.2.9
+      uses: github/codeql-action/autobuild@d186a2a36cc67bfa1b860e6170d37fb9634742c7 # v2.2.11
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@04df1262e6247151b5ac09cd2c303ac36ad3f62b # v2.2.9
+      uses: github/codeql-action/analyze@d186a2a36cc67bfa1b860e6170d37fb9634742c7 # v2.2.11

+ 7 - 7
.github/workflows/commands.yml

@@ -17,14 +17,14 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Notify as seen
-        uses: peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d # v2.1.1
+        uses: peter-evans/create-or-update-comment@3383acd359705b10cb1eeef05c0e88c056ea4666 # v3.0.0
         with:
           token: ${{ secrets.JF_BOT_TOKEN }}
           comment-id: ${{ github.event.comment.id }}
           reactions: '+1'
 
       - name: Checkout the latest code
-        uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
+        uses: actions/checkout@83b7061638ee4956cf7545a6f7efe594e5ad0247 # v3.5.1
         with:
           token: ${{ secrets.JF_BOT_TOKEN }}
           fetch-depth: 0
@@ -43,7 +43,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Notify as seen
-        uses: peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d # v2.1.1
+        uses: peter-evans/create-or-update-comment@3383acd359705b10cb1eeef05c0e88c056ea4666 # v3.0.0
         if: ${{ github.event.comment != null }}
         with:
           token: ${{ secrets.JF_BOT_TOKEN }}
@@ -51,14 +51,14 @@ jobs:
           reactions: eyes
 
       - name: Checkout the latest code
-        uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
+        uses: actions/checkout@83b7061638ee4956cf7545a6f7efe594e5ad0247 # v3.5.1
         with:
           token: ${{ secrets.JF_BOT_TOKEN }}
           fetch-depth: 0
 
       - name: Notify as running
         id: comment_running
-        uses: peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d # v2.1.1
+        uses: peter-evans/create-or-update-comment@3383acd359705b10cb1eeef05c0e88c056ea4666 # v3.0.0
         if: ${{ github.event.comment != null }}
         with:
           token: ${{ secrets.JF_BOT_TOKEN }}
@@ -93,7 +93,7 @@ jobs:
           exit ${retcode}
 
       - name: Notify with result success
-        uses: peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d # v2.1.1
+        uses: peter-evans/create-or-update-comment@3383acd359705b10cb1eeef05c0e88c056ea4666 # v3.0.0
         if: ${{ github.event.comment != null && success() }}
         with:
           token: ${{ secrets.JF_BOT_TOKEN }}
@@ -108,7 +108,7 @@ jobs:
           reactions: hooray
 
       - name: Notify with result failure
-        uses: peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d # v2.1.1
+        uses: peter-evans/create-or-update-comment@3383acd359705b10cb1eeef05c0e88c056ea4666 # v3.0.0
         if: ${{ github.event.comment != null && failure() }}
         with:
           token: ${{ secrets.JF_BOT_TOKEN }}

+ 4 - 4
.github/workflows/openapi.yml

@@ -14,7 +14,7 @@ jobs:
     permissions: read-all
     steps:
       - name: Checkout repository
-        uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
+        uses: actions/checkout@83b7061638ee4956cf7545a6f7efe594e5ad0247 # v3.5.1
         with:
           ref: ${{ github.event.pull_request.head.sha }}
           repository: ${{ github.event.pull_request.head.repo.full_name }}
@@ -39,7 +39,7 @@ jobs:
     permissions: read-all
     steps:
       - name: Checkout repository
-        uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
+        uses: actions/checkout@83b7061638ee4956cf7545a6f7efe594e5ad0247 # v3.5.1
         with:
           ref: ${{ github.event.pull_request.head.sha }}
           repository: ${{ github.event.pull_request.head.repo.full_name }}
@@ -110,7 +110,7 @@ jobs:
           direction: last
           body-includes: openapi-diff-workflow-comment
       - name: Reply or edit difference comment (changed)
-        uses: peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d # v2.1.1
+        uses: peter-evans/create-or-update-comment@3383acd359705b10cb1eeef05c0e88c056ea4666 # v3.0.0
         if: ${{ steps.read-diff.outputs.body != '' }}
         with:
           issue-number: ${{ github.event.pull_request.number }}
@@ -125,7 +125,7 @@ jobs:
 
             </details>
       - name: Edit difference comment (unchanged)
-        uses: peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d # v2.1.1
+        uses: peter-evans/create-or-update-comment@3383acd359705b10cb1eeef05c0e88c056ea4666 # v3.0.0
         if: ${{ steps.read-diff.outputs.body == '' && steps.find-comment.outputs.comment-id != '' }}
         with:
           issue-number: ${{ github.event.pull_request.number }}

+ 8 - 8
Directory.Packages.props

@@ -23,13 +23,13 @@
     <PackageVersion Include="libse" Version="3.6.11" />
     <PackageVersion Include="LrcParser" Version="2023.308.0" />
     <PackageVersion Include="MetaBrainz.MusicBrainz" Version="5.0.0" />
-    <PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="7.0.4" />
-    <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.4" />
+    <PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="7.0.5" />
+    <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.5" />
     <PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" />
-    <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.4" />
-    <PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.4" />
-    <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.4" />
-    <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.4" />
+    <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.5" />
+    <PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.5" />
+    <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.5" />
+    <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.5" />
     <PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
@@ -38,8 +38,8 @@
     <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
-    <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.4" />
-    <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.4" />
+    <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.5" />
+    <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.5" />
     <PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Http" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />

+ 0 - 1
Emby.Dlna/Main/DlnaEntryPoint.cs

@@ -7,7 +7,6 @@ using System.Globalization;
 using System.Linq;
 using System.Net.Http;
 using System.Net.Sockets;
-using System.Runtime.InteropServices;
 using System.Threading.Tasks;
 using Emby.Dlna.PlayTo;
 using Emby.Dlna.Ssdp;

+ 6 - 6
Emby.Dlna/PlayTo/TransportCommands.cs

@@ -116,7 +116,7 @@ namespace Emby.Dlna.PlayTo
             return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamespace, stateString);
         }
 
-        public string BuildPost(ServiceAction action, string xmlNamesapce, object value, string commandParameter = "")
+        public string BuildPost(ServiceAction action, string xmlNamespace, object value, string commandParameter = "")
         {
             var stateString = string.Empty;
 
@@ -137,10 +137,10 @@ namespace Emby.Dlna.PlayTo
                 }
             }
 
-            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamesapce, stateString);
+            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamespace, stateString);
         }
 
-        public string BuildPost(ServiceAction action, string xmlNamesapce, object value, Dictionary<string, string> dictionary)
+        public string BuildPost(ServiceAction action, string xmlNamespace, object value, Dictionary<string, string> dictionary)
         {
             var stateString = string.Empty;
 
@@ -150,9 +150,9 @@ namespace Emby.Dlna.PlayTo
                 {
                     stateString += BuildArgumentXml(arg, "0");
                 }
-                else if (dictionary.ContainsKey(arg.Name))
+                else if (dictionary.TryGetValue(arg.Name, out var argValue))
                 {
-                    stateString += BuildArgumentXml(arg, dictionary[arg.Name]);
+                    stateString += BuildArgumentXml(arg, argValue);
                 }
                 else
                 {
@@ -160,7 +160,7 @@ namespace Emby.Dlna.PlayTo
                 }
             }
 
-            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamesapce, stateString);
+            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamespace, stateString);
         }
 
         private string BuildArgumentXml(Argument argument, string? value, string commandParameter = "")

+ 1 - 13
Emby.Server.Implementations/Data/BaseSqliteRepository.cs

@@ -139,7 +139,7 @@ namespace Emby.Server.Implementations.Data
 
             if (JournalSizeLimit.HasValue)
             {
-                WriteConnection.Execute("PRAGMA journal_size_limit=" + (int)JournalSizeLimit.Value);
+                WriteConnection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value);
             }
 
             if (Synchronous.HasValue)
@@ -166,18 +166,6 @@ namespace Emby.Server.Implementations.Data
         public IStatement PrepareStatement(IDatabaseConnection connection, string sql)
             => connection.PrepareStatement(sql);
 
-        public IStatement[] PrepareAll(IDatabaseConnection connection, IReadOnlyList<string> sql)
-        {
-            int len = sql.Count;
-            IStatement[] statements = new IStatement[len];
-            for (int i = 0; i < len; i++)
-            {
-                statements[i] = connection.PrepareStatement(sql[i]);
-            }
-
-            return statements;
-        }
-
         protected bool TableExists(ManagedConnection connection, string name)
         {
             return connection.RunInTransaction(

+ 68 - 74
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -624,14 +624,8 @@ namespace Emby.Server.Implementations.Data
 
         private void SaveItemsInTransaction(IDatabaseConnection db, IEnumerable<(BaseItem Item, List<Guid> AncestorIds, BaseItem TopParent, string UserDataKey, List<string> InheritedTags)> tuples)
         {
-            var statements = PrepareAll(db, new string[]
-            {
-                SaveItemCommandText,
-                "delete from AncestorIds where ItemId=@ItemId"
-            });
-
-            using (var saveItemStatement = statements[0])
-            using (var deleteAncestorsStatement = statements[1])
+            using (var saveItemStatement = PrepareStatement(db, SaveItemCommandText))
+            using (var deleteAncestorsStatement = PrepareStatement(db, "delete from AncestorIds where ItemId=@ItemId"))
             {
                 var requiresReset = false;
                 foreach (var tuple in tuples)
@@ -1286,15 +1280,13 @@ namespace Emby.Server.Implementations.Data
             CheckDisposed();
 
             using (var connection = GetConnection(true))
+            using (var statement = PrepareStatement(connection, _retrieveItemColumnsSelectQuery))
             {
-                using (var statement = PrepareStatement(connection, _retrieveItemColumnsSelectQuery))
-                {
-                    statement.TryBind("@guid", id);
+                statement.TryBind("@guid", id);
 
-                    foreach (var row in statement.ExecuteQuery())
-                    {
-                        return GetItem(row, new InternalItemsQuery());
-                    }
+                foreach (var row in statement.ExecuteQuery())
+                {
+                    return GetItem(row, new InternalItemsQuery());
                 }
             }
 
@@ -1309,7 +1301,8 @@ namespace Emby.Server.Implementations.Data
                 {
                     return false;
                 }
-                else if (type == typeof(UserRootFolder))
+
+                if (type == typeof(UserRootFolder))
                 {
                     return false;
                 }
@@ -1319,55 +1312,68 @@ namespace Emby.Server.Implementations.Data
             {
                 return false;
             }
-            else if (type == typeof(MusicArtist))
+
+            if (type == typeof(MusicArtist))
             {
                 return false;
             }
-            else if (type == typeof(Person))
+
+            if (type == typeof(Person))
             {
                 return false;
             }
-            else if (type == typeof(MusicGenre))
+
+            if (type == typeof(MusicGenre))
             {
                 return false;
             }
-            else if (type == typeof(Genre))
+
+            if (type == typeof(Genre))
             {
                 return false;
             }
-            else if (type == typeof(Studio))
+
+            if (type == typeof(Studio))
             {
                 return false;
             }
-            else if (type == typeof(PlaylistsFolder))
+
+            if (type == typeof(PlaylistsFolder))
             {
                 return false;
             }
-            else if (type == typeof(PhotoAlbum))
+
+            if (type == typeof(PhotoAlbum))
             {
                 return false;
             }
-            else if (type == typeof(Year))
+
+            if (type == typeof(Year))
             {
                 return false;
             }
-            else if (type == typeof(Book))
+
+            if (type == typeof(Book))
             {
                 return false;
             }
-            else if (type == typeof(LiveTvProgram))
+
+            if (type == typeof(LiveTvProgram))
             {
                 return false;
             }
-            else if (type == typeof(AudioBook))
+
+            if (type == typeof(AudioBook))
             {
                 return false;
             }
-            else if (type == typeof(Audio))
+
+            if (type == typeof(Audio))
             {
                 return false;
             }
-            else if (type == typeof(MusicAlbum))
+
+            if (type == typeof(MusicAlbum))
             {
                 return false;
             }
@@ -1958,22 +1964,19 @@ namespace Emby.Server.Implementations.Data
         {
             CheckDisposed();
 
+            var chapters = new List<ChapterInfo>();
             using (var connection = GetConnection(true))
+            using (var statement = PrepareStatement(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc"))
             {
-                var chapters = new List<ChapterInfo>();
+                statement.TryBind("@ItemId", item.Id);
 
-                using (var statement = PrepareStatement(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc"))
+                foreach (var row in statement.ExecuteQuery())
                 {
-                    statement.TryBind("@ItemId", item.Id);
-
-                    foreach (var row in statement.ExecuteQuery())
-                    {
-                        chapters.Add(GetChapter(row, item));
-                    }
+                    chapters.Add(GetChapter(row, item));
                 }
-
-                return chapters;
             }
+
+            return chapters;
         }
 
         /// <inheritdoc />
@@ -1982,16 +1985,14 @@ namespace Emby.Server.Implementations.Data
             CheckDisposed();
 
             using (var connection = GetConnection(true))
+            using (var statement = PrepareStatement(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex"))
             {
-                using (var statement = PrepareStatement(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex"))
-                {
-                    statement.TryBind("@ItemId", item.Id);
-                    statement.TryBind("@ChapterIndex", index);
+                statement.TryBind("@ItemId", item.Id);
+                statement.TryBind("@ChapterIndex", index);
 
-                    foreach (var row in statement.ExecuteQuery())
-                    {
-                        return GetChapter(row, item);
-                    }
+                foreach (var row in statement.ExecuteQuery())
+                {
+                    return GetChapter(row, item);
                 }
             }
 
@@ -2392,6 +2393,7 @@ namespace Emby.Server.Implementations.Data
 
                 // genres, tags, studios, person, year?
                 builder.Append("+ (Select count(1) * 10 from ItemValues where ItemId=Guid and CleanValue in (select CleanValue from ItemValues where ItemId=@SimilarItemId))");
+                builder.Append("+ (Select count(1) * 10 from People where ItemId=Guid and Name in (select Name from People where ItemId=@SimilarItemId))");
 
                 if (item is MusicArtist)
                 {
@@ -2843,13 +2845,10 @@ namespace Emby.Server.Implementations.Data
                 connection.RunInTransaction(
                     db =>
                     {
-                        var itemQueryStatement = PrepareStatement(db, itemQuery);
-                        var totalRecordCountQueryStatement = PrepareStatement(db, totalRecordCountQuery);
-
                         if (!isReturningZeroItems)
                         {
                             using (new QueryTimeLogger(Logger, itemQuery, "GetItems.ItemQuery"))
-                            using (var statement = itemQueryStatement)
+                            using (var statement = PrepareStatement(db, itemQuery))
                             {
                                 if (EnableJoinUserData(query))
                                 {
@@ -2884,7 +2883,7 @@ namespace Emby.Server.Implementations.Data
                         if (query.EnableTotalRecordCount)
                         {
                             using (new QueryTimeLogger(Logger, totalRecordCountQuery, "GetItems.TotalRecordCount"))
-                            using (var statement = totalRecordCountQueryStatement)
+                            using (var statement = PrepareStatement(db, totalRecordCountQuery))
                             {
                                 if (EnableJoinUserData(query))
                                 {
@@ -4753,22 +4752,20 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
                 commandText.Append(" LIMIT ").Append(query.Limit);
             }
 
+            var list = new List<string>();
             using (var connection = GetConnection(true))
+            using (var statement = PrepareStatement(connection, commandText.ToString()))
             {
-                var list = new List<string>();
-                using (var statement = PrepareStatement(connection, commandText.ToString()))
-                {
-                    // Run this again to bind the params
-                    GetPeopleWhereClauses(query, statement);
+                // Run this again to bind the params
+                GetPeopleWhereClauses(query, statement);
 
-                    foreach (var row in statement.ExecuteQuery())
-                    {
-                        list.Add(row.GetString(0));
-                    }
+                foreach (var row in statement.ExecuteQuery())
+                {
+                    list.Add(row.GetString(0));
                 }
-
-                return list;
             }
+
+            return list;
         }
 
         public List<PersonInfo> GetPeople(InternalPeopleQuery query)
@@ -4793,23 +4790,20 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
                 commandText += " LIMIT " + query.Limit;
             }
 
+            var list = new List<PersonInfo>();
             using (var connection = GetConnection(true))
+            using (var statement = PrepareStatement(connection, commandText))
             {
-                var list = new List<PersonInfo>();
+                // Run this again to bind the params
+                GetPeopleWhereClauses(query, statement);
 
-                using (var statement = PrepareStatement(connection, commandText))
+                foreach (var row in statement.ExecuteQuery())
                 {
-                    // Run this again to bind the params
-                    GetPeopleWhereClauses(query, statement);
-
-                    foreach (var row in statement.ExecuteQuery())
-                    {
-                        list.Add(GetPerson(row));
-                    }
+                    list.Add(GetPerson(row));
                 }
-
-                return list;
             }
+
+            return list;
         }
 
         private List<string> GetPeopleWhereClauses(InternalPeopleQuery query, IStatement statement)

+ 1 - 4
Emby.Server.Implementations/Dto/DtoService.cs

@@ -7,7 +7,6 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
-using Jellyfin.Api.Helpers;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
@@ -572,9 +571,7 @@ namespace Emby.Server.Implementations.Dto
                         return null;
                     }
                 }).Where(i => i is not null)
-                .Where(i => user is null ?
-                    true :
-                    i.IsVisible(user))
+                .Where(i => user is null || i.IsVisible(user))
                 .DistinctBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
                 .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
 

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

@@ -1503,6 +1503,12 @@ namespace Emby.Server.Implementations.Library
                 });
 
                 query.TopParentIds = userViews.SelectMany(i => GetTopParentIdsForQuery(i, user)).ToArray();
+
+                // Prevent searching in all libraries due to empty filter
+                if (query.TopParentIds.Length == 0)
+                {
+                    query.TopParentIds = new[] { Guid.NewGuid() };
+                }
             }
         }
 
@@ -1879,7 +1885,7 @@ namespace Emby.Server.Implementations.Library
                 catch (Exception ex)
                 {
                     _logger.LogError(ex, "Cannot get image dimensions for {ImagePath}", image.Path);
-                    size = new ImageDimensions(0, 0);
+                    size = default;
                     image.Width = 0;
                     image.Height = 0;
                 }
@@ -2743,9 +2749,7 @@ namespace Emby.Server.Implementations.Library
                 }
             })
             .Where(i => i is not null)
-            .Where(i => query.User is null ?
-                true :
-                i.IsVisible(query.User))
+            .Where(i => query.User is null || i.IsVisible(query.User))
             .ToList();
         }
 

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

@@ -154,8 +154,8 @@ namespace Emby.Server.Implementations.Library
             // If file is strm or main media stream is missing, force a metadata refresh with remote probing
             if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder
                 && (item.Path.EndsWith(".strm", StringComparison.OrdinalIgnoreCase)
-                    || (item.MediaType == MediaType.Video && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Video))
-                    || (item.MediaType == MediaType.Audio && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio))))
+                    || (item.MediaType == MediaType.Video && mediaSources[0].MediaStreams.All(i => i.Type != MediaStreamType.Video))
+                    || (item.MediaType == MediaType.Audio && mediaSources[0].MediaStreams.All(i => i.Type != MediaStreamType.Audio))))
             {
                 await item.RefreshMetadata(
                     new MetadataRefreshOptions(_directoryService)

+ 2 - 1
Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs

@@ -78,7 +78,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
                             Set3DFormat(videoTmp);
                             return videoTmp;
                         }
-                        else if (IsBluRayDirectory(filename))
+
+                        if (IsBluRayDirectory(filename))
                         {
                             var videoTmp = new TVideoType
                             {

+ 3 - 6
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -627,10 +627,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                     _timerProvider.Update(existingTimer);
                     return Task.FromResult(existingTimer.Id);
                 }
-                else
-                {
-                    throw new ArgumentException("A scheduled recording already exists for this program.");
-                }
+
+                throw new ArgumentException("A scheduled recording already exists for this program.");
             }
 
             info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
@@ -1866,8 +1864,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 {
                     await writer.WriteStartDocumentAsync(true).ConfigureAwait(false);
                     await writer.WriteStartElementAsync(null, "tvshow", null).ConfigureAwait(false);
-                    string id;
-                    if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out id))
+                    if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out var id))
                     {
                         await writer.WriteElementStringAsync(null, "id", null, id).ConfigureAwait(false);
                     }

+ 4 - 5
Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -415,14 +415,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             {
                 return null;
             }
-            else if (uri.IndexOf("http", StringComparison.OrdinalIgnoreCase) != -1)
+
+            if (uri.IndexOf("http", StringComparison.OrdinalIgnoreCase) != -1)
             {
                 return uri;
             }
-            else
-            {
-                return apiUrl + "/image/" + uri + "?token=" + token;
-            }
+
+            return apiUrl + "/image/" + uri + "?token=" + token;
         }
 
         private static double GetAspectRatio(ImageDataDto i)

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

@@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         public async Task<bool> CheckTunerAvailability(IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
         {
             using var client = new TcpClient();
-            await client.ConnectAsync(remoteIp, HdHomeRunPort).ConfigureAwait(false);
+            await client.ConnectAsync(remoteIp, HdHomeRunPort, cancellationToken).ConfigureAwait(false);
 
             using var stream = client.GetStream();
             return await CheckTunerAvailability(stream, tuner, cancellationToken).ConfigureAwait(false);

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

@@ -170,9 +170,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             var nameInExtInf = nameParts.Length > 1 ? nameParts[^1].AsSpan().Trim() : ReadOnlySpan<char>.Empty;
 
             string numberString = null;
-            string attributeValue;
 
-            if (attributes.TryGetValue("tvg-chno", out attributeValue)
+            if (attributes.TryGetValue("tvg-chno", out var attributeValue)
                 && double.TryParse(attributeValue, CultureInfo.InvariantCulture, out _))
             {
                 numberString = attributeValue;

+ 22 - 16
Emby.Server.Implementations/Localization/Core/bn.json

@@ -1,27 +1,27 @@
 {
     "DeviceOnlineWithName": "{0}-এর সাথে সংযুক্ত হয়েছে",
     "DeviceOfflineWithName": "{0}-এর সাথে সংযোগ বিচ্ছিন্ন হয়েছে",
-    "Collections": "সংগ্রহ",
+    "Collections": "সংগ্রহশালা",
     "ChapterNameValue": "অধ্যায় {0}",
-    "Channels": "চ্যানেল",
+    "Channels": "চ্যানেলসমূহ",
     "CameraImageUploadedFrom": "{0} থেকে একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে",
-    "Books": "বই",
+    "Books": "পুস্তকসমূহ",
     "AuthenticationSucceededWithUserName": "{0} অনুমোদন সফল",
-    "Artists": "শিল্পীরা",
+    "Artists": "শিল্পীগণ",
     "Application": "অ্যাপ্লিকেশন",
-    "Albums": "অ্যালবামগুলো",
+    "Albums": "অ্যালবামসমূহ",
     "HeaderFavoriteEpisodes": "প্রিব পর্বগুলো",
     "HeaderFavoriteArtists": "প্রিয় শিল্পীরা",
     "HeaderFavoriteAlbums": "প্রিয় এলবামগুলো",
     "HeaderContinueWatching": "দেখতে থাকুন",
-    "HeaderAlbumArtists": "লবাম শিল্পীবৃন্দ",
-    "Genres": "শৈলী",
-    "Folders": "ফোল্ডারগুলো",
+    "HeaderAlbumArtists": "অ্যালবাম শিল্পীবৃন্দ",
+    "Genres": "শৈলীধারাসমূহ",
+    "Folders": "ফোল্ডারসমূহ",
     "Favorites": "পছন্দসমূহ",
     "FailedLoginAttemptWithUserName": "{0} লগিন করতে ব্যর্থ হয়েছে",
     "AppDeviceValues": "অ্যাপ: {0}, ডিভাইস: {0}",
     "VersionNumber": "সংস্করণ {0}",
-    "ValueSpecialEpisodeName": "বিশেষ - {0}",
+    "ValueSpecialEpisodeName": "বিশেষ পর্ব - {0}",
     "ValueHasBeenAddedToLibrary": "আপনার লাইব্রেরিতে {0} যোগ করা হয়েছে",
     "UserStoppedPlayingItemWithValues": "{2}তে {1} বাজানো শেষ করেছেন {0}",
     "UserStartedPlayingItemWithValues": "{2}তে {1} বাজাচ্ছেন {0}",
@@ -36,10 +36,10 @@
     "User": "ব্যবহারকারী",
     "TvShows": "টিভি শোগুলো",
     "System": "সিস্টেম",
-    "Sync": "সিংক",
+    "Sync": "সমলয় স্থাপন",
     "SubtitleDownloadFailureFromForItem": "{2} থেকে {1} এর জন্য সাবটাইটেল ডাউনলোড ব্যর্থ",
     "StartupEmbyServerIsLoading": "জেলিফিন সার্ভার লোড হচ্ছে। দয়া করে একটু পরে আবার চেষ্টা করুন।",
-    "Songs": "গানগুলো",
+    "Songs": "সঙ্গীতসমূহ",
     "Shows": "টিভি পর্ব",
     "ServerNameNeedsToBeRestarted": "{0} রিস্টার্ট করা প্রয়োজন",
     "ScheduledTaskStartedWithName": "{0} শুরু হয়েছে",
@@ -49,8 +49,8 @@
     "PluginUninstalledWithName": "{0} বাদ দেয়া হয়েছে",
     "PluginInstalledWithName": "{0} ইন্সটল করা হয়েছে",
     "Plugin": "প্লাগিন",
-    "Playlists": "প্লেলিস্ট",
-    "Photos": "ছবিগুলো",
+    "Playlists": "প্লে লিস্ট সমূহ",
+    "Photos": "চিত্রসমূহ",
     "NotificationOptionVideoPlaybackStopped": "ভিডিও চলা বন্ধ",
     "NotificationOptionVideoPlayback": "ভিডিও চলা শুরু হয়েছে",
     "NotificationOptionUserLockedOut": "ব্যবহারকারী ঢুকতে পারছে না",
@@ -71,9 +71,9 @@
     "NameSeasonUnknown": "সিজন অজানা",
     "NameSeasonNumber": "সিজন {0}",
     "NameInstallFailed": "{0} ইন্সটল ব্যর্থ",
-    "MusicVideos": "গানের ভিডিও",
+    "MusicVideos": "সঙ্গীত ভিডিয়ো সমূহ",
     "Music": "গান",
-    "Movies": "চলচ্চিত্র",
+    "Movies": "চলচ্চিত্রসমূহ",
     "MixedContent": "মিশ্র কন্টেন্ট",
     "MessageServerConfigurationUpdated": "সার্ভারের কনফিগারেশন আপডেট করা হয়েছে",
     "HeaderRecordingGroups": "রেকর্ডিং দল",
@@ -117,5 +117,11 @@
     "Forced": "জোরকরে",
     "TaskCleanActivityLogDescription": "নির্ধারিত সময়ের আগের কাজের হিসাব মুছে দিন খালি করুন.",
     "TaskCleanActivityLog": "কাজের ফাইল খালি করুন",
-    "Default": "প্রাথমিক"
+    "Default": "প্রাথমিক",
+    "HearingImpaired": "দুর্বল শ্রবণক্ষমতাধরদের জন্য",
+    "TaskOptimizeDatabaseDescription": "তথ্যভাণ্ডার সুবিন্যস্ত করে ও অব্যবহৃত জায়গা ছেড়ে দেয়। লাইব্রেরী স্ক্যান অথবা যেকোনো তথ্যভাণ্ডার পরিবর্তনের পর এই প্রক্রিয়া চালালে তথ্যভাণ্ডারের তথ্য প্রদান দ্রুততর হতে পারে।",
+    "External": "বাহ্যিক",
+    "TaskOptimizeDatabase": "তথ্যভাণ্ডার সুবিন্যাস",
+    "TaskKeyframeExtractor": "কি-ফ্রেম নিষ্কাশক",
+    "TaskKeyframeExtractorDescription": "ভিডিয়ো থেকে কি-ফ্রেম নিষ্কাশনের মাধ্যমে অধিকতর সঠিক HLS প্লে লিস্ট তৈরী করে। এই প্রক্রিয়া দীর্ঘ সময় ধরে চলতে পারে।"
 }

+ 52 - 52
Emby.Server.Implementations/Localization/Core/da.json

@@ -1,9 +1,9 @@
 {
-    "Albums": "Albummer",
+    "Albums": "Album",
     "AppDeviceValues": "App: {0}, Enhed: {1}",
     "Application": "Applikation",
     "Artists": "Kunstnere",
-    "AuthenticationSucceededWithUserName": "{0} succesfuldt autentificeret",
+    "AuthenticationSucceededWithUserName": "{0} er logget ind",
     "Books": "Bøger",
     "CameraImageUploadedFrom": "Et nyt kamerabillede er blevet uploadet fra {0}",
     "Channels": "Kanaler",
@@ -11,17 +11,17 @@
     "Collections": "Samlinger",
     "DeviceOfflineWithName": "{0} har afbrudt forbindelsen",
     "DeviceOnlineWithName": "{0} er forbundet",
-    "FailedLoginAttemptWithUserName": "Fejlet loginforsøg fra {0}",
+    "FailedLoginAttemptWithUserName": "Mislykket loginforsøg fra {0}",
     "Favorites": "Favoritter",
     "Folders": "Mapper",
     "Genres": "Genrer",
-    "HeaderAlbumArtists": "Albumkunstner",
+    "HeaderAlbumArtists": "Albums kunstnere",
     "HeaderContinueWatching": "Fortsæt afspilning",
-    "HeaderFavoriteAlbums": "Favoritalbummer",
-    "HeaderFavoriteArtists": "Favoritkunstnere",
-    "HeaderFavoriteEpisodes": "Favoritepisoder",
-    "HeaderFavoriteShows": "Favoritserier",
-    "HeaderFavoriteSongs": "Favoritsange",
+    "HeaderFavoriteAlbums": "Favorit albummer",
+    "HeaderFavoriteArtists": "Favorit kunstnere",
+    "HeaderFavoriteEpisodes": "Favorit afsnit",
+    "HeaderFavoriteShows": "Favorit serier",
+    "HeaderFavoriteSongs": "Favorit sange",
     "HeaderLiveTV": "Live-TV",
     "HeaderNextUp": "Næste",
     "HeaderRecordingGroups": "Optagelsesgrupper",
@@ -39,90 +39,90 @@
     "MixedContent": "Blandet indhold",
     "Movies": "Film",
     "Music": "Musik",
-    "MusicVideos": "Musik videoer",
+    "MusicVideos": "Musikvideoer",
     "NameInstallFailed": "{0} installationen mislykkedes",
     "NameSeasonNumber": "Sæson {0}",
     "NameSeasonUnknown": "Ukendt sæson",
-    "NewVersionIsAvailable": "En ny version af Jellyfin Server er tilgængelig til download.",
-    "NotificationOptionApplicationUpdateAvailable": "Opdatering til applikation tilgængelig",
-    "NotificationOptionApplicationUpdateInstalled": "Opdatering til applikation installeret",
+    "NewVersionIsAvailable": "En ny version af Jellyfin Server er tilgængelig.",
+    "NotificationOptionApplicationUpdateAvailable": "Opdatering til applikationen er tilgængelig",
+    "NotificationOptionApplicationUpdateInstalled": "Opdatering til applikationen blev installeret",
     "NotificationOptionAudioPlayback": "Lydafspilning påbegyndt",
     "NotificationOptionAudioPlaybackStopped": "Lydafspilning stoppet",
     "NotificationOptionCameraImageUploaded": "Kamerabillede uploadet",
-    "NotificationOptionInstallationFailed": "Installationen fejlede",
+    "NotificationOptionInstallationFailed": "Installationen mislykkedes",
     "NotificationOptionNewLibraryContent": "Nyt indhold tilføjet",
-    "NotificationOptionPluginError": "Pluginfejl",
-    "NotificationOptionPluginInstalled": "Plugin installeret",
-    "NotificationOptionPluginUninstalled": "Plugin afinstalleret",
-    "NotificationOptionPluginUpdateInstalled": "Opdatering til plugin installeret",
-    "NotificationOptionServerRestartRequired": "Genstart af server påkrævet",
-    "NotificationOptionTaskFailed": "Planlagt opgave fejlet",
-    "NotificationOptionUserLockedOut": "Bruger låst ude",
+    "NotificationOptionPluginError": "Plugin fejl",
+    "NotificationOptionPluginInstalled": "Plugin blev installeret",
+    "NotificationOptionPluginUninstalled": "Plugin blev afinstalleret",
+    "NotificationOptionPluginUpdateInstalled": "Opdatering til plugin blev installeret",
+    "NotificationOptionServerRestartRequired": "Genstart af serveren er påkrævet",
+    "NotificationOptionTaskFailed": "Planlagt opgave er fejlet",
+    "NotificationOptionUserLockedOut": "Bruger er låst ude",
     "NotificationOptionVideoPlayback": "Videoafspilning påbegyndt",
-    "NotificationOptionVideoPlaybackStopped": "Videoafspilning stoppet",
-    "Photos": "Fotoer",
+    "NotificationOptionVideoPlaybackStopped": "Videoafspilning blev stoppet",
+    "Photos": "Fotos",
     "Playlists": "Afspilningslister",
     "Plugin": "Plugin",
     "PluginInstalledWithName": "{0} blev installeret",
     "PluginUninstalledWithName": "{0} blev afinstalleret",
     "PluginUpdatedWithName": "{0} blev opdateret",
     "ProviderValue": "Udbyder: {0}",
-    "ScheduledTaskFailedWithName": "{0} fejlet",
-    "ScheduledTaskStartedWithName": "{0} påbegyndt",
+    "ScheduledTaskFailedWithName": "{0} mislykkedes",
+    "ScheduledTaskStartedWithName": "{0} påbegyndte",
     "ServerNameNeedsToBeRestarted": "{0} skal genstartes",
     "Shows": "Serier",
     "Songs": "Sange",
-    "StartupEmbyServerIsLoading": "Jellyfin Server er i gang med at starte op. Prøv venligst igen om lidt.",
+    "StartupEmbyServerIsLoading": "Jellyfin Server er i gang med at starte. Forsøg igen om et øjeblik.",
     "SubtitleDownloadFailureForItem": "Fejlet i download af undertekster for {0}",
-    "SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke downloades fra {0} til {1}",
-    "Sync": "Synk",
+    "SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke hentes fra {0} til {1}",
+    "Sync": "Synkroniser",
     "System": "System",
-    "TvShows": "Tv-serier",
+    "TvShows": "TV-serier",
     "User": "Bruger",
     "UserCreatedWithName": "Bruger {0} er blevet oprettet",
-    "UserDeletedWithName": "Brugeren {0} er blevet slettet",
-    "UserDownloadingItemWithValues": "{0} downloader {1}",
+    "UserDeletedWithName": "Brugeren {0} er nu slettet",
+    "UserDownloadingItemWithValues": "{0} henter {1}",
     "UserLockedOutWithName": "Brugeren {0} er blevet låst ude",
     "UserOfflineFromDevice": "{0} har afbrudt fra {1}",
     "UserOnlineFromDevice": "{0} er online fra {1}",
-    "UserPasswordChangedWithName": "Adgangskode er ændret for bruger {0}",
-    "UserPolicyUpdatedWithName": "Brugerpolitik er blevet opdateret for {0}",
+    "UserPasswordChangedWithName": "Adgangskode er ændret for brugeren {0}",
+    "UserPolicyUpdatedWithName": "Brugerpolitikken er blevet opdateret for {0}",
     "UserStartedPlayingItemWithValues": "{0} har påbegyndt afspilning af {1}",
     "UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1} på {2}",
     "ValueHasBeenAddedToLibrary": "{0} er blevet tilføjet til dit mediebibliotek",
     "ValueSpecialEpisodeName": "Special - {0}",
     "VersionNumber": "Version {0}",
-    "TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata konfiguration.",
-    "TaskDownloadMissingSubtitles": "Download manglende undertekster",
-    "TaskUpdatePluginsDescription": "Downloader og installere opdateringer for plugins som er konfigureret til at opdatere automatisk.",
+    "TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata konfigurationen.",
+    "TaskDownloadMissingSubtitles": "Hent manglende undertekster",
+    "TaskUpdatePluginsDescription": "Henter og installerer opdateringer for plugins, som er indstillet til at blive opdateret automatisk.",
     "TaskUpdatePlugins": "Opdater Plugins",
-    "TaskCleanLogsDescription": "Sletter log filer som er mere end {0} dage gammle.",
-    "TaskCleanLogs": "Ryd Log Mappe",
-    "TaskRefreshLibraryDescription": "Scanner dit medie bibliotek for nye filer og opdaterer metadata.",
+    "TaskCleanLogsDescription": "Sletter log filer som er mere end {0} dage gamle.",
+    "TaskCleanLogs": "Ryd Log mappe",
+    "TaskRefreshLibraryDescription": "Scanner dit medie bibliotek for nye filer og opdateret metadata.",
     "TaskRefreshLibrary": "Scan Medie Bibliotek",
-    "TaskCleanCacheDescription": "Sletter cache filer som systemet ikke har brug for længere.",
-    "TaskCleanCache": "Ryd Cache Mappe",
+    "TaskCleanCacheDescription": "Sletter cache filer som systemet ikke længere bruger.",
+    "TaskCleanCache": "Ryd Cache mappe",
     "TasksChannelsCategory": "Internet Kanaler",
     "TasksApplicationCategory": "Applikation",
     "TasksLibraryCategory": "Bibliotek",
     "TasksMaintenanceCategory": "Vedligeholdelse",
-    "TaskRefreshChapterImages": "Udtræk Kapitel billeder",
+    "TaskRefreshChapterImages": "Udtræk kapitel billeder",
     "TaskRefreshChapterImagesDescription": "Lav miniaturebilleder for videoer der har kapitler.",
-    "TaskRefreshChannelsDescription": "Genopfrisker internet kanal information.",
-    "TaskRefreshChannels": "Genopfrisk Kanaler",
-    "TaskCleanTranscodeDescription": "Fjern transcode filer som er mere end en dag gammel.",
-    "TaskCleanTranscode": "Rengør Transcode Mappen",
-    "TaskRefreshPeople": "Genopfrisk Personer",
-    "TaskRefreshPeopleDescription": "Opdatere metadata for skuespillere og instruktører i dit bibliotek.",
-    "TaskCleanActivityLogDescription": "Sletter linjer i aktivitetsloggen ældre end den konfigureret alder.",
+    "TaskRefreshChannelsDescription": "Opdater internet kanal information.",
+    "TaskRefreshChannels": "Opdater Kanaler",
+    "TaskCleanTranscodeDescription": "Fjern transcode filer som er mere end 1 dag gammel.",
+    "TaskCleanTranscode": "Tøm Transcode mappen",
+    "TaskRefreshPeople": "Opdater Personer",
+    "TaskRefreshPeopleDescription": "Opdaterer metadata for skuespillere og instruktører i dit mediebibliotek.",
+    "TaskCleanActivityLogDescription": "Sletter linjer i aktivitetsloggen ældre end den konfigurerede alder.",
     "TaskCleanActivityLog": "Ryd Aktivitetslog",
     "Undefined": "Udefineret",
     "Forced": "Tvunget",
     "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.",
+    "TaskOptimizeDatabaseDescription": "Komprimerer databasen og frigør plads. Denne handling køres efter at have scannet mediebiblioteket, eller efter at have lavet ændringer til databasen, for at højne ydeevnen.",
     "TaskOptimizeDatabase": "Optimér database",
-    "TaskKeyframeExtractorDescription": "Udtrækker billeder fra videofiler for at lave mere præcise HLS playlister. Denne opgave kan godt tage lang tid.",
-    "TaskKeyframeExtractor": "Billedramme udtrækker",
+    "TaskKeyframeExtractorDescription": "Udtrækker billeder fra videofiler for at lave mere præcise HLS playlister. Denne opgave kan tage lang tid.",
+    "TaskKeyframeExtractor": "Nøglebillede udtræk",
     "External": "Ekstern",
     "HearingImpaired": "Hørehæmmet"
 }

+ 21 - 1
Emby.Server.Implementations/Localization/Core/hi.json

@@ -73,5 +73,25 @@
     "Songs": "गाने",
     "UserStartedPlayingItemWithValues": "{0} {2} पर {1} खेल रहे हैं",
     "UserStoppedPlayingItemWithValues": "{0} ने {2} पर {1} खेलना खत्म किया",
-    "StartupEmbyServerIsLoading": "जेलीफ़िन सर्वर लोड हो रहा है। कृपया शीघ्र ही पुन: प्रयास करें।"
+    "StartupEmbyServerIsLoading": "जेलीफ़िन सर्वर लोड हो रहा है। कृपया शीघ्र ही पुन: प्रयास करें।",
+    "ServerNameNeedsToBeRestarted": "{0} रीस्टार्ट करने की आवश्यकता है",
+    "UserCreatedWithName": "उपयोगकर्ता {0} बनाया गया",
+    "UserDownloadingItemWithValues": "{0} डाउनलोड हो रहा है",
+    "UserOfflineFromDevice": "{0} {1} से डिस्कनेक्ट हो गया है",
+    "Undefined": "अनिर्धारित",
+    "UserOnlineFromDevice": "{0} {1} से ऑनलाइन है",
+    "Shows": "शो",
+    "UserPasswordChangedWithName": "उपयोगकर्ता {0} के लिए पासवर्ड बदल दिया गया है",
+    "UserDeletedWithName": "उपयोगकर्ता {0} हटा दिया गया",
+    "UserPolicyUpdatedWithName": "{0} के लिए उपयोगकर्ता नीति अपडेट कर दी गई है",
+    "User": "उपयोगकर्ता",
+    "SubtitleDownloadFailureFromForItem": "{1} के लिए {0} से उपशीर्षक डाउनलोड करने में विफल",
+    "ProviderValue": "प्रदाता: {0}",
+    "ScheduledTaskFailedWithName": "{0}असफल",
+    "UserLockedOutWithName": "उपयोगकर्ता {0} को लॉक आउट कर दिया गया है",
+    "System": "प्रणाली",
+    "TvShows": "टीवी शो",
+    "HearingImpaired": "मूक बधिर",
+    "ValueSpecialEpisodeName": "विशेष",
+    "TasksMaintenanceCategory": "रखरखाव"
 }

+ 22 - 20
Emby.Server.Implementations/Localization/Core/pa.json

@@ -28,22 +28,22 @@
     "ValueHasBeenAddedToLibrary": "{0} ਤੁਹਾਡੀ ਮੀਡੀਆ ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਹੈ",
     "UserStoppedPlayingItemWithValues": "{0} ਨੇ {2} 'ਤੇ {1} ਖੇਡਣਾ ਪੂਰਾ ਕਰ ਲਿਆ ਹੈ",
     "UserStartedPlayingItemWithValues": "{0} {2} 'ਤੇ {1} ਖੇਡ ਰਿਹਾ ਹੈ",
-    "UserPolicyUpdatedWithName": "ਉਪਭੋਗਤਾ ਨੀਤੀ ਨੂੰ {0} ਲਈ ਅਪਡੇਟ ਕੀਤਾ ਗਿਆ ਹੈ",
-    "UserPasswordChangedWithName": "ਪਾਸਵਰਡ ਯੂਜ਼ਰ ਲਈ ਬਦਲਿਆ ਗਿਆ ਹੈ {0}",
-    "UserOnlineFromDevice": "{0} ਤੋਂ isਨਲਾਈਨ ਹੈ {1}",
+    "UserPolicyUpdatedWithName": "ਵਰਤੋਂਕਾਰ ਨੀਤੀ ਨੂੰ {0} ਲਈ ਅਪਡੇਟ ਕੀਤਾ ਗਿਆ ਹੈ",
+    "UserPasswordChangedWithName": "{0} ਵਰਤੋਂਕਾਰ ਲਈ ਪਾਸਵਰਡ ਬਦਲਿਆ ਗਿਆ ਸੀ",
+    "UserOnlineFromDevice": "{0} ਨੂੰ {1} ਤੋਂ ਆਨਲਾਈਨ ਹੈ",
     "UserOfflineFromDevice": "{0} ਤੋਂ ਡਿਸਕਨੈਕਟ ਹੋ ਗਿਆ ਹੈ {1}",
-    "UserLockedOutWithName": "ਯੂਜ਼ਰ {0} ਨੂੰ ਲਾਕ ਆਉਟ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ",
-    "UserDownloadingItemWithValues": "{0} ਡਾ{ਨਲੋਡ ਕਰ ਰਿਹਾ ਹੈ {1}",
-    "UserDeletedWithName": "ਯੂਜ਼ਰ {0} ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ",
-    "UserCreatedWithName": "ਯੂਜ਼ਰ {0} ਬਣਾਇਆ ਗਿਆ ਹੈ",
-    "User": "ਯੂਜ਼ਰ",
+    "UserLockedOutWithName": "ਵਰਤੋਂਕਾਰ {0} ਨੂੰ ਲਾਕ ਕੀਤਾ ਗਿਆ ਹੈ",
+    "UserDownloadingItemWithValues": "{0} {1} ਨੂੰ ਡਾਊਨਲੋਡ ਕਰ ਰਿਹਾ ਹੈ",
+    "UserDeletedWithName": "ਵਰਤੋਂਕਾਰ {0} ਨੂੰ ਹਟਾਇਆ ਗਿਆ",
+    "UserCreatedWithName": "ਵਰਤੋਂਕਾਰ {0} ਬਣਾਇਆ ਗਿਆ ਹੈ",
+    "User": "ਵਰਤੋਂਕਾਰ",
     "Undefined": "ਪਰਿਭਾਸ਼ਤ",
-    "TvShows": "ਟੀਵੀ ਸ਼ੋਅਜ਼",
+    "TvShows": "ਟੀਵੀ ਸ਼ੋਅ",
     "System": "ਸਿਸਟਮ",
     "Sync": "ਸਿੰਕ",
-    "SubtitleDownloadFailureFromForItem": "ਉਪਸਿਰਲੇਖ {1} ਲਈ {0} ਤੋਂ ਡਾ toਨਲੋਡ ਕਰਨ ਵਿੱਚ ਅਸਫਲ ਰਹੇ",
-    "StartupEmbyServerIsLoading": "ਜੈਲੀਫਿਨ ਸਰਵਰ ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ. ਕਿਰਪਾ ਕਰਕੇ ਜਲਦੀ ਹੀ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ.",
-    "Songs": "ਗਾਣੇ",
+    "SubtitleDownloadFailureFromForItem": "ਉਪਸਿਰਲੇਖ {1} ਲਈ {0} ਤੋਂ ਡਾਨਲੋਡ ਕਰਨ ਵਿੱਚ ਅਸਫਲ ਰਹੇ",
+    "StartupEmbyServerIsLoading": "Jellyfin ਸਰਵਰ ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ। ਛੇਤੀ ਹੀ ਫ਼ੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ।",
+    "Songs": "ਗਾਣੇ",
     "Shows": "ਸ਼ੋਅ",
     "ServerNameNeedsToBeRestarted": "{0} ਮੁੜ ਚਾਲੂ ਕਰਨ ਦੀ ਲੋੜ ਹੈ",
     "ScheduledTaskStartedWithName": "{0} ਸ਼ੁਰੂ ਹੋਇਆ",
@@ -57,12 +57,12 @@
     "Photos": "ਫੋਟੋਆਂ",
     "NotificationOptionVideoPlaybackStopped": "ਵੀਡੀਓ ਪਲੇਬੈਕ ਰੋਕਿਆ ਗਿਆ",
     "NotificationOptionVideoPlayback": "ਵੀਡੀਓ ਪਲੇਬੈਕ ਸ਼ੁਰੂ ਹੋਇਆ",
-    "NotificationOptionUserLockedOut": "ਉਪਭੋਗਤਾ ਨੂੰ ਲਾਕ ਆਉਟ ਕੀਤਾ ਗਿਆ",
+    "NotificationOptionUserLockedOut": "ਵਰਤੋਂਕਾਰ ਨੂੰ ਲਾਕ ਕੀਤਾ",
     "NotificationOptionTaskFailed": "ਨਿਰਧਾਰਤ ਕਾਰਜ ਅਸਫਲਤਾ",
     "NotificationOptionServerRestartRequired": "ਸਰਵਰ ਨੂੰ ਮੁੜ ਚਾਲੂ ਕਰਨ ਦੀ ਲੋੜ ਹੈ",
     "NotificationOptionPluginUpdateInstalled": "ਪਲੱਗਇਨ ਅਪਡੇਟ ਇੰਸਟੌਲ ਕੀਤਾ ਗਿਆ",
     "NotificationOptionPluginUninstalled": "ਪਲੱਗਇਨ ਅਣਇੰਸਟੌਲ ਕੀਤਾ",
-    "NotificationOptionPluginInstalled": "ਪਲੱਗਇਨ ਸਥਾਪਿਤ ਕੀਤਾ",
+    "NotificationOptionPluginInstalled": "ਪਲੱਗਇਨ ਇੰਸਟਾਲ ਕੀਤੀ",
     "NotificationOptionPluginError": "ਪਲੱਗਇਨ ਅਸਫਲ",
     "NotificationOptionNewLibraryContent": "ਨਵੀਂ ਸਮੱਗਰੀ ਸ਼ਾਮਲ ਕੀਤੀ ਗਈ",
     "NotificationOptionInstallationFailed": "ਇੰਸਟਾਲੇਸ਼ਨ ਅਸਫਲ",
@@ -92,7 +92,7 @@
     "HomeVideos": "ਘਰੇਲੂ ਵੀਡੀਓ",
     "HeaderRecordingGroups": "ਰਿਕਾਰਡਿੰਗ ਸਮੂਹ",
     "HeaderNextUp": "ਅੱਗੇ",
-    "HeaderLiveTV": "ਲਾਈਵ ਟੀ",
+    "HeaderLiveTV": "ਲਾਈਵ ਟੀਵੀ",
     "HeaderFavoriteSongs": "ਮਨਪਸੰਦ ਗਾਣੇ",
     "HeaderFavoriteShows": "ਮਨਪਸੰਦ ਸ਼ੋਅ",
     "HeaderFavoriteEpisodes": "ਮਨਪਸੰਦ ਐਪੀਸੋਡ",
@@ -102,20 +102,22 @@
     "HeaderAlbumArtists": "ਐਲਬਮ ਕਲਾਕਾਰ",
     "Genres": "ਸ਼ੈਲੀਆਂ",
     "Forced": "ਮਜਬੂਰ",
-    "Folders": "ਫੋਲਡਰ",
+    "Folders": "ਫੋਲਡਰ",
     "Favorites": "ਮਨਪਸੰਦ",
-    "FailedLoginAttemptWithUserName": "ਤੋਂ ਲਾਗਇਨ ਕੋਸ਼ਿਸ਼ ਫੇਲ ਹੋਈ {0}",
+    "FailedLoginAttemptWithUserName": "{0} ਤੋਂ ਲਾਗਇਨ ਕੋਸ਼ਿਸ਼ ਫੇਲ ਹੋਈ",
     "DeviceOnlineWithName": "{0} ਜੁੜਿਆ ਹੋਇਆ ਹੈ",
     "DeviceOfflineWithName": "{0} ਡਿਸਕਨੈਕਟ ਹੋ ਗਿਆ ਹੈ",
     "Default": "ਡਿਫੌਲਟ",
     "Collections": "ਸੰਗ੍ਰਹਿਣ",
-    "ChapterNameValue": "ਅਧਿਆਇ {0}",
+    "ChapterNameValue": "ਚੈਪਟਰ {0}",
     "Channels": "ਚੈਨਲ",
-    "CameraImageUploadedFrom": "ਤੋਂ ਇੱਕ ਨਵਾਂ ਕੈਮਰਾ ਚਿੱਤਰ ਅਪਲੋਡ ਕੀਤਾ ਗਿਆ ਹੈ {0}",
+    "CameraImageUploadedFrom": "{0} ਤੋਂ ਇੱਕ ਨਵਾਂ ਕੈਮਰਾ ਚਿੱਤਰ ਅਪਲੋਡ ਕੀਤਾ ਗਿਆ ਹੈ",
     "Books": "ਕਿਤਾਬਾਂ",
     "AuthenticationSucceededWithUserName": "{0} ਸਫਲਤਾਪੂਰਕ ਪ੍ਰਮਾਣਿਤ",
     "Artists": "ਕਲਾਕਾਰ",
     "Application": "ਐਪਲੀਕੇਸ਼ਨ",
     "AppDeviceValues": "ਐਪ: {0}, ਜੰਤਰ: {1}",
-    "Albums": "ਐਲਬਮਾਂ"
+    "Albums": "ਐਲਬਮਾਂ",
+    "TaskOptimizeDatabase": "ਡਾਟਾਬੇਸ ਅਨੁਕੂਲ ਬਣਾਓ",
+    "External": "ਬਾਹਰੀ"
 }

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

@@ -122,5 +122,6 @@
     "TaskOptimizeDatabaseDescription": "Base de dados compacta e corta espaço livre. A execução desta tarefa depois de digitalizar a biblioteca ou de fazer outras alterações que impliquem modificações na base de dados pode melhorar o desempenho.",
     "External": "Externo",
     "HearingImpaired": "Problemas auditivos",
-    "TaskKeyframeExtractor": "Extrator de quadro-chave"
+    "TaskKeyframeExtractor": "Extrator de quadro-chave",
+    "TaskKeyframeExtractorDescription": "Retira frames chave do video para criar listas HLS precisas. Esta tarefa pode correr durante algum tempo."
 }

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

@@ -42,7 +42,7 @@
     "MusicVideos": "Муз. видео",
     "NameInstallFailed": "Установка {0} неудачна",
     "NameSeasonNumber": "Сезон {0}",
-    "NameSeasonUnknown": "Сезон неопознан",
+    "NameSeasonUnknown": "Сезон не опознан",
     "NewVersionIsAvailable": "Новая версия Jellyfin Server доступна для загрузки.",
     "NotificationOptionApplicationUpdateAvailable": "Имеется обновление приложения",
     "NotificationOptionApplicationUpdateInstalled": "Обновление приложения установлено",
@@ -96,7 +96,7 @@
     "TaskRefreshChannels": "Обновление каналов",
     "TaskCleanTranscode": "Очистка каталога перекодировки",
     "TaskUpdatePlugins": "Обновление плагинов",
-    "TaskRefreshPeople": "Подновление людей",
+    "TaskRefreshPeople": "Обновление информации о персонах",
     "TaskCleanLogs": "Очистка каталога журналов",
     "TaskRefreshLibrary": "Сканирование медиатеки",
     "TaskRefreshChapterImages": "Извлечение изображений сцен",

+ 46 - 46
Emby.Server.Implementations/Localization/Core/zh-HK.json

@@ -5,12 +5,12 @@
     "Artists": "藝人",
     "AuthenticationSucceededWithUserName": "{0} 授權成功",
     "Books": "圖書",
-    "CameraImageUploadedFrom": "{0} 成功上傳一張新片",
+    "CameraImageUploadedFrom": "{0} 成功上傳一張新片",
     "Channels": "頻道",
-    "ChapterNameValue": "章節 {0}",
+    "ChapterNameValue": "第 {0} 章",
     "Collections": "合輯",
-    "DeviceOfflineWithName": "{0} 已斷開連接",
-    "DeviceOnlineWithName": "{0} 已連接",
+    "DeviceOfflineWithName": "{0} 已斷開連接",
+    "DeviceOnlineWithName": "{0} 已連接",
     "FailedLoginAttemptWithUserName": "{0} 登入失敗",
     "Favorites": "我的最愛",
     "Folders": "資料夾",
@@ -23,43 +23,43 @@
     "HeaderFavoriteShows": "最愛的節目",
     "HeaderFavoriteSongs": "最愛的歌曲",
     "HeaderLiveTV": "電視直播",
-    "HeaderNextUp": "接下來",
+    "HeaderNextUp": "接著播放",
     "HeaderRecordingGroups": "錄製組",
     "HomeVideos": "家庭影片",
     "Inherit": "繼承",
-    "ItemAddedWithName": "{0} 已添加至媒體庫",
+    "ItemAddedWithName": "{0} 已添加至媒體庫",
     "ItemRemovedWithName": "{0} 已從媒體庫移除",
     "LabelIpAddressValue": "IP 地址: {0}",
     "LabelRunningTimeValue": "運行時間: {0}",
     "Latest": "最新",
-    "MessageApplicationUpdated": "Jellyfin 伺服器已更新",
-    "MessageApplicationUpdatedTo": "Jellyfin 伺服器已更新至 {0}",
-    "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 已更新",
-    "MessageServerConfigurationUpdated": "伺服器設定已經更新",
+    "MessageApplicationUpdated": "Jellyfin 已更新",
+    "MessageApplicationUpdatedTo": "Jellyfin 已更新至 {0}",
+    "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 已更新",
+    "MessageServerConfigurationUpdated": "伺服器設定已經更新",
     "MixedContent": "混合內容",
     "Movies": "電影",
     "Music": "音樂",
-    "MusicVideos": "音樂影片",
+    "MusicVideos": "MV",
     "NameInstallFailed": "{0} 安裝失敗",
     "NameSeasonNumber": "第 {0} 季",
-    "NameSeasonUnknown": "未知季數",
-    "NewVersionIsAvailable": "新版本的 Jellyfin 伺服器可供下載。",
+    "NameSeasonUnknown": "未知的季度",
+    "NewVersionIsAvailable": "有較新版本的 Jellyfin 可供下載。",
     "NotificationOptionApplicationUpdateAvailable": "有可用的更新",
-    "NotificationOptionApplicationUpdateInstalled": "應用程式已更新",
+    "NotificationOptionApplicationUpdateInstalled": "應用程式已更新",
     "NotificationOptionAudioPlayback": "開始播放音訊",
-    "NotificationOptionAudioPlaybackStopped": "停止播放音訊",
-    "NotificationOptionCameraImageUploaded": "相片已上傳",
+    "NotificationOptionAudioPlaybackStopped": "停止播放音訊",
+    "NotificationOptionCameraImageUploaded": "相片已上傳",
     "NotificationOptionInstallationFailed": "安裝失敗",
     "NotificationOptionNewLibraryContent": "已添加新内容",
-    "NotificationOptionPluginError": "擴充元件錯誤",
-    "NotificationOptionPluginInstalled": "擴充元件已安裝",
-    "NotificationOptionPluginUninstalled": "擴充元件已移除",
-    "NotificationOptionPluginUpdateInstalled": "擴充元件更新已安裝",
-    "NotificationOptionServerRestartRequired": "伺服器需要重",
-    "NotificationOptionTaskFailed": "計劃任務失敗",
-    "NotificationOptionUserLockedOut": "用家已鎖定",
-    "NotificationOptionVideoPlayback": "開始播放視頻",
-    "NotificationOptionVideoPlaybackStopped": "已停止播放視頻",
+    "NotificationOptionPluginError": "插件出現錯誤",
+    "NotificationOptionPluginInstalled": "插件已被安裝",
+    "NotificationOptionPluginUninstalled": "插件已被移除",
+    "NotificationOptionPluginUpdateInstalled": "插件已被更新",
+    "NotificationOptionServerRestartRequired": "伺服器需要重",
+    "NotificationOptionTaskFailed": "排程任務執行失敗",
+    "NotificationOptionUserLockedOut": "用戶已被鎖定",
+    "NotificationOptionVideoPlayback": "開始播放影片",
+    "NotificationOptionVideoPlaybackStopped": "已停止播放影片",
     "Photos": "相片",
     "Playlists": "播放清單",
     "Plugin": "插件",
@@ -69,51 +69,51 @@
     "ProviderValue": "提供者: {0}",
     "ScheduledTaskFailedWithName": "{0} 任務失敗",
     "ScheduledTaskStartedWithName": "{0} 任務開始",
-    "ServerNameNeedsToBeRestarted": "{0} 需要重",
+    "ServerNameNeedsToBeRestarted": "{0} 需要重",
     "Shows": "節目",
     "Songs": "歌曲",
-    "StartupEmbyServerIsLoading": "Jellyfin 伺服器載入中,請稍後再試。",
+    "StartupEmbyServerIsLoading": "Jellyfin 載入中,請稍後再試。",
     "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
     "SubtitleDownloadFailureFromForItem": "無法從 {0} 下載 {1} 的字幕",
     "Sync": "同步",
     "System": "系統",
     "TvShows": "電視節目",
-    "User": "使用者",
-    "UserCreatedWithName": "使用者 {0} 已創建",
-    "UserDeletedWithName": "使用者 {0} 已移除",
+    "User": "用戶",
+    "UserCreatedWithName": "用戶 {0} 已被建立",
+    "UserDeletedWithName": "用戶 {0} 已被移除",
     "UserDownloadingItemWithValues": "{0} 正在下載 {1}",
     "UserLockedOutWithName": "使用者 {0} 已被鎖定",
-    "UserOfflineFromDevice": "{0} 從 {1} 斷開",
-    "UserOnlineFromDevice": "{0} 已連綫,來自 {1}",
-    "UserPasswordChangedWithName": "使用者 {0} 的密碼已變更",
+    "UserOfflineFromDevice": "{0} 從 {1} 斷開連接",
+    "UserOnlineFromDevice": "{0} 從 {1} 連線",
+    "UserPasswordChangedWithName": "{0} 的密碼已被變改",
     "UserPolicyUpdatedWithName": "使用者協議已更新為 {0}",
     "UserStartedPlayingItemWithValues": "{0} 正在 {2} 上播放 {1}",
-    "UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}",
-    "ValueHasBeenAddedToLibrary": "{0} 已添加到你的媒體庫",
+    "UserStoppedPlayingItemWithValues": "{0} 已停止在 {2} 上播放 {1}",
+    "ValueHasBeenAddedToLibrary": "已添加 {0} 到你的媒體庫",
     "ValueSpecialEpisodeName": "特典 - {0}",
-    "VersionNumber": "版本{0}",
-    "TaskDownloadMissingSubtitles": "下載遺失的字幕",
+    "VersionNumber": "版本 {0}",
+    "TaskDownloadMissingSubtitles": "下載缺少的字幕",
     "TaskUpdatePlugins": "更新插件",
     "TasksApplicationCategory": "應用程式",
-    "TaskRefreshLibraryDescription": "掃描媒體庫以查找新文件並刷新metadata。",
+    "TaskRefreshLibraryDescription": "掃描媒體庫以加入新增檔案及重新載入 metadata。",
     "TasksMaintenanceCategory": "維護",
-    "TaskDownloadMissingSubtitlesDescription": "根據metadata配置在互聯網上搜索缺少的字幕。",
-    "TaskRefreshChannelsDescription": "刷新互聯網頻道信息。",
-    "TaskRefreshChannels": "刷新頻道",
+    "TaskDownloadMissingSubtitlesDescription": "根據元數據中的設定,在互聯網上搜索缺少的字幕。",
+    "TaskRefreshChannelsDescription": "重新載入網絡頻道的資訊。",
+    "TaskRefreshChannels": "重新載入頻道",
     "TaskCleanTranscodeDescription": "刪除超過一天的轉碼文件。",
     "TaskCleanTranscode": "清理轉碼目錄",
-    "TaskUpdatePluginsDescription": "下載並安裝配置為自動更新的插件的更新。",
+    "TaskUpdatePluginsDescription": "下載並更新能夠被自動更新的插件。",
     "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的元數據。",
     "TaskCleanLogsDescription": "刪除超過{0}天的日誌文件。",
     "TaskCleanLogs": "清理日誌目錄",
     "TaskRefreshLibrary": "掃描媒體庫",
-    "TaskRefreshChapterImagesDescription": "為帶有章節的視頻創建縮略圖。",
+    "TaskRefreshChapterImagesDescription": "為帶有章節的影片建立縮圖。",
     "TaskRefreshChapterImages": "提取章節圖像",
     "TaskCleanCacheDescription": "刪除系統不再需要的緩存文件。",
     "TaskCleanCache": "清理緩存目錄",
-    "TasksChannelsCategory": "互聯網頻道",
+    "TasksChannelsCategory": "網頻道",
     "TasksLibraryCategory": "庫",
-    "TaskRefreshPeople": "刷新人物",
+    "TaskRefreshPeople": "重新載入人物",
     "TaskCleanActivityLog": "清理活動記錄",
     "Undefined": "未定義",
     "Forced": "強制",
@@ -121,7 +121,7 @@
     "TaskOptimizeDatabaseDescription": "壓縮數據庫並截斷可用空間。在掃描媒體庫或執行其他數據庫的修改後運行此任務可能會提高性能。",
     "TaskOptimizeDatabase": "最佳化數據庫",
     "TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。",
-    "TaskKeyframeExtractorDescription": "提取關鍵格以創建更準確的HLS播放列表。次指示可能用時很長。",
+    "TaskKeyframeExtractorDescription": "提取關鍵幀以建立更準確的 HLS 播放列表。此工作或需要使用較長時間來完成。",
     "TaskKeyframeExtractor": "關鍵幀提取器",
     "External": "外部",
     "HearingImpaired": "聽力障礙"

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

@@ -91,14 +91,14 @@
     "HeaderRecordingGroups": "錄製組",
     "Inherit": "繼承",
     "SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕",
-    "TaskDownloadMissingSubtitlesDescription": "透過中繼資料從網路上搜尋遺失的字幕。",
+    "TaskDownloadMissingSubtitlesDescription": "透過媒體資訊從網路上搜尋遺失的字幕。",
     "TaskDownloadMissingSubtitles": "下載遺失的字幕",
     "TaskRefreshChannels": "重新整理頻道",
     "TaskUpdatePlugins": "更新附加元件",
     "TaskRefreshPeople": "更新人物",
     "TaskCleanLogsDescription": "刪除超過 {0} 天的日誌文件。",
     "TaskCleanLogs": "清空日誌資料夾",
-    "TaskRefreshLibraryDescription": "重新掃描媒體庫的新檔案並更新中繼資料。",
+    "TaskRefreshLibraryDescription": "重新掃描媒體庫的新檔案並更新媒體資訊。",
     "TaskRefreshLibrary": "重新掃描媒體庫",
     "TaskRefreshChapterImages": "擷取章節圖片",
     "TaskCleanCacheDescription": "刪除系統已不需要的快取。",
@@ -108,7 +108,7 @@
     "TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。",
     "TaskCleanTranscode": "清除轉碼資料夾",
     "TaskUpdatePluginsDescription": "為已設置為自動更新的附加元件下載並安裝更新。",
-    "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的中繼資料。",
+    "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的資訊。",
     "TaskRefreshChapterImagesDescription": "為有章節的影片建立縮圖。",
     "TasksChannelsCategory": "網路頻道",
     "TasksApplicationCategory": "應用程式",

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

@@ -198,25 +198,25 @@ namespace Emby.Server.Implementations.Localization
             }
 
             // Minimum rating possible
-            if (!ratings.Any(x => x.Value == 0))
+            if (ratings.All(x => x.Value != 0))
             {
                 ratings.Add(new ParentalRating("Approved", 0));
             }
 
             // Matches PG (this has different age restrictions depending on country)
-            if (!ratings.Any(x => x.Value == 10))
+            if (ratings.All(x => x.Value != 10))
             {
                 ratings.Add(new ParentalRating("10", 10));
             }
 
             // Matches PG-13
-            if (!ratings.Any(x => x.Value == 13))
+            if (ratings.All(x => x.Value != 13))
             {
                 ratings.Add(new ParentalRating("13", 13));
             }
 
             // Matches TV-14
-            if (!ratings.Any(x => x.Value == 14))
+            if (ratings.All(x => x.Value != 14))
             {
                 ratings.Add(new ParentalRating("14", 14));
             }
@@ -229,13 +229,13 @@ namespace Emby.Server.Implementations.Localization
             }
 
             // A lot of countries don't excplicitly have a seperate rating for adult content
-            if (!ratings.Any(x => x.Value == 1000))
+            if (ratings.All(x => x.Value != 1000))
             {
                 ratings.Add(new ParentalRating("XXX", 1000));
             }
 
             // A lot of countries don't excplicitly have a seperate rating for banned content
-            if (!ratings.Any(x => x.Value == 1001))
+            if (ratings.All(x => x.Value != 1001))
             {
                 ratings.Add(new ParentalRating("Banned", 1001));
             }

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

@@ -309,7 +309,7 @@ namespace Emby.Server.Implementations.Plugins
                 // If no version is given, return the current instance.
                 var plugins = _plugins.Where(p => p.Id.Equals(id)).ToList();
 
-                plugin = plugins.FirstOrDefault(p => p.Instance is not null) ?? plugins.OrderByDescending(p => p.Version).FirstOrDefault();
+                plugin = plugins.FirstOrDefault(p => p.Instance is not null) ?? plugins.MaxBy(p => p.Version);
             }
             else
             {

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

@@ -606,7 +606,7 @@ namespace Emby.Server.Implementations.Session
                     }
                     catch (Exception ex)
                     {
-                        _logger.LogDebug("Error calling OnPlaybackStopped", ex);
+                        _logger.LogDebug(ex, "Error calling OnPlaybackStopped");
                     }
                 }
 
@@ -953,7 +953,7 @@ namespace Emby.Server.Implementations.Session
                 }
                 catch (Exception ex)
                 {
-                    _logger.LogError("Error closing live stream", ex);
+                    _logger.LogError(ex, "Error closing live stream");
                 }
             }
 

+ 1 - 3
Emby.Server.Implementations/Session/WebSocketController.cs

@@ -69,9 +69,7 @@ namespace Emby.Server.Implementations.Session
             T data,
             CancellationToken cancellationToken)
         {
-            var socket = GetActiveSockets()
-                .OrderByDescending(i => i.LastActivityDate)
-                .FirstOrDefault();
+            var socket = GetActiveSockets().MaxBy(i => i.LastActivityDate);
 
             if (socket is null)
             {

+ 4 - 8
Emby.Server.Implementations/SyncPlay/Group.cs

@@ -620,10 +620,8 @@ namespace Emby.Server.Implementations.SyncPlay
                 RestartCurrentItem();
                 return true;
             }
-            else
-            {
-                return false;
-            }
+
+            return false;
         }
 
         /// <inheritdoc />
@@ -637,10 +635,8 @@ namespace Emby.Server.Implementations.SyncPlay
                 RestartCurrentItem();
                 return true;
             }
-            else
-            {
-                return false;
-            }
+
+            return false;
         }
 
         /// <inheritdoc />

+ 2 - 4
Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs

@@ -339,10 +339,8 @@ namespace Emby.Server.Implementations.SyncPlay
             {
                 return sessionsCounter > 0;
             }
-            else
-            {
-                return false;
-            }
+
+            return false;
         }
 
         /// <summary>

+ 1 - 3
Jellyfin.Api/Controllers/DynamicHlsController.cs

@@ -20,7 +20,6 @@ using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Net;
 using MediaBrowser.MediaEncoding.Encoder;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Dlna;
@@ -2030,8 +2029,7 @@ public class DynamicHlsController : BaseJellyfinApiController
         {
             return fileSystem.GetFiles(folder, new[] { segmentExtension }, true, false)
                 .Where(i => Path.GetFileNameWithoutExtension(i.Name).StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase))
-                .OrderByDescending(fileSystem.GetLastWriteTimeUtc)
-                .FirstOrDefault();
+                .MaxBy(fileSystem.GetLastWriteTimeUtc);
         }
         catch (IOException)
         {

+ 0 - 1
Jellyfin.Api/Controllers/FilterController.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Enums;

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

@@ -146,7 +146,7 @@ public class PluginsController : BaseJellyfinApiController
         var plugins = _pluginManager.Plugins.Where(p => p.Id.Equals(pluginId)).ToList();
 
         // Select the un-instanced one first.
-        var plugin = plugins.FirstOrDefault(p => p.Instance is null) ?? plugins.OrderBy(p => p.Manifest.Status).FirstOrDefault();
+        var plugin = plugins.FirstOrDefault(p => p.Instance is null) ?? plugins.MinBy(p => p.Manifest.Status);
 
         if (plugin is not null)
         {

+ 0 - 2
Jellyfin.Api/Controllers/QuickConnectController.cs

@@ -1,8 +1,6 @@
 using System;
 using System.ComponentModel.DataAnnotations;
 using System.Threading.Tasks;
-using Jellyfin.Api.Constants;
-using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Authentication;

+ 0 - 1
Jellyfin.Api/Controllers/SearchController.cs

@@ -3,7 +3,6 @@ using System.ComponentModel;
 using System.ComponentModel.DataAnnotations;
 using System.Globalization;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Enums;

+ 2 - 4
Jellyfin.Api/Controllers/SubtitleController.cs

@@ -533,10 +533,8 @@ public class SubtitleController : BaseJellyfinApiController
                 _logger.LogDebug("Fallback font size is {FileSize} Bytes", fileSize);
                 return PhysicalFile(fontFile.FullName, MimeTypes.GetMimeType(fontFile.FullName));
             }
-            else
-            {
-                _logger.LogWarning("The selected font is null or empty");
-            }
+
+            _logger.LogWarning("The selected font is null or empty");
         }
         else
         {

+ 0 - 1
Jellyfin.Api/Controllers/UniversalAudioController.cs

@@ -5,7 +5,6 @@ using System.Globalization;
 using System.Linq;
 using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
-using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Api.Models.StreamingDtos;

+ 7 - 8
Jellyfin.Networking/Manager/NetworkManager.cs

@@ -500,10 +500,8 @@ namespace Jellyfin.Networking.Manager
             {
                 return true;
             }
-            else
-            {
-                return address.IsPrivateAddressRange();
-            }
+
+            return address.IsPrivateAddressRange();
         }
 
         /// <inheritdoc/>
@@ -1171,13 +1169,15 @@ namespace Jellyfin.Networking.Manager
                     bindPreference = addr.Value;
                     break;
                 }
-                else if ((addr.Key.Address.Equals(IPAddress.Any) || addr.Key.Address.Equals(IPAddress.IPv6Any)) && isInExternalSubnet)
+
+                if ((addr.Key.Address.Equals(IPAddress.Any) || addr.Key.Address.Equals(IPAddress.IPv6Any)) && isInExternalSubnet)
                 {
                     // External.
                     bindPreference = addr.Value;
                     break;
                 }
-                else if (addr.Key.Contains(source))
+
+                if (addr.Key.Contains(source))
                 {
                     // Match ip address.
                     bindPreference = addr.Value;
@@ -1256,8 +1256,7 @@ namespace Jellyfin.Networking.Manager
                     // Look for the best internal address.
                     bindAddress = addresses
                         .Where(p => IsInLocalNetwork(p) && (p.Contains(source) || p.Equals(IPAddress.None)))
-                        .OrderBy(p => p.Tag)
-                        .FirstOrDefault()?.Address;
+                        .MinBy(p => p.Tag)?.Address;
                 }
 
                 if (bindAddress is not null)

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

@@ -4,7 +4,6 @@ using System;
 using System.Collections.Generic;
 using System.Net;
 using System.Threading.Tasks;
-using EFCoreSecondLevelCacheInterceptor;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;

+ 0 - 1
Jellyfin.Server/CoreAppHost.cs

@@ -22,7 +22,6 @@ using MediaBrowser.Controller.Lyrics;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Security;
 using MediaBrowser.Model.Activity;
-using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;

+ 1 - 3
Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs

@@ -133,9 +133,7 @@ namespace Jellyfin.Server.Migrations.Routines
                         SkipBackwardLength = dto.CustomPrefs.TryGetValue("skipBackLength", out length) && int.TryParse(length, out var skipBackwardLength)
                             ? skipBackwardLength
                             : 10000,
-                        EnableNextVideoInfoOverlay = dto.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enabled) && !string.IsNullOrEmpty(enabled)
-                            ? bool.Parse(enabled)
-                            : true,
+                        EnableNextVideoInfoOverlay = !dto.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enabled) || string.IsNullOrEmpty(enabled) || bool.Parse(enabled),
                         DashboardTheme = dto.CustomPrefs.TryGetValue("dashboardtheme", out var theme) ? theme : string.Empty,
                         TvHome = dto.CustomPrefs.TryGetValue("tvhome", out var home) ? home : string.Empty
                     };

+ 0 - 1
Jellyfin.Server/Startup.cs

@@ -4,7 +4,6 @@ using System.Net;
 using System.Net.Http;
 using System.Net.Http.Headers;
 using System.Net.Mime;
-using System.Runtime.InteropServices;
 using System.Text;
 using Jellyfin.Api.Middleware;
 using Jellyfin.MediaEncoding.Hls.Extensions;

+ 1 - 0
Jellyfin.sln.DotSettings

@@ -1,3 +1,4 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:Boolean x:Key="/Default/UserDictionary/Words/=Emby/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Jellyfin/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Playstate/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

+ 1 - 1
MediaBrowser.Common/Plugins/BasePluginOfT.cs

@@ -50,7 +50,7 @@ namespace MediaBrowser.Common.Plugins
             if (Version is not null && !Directory.Exists(dataFolderPath))
             {
                 // Try again with the version number appended to the folder name.
-                dataFolderPath += "_" + Version.ToString();
+                dataFolderPath += "_" + Version;
             }
 
             SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version);

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

@@ -801,16 +801,14 @@ namespace MediaBrowser.Controller.Entities
             {
                 return allowed.Contains(ChannelId);
             }
-            else
-            {
-                var collectionFolders = LibraryManager.GetCollectionFolders(this, allCollectionFolders);
 
-                foreach (var folder in collectionFolders)
+            var collectionFolders = LibraryManager.GetCollectionFolders(this, allCollectionFolders);
+
+            foreach (var folder in collectionFolders)
+            {
+                if (allowed.Contains(folder.Id))
                 {
-                    if (allowed.Contains(folder.Id))
-                    {
-                        return true;
-                    }
+                    return true;
                 }
             }
 

+ 2 - 4
MediaBrowser.Controller/LiveTv/LiveTvProgram.cs

@@ -197,10 +197,8 @@ namespace MediaBrowser.Controller.LiveTv
             {
                 return 2.0 / 3;
             }
-            else
-            {
-                return 16.0 / 9;
-            }
+
+            return 16.0 / 9;
         }
 
         public override string GetClientTypeName()

+ 0 - 2
MediaBrowser.Controller/Lyrics/LyricMetadata.cs

@@ -1,5 +1,3 @@
-using System;
-
 namespace MediaBrowser.Controller.Lyrics;
 
 /// <summary>

+ 100 - 101
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -138,14 +138,10 @@ namespace MediaBrowser.Controller.MediaEncoding
 
                 if (!string.IsNullOrEmpty(hwType)
                     && encodingOptions.EnableHardwareEncoding
-                    && codecMap.ContainsKey(hwType))
+                    && codecMap.TryGetValue(hwType, out var preferredEncoder)
+                    && _mediaEncoder.SupportsEncoder(preferredEncoder))
                 {
-                    var preferredEncoder = codecMap[hwType];
-
-                    if (_mediaEncoder.SupportsEncoder(preferredEncoder))
-                    {
-                        return preferredEncoder;
-                    }
+                    return preferredEncoder;
                 }
             }
 
@@ -561,7 +557,8 @@ namespace MediaBrowser.Controller.MediaEncoding
             {
                 return Array.FindIndex(_videoProfilesH264, x => string.Equals(x, profile, StringComparison.OrdinalIgnoreCase));
             }
-            else if (string.Equals("hevc", videoCodec, StringComparison.OrdinalIgnoreCase))
+
+            if (string.Equals("hevc", videoCodec, StringComparison.OrdinalIgnoreCase))
             {
                 return Array.FindIndex(_videoProfilesH265, x => string.Equals(x, profile, StringComparison.OrdinalIgnoreCase));
             }
@@ -842,39 +839,38 @@ namespace MediaBrowser.Controller.MediaEncoding
                 }
 
                 var filterDevArgs = GetFilterHwDeviceArgs(VaapiAlias);
+                var doOclTonemap = isHwTonemapAvailable && IsOpenclFullSupported();
 
-                if (isHwTonemapAvailable && IsOpenclFullSupported())
+                if (_mediaEncoder.IsVaapiDeviceInteliHD || _mediaEncoder.IsVaapiDeviceInteli965)
                 {
-                    if (_mediaEncoder.IsVaapiDeviceInteliHD || _mediaEncoder.IsVaapiDeviceInteli965)
+                    if (doOclTonemap && !isVaapiDecoder)
                     {
-                        if (!isVaapiDecoder)
-                        {
-                            args.Append(GetOpenclDeviceArgs(0, null, VaapiAlias, OpenclAlias));
-                            filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
-                        }
+                        args.Append(GetOpenclDeviceArgs(0, null, VaapiAlias, OpenclAlias));
+                        filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
                     }
-                    else if (_mediaEncoder.IsVaapiDeviceAmd)
+                }
+                else if (_mediaEncoder.IsVaapiDeviceAmd)
+                {
+                    if (IsVulkanFullSupported()
+                        && _mediaEncoder.IsVaapiDeviceSupportVulkanFmtModifier
+                        && Environment.OSVersion.Version >= _minKernelVersionAmdVkFmtModifier)
                     {
-                        if (!IsVulkanFullSupported()
-                            || !_mediaEncoder.IsVaapiDeviceSupportVulkanFmtModifier
-                            || Environment.OSVersion.Version < _minKernelVersionAmdVkFmtModifier)
-                        {
-                            args.Append(GetOpenclDeviceArgs(0, "Advanced Micro Devices", null, OpenclAlias));
-                            filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
-                        }
-                        else
-                        {
-                            // libplacebo wants an explicitly set vulkan filter device.
-                            args.Append(GetVulkanDeviceArgs(0, null, VaapiAlias, VulkanAlias));
-                            filterDevArgs = GetFilterHwDeviceArgs(VulkanAlias);
-                        }
+                        // libplacebo wants an explicitly set vulkan filter device.
+                        args.Append(GetVulkanDeviceArgs(0, null, VaapiAlias, VulkanAlias));
+                        filterDevArgs = GetFilterHwDeviceArgs(VulkanAlias);
                     }
-                    else
+                    else if (doOclTonemap)
                     {
-                        args.Append(GetOpenclDeviceArgs(0, null, null, OpenclAlias));
+                        // ROCm/ROCr OpenCL runtime
+                        args.Append(GetOpenclDeviceArgs(0, "Advanced Micro Devices", null, OpenclAlias));
                         filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
                     }
                 }
+                else if (doOclTonemap)
+                {
+                    args.Append(GetOpenclDeviceArgs(0, null, null, OpenclAlias));
+                    filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
+                }
 
                 args.Append(filterDevArgs);
             }
@@ -1114,19 +1110,19 @@ namespace MediaBrowser.Controller.MediaEncoding
             {
                 return "-bsf:v h264_mp4toannexb";
             }
-            else if (IsH265(stream))
+
+            if (IsH265(stream))
             {
                 return "-bsf:v hevc_mp4toannexb";
             }
-            else if (IsAAC(stream))
+
+            if (IsAAC(stream))
             {
                 // Convert adts header(mpegts) to asc header(mp4).
                 return "-bsf:a aac_adtstoasc";
             }
-            else
-            {
-                return null;
-            }
+
+            return null;
         }
 
         public static string GetAudioBitStreamArguments(EncodingJobInfo state, string segmentContainer, string mediaSourceContainer)
@@ -1204,10 +1200,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                 {
                     return FormattableString.Invariant($" -rc_mode CBR -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
                 }
-                else
-                {
-                    return FormattableString.Invariant($" -rc_mode VBR -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
-                }
+
+                return FormattableString.Invariant($" -rc_mode VBR -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
             }
 
             return FormattableString.Invariant($" -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
@@ -2619,8 +2613,8 @@ namespace MediaBrowser.Controller.MediaEncoding
 
             if (outputWidth > maximumWidth || outputHeight > maximumHeight)
             {
-                var scaleW = (double)maximumWidth / (double)outputWidth;
-                var scaleH = (double)maximumHeight / (double)outputHeight;
+                var scaleW = (double)maximumWidth / outputWidth;
+                var scaleH = (double)maximumHeight / outputHeight;
                 var scale = Math.Min(scaleW, scaleH);
                 outputWidth = Math.Min(maximumWidth, (int)(outputWidth * scale));
                 outputHeight = Math.Min(maximumHeight, (int)(outputHeight * scale));
@@ -2767,79 +2761,76 @@ namespace MediaBrowser.Controller.MediaEncoding
                             widthParam,
                             heightParam);
                 }
-                else
-                {
-                    return GetFixedSwScaleFilter(threedFormat, requestedWidth.Value, requestedHeight.Value);
-                }
+
+                return GetFixedSwScaleFilter(threedFormat, requestedWidth.Value, requestedHeight.Value);
             }
 
             // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
-            else if (requestedMaxWidth.HasValue && requestedMaxHeight.HasValue)
+
+            if (requestedMaxWidth.HasValue && requestedMaxHeight.HasValue)
             {
                 var maxWidthParam = requestedMaxWidth.Value.ToString(CultureInfo.InvariantCulture);
                 var maxHeightParam = requestedMaxHeight.Value.ToString(CultureInfo.InvariantCulture);
 
                 return string.Format(
-                        CultureInfo.InvariantCulture,
-                        "scale=trunc(min(max(iw\\,ih*a)\\,min({0}\\,{1}*a))/{2})*{2}:trunc(min(max(iw/a\\,ih)\\,min({0}/a\\,{1}))/2)*2",
-                        maxWidthParam,
-                        maxHeightParam,
-                        scaleVal);
+                    CultureInfo.InvariantCulture,
+                    "scale=trunc(min(max(iw\\,ih*a)\\,min({0}\\,{1}*a))/{2})*{2}:trunc(min(max(iw/a\\,ih)\\,min({0}/a\\,{1}))/2)*2",
+                    maxWidthParam,
+                    maxHeightParam,
+                    scaleVal);
             }
 
             // If a fixed width was requested
-            else if (requestedWidth.HasValue)
+            if (requestedWidth.HasValue)
             {
                 if (threedFormat.HasValue)
                 {
                     // This method can handle 0 being passed in for the requested height
                     return GetFixedSwScaleFilter(threedFormat, requestedWidth.Value, 0);
                 }
-                else
-                {
-                    var widthParam = requestedWidth.Value.ToString(CultureInfo.InvariantCulture);
 
-                    return string.Format(
-                            CultureInfo.InvariantCulture,
-                            "scale={0}:trunc(ow/a/2)*2",
-                            widthParam);
-                }
+                var widthParam = requestedWidth.Value.ToString(CultureInfo.InvariantCulture);
+
+                return string.Format(
+                    CultureInfo.InvariantCulture,
+                    "scale={0}:trunc(ow/a/2)*2",
+                    widthParam);
             }
 
             // If a fixed height was requested
-            else if (requestedHeight.HasValue)
+            if (requestedHeight.HasValue)
             {
                 var heightParam = requestedHeight.Value.ToString(CultureInfo.InvariantCulture);
 
                 return string.Format(
-                        CultureInfo.InvariantCulture,
-                        "scale=trunc(oh*a/{1})*{1}:{0}",
-                        heightParam,
-                        scaleVal);
+                    CultureInfo.InvariantCulture,
+                    "scale=trunc(oh*a/{1})*{1}:{0}",
+                    heightParam,
+                    scaleVal);
             }
 
             // If a max width was requested
-            else if (requestedMaxWidth.HasValue)
+            if (requestedMaxWidth.HasValue)
             {
                 var maxWidthParam = requestedMaxWidth.Value.ToString(CultureInfo.InvariantCulture);
 
                 return string.Format(
-                        CultureInfo.InvariantCulture,
-                        "scale=trunc(min(max(iw\\,ih*a)\\,{0})/{1})*{1}:trunc(ow/a/2)*2",
-                        maxWidthParam,
-                        scaleVal);
+                    CultureInfo.InvariantCulture,
+                    "scale=trunc(min(max(iw\\,ih*a)\\,{0})/{1})*{1}:trunc(ow/a/2)*2",
+                    maxWidthParam,
+                    scaleVal);
             }
 
             // If a max height was requested
-            else if (requestedMaxHeight.HasValue)
+            if (requestedMaxHeight.HasValue)
             {
                 var maxHeightParam = requestedMaxHeight.Value.ToString(CultureInfo.InvariantCulture);
 
                 return string.Format(
-                        CultureInfo.InvariantCulture,
-                        "scale=trunc(oh*a/{1})*{1}:min(max(iw/a\\,ih)\\,{0})",
-                        maxHeightParam,
-                        scaleVal);
+                    CultureInfo.InvariantCulture,
+                    "scale=trunc(oh*a/{1})*{1}:min(max(iw/a\\,ih)\\,{0})",
+                    maxHeightParam,
+                    scaleVal);
             }
 
             return string.Empty;
@@ -2913,18 +2904,21 @@ namespace MediaBrowser.Controller.MediaEncoding
                     "yadif_cuda={0}:-1:0",
                     doubleRateDeint ? "1" : "0");
             }
-            else if (hwDeintSuffix.Contains("vaapi", StringComparison.OrdinalIgnoreCase))
+
+            if (hwDeintSuffix.Contains("vaapi", StringComparison.OrdinalIgnoreCase))
             {
                 return string.Format(
                     CultureInfo.InvariantCulture,
                     "deinterlace_vaapi=rate={0}",
                     doubleRateDeint ? "field" : "frame");
             }
-            else if (hwDeintSuffix.Contains("qsv", StringComparison.OrdinalIgnoreCase))
+
+            if (hwDeintSuffix.Contains("qsv", StringComparison.OrdinalIgnoreCase))
             {
                 return "deinterlace_qsv=mode=2";
             }
-            else if (hwDeintSuffix.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase))
+
+            if (hwDeintSuffix.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase))
             {
                 return string.Format(
                     CultureInfo.InvariantCulture,
@@ -2955,7 +2949,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                         options.VppTonemappingBrightness,
                         options.VppTonemappingContrast);
             }
-            else if (string.Equals(hwTonemapSuffix, "vulkan", StringComparison.OrdinalIgnoreCase))
+
+            if (string.Equals(hwTonemapSuffix, "vulkan", StringComparison.OrdinalIgnoreCase))
             {
                 args = "libplacebo=format={1}:tonemapping={2}:color_primaries=bt709:color_trc=bt709:colorspace=bt709:peak_detect=0:upscaler=none:downscaler=none";
 
@@ -4269,7 +4264,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                 // sw => hw
                 if (doVkTonemap)
                 {
-                    mainFilters.Add("hwupload_vaapi");
+                    mainFilters.Add("hwupload=derive_device=vaapi");
+                    mainFilters.Add("format=vaapi");
                     mainFilters.Add("hwmap=derive_device=vulkan");
                     mainFilters.Add("format=vulkan");
                 }
@@ -4380,12 +4376,15 @@ namespace MediaBrowser.Controller.MediaEncoding
 
                     // prefer vaapi hwupload to vulkan hwupload,
                     // Mesa RADV does not support a dedicated transfer queue.
-                    subFilters.Add("hwupload_vaapi");
+                    subFilters.Add("hwupload=derive_device=vaapi");
+                    subFilters.Add("format=vaapi");
                     subFilters.Add("hwmap=derive_device=vulkan");
                     subFilters.Add("format=vulkan");
 
                     overlayFilters.Add("overlay_vulkan=eof_action=endall:shortest=1:repeatlast=0");
-                    overlayFilters.Add("scale_vulkan=format=nv12");
+
+                    // TODO: figure out why libplacebo can sync without vaSyncSurface VPP support in radeonsi.
+                    overlayFilters.Add("libplacebo=format=nv12:apply_filmgrain=0:apply_dolbyvision=0:upscaler=none:downscaler=none:dithering=none");
 
                     // OUTPUT vaapi(nv12/bgra) surface(vram)
                     // reverse-mapping via vaapi-vulkan interop.
@@ -4827,26 +4826,27 @@ namespace MediaBrowser.Controller.MediaEncoding
                 {
                     return videoStream.BitDepth.Value;
                 }
-                else if (string.Equals(videoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase)
-                         || string.Equals(videoStream.PixelFormat, "yuvj420p", StringComparison.OrdinalIgnoreCase)
-                         || string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase))
+
+                if (string.Equals(videoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(videoStream.PixelFormat, "yuvj420p", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase))
                 {
                     return 8;
                 }
-                else if (string.Equals(videoStream.PixelFormat, "yuv420p10le", StringComparison.OrdinalIgnoreCase)
-                         || string.Equals(videoStream.PixelFormat, "yuv444p10le", StringComparison.OrdinalIgnoreCase))
+
+                if (string.Equals(videoStream.PixelFormat, "yuv420p10le", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(videoStream.PixelFormat, "yuv444p10le", StringComparison.OrdinalIgnoreCase))
                 {
                     return 10;
                 }
-                else if (string.Equals(videoStream.PixelFormat, "yuv420p12le", StringComparison.OrdinalIgnoreCase)
-                         || string.Equals(videoStream.PixelFormat, "yuv444p12le", StringComparison.OrdinalIgnoreCase))
+
+                if (string.Equals(videoStream.PixelFormat, "yuv420p12le", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(videoStream.PixelFormat, "yuv444p12le", StringComparison.OrdinalIgnoreCase))
                 {
                     return 12;
                 }
-                else
-                {
-                    return 8;
-                }
+
+                return 8;
             }
 
             return 0;
@@ -5078,11 +5078,9 @@ namespace MediaBrowser.Controller.MediaEncoding
                         return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty)
                             + (nvdecNoInternalCopy ? " -hwaccel_flags +unsafe_output" : string.Empty) + " -threads 1" + (isAv1 ? " -c:v av1" : string.Empty);
                     }
-                    else
-                    {
-                        // cuvid decoder doesn't have threading issue.
-                        return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty);
-                    }
+
+                    // cuvid decoder doesn't have threading issue.
+                    return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty);
                 }
             }
 
@@ -5440,7 +5438,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                 // Automatically set thread count
                 return mustSetThreadCount ? Math.Max(Environment.ProcessorCount - 1, 1) : 0;
             }
-            else if (threads >= Environment.ProcessorCount)
+
+            if (threads >= Environment.ProcessorCount)
             {
                 return Environment.ProcessorCount;
             }

+ 0 - 1
MediaBrowser.Controller/Session/ISessionManager.cs

@@ -7,7 +7,6 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 using Jellyfin.Data.Entities.Security;
-using Jellyfin.Data.Events;
 using MediaBrowser.Controller.Authentication;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Session;

+ 0 - 1
MediaBrowser.Controller/Subtitles/ISubtitleManager.cs

@@ -1,7 +1,6 @@
 #pragma warning disable CS1591
 
 using System;
-using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Entities;

+ 3 - 5
MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs

@@ -533,11 +533,9 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates
                     _logger.LogWarning("Session {SessionId} is seeking to wrong position, correcting.", session.Id);
                     return;
                 }
-                else
-                {
-                    // Session is ready.
-                    context.SetBuffering(session, false);
-                }
+
+                // Session is ready.
+                context.SetBuffering(session, false);
 
                 if (!context.IsBuffering())
                 {

+ 11 - 18
MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs

@@ -313,17 +313,13 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
 
                     return true;
                 }
-                else
-                {
-                    // Restoring playing item.
-                    SetPlayingItemByPlaylistId(playingItem.PlaylistItemId);
-                    return false;
-                }
-            }
-            else
-            {
+
+                // Restoring playing item.
+                SetPlayingItemByPlaylistId(playingItem.PlaylistItemId);
                 return false;
             }
+
+            return false;
         }
 
         /// <summary>
@@ -528,10 +524,8 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
             {
                 return _shuffledPlaylist;
             }
-            else
-            {
-                return _sortedPlaylist;
-            }
+
+            return _sortedPlaylist;
         }
 
         /// <summary>
@@ -544,14 +538,13 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
             {
                 return null;
             }
-            else if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
+
+            if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
             {
                 return _shuffledPlaylist[PlayingItemIndex];
             }
-            else
-            {
-                return _sortedPlaylist[PlayingItemIndex];
-            }
+
+            return _sortedPlaylist[PlayingItemIndex];
         }
     }
 }

+ 1 - 4
MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs

@@ -73,10 +73,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
             foreach (var info in idInfos)
             {
                 var id = info.Key + "Id";
-                if (!_validProviderIds.ContainsKey(id))
-                {
-                    _validProviderIds.Add(id, info.Key);
-                }
+                _validProviderIds.TryAdd(id, info.Key);
             }
 
             // Additional Mappings

+ 1 - 1
MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs

@@ -375,7 +375,7 @@ namespace MediaBrowser.LocalMetadata.Savers
                     await writer.WriteStartElementAsync(null, "Person", null).ConfigureAwait(false);
                     await writer.WriteElementStringAsync(null, "Name", null, person.Name).ConfigureAwait(false);
                     await writer.WriteElementStringAsync(null, "Type", null, person.Type.ToString()).ConfigureAwait(false);
-                    await writer.WriteElementStringAsync(null, "Role", null, person.Role.ToString()).ConfigureAwait(false);
+                    await writer.WriteElementStringAsync(null, "Role", null, person.Role).ConfigureAwait(false);
 
                     if (person.SortOrder.HasValue)
                     {

+ 4 - 8
MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs

@@ -231,10 +231,8 @@ namespace MediaBrowser.MediaEncoding.Attachments
                 throw new InvalidOperationException(
                     string.Format(CultureInfo.InvariantCulture, "ffmpeg attachment extraction failed for {0} to {1}", inputPath, outputPath));
             }
-            else
-            {
-                _logger.LogInformation("ffmpeg attachment extraction completed for {Path} to {Path}", inputPath, outputPath);
-            }
+
+            _logger.LogInformation("ffmpeg attachment extraction completed for {InputPath} to {OutputPath}", inputPath, outputPath);
         }
 
         private async Task<Stream> GetAttachmentStream(
@@ -376,10 +374,8 @@ namespace MediaBrowser.MediaEncoding.Attachments
                 throw new InvalidOperationException(
                     string.Format(CultureInfo.InvariantCulture, "ffmpeg attachment extraction failed for {0} to {1}", inputPath, outputPath));
             }
-            else
-            {
-                _logger.LogInformation("ffmpeg attachment extraction completed for {Path} to {Path}", inputPath, outputPath);
-            }
+
+            _logger.LogInformation("ffmpeg attachment extraction completed for {InputPath} to {OutputPath}", inputPath, outputPath);
         }
 
         private string GetAttachmentCachePath(string mediaPath, MediaSourceInfo mediaSource, int attachmentStreamIndex)

+ 1 - 2
MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs

@@ -1,4 +1,3 @@
-using System;
 using System.IO;
 using System.Linq;
 using BDInfo.IO;
@@ -105,7 +104,7 @@ public class BdInfoDirectoryInfo : IDirectoryInfo
                 _impl.FullName,
                 new[] { searchPattern },
                 false,
-                (searchOption & SearchOption.AllDirectories) == SearchOption.AllDirectories)
+                searchOption == SearchOption.AllDirectories)
             .Select(x => new BdInfoFileInfo(x))
             .ToArray();
     }

+ 4 - 4
MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs

@@ -217,12 +217,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
                 return false;
             }
-            else if (version < MinVersion) // Version is below what we recommend
+
+            if (version < MinVersion) // Version is below what we recommend
             {
                 _logger.LogWarning("FFmpeg validation: The minimum recommended version is {MinVersion}", MinVersion);
                 return false;
             }
-            else if (MaxVersion is not null && version > MaxVersion) // Version is above what we recommend
+
+            if (MaxVersion is not null && version > MaxVersion) // Version is above what we recommend
             {
                 _logger.LogWarning("FFmpeg validation: The maximum recommended version is {MaxVersion}", MaxVersion);
                 return false;
@@ -491,7 +493,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             var found = Regex
                 .Matches(output, @"^\s\S{6}\s(?<codec>[\w|-]+)\s+.+$", RegexOptions.Multiline)
-                .Cast<Match>()
                 .Select(x => x.Groups["codec"].Value)
                 .Where(x => required.Contains(x));
 
@@ -520,7 +521,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             var found = Regex
                 .Matches(output, @"^\s\S{3}\s(?<filter>[\w|-]+)\s+.+$", RegexOptions.Multiline)
-                .Cast<Match>()
                 .Select(x => x.Groups["filter"].Value)
                 .Where(x => _requiredFilters.Contains(x));
 

+ 3 - 5
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -449,7 +449,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 {
                     try
                     {
-                        _logger.LogInformation("Deleting converted subtitle due to failure: ", outputPath);
+                        _logger.LogInformation("Deleting converted subtitle due to failure: {Path}", outputPath);
                         _fileSystem.DeleteFile(outputPath);
                     }
                     catch (IOException ex)
@@ -624,10 +624,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 throw new FfmpegException(
                     string.Format(CultureInfo.InvariantCulture, "ffmpeg subtitle extraction failed for {0} to {1}", inputPath, outputPath));
             }
-            else
-            {
-                _logger.LogInformation("ffmpeg subtitle extraction completed for {InputPath} to {OutputPath}", inputPath, outputPath);
-            }
+
+            _logger.LogInformation("ffmpeg subtitle extraction completed for {InputPath} to {OutputPath}", inputPath, outputPath);
 
             if (string.Equals(outputCodec, "ass", StringComparison.OrdinalIgnoreCase))
             {

+ 0 - 1
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -2,7 +2,6 @@
 #pragma warning disable CA1819
 
 using System;
-using System.Collections.Generic;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Updates;

+ 2 - 1
MediaBrowser.Model/Cryptography/PasswordHash.cs

@@ -80,7 +80,8 @@ namespace MediaBrowser.Model.Cryptography
             {
                 throw new FormatException("Hash string must contain a valid id");
             }
-            else if (nextSegment == -1)
+
+            if (nextSegment == -1)
             {
                 return new PasswordHash(hashString.ToString(), Array.Empty<byte>());
             }

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

@@ -1075,31 +1075,38 @@ namespace MediaBrowser.Model.Dlna
             {
                 return 128000;
             }
-            else if (totalBitrate <= 2000000)
+
+            if (totalBitrate <= 2000000)
             {
                 return 384000;
             }
-            else if (totalBitrate <= 3000000)
+
+            if (totalBitrate <= 3000000)
             {
                 return 448000;
             }
-            else if (totalBitrate <= 4000000)
+
+            if (totalBitrate <= 4000000)
             {
                 return 640000;
             }
-            else if (totalBitrate <= 5000000)
+
+            if (totalBitrate <= 5000000)
             {
                 return 768000;
             }
-            else if (totalBitrate <= 10000000)
+
+            if (totalBitrate <= 10000000)
             {
                 return 1536000;
             }
-            else if (totalBitrate <= 15000000)
+
+            if (totalBitrate <= 15000000)
             {
                 return 2304000;
             }
-            else if (totalBitrate <= 20000000)
+
+            if (totalBitrate <= 20000000)
             {
                 return 3584000;
             }
@@ -1443,7 +1450,8 @@ namespace MediaBrowser.Model.Dlna
                 {
                     return false;
                 }
-                else if (ContainerProfile.ContainsContainer(normalizedContainers, "mkv")
+
+                if (ContainerProfile.ContainsContainer(normalizedContainers, "mkv")
                     || ContainerProfile.ContainsContainer(normalizedContainers, "matroska"))
                 {
                     return true;

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

@@ -430,7 +430,7 @@ namespace MediaBrowser.Model.Dlna
 
                     return totalBitrate.HasValue ?
                         Convert.ToInt64(totalBitrate.Value * totalSeconds) :
-                        (long?)null;
+                        null;
                 }
 
                 return null;

+ 4 - 2
MediaBrowser.Model/MediaInfo/AudioCodec.cs

@@ -17,11 +17,13 @@ namespace MediaBrowser.Model.MediaInfo
             {
                 return "Dolby Digital";
             }
-            else if (string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase))
+
+            if (string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase))
             {
                 return "Dolby Digital+";
             }
-            else if (string.Equals(codec, "dca", StringComparison.OrdinalIgnoreCase))
+
+            if (string.Equals(codec, "dca", StringComparison.OrdinalIgnoreCase))
             {
                 return "DTS";
             }

+ 0 - 1
MediaBrowser.Providers/Lyric/TxtLyricProvider.cs

@@ -1,6 +1,5 @@
 using System.Collections.Generic;
 using System.IO;
-using System.Linq;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Lyrics;

+ 1 - 4
MediaBrowser.Providers/Manager/MetadataService.cs

@@ -882,10 +882,7 @@ namespace MediaBrowser.Providers.Manager
                 var key = providerId.Key;
 
                 // Don't replace existing Id's.
-                if (!lookupInfo.ProviderIds.ContainsKey(key))
-                {
-                    lookupInfo.ProviderIds[key] = providerId.Value;
-                }
+                lookupInfo.ProviderIds.TryAdd(key, providerId.Value);
             }
         }
 

+ 1 - 4
MediaBrowser.Providers/Manager/ProviderManager.cs

@@ -780,10 +780,7 @@ namespace MediaBrowser.Providers.Manager
                         {
                             foreach (var providerId in result.ProviderIds)
                             {
-                                if (!existingMatch.ProviderIds.ContainsKey(providerId.Key))
-                                {
-                                    existingMatch.ProviderIds.Add(providerId.Key, providerId.Value);
-                                }
+                                existingMatch.ProviderIds.TryAdd(providerId.Key, providerId.Value);
                             }
 
                             if (string.IsNullOrWhiteSpace(existingMatch.ImageUrl))

+ 1 - 4
MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs

@@ -100,10 +100,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
             foreach (var info in idInfos)
             {
                 var id = info.Key + "Id";
-                if (!_validProviderIds.ContainsKey(id))
-                {
-                    _validProviderIds.Add(id, info.Key);
-                }
+                _validProviderIds.TryAdd(id, info.Key);
             }
 
             // Additional Mappings

+ 2 - 4
RSSDP/HttpParserBase.cs

@@ -221,10 +221,8 @@ namespace Rssdp.Infrastructure
             {
                 return trimmedSegment.Substring(1, trimmedSegment.Length - 2);
             }
-            else
-            {
-                return trimmedSegment;
-            }
+
+            return trimmedSegment;
         }
     }
 }

+ 1 - 2
RSSDP/HttpRequestParser.cs

@@ -64,8 +64,7 @@ namespace Rssdp.Infrastructure
             }
 
             message.Method = new HttpMethod(parts[0].Trim());
-            Uri requestUri;
-            if (Uri.TryCreate(parts[1].Trim(), UriKind.RelativeOrAbsolute, out requestUri))
+            if (Uri.TryCreate(parts[1].Trim(), UriKind.RelativeOrAbsolute, out var requestUri))
             {
                 message.RequestUri = requestUri;
             }

+ 1 - 2
RSSDP/HttpResponseParser.cs

@@ -77,8 +77,7 @@ namespace Rssdp.Infrastructure
 
             message.Version = ParseHttpVersion(parts[0].Trim());
 
-            int statusCode = -1;
-            if (!Int32.TryParse(parts[1].Trim(), out statusCode))
+            if (!Int32.TryParse(parts[1].Trim(), out var statusCode))
             {
                 throw new ArgumentException("data status line is invalid. Status code is not a valid integer.", nameof(data));
             }

+ 2 - 4
RSSDP/SsdpDevice.cs

@@ -171,10 +171,8 @@ namespace Rssdp
                 {
                     return "uuid:" + this.Uuid;
                 }
-                else
-                {
-                    return _Udn;
-                }
+
+                return _Udn;
             }
 
             set

+ 4 - 8
RSSDP/SsdpDeviceLocator.cs

@@ -483,8 +483,7 @@ namespace Rssdp.Infrastructure
                 }
             }
 
-            Uri retVal;
-            Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out retVal);
+            Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out var retVal);
             return retVal;
         }
 
@@ -501,8 +500,7 @@ namespace Rssdp.Infrastructure
                 }
             }
 
-            Uri retVal;
-            Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out retVal);
+            Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out var retVal);
             return retVal;
         }
 
@@ -587,10 +585,8 @@ namespace Rssdp.Infrastructure
             {
                 return OneSecond;
             }
-            else
-            {
-                return searchWaitTime.Subtract(OneSecond);
-            }
+
+            return searchWaitTime.Subtract(OneSecond);
         }
 
         private DiscoveredSsdpDevice FindExistingDeviceNotification(IEnumerable<DiscoveredSsdpDevice> devices, string notificationType, string usn)

+ 5 - 9
RSSDP/SsdpDevicePublisher.cs

@@ -244,7 +244,6 @@ namespace Rssdp.Infrastructure
             // Wait on random interval up to MX, as per SSDP spec.
             // Also, as per UPnP 1.1/SSDP spec ignore missing/bank MX header. If over 120, assume random value between 0 and 120.
             // Using 16 as minimum as that's often the minimum system clock frequency anyway.
-            int maxWaitInterval = 0;
             if (String.IsNullOrEmpty(mx))
             {
                 // Windows Explorer is poorly behaved and doesn't supply an MX header value.
@@ -254,7 +253,7 @@ namespace Rssdp.Infrastructure
                 // return;
             }
 
-            if (!Int32.TryParse(mx, out maxWaitInterval) || maxWaitInterval <= 0)
+            if (!Int32.TryParse(mx, out var maxWaitInterval) || maxWaitInterval <= 0)
             {
                 return;
             }
@@ -572,17 +571,14 @@ namespace Rssdp.Infrastructure
             {
                 return nonzeroCacheLifetimesQuery.Min();
             }
-            else
-            {
-                return TimeSpan.Zero;
-            }
+
+            return TimeSpan.Zero;
         }
 
         private string GetFirstHeaderValue(System.Net.Http.Headers.HttpRequestHeaders httpRequestHeaders, string headerName)
         {
             string retVal = null;
-            IEnumerable<String> values = null;
-            if (httpRequestHeaders.TryGetValues(headerName, out values) && values != null)
+            if (httpRequestHeaders.TryGetValues(headerName, out var values) && values != null)
             {
                 retVal = values.FirstOrDefault();
             }
@@ -644,7 +640,7 @@ namespace Rssdp.Infrastructure
 
             public string Key
             {
-                get { return this.SearchTarget + ":" + this.EndPoint.ToString(); }
+                get { return this.SearchTarget + ":" + this.EndPoint; }
             }
 
             public bool IsOld()

+ 1 - 1
deployment/Dockerfile.centos.amd64

@@ -13,7 +13,7 @@ RUN yum update -yq \
   && yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget
 
 # Install DotNET SDK
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/bda88810-e1a6-4cf0-8139-7fd7fe7b2c7a/7a9ffa3e12e5f1c3d8b640e326c1eb14/dotnet-sdk-7.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/ebfd0bf8-79bd-480a-9e81-0b217463738d/9adc6bf0614ce02670101e278a2d8555/dotnet-sdk-7.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
   && mkdir -p dotnet-sdk \
   && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
   && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet

+ 1 - 1
deployment/Dockerfile.fedora.amd64

@@ -12,7 +12,7 @@ RUN dnf update -yq \
   && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget make
 
 # Install DotNET SDK
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/bda88810-e1a6-4cf0-8139-7fd7fe7b2c7a/7a9ffa3e12e5f1c3d8b640e326c1eb14/dotnet-sdk-7.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/ebfd0bf8-79bd-480a-9e81-0b217463738d/9adc6bf0614ce02670101e278a2d8555/dotnet-sdk-7.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
   && mkdir -p dotnet-sdk \
   && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
   && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet

+ 1 - 1
deployment/Dockerfile.ubuntu.amd64

@@ -17,7 +17,7 @@ RUN apt-get update -yqq \
     libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
 
 # Install dotnet repository
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/bda88810-e1a6-4cf0-8139-7fd7fe7b2c7a/7a9ffa3e12e5f1c3d8b640e326c1eb14/dotnet-sdk-7.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/ebfd0bf8-79bd-480a-9e81-0b217463738d/9adc6bf0614ce02670101e278a2d8555/dotnet-sdk-7.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
   && mkdir -p dotnet-sdk \
   && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
   && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet

+ 1 - 1
deployment/Dockerfile.ubuntu.arm64

@@ -16,7 +16,7 @@ RUN apt-get update -yqq \
     mmv build-essential lsb-release
 
 # Install dotnet repository
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/bda88810-e1a6-4cf0-8139-7fd7fe7b2c7a/7a9ffa3e12e5f1c3d8b640e326c1eb14/dotnet-sdk-7.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/ebfd0bf8-79bd-480a-9e81-0b217463738d/9adc6bf0614ce02670101e278a2d8555/dotnet-sdk-7.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
   && mkdir -p dotnet-sdk \
   && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
   && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet

+ 1 - 1
deployment/Dockerfile.ubuntu.armhf

@@ -16,7 +16,7 @@ RUN apt-get update -yqq \
     mmv build-essential lsb-release
 
 # Install dotnet repository
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/bda88810-e1a6-4cf0-8139-7fd7fe7b2c7a/7a9ffa3e12e5f1c3d8b640e326c1eb14/dotnet-sdk-7.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/ebfd0bf8-79bd-480a-9e81-0b217463738d/9adc6bf0614ce02670101e278a2d8555/dotnet-sdk-7.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
   && mkdir -p dotnet-sdk \
   && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
   && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet

+ 14 - 4
src/Jellyfin.Drawing.Skia/SkiaEncoder.cs

@@ -120,8 +120,18 @@ public class SkiaEncoder : IImageEncoder
         if (extension.Equals(".svg", StringComparison.OrdinalIgnoreCase))
         {
             var svg = new SKSvg();
-            svg.Load(path);
-            return new ImageDimensions(Convert.ToInt32(svg.Picture.CullRect.Width), Convert.ToInt32(svg.Picture.CullRect.Height));
+            try
+            {
+                svg.Load(path);
+                return new ImageDimensions(Convert.ToInt32(svg.Picture.CullRect.Width), Convert.ToInt32(svg.Picture.CullRect.Height));
+            }
+            catch (FormatException skiaColorException)
+            {
+                // This exception is known to be thrown on vector images that define custom styles
+                // Skia SVG is not able to handle that and as the repository is quite stale and has not received updates we just catch them
+                _logger.LogDebug(skiaColorException, "There was a issue loading the requested svg file");
+                return default;
+            }
         }
 
         using var codec = SKCodec.Create(path, out SKCodecResult result);
@@ -132,10 +142,10 @@ public class SkiaEncoder : IImageEncoder
                 return new ImageDimensions(info.Width, info.Height);
             case SKCodecResult.Unimplemented:
                 _logger.LogDebug("Image format not supported: {FilePath}", path);
-                return new ImageDimensions(0, 0);
+                return default;
             default:
                 _logger.LogError("Unable to determine image dimensions for {FilePath}: {SkCodecResult}", path, result);
-                return new ImageDimensions(0, 0);
+                return default;
         }
     }
 

+ 10 - 5
src/Jellyfin.Extensions/AlphanumericComparator.cs

@@ -20,11 +20,13 @@ namespace Jellyfin.Extensions
             {
                 return 0;
             }
-            else if (s1 is null)
+
+            if (s1 is null)
             {
                 return -1;
             }
-            else if (s2 is null)
+
+            if (s2 is null)
             {
                 return 1;
             }
@@ -37,11 +39,13 @@ namespace Jellyfin.Extensions
             {
                 return 0;
             }
-            else if (len1 == 0)
+
+            if (len1 == 0)
             {
                 return -1;
             }
-            else if (len2 == 0)
+
+            if (len2 == 0)
             {
                 return 1;
             }
@@ -82,7 +86,8 @@ namespace Jellyfin.Extensions
                     {
                         return -1;
                     }
-                    else if (span1Len > span2Len)
+
+                    if (span1Len > span2Len)
                     {
                         return 1;
                     }

+ 0 - 1
src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Buffers;
 using System.Buffers.Text;
-using System.Linq;
 using System.Text.Json;
 using System.Text.Json.Serialization;
 

+ 0 - 2
src/Jellyfin.Extensions/StringExtensions.cs

@@ -1,6 +1,4 @@
 using System;
-using System.Globalization;
-using System.Text;
 using System.Text.RegularExpressions;
 
 namespace Jellyfin.Extensions

+ 0 - 1
tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs

@@ -2,7 +2,6 @@ using System.Collections.Generic;
 using System.Threading.Tasks;
 using AutoFixture;
 using AutoFixture.AutoMoq;
-using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
 using Jellyfin.Api.Auth.FirstTimeSetupPolicy;
 using Jellyfin.Api.Constants;
 using MediaBrowser.Common.Configuration;

+ 0 - 1
tests/Jellyfin.Api.Tests/Controllers/ImageControllerTests.cs

@@ -1,4 +1,3 @@
-using System;
 using Jellyfin.Api.Controllers;
 using Xunit;
 

+ 3 - 3
tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs

@@ -162,7 +162,7 @@ namespace Jellyfin.Model.Tests
         [InlineData("Tizen4-4K-5.1", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
         [InlineData("Tizen4-4K-5.1", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
         [InlineData("Tizen4-4K-5.1", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)]
-        public async Task BuildVideoItemSimple(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = (TranscodeReason)0, string transcodeMode = "DirectStream", string transcodeProtocol = "")
+        public async Task BuildVideoItemSimple(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = default, string transcodeMode = "DirectStream", string transcodeProtocol = "")
         {
             var options = await GetMediaOptions(deviceName, mediaSource);
             BuildVideoItemSimpleTest(options, playMethod, why, transcodeMode, transcodeProtocol);
@@ -260,7 +260,7 @@ namespace Jellyfin.Model.Tests
         [InlineData("Tizen4-4K-5.1", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
         [InlineData("Tizen4-4K-5.1", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
         [InlineData("Tizen4-4K-5.1", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)]
-        public async Task BuildVideoItemWithFirstExplicitStream(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = (TranscodeReason)0, string transcodeMode = "DirectStream", string transcodeProtocol = "")
+        public async Task BuildVideoItemWithFirstExplicitStream(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = default, string transcodeMode = "DirectStream", string transcodeProtocol = "")
         {
             var options = await GetMediaOptions(deviceName, mediaSource);
             options.AudioStreamIndex = 1;
@@ -296,7 +296,7 @@ namespace Jellyfin.Model.Tests
         // Tizen 4 4K 5.1
         [InlineData("Tizen4-4K-5.1", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")]
         [InlineData("Tizen4-4K-5.1", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")]
-        public async Task BuildVideoItemWithDirectPlayExplicitStreams(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = (TranscodeReason)0, string transcodeMode = "DirectStream", string transcodeProtocol = "")
+        public async Task BuildVideoItemWithDirectPlayExplicitStreams(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = default, string transcodeMode = "DirectStream", string transcodeProtocol = "")
         {
             var options = await GetMediaOptions(deviceName, mediaSource);
             var streamCount = options.MediaSources[0].MediaStreams.Count;

+ 2 - 5
tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs

@@ -238,9 +238,6 @@ namespace Jellyfin.Providers.Tests.Manager
                     }
                 };
 
-            object? result;
-            List<PersonInfo> actual;
-
             // overwrite provider id
             var overwriteNewValue = new List<PersonInfo>
             {
@@ -249,9 +246,9 @@ namespace Jellyfin.Providers.Tests.Manager
                     Name = "Name 2"
                 }
             };
-            Assert.False(TestMergeBaseItemDataPerson(GetOldValue(), overwriteNewValue, null, false, out result));
+            Assert.False(TestMergeBaseItemDataPerson(GetOldValue(), overwriteNewValue, null, false, out var result));
             // People not already in target are not merged into it from source
-            actual = (List<PersonInfo>)result!;
+            List<PersonInfo> actual = (List<PersonInfo>)result!;
             Assert.Single(actual);
             Assert.Equal("Name 1", actual[0].Name);
 

+ 1 - 1
tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs

@@ -90,7 +90,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
             };
 
             _parser.Fetch(result, path, CancellationToken.None);
-            var item = (Series)result.Item;
+            var item = result.Item;
 
             Assert.Equal(id, item.ProviderIds[provider]);
         }