Browse Source

Merge remote-tracking branch 'upstream/master' into client-logger

Cody Robibero 3 years ago
parent
commit
f444e93a56
100 changed files with 514 additions and 123 deletions
  1. 121 0
      .github/workflows/openapi.yml
  2. 2 2
      Emby.Dlna/DlnaManager.cs
  3. 16 0
      Emby.Naming/Common/NamingOptions.cs
  4. 29 0
      Emby.Naming/TV/SeriesInfo.cs
  5. 61 0
      Emby.Naming/TV/SeriesPathParser.cs
  6. 19 0
      Emby.Naming/TV/SeriesPathParserResult.cs
  7. 49 0
      Emby.Naming/TV/SeriesResolver.cs
  8. 16 23
      Emby.Server.Implementations/Library/MediaStreamSelector.cs
  9. 5 3
      Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
  10. 6 6
      Emby.Server.Implementations/Localization/Core/ar.json
  11. 1 1
      Emby.Server.Implementations/Localization/Core/da.json
  12. 8 8
      Emby.Server.Implementations/Localization/Core/eo.json
  13. 1 1
      Emby.Server.Implementations/Localization/Core/fr.json
  14. 4 2
      Emby.Server.Implementations/Localization/Core/pt-PT.json
  15. 18 0
      Emby.Server.Implementations/Localization/LocalizationManager.cs
  16. 1 1
      Jellyfin.Api/Controllers/DlnaController.cs
  17. 1 1
      Jellyfin.Server/Jellyfin.Server.csproj
  18. 2 1
      MediaBrowser.Controller/Dlna/IDlnaManager.cs
  19. 1 3
      MediaBrowser.Controller/Providers/IExternalId.cs
  20. 1 1
      MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
  21. 2 2
      MediaBrowser.Model/Providers/ExternalIdInfo.cs
  22. 2 0
      MediaBrowser.Providers/Manager/ImageSaver.cs
  23. 2 0
      MediaBrowser.Providers/Manager/ItemImageProvider.cs
  24. 2 0
      MediaBrowser.Providers/Manager/MetadataService.cs
  25. 2 0
      MediaBrowser.Providers/Manager/ProviderManager.cs
  26. 2 0
      MediaBrowser.Providers/Manager/ProviderUtils.cs
  27. 1 1
      MediaBrowser.Providers/Manager/RefreshResult.cs
  28. 0 1
      MediaBrowser.Providers/MediaBrowser.Providers.csproj
  29. 2 0
      MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
  30. 2 0
      MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs
  31. 2 0
      MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
  32. 2 0
      MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
  33. 2 0
      MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
  34. 2 0
      MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
  35. 2 0
      MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
  36. 2 0
      MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
  37. 1 1
      MediaBrowser.Providers/Movies/ImdbExternalId.cs
  38. 1 1
      MediaBrowser.Providers/Movies/ImdbPersonExternalId.cs
  39. 6 6
      MediaBrowser.Providers/Music/AlbumInfoExtensions.cs
  40. 2 2
      MediaBrowser.Providers/Music/AlbumMetadataService.cs
  41. 1 1
      MediaBrowser.Providers/Music/ImvdbId.cs
  42. 2 0
      MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs
  43. 1 1
      MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumExternalId.cs
  44. 2 0
      MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumImageProvider.cs
  45. 2 0
      MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs
  46. 1 1
      MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistExternalId.cs
  47. 2 0
      MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs
  48. 2 0
      MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs
  49. 1 1
      MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherAlbumExternalId.cs
  50. 1 1
      MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherArtistExternalId.cs
  51. 1 1
      MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs
  52. 1 0
      MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs
  53. 4 15
      MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs
  54. 1 1
      MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumArtistExternalId.cs
  55. 1 1
      MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumExternalId.cs
  56. 2 0
      MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
  57. 1 1
      MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistExternalId.cs
  58. 2 0
      MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs
  59. 1 1
      MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzOtherArtistExternalId.cs
  60. 1 1
      MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzReleaseGroupExternalId.cs
  61. 1 1
      MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzTrackId.cs
  62. 1 0
      MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs
  63. 1 1
      MediaBrowser.Providers/Plugins/Omdb/Configuration/PluginConfiguration.cs
  64. 0 2
      MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableInt32Converter.cs
  65. 0 2
      MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableStringConverter.cs
  66. 1 1
      MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
  67. 2 0
      MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
  68. 2 0
      MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
  69. 2 0
      MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
  70. 1 0
      MediaBrowser.Providers/Plugins/Omdb/Plugin.cs
  71. 1 1
      MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs
  72. 2 0
      MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
  73. 2 0
      MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
  74. 10 0
      MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs
  75. 7 3
      MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html
  76. 1 1
      MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs
  77. 2 0
      MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs
  78. 2 0
      MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
  79. 1 1
      MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs
  80. 2 0
      MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
  81. 2 0
      MediaBrowser.Providers/Plugins/Tmdb/Plugin.cs
  82. 2 0
      MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
  83. 2 0
      MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
  84. 1 1
      MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
  85. 1 1
      MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs
  86. 2 0
      MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
  87. 17 3
      MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
  88. 0 2
      MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
  89. 2 0
      MediaBrowser.Providers/Studios/StudiosImageProvider.cs
  90. 2 1
      MediaBrowser.Providers/Subtitles/SubtitleManager.cs
  91. 1 1
      MediaBrowser.Providers/TV/SeasonMetadataService.cs
  92. 2 0
      MediaBrowser.Providers/TV/SeriesMetadataService.cs
  93. 1 1
      MediaBrowser.Providers/TV/Zap2ItExternalId.cs
  94. 1 1
      deployment/unraid/docker-templates/README.md
  95. 1 1
      tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
  96. 1 1
      tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
  97. 1 1
      tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
  98. 1 1
      tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
  99. 1 1
      tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
  100. 1 1
      tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj

+ 121 - 0
.github/workflows/openapi.yml

@@ -0,0 +1,121 @@
+name: OpenAPI
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+
+jobs:
+  openapi-head:
+    name: OpenAPI - HEAD
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v2
+      - name: Setup .NET Core
+        uses: actions/setup-dotnet@v1
+        with:
+          dotnet-version: '6.0.x'
+          include-prerelease: true
+      - name: Generate openapi.json
+        run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests"
+      - name: Upload openapi.json
+        uses: actions/upload-artifact@v2
+        with:
+          name: openapi-head
+          retention-days: 14
+          if-no-files-found: error
+          path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net6.0/openapi.json
+
+  openapi-base:
+    name: OpenAPI - BASE
+    if: ${{ github.base_ref != '' }}
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v2
+        with:
+          ref: ${{ github.base_ref }}
+      - name: Setup .NET Core
+        uses: actions/setup-dotnet@v1
+        with:
+          dotnet-version: '6.0.x'
+          include-prerelease: true
+      - name: Generate openapi.json
+        run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests"
+      - name: Upload openapi.json
+        uses: actions/upload-artifact@v2
+        with:
+          name: openapi-base
+          retention-days: 14
+          if-no-files-found: error
+          path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net6.0/openapi.json
+
+  openapi-diff:
+    name: OpenAPI - Difference
+    if: ${{ github.event_name == 'pull_request' }}
+    runs-on: ubuntu-latest
+    needs:
+      - openapi-head
+      - openapi-base
+    steps:
+      - name: Download openapi-head
+        uses: actions/download-artifact@v2
+        with:
+          name: openapi-head
+          path: openapi-head
+      - name: Download openapi-base
+        uses: actions/download-artifact@v2
+        with:
+          name: openapi-base
+          path: openapi-base
+      - name: Workaround openapi-diff issue
+        run: |
+          sed -i 's/"allOf"/"oneOf"/g' openapi-head/openapi.json
+          sed -i 's/"allOf"/"oneOf"/g' openapi-base/openapi.json
+      - name: Calculate OpenAPI difference
+        uses: docker://openapitools/openapi-diff
+        continue-on-error: true
+        with:
+          args: --fail-on-changed --markdown openapi-changes.md openapi-base/openapi.json openapi-head/openapi.json
+      - id: read-diff
+        name: Read openapi-diff output
+        run: |
+          body=$(cat openapi-changes.md)
+          body="${body//'%'/'%25'}"
+          body="${body//$'\n'/'%0A'}"
+          body="${body//$'\r'/'%0D'}"
+          echo ::set-output name=body::$body
+      - name: Find difference comment
+        uses: peter-evans/find-comment@v1
+        id: find-comment
+        with:
+          issue-number: ${{ github.event.pull_request.number }}
+          direction: last
+          body-includes: openapi-diff-workflow-comment
+      - name: Reply or edit difference comment (changed)
+        uses: peter-evans/create-or-update-comment@v1.4.5
+        if: ${{ steps.read-diff.outputs.body != '' }}
+        with:
+          issue-number: ${{ github.event.pull_request.number }}
+          comment-id: ${{ steps.find-comment.outputs.comment-id }}
+          edit-mode: replace
+          body: |
+            <!--openapi-diff-workflow-comment-->
+            <details>
+            <summary>Changes in OpenAPI specification found. Expand to see details.</summary>
+
+            ${{ steps.read-diff.outputs.body }}
+
+            </details>
+      - name: Edit difference comment (unchanged)
+        uses: peter-evans/create-or-update-comment@v1.4.5
+        if: ${{ steps.read-diff.outputs.body == '' && steps.find-comment.outputs.comment-id != '' }}
+        with:
+          issue-number: ${{ github.event.pull_request.number }}
+          comment-id: ${{ steps.find-comment.outputs.comment-id }}
+          edit-mode: replace
+          body: |
+            <!--openapi-diff-workflow-comment-->
+
+            No changes to OpenAPI specification found. See history of this comment for previous changes.

+ 2 - 2
Emby.Dlna/DlnaManager.cs

@@ -416,7 +416,7 @@ namespace Emby.Dlna
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public void UpdateProfile(DeviceProfile profile)
+        public void UpdateProfile(string profileId, DeviceProfile profile)
         {
         {
             profile = ReserializeProfile(profile);
             profile = ReserializeProfile(profile);
 
 
@@ -430,7 +430,7 @@ namespace Emby.Dlna
                 throw new ArgumentException("Profile is missing Name");
                 throw new ArgumentException("Profile is missing Name");
             }
             }
 
 
-            var current = GetProfileInfosInternal().First(i => string.Equals(i.Info.Id, profile.Id, StringComparison.OrdinalIgnoreCase));
+            var current = GetProfileInfosInternal().First(i => string.Equals(i.Info.Id, profileId, StringComparison.OrdinalIgnoreCase));
 
 
             var newFilename = _fileSystem.GetValidFilename(profile.Name) + ".xml";
             var newFilename = _fileSystem.GetValidFilename(profile.Name) + ".xml";
             var path = Path.Combine(UserProfilesPath, newFilename);
             var path = Path.Combine(UserProfilesPath, newFilename);

+ 16 - 0
Emby.Naming/Common/NamingOptions.cs

@@ -253,6 +253,8 @@ namespace Emby.Naming.Common
                 },
                 },
                 // <!-- foo.ep01, foo.EP_01 -->
                 // <!-- foo.ep01, foo.EP_01 -->
                 new EpisodeExpression(@"[\._ -]()[Ee][Pp]_?([0-9]+)([^\\/]*)$"),
                 new EpisodeExpression(@"[\._ -]()[Ee][Pp]_?([0-9]+)([^\\/]*)$"),
+                // <!-- foo.E01., foo.e01. -->
+                new EpisodeExpression(@"[^\\/]*?()\.?[Ee]([0-9]+)\.([^\\/]*)$"),
                 new EpisodeExpression("(?<year>[0-9]{4})[\\.-](?<month>[0-9]{2})[\\.-](?<day>[0-9]{2})", true)
                 new EpisodeExpression("(?<year>[0-9]{4})[\\.-](?<month>[0-9]{2})[\\.-](?<day>[0-9]{2})", true)
                 {
                 {
                     DateTimeFormats = new[]
                     DateTimeFormats = new[]
@@ -371,6 +373,20 @@ namespace Emby.Naming.Common
                     IsOptimistic = true,
                     IsOptimistic = true,
                     IsNamed = true
                     IsNamed = true
                 },
                 },
+
+                // Series and season only expression
+                // "the show/season 1", "the show/s01"
+                new EpisodeExpression(@"(.*(\\|\/))*(?<seriesname>.+)\/[Ss](eason)?[\. _\-]*(?<seasonnumber>[0-9]+)")
+                {
+                    IsNamed = true
+                },
+
+                // Series and season only expression
+                // "the show S01", "the show season 1"
+                new EpisodeExpression(@"(.*(\\|\/))*(?<seriesname>.+)[\. _\-]+[sS](eason)?[\. _\-]*(?<seasonnumber>[0-9]+)")
+                {
+                    IsNamed = true
+                },
             };
             };
 
 
             EpisodeWithoutSeasonExpressions = new[]
             EpisodeWithoutSeasonExpressions = new[]

+ 29 - 0
Emby.Naming/TV/SeriesInfo.cs

@@ -0,0 +1,29 @@
+namespace Emby.Naming.TV
+{
+    /// <summary>
+    /// Holder object for Series information.
+    /// </summary>
+    public class SeriesInfo
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SeriesInfo"/> class.
+        /// </summary>
+        /// <param name="path">Path to the file.</param>
+        public SeriesInfo(string path)
+        {
+            Path = path;
+        }
+
+        /// <summary>
+        /// Gets or sets the path.
+        /// </summary>
+        /// <value>The path.</value>
+        public string Path { get; set; }
+
+        /// <summary>
+        /// Gets or sets the name of the series.
+        /// </summary>
+        /// <value>The name of the series.</value>
+        public string? Name { get; set; }
+    }
+}

+ 61 - 0
Emby.Naming/TV/SeriesPathParser.cs

@@ -0,0 +1,61 @@
+using System.Globalization;
+using Emby.Naming.Common;
+
+namespace Emby.Naming.TV
+{
+    /// <summary>
+    /// Used to parse information about series from paths containing more information that only the series name.
+    /// Uses the same regular expressions as the EpisodePathParser but have different success criteria.
+    /// </summary>
+    public static class SeriesPathParser
+    {
+        /// <summary>
+        /// Parses information about series from path.
+        /// </summary>
+        /// <param name="options"><see cref="NamingOptions"/> object containing EpisodeExpressions and MultipleEpisodeExpressions.</param>
+        /// <param name="path">Path.</param>
+        /// <returns>Returns <see cref="SeriesPathParserResult"/> object.</returns>
+        public static SeriesPathParserResult Parse(NamingOptions options, string path)
+        {
+            SeriesPathParserResult? result = null;
+
+            foreach (var expression in options.EpisodeExpressions)
+            {
+                var currentResult = Parse(path, expression);
+                if (currentResult.Success)
+                {
+                    result = currentResult;
+                    break;
+                }
+            }
+
+            if (result != null)
+            {
+                if (!string.IsNullOrEmpty(result.SeriesName))
+                {
+                    result.SeriesName = result.SeriesName.Trim(' ', '_', '.', '-');
+                }
+            }
+
+            return result ?? new SeriesPathParserResult();
+        }
+
+        private static SeriesPathParserResult Parse(string name, EpisodeExpression expression)
+        {
+            var result = new SeriesPathParserResult();
+
+            var match = expression.Regex.Match(name);
+
+            if (match.Success && match.Groups.Count >= 3)
+            {
+                if (expression.IsNamed)
+                {
+                    result.SeriesName = match.Groups["seriesname"].Value;
+                    result.Success = !string.IsNullOrEmpty(result.SeriesName) && !string.IsNullOrEmpty(match.Groups["seasonnumber"]?.Value);
+                }
+            }
+
+            return result;
+        }
+    }
+}

+ 19 - 0
Emby.Naming/TV/SeriesPathParserResult.cs

@@ -0,0 +1,19 @@
+namespace Emby.Naming.TV
+{
+    /// <summary>
+    /// Holder object for <see cref="SeriesPathParser"/> result.
+    /// </summary>
+    public class SeriesPathParserResult
+    {
+        /// <summary>
+        /// Gets or sets the name of the series.
+        /// </summary>
+        /// <value>The name of the series.</value>
+        public string? SeriesName { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether parsing was successful.
+        /// </summary>
+        public bool Success { get; set; }
+    }
+}

+ 49 - 0
Emby.Naming/TV/SeriesResolver.cs

@@ -0,0 +1,49 @@
+using System.IO;
+using System.Text.RegularExpressions;
+using Emby.Naming.Common;
+
+namespace Emby.Naming.TV
+{
+    /// <summary>
+    /// Used to resolve information about series from path.
+    /// </summary>
+    public static class SeriesResolver
+    {
+        /// <summary>
+        /// Regex that matches strings of at least 2 characters separated by a dot or underscore.
+        /// Used for removing separators between words, i.e turns "The_show" into "The show" while
+        /// preserving namings like "S.H.O.W".
+        /// </summary>
+        private static readonly Regex _seriesNameRegex = new Regex(@"((?<a>[^\._]{2,})[\._]*)|([\._](?<b>[^\._]{2,}))");
+
+        /// <summary>
+        /// Resolve information about series from path.
+        /// </summary>
+        /// <param name="options"><see cref="NamingOptions"/> object passed to <see cref="SeriesPathParser"/>.</param>
+        /// <param name="path">Path to series.</param>
+        /// <returns>SeriesInfo.</returns>
+        public static SeriesInfo Resolve(NamingOptions options, string path)
+        {
+            string seriesName = Path.GetFileName(path);
+
+            SeriesPathParserResult result = SeriesPathParser.Parse(options, path);
+            if (result.Success)
+            {
+                if (!string.IsNullOrEmpty(result.SeriesName))
+                {
+                    seriesName = result.SeriesName;
+                }
+            }
+
+            if (!string.IsNullOrEmpty(seriesName))
+            {
+                seriesName = _seriesNameRegex.Replace(seriesName, "${a} ${b}").Trim();
+            }
+
+            return new SeriesInfo(path)
+            {
+                Name = seriesName
+            };
+        }
+    }
+}

+ 16 - 23
Emby.Server.Implementations/Library/MediaStreamSelector.cs

@@ -38,14 +38,11 @@ namespace Emby.Server.Implementations.Library
         }
         }
 
 
         public static int? GetDefaultSubtitleStreamIndex(
         public static int? GetDefaultSubtitleStreamIndex(
-            List<MediaStream> streams,
+            IEnumerable<MediaStream> streams,
             string[] preferredLanguages,
             string[] preferredLanguages,
             SubtitlePlaybackMode mode,
             SubtitlePlaybackMode mode,
             string audioTrackLanguage)
             string audioTrackLanguage)
         {
         {
-            streams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages)
-                .ToList();
-
             MediaStream stream = null;
             MediaStream stream = null;
 
 
             if (mode == SubtitlePlaybackMode.None)
             if (mode == SubtitlePlaybackMode.None)
@@ -53,52 +50,48 @@ namespace Emby.Server.Implementations.Library
                 return null;
                 return null;
             }
             }
 
 
+            var sortedStreams = streams
+                .Where(i => i.Type == MediaStreamType.Subtitle)
+                .OrderByDescending(x => x.IsExternal)
+                .ThenByDescending(x => x.IsForced && string.Equals(x.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
+                .ThenByDescending(x => x.IsForced)
+                .ThenByDescending(x => x.IsDefault)
+                .ToList();
+
             if (mode == SubtitlePlaybackMode.Default)
             if (mode == SubtitlePlaybackMode.Default)
             {
             {
                 // Prefer embedded metadata over smart logic
                 // Prefer embedded metadata over smart logic
-
-                stream = streams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase)) ??
-                    streams.FirstOrDefault(s => s.IsForced) ??
-                    streams.FirstOrDefault(s => s.IsDefault);
+                stream = sortedStreams.FirstOrDefault(s => s.IsExternal || s.IsForced || s.IsDefault);
 
 
                 // if the audio language is not understood by the user, load their preferred subs, if there are any
                 // if the audio language is not understood by the user, load their preferred subs, if there are any
                 if (stream == null && !preferredLanguages.Contains(audioTrackLanguage, StringComparer.OrdinalIgnoreCase))
                 if (stream == null && !preferredLanguages.Contains(audioTrackLanguage, StringComparer.OrdinalIgnoreCase))
                 {
                 {
-                    stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase));
+                    stream = sortedStreams.FirstOrDefault(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase));
                 }
                 }
             }
             }
             else if (mode == SubtitlePlaybackMode.Smart)
             else if (mode == SubtitlePlaybackMode.Smart)
             {
             {
-                // Prefer smart logic over embedded metadata
-
                 // if the audio language is not understood by the user, load their preferred subs, if there are any
                 // if the audio language is not understood by the user, load their preferred subs, if there are any
                 if (!preferredLanguages.Contains(audioTrackLanguage, StringComparer.OrdinalIgnoreCase))
                 if (!preferredLanguages.Contains(audioTrackLanguage, StringComparer.OrdinalIgnoreCase))
                 {
                 {
-                    stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase)) ??
+                    stream = streams.FirstOrDefault(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase)) ??
                         streams.FirstOrDefault(s => preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase));
                         streams.FirstOrDefault(s => preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase));
                 }
                 }
             }
             }
             else if (mode == SubtitlePlaybackMode.Always)
             else if (mode == SubtitlePlaybackMode.Always)
             {
             {
                 // always load the most suitable full subtitles
                 // always load the most suitable full subtitles
-                stream = streams.FirstOrDefault(s => !s.IsForced);
+                stream = sortedStreams.FirstOrDefault(s => !s.IsForced);
             }
             }
             else if (mode == SubtitlePlaybackMode.OnlyForced)
             else if (mode == SubtitlePlaybackMode.OnlyForced)
             {
             {
                 // always load the most suitable full subtitles
                 // always load the most suitable full subtitles
-                stream = streams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase)) ??
-                    streams.FirstOrDefault(s => s.IsForced);
+                stream = sortedStreams.FirstOrDefault(x => x.IsForced);
             }
             }
 
 
             // load forced subs if we have found no suitable full subtitles
             // load forced subs if we have found no suitable full subtitles
-            stream ??= streams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase));
-
-            if (stream != null)
-            {
-                return stream.Index;
-            }
-
-            return null;
+            stream ??= sortedStreams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase));
+            return stream?.Index;
         }
         }
 
 
         private static IEnumerable<MediaStream> GetSortedStreams(IEnumerable<MediaStream> streams, MediaStreamType type, string[] languagePreferences)
         private static IEnumerable<MediaStream> GetSortedStreams(IEnumerable<MediaStream> streams, MediaStreamType type, string[] languagePreferences)

+ 5 - 3
Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs

@@ -54,6 +54,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                     return null;
                     return null;
                 }
                 }
 
 
+                var seriesInfo = Naming.TV.SeriesResolver.Resolve(_libraryManager.GetNamingOptions(), args.Path);
+
                 var collectionType = args.GetCollectionType();
                 var collectionType = args.GetCollectionType();
                 if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
                 if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
                 {
                 {
@@ -63,7 +65,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                         return new Series
                         return new Series
                         {
                         {
                             Path = args.Path,
                             Path = args.Path,
-                            Name = Path.GetFileName(args.Path)
+                            Name = seriesInfo.Name
                         };
                         };
                     }
                     }
                 }
                 }
@@ -80,7 +82,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                         return new Series
                         return new Series
                         {
                         {
                             Path = args.Path,
                             Path = args.Path,
-                            Name = Path.GetFileName(args.Path)
+                            Name = seriesInfo.Name
                         };
                         };
                     }
                     }
 
 
@@ -94,7 +96,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                         return new Series
                         return new Series
                         {
                         {
                             Path = args.Path,
                             Path = args.Path,
-                            Name = Path.GetFileName(args.Path)
+                            Name = seriesInfo.Name
                         };
                         };
                     }
                     }
                 }
                 }

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

@@ -12,11 +12,11 @@
     "DeviceOfflineWithName": "قُطِع الاتصال ب{0}",
     "DeviceOfflineWithName": "قُطِع الاتصال ب{0}",
     "DeviceOnlineWithName": "{0} متصل",
     "DeviceOnlineWithName": "{0} متصل",
     "FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",
     "FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",
-    "Favorites": "المفضلة",
+    "Favorites": "مفضلات",
     "Folders": "المجلدات",
     "Folders": "المجلدات",
     "Genres": "التضنيفات",
     "Genres": "التضنيفات",
     "HeaderAlbumArtists": "ألبوم الفنان",
     "HeaderAlbumArtists": "ألبوم الفنان",
-    "HeaderContinueWatching": "استئناف",
+    "HeaderContinueWatching": "استمر بالمشاهدة",
     "HeaderFavoriteAlbums": "الألبومات المفضلة",
     "HeaderFavoriteAlbums": "الألبومات المفضلة",
     "HeaderFavoriteArtists": "الفنانون المفضلون",
     "HeaderFavoriteArtists": "الفنانون المفضلون",
     "HeaderFavoriteEpisodes": "الحلقات المفضلة",
     "HeaderFavoriteEpisodes": "الحلقات المفضلة",
@@ -33,7 +33,7 @@
     "LabelRunningTimeValue": "المدة: {0}",
     "LabelRunningTimeValue": "المدة: {0}",
     "Latest": "الأحدث",
     "Latest": "الأحدث",
     "MessageApplicationUpdated": "لقد تم تحديث خادم Jellyfin",
     "MessageApplicationUpdated": "لقد تم تحديث خادم Jellyfin",
-    "MessageApplicationUpdatedTo": "تم تحديث سيرفر Jellyfin الى {0}",
+    "MessageApplicationUpdatedTo": "تم تحديث خادم Jellyfin الى {0}",
     "MessageNamedServerConfigurationUpdatedWithValue": "تم تحديث إعدادات الخادم في قسم {0}",
     "MessageNamedServerConfigurationUpdatedWithValue": "تم تحديث إعدادات الخادم في قسم {0}",
     "MessageServerConfigurationUpdated": "تم تحديث إعدادات الخادم",
     "MessageServerConfigurationUpdated": "تم تحديث إعدادات الخادم",
     "MixedContent": "محتوى مختلط",
     "MixedContent": "محتوى مختلط",
@@ -43,7 +43,7 @@
     "NameInstallFailed": "فشل التثبيت {0}",
     "NameInstallFailed": "فشل التثبيت {0}",
     "NameSeasonNumber": "الموسم {0}",
     "NameSeasonNumber": "الموسم {0}",
     "NameSeasonUnknown": "الموسم غير معروف",
     "NameSeasonUnknown": "الموسم غير معروف",
-    "NewVersionIsAvailable": "نسخة جديدة من سيرفر Jellyfin متوفرة للتحميل.",
+    "NewVersionIsAvailable": "نسخة جديدة من خادم Jellyfin متوفرة للتحميل.",
     "NotificationOptionApplicationUpdateAvailable": "يوجد تحديث للتطبيق",
     "NotificationOptionApplicationUpdateAvailable": "يوجد تحديث للتطبيق",
     "NotificationOptionApplicationUpdateInstalled": "تم تحديث التطبيق",
     "NotificationOptionApplicationUpdateInstalled": "تم تحديث التطبيق",
     "NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي",
     "NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي",
@@ -55,7 +55,7 @@
     "NotificationOptionPluginInstalled": "تم تثبيت الملحق",
     "NotificationOptionPluginInstalled": "تم تثبيت الملحق",
     "NotificationOptionPluginUninstalled": "تمت إزالة الملحق",
     "NotificationOptionPluginUninstalled": "تمت إزالة الملحق",
     "NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق",
     "NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق",
-    "NotificationOptionServerRestartRequired": "يجب إعادة تشغيل السيرفر",
+    "NotificationOptionServerRestartRequired": "يجب إعادة تشغيل الخادم",
     "NotificationOptionTaskFailed": "فشل في المهمة المجدولة",
     "NotificationOptionTaskFailed": "فشل في المهمة المجدولة",
     "NotificationOptionUserLockedOut": "تم إقفال حساب المستخدم",
     "NotificationOptionUserLockedOut": "تم إقفال حساب المستخدم",
     "NotificationOptionVideoPlayback": "بدأ تشغيل الفيديو",
     "NotificationOptionVideoPlayback": "بدأ تشغيل الفيديو",
@@ -72,7 +72,7 @@
     "ServerNameNeedsToBeRestarted": "يحتاج لإعادة تشغيله {0}",
     "ServerNameNeedsToBeRestarted": "يحتاج لإعادة تشغيله {0}",
     "Shows": "الحلقات",
     "Shows": "الحلقات",
     "Songs": "الأغاني",
     "Songs": "الأغاني",
-    "StartupEmbyServerIsLoading": "سيرفر Jellyfin قيد التشغيل . الرجاء المحاولة بعد قليل.",
+    "StartupEmbyServerIsLoading": "خادم Jellyfin قيد التشغيل . الرجاء المحاولة بعد قليل.",
     "SubtitleDownloadFailureForItem": "عملية إنزال الترجمة فشلت لـ{0}",
     "SubtitleDownloadFailureForItem": "عملية إنزال الترجمة فشلت لـ{0}",
     "SubtitleDownloadFailureFromForItem": "الترجمات فشلت في التحميل من {0} الى {1}",
     "SubtitleDownloadFailureFromForItem": "الترجمات فشلت في التحميل من {0} الى {1}",
     "Sync": "مزامنة",
     "Sync": "مزامنة",

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

@@ -15,7 +15,7 @@
     "Favorites": "Favoritter",
     "Favorites": "Favoritter",
     "Folders": "Mapper",
     "Folders": "Mapper",
     "Genres": "Genrer",
     "Genres": "Genrer",
-    "HeaderAlbumArtists": "Albumkunstnere",
+    "HeaderAlbumArtists": "Kunstnerens album",
     "HeaderContinueWatching": "Fortsæt Afspilning",
     "HeaderContinueWatching": "Fortsæt Afspilning",
     "HeaderFavoriteAlbums": "Favoritalbummer",
     "HeaderFavoriteAlbums": "Favoritalbummer",
     "HeaderFavoriteArtists": "Favoritkunstnere",
     "HeaderFavoriteArtists": "Favoritkunstnere",

+ 8 - 8
Emby.Server.Implementations/Localization/Core/eo.json

@@ -7,8 +7,8 @@
     "NameInstallFailed": "{0} instalado fiaskis",
     "NameInstallFailed": "{0} instalado fiaskis",
     "Music": "Muziko",
     "Music": "Muziko",
     "Movies": "Filmoj",
     "Movies": "Filmoj",
-    "ItemRemovedWithName": "{0} forigis el la libraro",
-    "ItemAddedWithName": "{0} aldonis al la libraro",
+    "ItemRemovedWithName": "{0} forigis el la plurmediteko",
+    "ItemAddedWithName": "{0} aldonis al la plurmediteko",
     "HeaderLiveTV": "TV-etero",
     "HeaderLiveTV": "TV-etero",
     "HeaderContinueWatching": "Daŭrigi Spektadon",
     "HeaderContinueWatching": "Daŭrigi Spektadon",
     "HeaderAlbumArtists": "Albumo de artisto",
     "HeaderAlbumArtists": "Albumo de artisto",
@@ -23,7 +23,7 @@
     "Application": "Aplikaĵo",
     "Application": "Aplikaĵo",
     "AppDeviceValues": "Aplikaĵo: {0}, Aparato: {1}",
     "AppDeviceValues": "Aplikaĵo: {0}, Aparato: {1}",
     "Albums": "Albumoj",
     "Albums": "Albumoj",
-    "TasksLibraryCategory": "Libraro",
+    "TasksLibraryCategory": "Plurmediteko",
     "VersionNumber": "Versio {0}",
     "VersionNumber": "Versio {0}",
     "UserDownloadingItemWithValues": "{0} elŝutas {1}",
     "UserDownloadingItemWithValues": "{0} elŝutas {1}",
     "UserCreatedWithName": "Uzanto {0} kreiĝis",
     "UserCreatedWithName": "Uzanto {0} kreiĝis",
@@ -50,7 +50,7 @@
     "TvShows": "TV-serioj",
     "TvShows": "TV-serioj",
     "Favorites": "Favoratoj",
     "Favorites": "Favoratoj",
     "TaskCleanLogs": "Purigi Ĵurnalan Katalogon",
     "TaskCleanLogs": "Purigi Ĵurnalan Katalogon",
-    "TaskRefreshLibrary": "Skanu Plurmedian Libraron",
+    "TaskRefreshLibrary": "Skanu Plurmeditekon",
     "ValueSpecialEpisodeName": "Speciala - {0}",
     "ValueSpecialEpisodeName": "Speciala - {0}",
     "TaskOptimizeDatabase": "Optimigi datumbazon",
     "TaskOptimizeDatabase": "Optimigi datumbazon",
     "TaskRefreshChannels": "Refreŝigi Kanalojn",
     "TaskRefreshChannels": "Refreŝigi Kanalojn",
@@ -75,17 +75,17 @@
     "ServerNameNeedsToBeRestarted": "{0} devas esti relanĉita",
     "ServerNameNeedsToBeRestarted": "{0} devas esti relanĉita",
     "NotificationOptionVideoPlayback": "La videoludado lanĉis",
     "NotificationOptionVideoPlayback": "La videoludado lanĉis",
     "NotificationOptionServerRestartRequired": "Servila relanĉigo bezonata",
     "NotificationOptionServerRestartRequired": "Servila relanĉigo bezonata",
-    "TaskOptimizeDatabaseDescription": "Kompaktigas datenbazon kaj trunkas liberan lokon. Lanĉi ĉi tiun taskon post la librara skanado aŭ fari aliajn ŝanĝojn, kiuj implicas datenbazajn modifojn, povus plibonigi rendimenton.",
+    "TaskOptimizeDatabaseDescription": "Kompaktigas datenbazon kaj trunkas liberan lokon. Lanĉi ĉi tiun taskon post la teka skanado aŭ fari aliajn ŝanĝojn, kiuj implicas datenbazajn modifojn, povus plibonigi rendimenton.",
     "TaskUpdatePluginsDescription": "Elŝutas kaj instalas ĝisdatigojn por kromprogramojn, kiuj estas agorditaj por ĝisdatigi aŭtomate.",
     "TaskUpdatePluginsDescription": "Elŝutas kaj instalas ĝisdatigojn por kromprogramojn, kiuj estas agorditaj por ĝisdatigi aŭtomate.",
     "TaskDownloadMissingSubtitlesDescription": "Serĉas en interreto mankantajn subtekstojn surbaze de metadatena agordaro.",
     "TaskDownloadMissingSubtitlesDescription": "Serĉas en interreto mankantajn subtekstojn surbaze de metadatena agordaro.",
-    "TaskRefreshPeopleDescription": "Ĝisdatigas metadatenojn por aktoroj kaj reĵisoroj en via plurmedia libraro.",
+    "TaskRefreshPeopleDescription": "Ĝisdatigas metadatenojn por aktoroj kaj reĵisoroj en via plurmediteko.",
     "TaskCleanLogsDescription": "Forigas ĵurnalajn dosierojn aĝajn pli ol {0} tagojn.",
     "TaskCleanLogsDescription": "Forigas ĵurnalajn dosierojn aĝajn pli ol {0} tagojn.",
-    "TaskRefreshLibraryDescription": "Skanas vian plurmedian libraron por novaj dosieroj kaj refreŝigas metadatenaron.",
+    "TaskRefreshLibraryDescription": "Skanas vian plurmeditekon por novaj dosieroj kaj refreŝigas metadatenaron.",
     "NewVersionIsAvailable": "Nova versio de Jellyfin Server estas elŝutebla.",
     "NewVersionIsAvailable": "Nova versio de Jellyfin Server estas elŝutebla.",
     "TaskCleanCacheDescription": "Forigas stapla dosierojn ne plu necesajn de la sistemo.",
     "TaskCleanCacheDescription": "Forigas stapla dosierojn ne plu necesajn de la sistemo.",
     "TaskCleanActivityLogDescription": "Forigas aktivecan ĵurnalaĵojn pli malnovajn ol la agordita aĝo.",
     "TaskCleanActivityLogDescription": "Forigas aktivecan ĵurnalaĵojn pli malnovajn ol la agordita aĝo.",
     "TaskCleanTranscodeDescription": "Forigas transkodajn dosierojn aĝajn pli ol unu tagon.",
     "TaskCleanTranscodeDescription": "Forigas transkodajn dosierojn aĝajn pli ol unu tagon.",
-    "ValueHasBeenAddedToLibrary": "{0} estis aldonita al via plurmedia libraro",
+    "ValueHasBeenAddedToLibrary": "{0} estis aldonita al via plurmediteko",
     "SubtitleDownloadFailureFromForItem": "Subtekstoj malsukcesis elŝuti de {0} por {1}",
     "SubtitleDownloadFailureFromForItem": "Subtekstoj malsukcesis elŝuti de {0} por {1}",
     "StartupEmbyServerIsLoading": "Jellyfin Server ŝarĝas. Provi denove baldaŭ.",
     "StartupEmbyServerIsLoading": "Jellyfin Server ŝarĝas. Provi denove baldaŭ.",
     "TaskRefreshChapterImagesDescription": "Kreas bildetojn por videoj kiuj havas ĉapitrojn.",
     "TaskRefreshChapterImagesDescription": "Kreas bildetojn por videoj kiuj havas ĉapitrojn.",

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

@@ -1,7 +1,7 @@
 {
 {
     "Albums": "Albums",
     "Albums": "Albums",
     "AppDeviceValues": "Application : {0}, Appareil : {1}",
     "AppDeviceValues": "Application : {0}, Appareil : {1}",
-    "Application": "Application",
+    "Application": "Applications",
     "Artists": "Artistes",
     "Artists": "Artistes",
     "AuthenticationSucceededWithUserName": "{0} authentifié avec succès",
     "AuthenticationSucceededWithUserName": "{0} authentifié avec succès",
     "Books": "Livres",
     "Books": "Livres",

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

@@ -39,7 +39,7 @@
     "MixedContent": "Conteúdo Misto",
     "MixedContent": "Conteúdo Misto",
     "Movies": "Filmes",
     "Movies": "Filmes",
     "Music": "Música",
     "Music": "Música",
-    "MusicVideos": "Videoclips",
+    "MusicVideos": "Videoclipes",
     "NameInstallFailed": "{0} falha na instalação",
     "NameInstallFailed": "{0} falha na instalação",
     "NameSeasonNumber": "Temporada {0}",
     "NameSeasonNumber": "Temporada {0}",
     "NameSeasonUnknown": "Temporada Desconhecida",
     "NameSeasonUnknown": "Temporada Desconhecida",
@@ -118,5 +118,7 @@
     "TaskCleanActivityLog": "Limpar registo de atividade",
     "TaskCleanActivityLog": "Limpar registo de atividade",
     "Undefined": "Indefinido",
     "Undefined": "Indefinido",
     "Forced": "Forçado",
     "Forced": "Forçado",
-    "Default": "Padrão"
+    "Default": "Padrão",
+    "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.",
+    "TaskOptimizeDatabase": "Otimizar base de dados"
 }
 }

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

@@ -372,9 +372,11 @@ namespace Emby.Server.Implementations.Localization
         /// <inheritdoc />
         /// <inheritdoc />
         public IEnumerable<LocalizationOption> GetLocalizationOptions()
         public IEnumerable<LocalizationOption> GetLocalizationOptions()
         {
         {
+            yield return new LocalizationOption("Afrikaans", "af");
             yield return new LocalizationOption("Arabic", "ar");
             yield return new LocalizationOption("Arabic", "ar");
             yield return new LocalizationOption("Bulgarian (Bulgaria)", "bg-BG");
             yield return new LocalizationOption("Bulgarian (Bulgaria)", "bg-BG");
             yield return new LocalizationOption("Catalan", "ca");
             yield return new LocalizationOption("Catalan", "ca");
+            yield return new LocalizationOption("Chinese (Hong Kong)", "zh-HK");
             yield return new LocalizationOption("Chinese Simplified", "zh-CN");
             yield return new LocalizationOption("Chinese Simplified", "zh-CN");
             yield return new LocalizationOption("Chinese Traditional", "zh-TW");
             yield return new LocalizationOption("Chinese Traditional", "zh-TW");
             yield return new LocalizationOption("Croatian", "hr");
             yield return new LocalizationOption("Croatian", "hr");
@@ -383,32 +385,48 @@ namespace Emby.Server.Implementations.Localization
             yield return new LocalizationOption("Dutch", "nl");
             yield return new LocalizationOption("Dutch", "nl");
             yield return new LocalizationOption("English (United Kingdom)", "en-GB");
             yield return new LocalizationOption("English (United Kingdom)", "en-GB");
             yield return new LocalizationOption("English (United States)", "en-US");
             yield return new LocalizationOption("English (United States)", "en-US");
+            yield return new LocalizationOption("Esperanto", "eo");
+            yield return new LocalizationOption("Estonian", "et");
+            yield return new LocalizationOption("Finnish", "fi");
             yield return new LocalizationOption("French", "fr");
             yield return new LocalizationOption("French", "fr");
             yield return new LocalizationOption("French (Canada)", "fr-CA");
             yield return new LocalizationOption("French (Canada)", "fr-CA");
             yield return new LocalizationOption("German", "de");
             yield return new LocalizationOption("German", "de");
             yield return new LocalizationOption("Greek", "el");
             yield return new LocalizationOption("Greek", "el");
             yield return new LocalizationOption("Hebrew", "he");
             yield return new LocalizationOption("Hebrew", "he");
             yield return new LocalizationOption("Hungarian", "hu");
             yield return new LocalizationOption("Hungarian", "hu");
+            yield return new LocalizationOption("Icelandic", "is");
+            yield return new LocalizationOption("Indonesian", "id");
             yield return new LocalizationOption("Italian", "it");
             yield return new LocalizationOption("Italian", "it");
+            yield return new LocalizationOption("Japanese", "ja");
             yield return new LocalizationOption("Kazakh", "kk");
             yield return new LocalizationOption("Kazakh", "kk");
             yield return new LocalizationOption("Korean", "ko");
             yield return new LocalizationOption("Korean", "ko");
+            yield return new LocalizationOption("Latvian", "lv");
             yield return new LocalizationOption("Lithuanian", "lt-LT");
             yield return new LocalizationOption("Lithuanian", "lt-LT");
             yield return new LocalizationOption("Malay", "ms");
             yield return new LocalizationOption("Malay", "ms");
+            yield return new LocalizationOption("Malayalam", "ml");
             yield return new LocalizationOption("Norwegian Bokmål", "nb");
             yield return new LocalizationOption("Norwegian Bokmål", "nb");
+            yield return new LocalizationOption("Norwegian Nynorsk", "nn");
             yield return new LocalizationOption("Persian", "fa");
             yield return new LocalizationOption("Persian", "fa");
             yield return new LocalizationOption("Polish", "pl");
             yield return new LocalizationOption("Polish", "pl");
+            yield return new LocalizationOption("Portuguese", "pt");
             yield return new LocalizationOption("Portuguese (Brazil)", "pt-BR");
             yield return new LocalizationOption("Portuguese (Brazil)", "pt-BR");
             yield return new LocalizationOption("Portuguese (Portugal)", "pt-PT");
             yield return new LocalizationOption("Portuguese (Portugal)", "pt-PT");
+            yield return new LocalizationOption("Romanian", "ro");
             yield return new LocalizationOption("Russian", "ru");
             yield return new LocalizationOption("Russian", "ru");
+            yield return new LocalizationOption("Serbian", "sr");
             yield return new LocalizationOption("Slovak", "sk");
             yield return new LocalizationOption("Slovak", "sk");
             yield return new LocalizationOption("Slovenian (Slovenia)", "sl-SI");
             yield return new LocalizationOption("Slovenian (Slovenia)", "sl-SI");
             yield return new LocalizationOption("Spanish", "es");
             yield return new LocalizationOption("Spanish", "es");
             yield return new LocalizationOption("Spanish (Argentina)", "es-AR");
             yield return new LocalizationOption("Spanish (Argentina)", "es-AR");
+            yield return new LocalizationOption("Spanish (Latin America)", "es-419");
             yield return new LocalizationOption("Spanish (Mexico)", "es-MX");
             yield return new LocalizationOption("Spanish (Mexico)", "es-MX");
             yield return new LocalizationOption("Swedish", "sv");
             yield return new LocalizationOption("Swedish", "sv");
             yield return new LocalizationOption("Swiss German", "gsw");
             yield return new LocalizationOption("Swiss German", "gsw");
+            yield return new LocalizationOption("Tamil", "ta");
+            yield return new LocalizationOption("Telugu", "te");
             yield return new LocalizationOption("Turkish", "tr");
             yield return new LocalizationOption("Turkish", "tr");
             yield return new LocalizationOption("Tiếng Việt", "vi");
             yield return new LocalizationOption("Tiếng Việt", "vi");
+            yield return new LocalizationOption("Ukrainian", "uk");
         }
         }
     }
     }
 }
 }

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

@@ -126,7 +126,7 @@ namespace Jellyfin.Api.Controllers
                 return NotFound();
                 return NotFound();
             }
             }
 
 
-            _dlnaManager.UpdateProfile(deviceProfile);
+            _dlnaManager.UpdateProfile(profileId, deviceProfile);
             return NoContent();
             return NoContent();
         }
         }
     }
     }

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

@@ -45,7 +45,7 @@
     <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
     <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
     <PackageReference Include="Serilog.Sinks.Graylog" Version="2.2.2" />
     <PackageReference Include="Serilog.Sinks.Graylog" Version="2.2.2" />
     <PackageReference Include="Serilog.Sinks.Map" Version="1.0.2" />
     <PackageReference Include="Serilog.Sinks.Map" Version="1.0.2" />
-    <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.6" />
+    <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.7" />
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>

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

@@ -37,8 +37,9 @@ namespace MediaBrowser.Controller.Dlna
         /// <summary>
         /// <summary>
         /// Updates the profile.
         /// Updates the profile.
         /// </summary>
         /// </summary>
+        /// <param name="profileId">The profile id.</param>
         /// <param name="profile">The profile.</param>
         /// <param name="profile">The profile.</param>
-        void UpdateProfile(DeviceProfile profile);
+        void UpdateProfile(string profileId, DeviceProfile profile);
 
 
         /// <summary>
         /// <summary>
         /// Deletes the profile.
         /// Deletes the profile.

+ 1 - 3
MediaBrowser.Controller/Providers/IExternalId.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Providers;
 
 
@@ -35,7 +33,7 @@ namespace MediaBrowser.Controller.Providers
         /// <summary>
         /// <summary>
         /// Gets the URL format string for this id.
         /// Gets the URL format string for this id.
         /// </summary>
         /// </summary>
-        string UrlFormatString { get; }
+        string? UrlFormatString { get; }
 
 
         /// <summary>
         /// <summary>
         /// Determines whether this id supports a given item type.
         /// Determines whether this id supports a given item type.

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

@@ -26,7 +26,7 @@
     <PackageReference Include="libse" Version="3.6.2" />
     <PackageReference Include="libse" Version="3.6.2" />
     <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0-rc.2*" />
     <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0-rc.2*" />
     <PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0-rc.2*" />
     <PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0-rc.2*" />
-    <PackageReference Include="UTF.Unknown" Version="2.4.0" />
+    <PackageReference Include="UTF.Unknown" Version="2.5.0" />
   </ItemGroup>
   </ItemGroup>
 
 
   <!-- Code Analyzers-->
   <!-- Code Analyzers-->

+ 2 - 2
MediaBrowser.Model/Providers/ExternalIdInfo.cs

@@ -12,7 +12,7 @@ namespace MediaBrowser.Model.Providers
         /// <param name="key">Key for this id. This key should be unique across all providers.</param>
         /// <param name="key">Key for this id. This key should be unique across all providers.</param>
         /// <param name="type">Specific media type for this id.</param>
         /// <param name="type">Specific media type for this id.</param>
         /// <param name="urlFormatString">URL format string.</param>
         /// <param name="urlFormatString">URL format string.</param>
-        public ExternalIdInfo(string name, string key, ExternalIdMediaType? type, string urlFormatString)
+        public ExternalIdInfo(string name, string key, ExternalIdMediaType? type, string? urlFormatString)
         {
         {
             Name = name;
             Name = name;
             Key = key;
             Key = key;
@@ -46,6 +46,6 @@ namespace MediaBrowser.Model.Providers
         /// <summary>
         /// <summary>
         /// Gets or sets the URL format string.
         /// Gets or sets the URL format string.
         /// </summary>
         /// </summary>
-        public string UrlFormatString { get; set; }
+        public string? UrlFormatString { get; set; }
     }
     }
 }
 }

+ 2 - 0
MediaBrowser.Providers/Manager/ImageSaver.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

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

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CA1002, CS1591
 #pragma warning disable CA1002, CS1591
 
 
 using System;
 using System;

+ 2 - 0
MediaBrowser.Providers/Manager/MetadataService.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 2 - 0
MediaBrowser.Providers/Manager/ProviderManager.cs

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

+ 2 - 0
MediaBrowser.Providers/Manager/ProviderUtils.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

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

@@ -8,7 +8,7 @@ namespace MediaBrowser.Providers.Manager
     {
     {
         public ItemUpdateType UpdateType { get; set; }
         public ItemUpdateType UpdateType { get; set; }
 
 
-        public string ErrorMessage { get; set; }
+        public string? ErrorMessage { get; set; }
 
 
         public int Failures { get; set; }
         public int Failures { get; set; }
     }
     }

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

@@ -30,7 +30,6 @@
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
     <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
-    <Nullable>disable</Nullable>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <!-- Code Analyzers-->
   <!-- Code Analyzers-->

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

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CA1002, CS1591
 #pragma warning disable CA1002, CS1591
 
 
 using System;
 using System;

+ 2 - 0
MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs

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

+ 2 - 0
MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 2 - 0
MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 2 - 0
MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CA1068, CS1591
 #pragma warning disable CA1068, CS1591
 
 
 using System;
 using System;

+ 2 - 0
MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CA1002, CS1591
 #pragma warning disable CA1002, CS1591
 
 
 using System;
 using System;

+ 2 - 0
MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CA1002, CS1591
 #pragma warning disable CA1002, CS1591
 
 
 using System;
 using System;

+ 2 - 0
MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 1 - 1
MediaBrowser.Providers/Movies/ImdbExternalId.cs

@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Movies
         public ExternalIdMediaType? Type => null;
         public ExternalIdMediaType? Type => null;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => "https://www.imdb.com/title/{0}";
+        public string? UrlFormatString => "https://www.imdb.com/title/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item)
         public bool Supports(IHasProviderIds item)

+ 1 - 1
MediaBrowser.Providers/Movies/ImdbPersonExternalId.cs

@@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.Movies
         public ExternalIdMediaType? Type => ExternalIdMediaType.Person;
         public ExternalIdMediaType? Type => ExternalIdMediaType.Person;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => "https://www.imdb.com/name/{0}";
+        public string? UrlFormatString => "https://www.imdb.com/name/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item) => item is Person;
         public bool Supports(IHasProviderIds item) => item is Person;

+ 6 - 6
MediaBrowser.Providers/Music/AlbumInfoExtensions.cs

@@ -8,7 +8,7 @@ namespace MediaBrowser.Providers.Music
 {
 {
     public static class AlbumInfoExtensions
     public static class AlbumInfoExtensions
     {
     {
-        public static string GetAlbumArtist(this AlbumInfo info)
+        public static string? GetAlbumArtist(this AlbumInfo info)
         {
         {
             var id = info.SongInfos.SelectMany(i => i.AlbumArtists)
             var id = info.SongInfos.SelectMany(i => i.AlbumArtists)
                     .FirstOrDefault(i => !string.IsNullOrEmpty(i));
                     .FirstOrDefault(i => !string.IsNullOrEmpty(i));
@@ -21,7 +21,7 @@ namespace MediaBrowser.Providers.Music
             return info.AlbumArtists.Count > 0 ? info.AlbumArtists[0] : default;
             return info.AlbumArtists.Count > 0 ? info.AlbumArtists[0] : default;
         }
         }
 
 
-        public static string GetReleaseGroupId(this AlbumInfo info)
+        public static string? GetReleaseGroupId(this AlbumInfo info)
         {
         {
             var id = info.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup);
             var id = info.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup);
 
 
@@ -34,7 +34,7 @@ namespace MediaBrowser.Providers.Music
             return id;
             return id;
         }
         }
 
 
-        public static string GetReleaseId(this AlbumInfo info)
+        public static string? GetReleaseId(this AlbumInfo info)
         {
         {
             var id = info.GetProviderId(MetadataProvider.MusicBrainzAlbum);
             var id = info.GetProviderId(MetadataProvider.MusicBrainzAlbum);
 
 
@@ -47,9 +47,9 @@ namespace MediaBrowser.Providers.Music
             return id;
             return id;
         }
         }
 
 
-        public static string GetMusicBrainzArtistId(this AlbumInfo info)
+        public static string? GetMusicBrainzArtistId(this AlbumInfo info)
         {
         {
-            info.ProviderIds.TryGetValue(MetadataProvider.MusicBrainzAlbumArtist.ToString(), out string id);
+            info.ProviderIds.TryGetValue(MetadataProvider.MusicBrainzAlbumArtist.ToString(), out string? id);
 
 
             if (string.IsNullOrEmpty(id))
             if (string.IsNullOrEmpty(id))
             {
             {
@@ -65,7 +65,7 @@ namespace MediaBrowser.Providers.Music
             return id;
             return id;
         }
         }
 
 
-        public static string GetMusicBrainzArtistId(this ArtistInfo info)
+        public static string? GetMusicBrainzArtistId(this ArtistInfo info)
         {
         {
             info.ProviderIds.TryGetValue(MetadataProvider.MusicBrainzArtist.ToString(), out var id);
             info.ProviderIds.TryGetValue(MetadataProvider.MusicBrainzArtist.ToString(), out var id);
 
 

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

@@ -81,7 +81,7 @@ namespace MediaBrowser.Providers.Music
             if (!item.AlbumArtists.SequenceEqual(artists, StringComparer.OrdinalIgnoreCase))
             if (!item.AlbumArtists.SequenceEqual(artists, StringComparer.OrdinalIgnoreCase))
             {
             {
                 item.AlbumArtists = artists;
                 item.AlbumArtists = artists;
-                updateType = updateType | ItemUpdateType.MetadataEdit;
+                updateType |= ItemUpdateType.MetadataEdit;
             }
             }
 
 
             return updateType;
             return updateType;
@@ -100,7 +100,7 @@ namespace MediaBrowser.Providers.Music
             if (!item.Artists.SequenceEqual(artists, StringComparer.OrdinalIgnoreCase))
             if (!item.Artists.SequenceEqual(artists, StringComparer.OrdinalIgnoreCase))
             {
             {
                 item.Artists = artists;
                 item.Artists = artists;
-                updateType = updateType | ItemUpdateType.MetadataEdit;
+                updateType |= ItemUpdateType.MetadataEdit;
             }
             }
 
 
             return updateType;
             return updateType;

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

@@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.Music
         public ExternalIdMediaType? Type => null;
         public ExternalIdMediaType? Type => null;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => null;
+        public string? UrlFormatString => null;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item)
         public bool Supports(IHasProviderIds item)

+ 2 - 0
MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

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

@@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
         public ExternalIdMediaType? Type => null;
         public ExternalIdMediaType? Type => null;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
+        public string? UrlFormatString => "https://www.theaudiodb.com/album/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item) => item is MusicAlbum;
         public bool Supports(IHasProviderIds item) => item is MusicAlbum;

+ 2 - 0
MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumImageProvider.cs

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

+ 2 - 0
MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CA1002, CS1591, SA1300
 #pragma warning disable CA1002, CS1591, SA1300
 
 
 using System;
 using System;

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

@@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
         public ExternalIdMediaType? Type => ExternalIdMediaType.Artist;
         public ExternalIdMediaType? Type => ExternalIdMediaType.Artist;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
+        public string? UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item) => item is MusicArtist;
         public bool Supports(IHasProviderIds item) => item is MusicArtist;

+ 2 - 0
MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs

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

+ 2 - 0
MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CA1034, CS1591, CA1002, SA1028, SA1300
 #pragma warning disable CA1034, CS1591, CA1002, SA1028, SA1300
 
 
 using System;
 using System;

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

@@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
         public ExternalIdMediaType? Type => ExternalIdMediaType.Album;
         public ExternalIdMediaType? Type => ExternalIdMediaType.Album;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
+        public string? UrlFormatString => "https://www.theaudiodb.com/album/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item) => item is Audio;
         public bool Supports(IHasProviderIds item) => item is Audio;

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

@@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
         public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist;
         public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
+        public string? UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
         public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;

+ 1 - 1
MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs

@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
 
 
 using MediaBrowser.Model.Plugins;
 using MediaBrowser.Model.Plugins;
 
 

+ 1 - 0
MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs

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

+ 4 - 15
MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs

@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
 
 
 using MediaBrowser.Model.Plugins;
 using MediaBrowser.Model.Plugins;
 
 
@@ -12,24 +12,13 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz
 
 
         public string Server
         public string Server
         {
         {
-            get
-            {
-                return _server;
-            }
-
-            set
-            {
-                _server = value.TrimEnd('/');
-            }
+            get => _server;
+            set => _server = value.TrimEnd('/');
         }
         }
 
 
         public long RateLimit
         public long RateLimit
         {
         {
-            get
-            {
-                return _rateLimit;
-            }
-
+            get => _rateLimit;
             set
             set
             {
             {
                 if (value < Plugin.DefaultRateLimit && _server == Plugin.DefaultServer)
                 if (value < Plugin.DefaultRateLimit && _server == Plugin.DefaultServer)

+ 1 - 1
MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumArtistExternalId.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.Music
         public ExternalIdMediaType? Type => ExternalIdMediaType.AlbumArtist;
         public ExternalIdMediaType? Type => ExternalIdMediaType.AlbumArtist;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
+        public string? UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item) => item is Audio;
         public bool Supports(IHasProviderIds item) => item is Audio;

+ 1 - 1
MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumExternalId.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.Music
         public ExternalIdMediaType? Type => ExternalIdMediaType.Album;
         public ExternalIdMediaType? Type => ExternalIdMediaType.Album;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release/{0}";
+        public string? UrlFormatString => Plugin.Instance.Configuration.Server + "/release/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
         public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;

+ 2 - 0
MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591, SA1401
 #pragma warning disable CS1591, SA1401
 
 
 using System;
 using System;

+ 1 - 1
MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistExternalId.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.Music
         public ExternalIdMediaType? Type => ExternalIdMediaType.Artist;
         public ExternalIdMediaType? Type => ExternalIdMediaType.Artist;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
+        public string? UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item) => item is MusicArtist;
         public bool Supports(IHasProviderIds item) => item is MusicArtist;

+ 2 - 0
MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 1 - 1
MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzOtherArtistExternalId.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.Music
         public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist;
         public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
+        public string? UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
         public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;

+ 1 - 1
MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzReleaseGroupExternalId.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.Music
         public ExternalIdMediaType? Type => ExternalIdMediaType.ReleaseGroup;
         public ExternalIdMediaType? Type => ExternalIdMediaType.ReleaseGroup;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release-group/{0}";
+        public string? UrlFormatString => Plugin.Instance.Configuration.Server + "/release-group/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
         public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;

+ 1 - 1
MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzTrackId.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.Music
         public ExternalIdMediaType? Type => ExternalIdMediaType.Track;
         public ExternalIdMediaType? Type => ExternalIdMediaType.Track;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => Plugin.Instance.Configuration.Server + "/track/{0}";
+        public string? UrlFormatString => Plugin.Instance.Configuration.Server + "/track/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item) => item is Audio;
         public bool Supports(IHasProviderIds item) => item is Audio;

+ 1 - 0
MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs

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

+ 1 - 1
MediaBrowser.Providers/Plugins/Omdb/Configuration/PluginConfiguration.cs

@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
 
 
 using MediaBrowser.Model.Plugins;
 using MediaBrowser.Model.Plugins;
 
 

+ 0 - 2
MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableInt32Converter.cs

@@ -1,5 +1,3 @@
-#nullable enable
-
 using System;
 using System;
 using System.ComponentModel;
 using System.ComponentModel;
 using System.Text.Json;
 using System.Text.Json;

+ 0 - 2
MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableStringConverter.cs

@@ -1,5 +1,3 @@
-#nullable enable
-
 using System;
 using System;
 using System.Text.Json;
 using System.Text.Json;
 using System.Text.Json.Serialization;
 using System.Text.Json.Serialization;

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

@@ -61,7 +61,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                 return result;
                 return result;
             }
             }
 
 
-            if (info.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string seriesImdbId) && !string.IsNullOrEmpty(seriesImdbId))
+            if (info.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string? seriesImdbId) && !string.IsNullOrEmpty(seriesImdbId))
             {
             {
                 if (info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue)
                 if (info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue)
                 {
                 {

+ 2 - 0
MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs

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

+ 2 - 0
MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591, SA1300
 #pragma warning disable CS1591, SA1300
 
 
 using System;
 using System;

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

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS159, SA1300
 #pragma warning disable CS159, SA1300
 
 
 using System;
 using System;

+ 1 - 0
MediaBrowser.Providers/Plugins/Omdb/Plugin.cs

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

+ 1 - 1
MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs

@@ -21,7 +21,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
         public ExternalIdMediaType? Type => ExternalIdMediaType.BoxSet;
         public ExternalIdMediaType? Type => ExternalIdMediaType.BoxSet;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "collection/{0}";
+        public string? UrlFormatString => TmdbUtils.BaseTmdbUrl + "collection/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item)
         public bool Supports(IHasProviderIds item)

+ 2 - 0
MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 2 - 0
MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 10 - 0
MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs

@@ -11,5 +11,15 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
         /// Gets or sets a value indicating whether include adult content when searching with TMDb.
         /// Gets or sets a value indicating whether include adult content when searching with TMDb.
         /// </summary>
         /// </summary>
         public bool IncludeAdult { get; set; }
         public bool IncludeAdult { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether tags should be imported for series from TMDb.
+        /// </summary>
+        public bool ExcludeTagsSeries { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether tags should be imported for movies from TMDb.
+        /// </summary>
+        public bool ExcludeTagsMovies { get; set; }
     }
     }
 }
 }

+ 7 - 3
MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html

@@ -29,20 +29,24 @@
                     Dashboard.showLoadingMsg();
                     Dashboard.showLoadingMsg();
                     ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
                     ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
                         document.querySelector('#includeAdult').checked = config.IncludeAdult;
                         document.querySelector('#includeAdult').checked = config.IncludeAdult;
+                        document.querySelector('#excludeTagsSeries').checked = config.ExcludeTagsSeries;
+                        document.querySelector('#excludeTagsMovies').checked = config.ExcludeTagsMovies;
                         Dashboard.hideLoadingMsg();
                         Dashboard.hideLoadingMsg();
                     });
                     });
                 });
                 });
 
 
-            
+
             document.querySelector('.configForm')
             document.querySelector('.configForm')
                 .addEventListener('submit', function (e) {
                 .addEventListener('submit', function (e) {
                     Dashboard.showLoadingMsg();
                     Dashboard.showLoadingMsg();
-    
+
                     ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
                     ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
                         config.IncludeAdult = document.querySelector('#includeAdult').checked;
                         config.IncludeAdult = document.querySelector('#includeAdult').checked;
+                        config.ExcludeTagsSeries = document.querySelector('#excludeTagsSeries').checked;
+                        config.ExcludeTagsMovies = document.querySelector('#excludeTagsMovies').checked;
                         ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
                         ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
                     });
                     });
-                    
+
                     e.preventDefault();
                     e.preventDefault();
                     return false;
                     return false;
                 });
                 });

+ 1 - 1
MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs

@@ -21,7 +21,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
         public ExternalIdMediaType? Type => ExternalIdMediaType.Movie;
         public ExternalIdMediaType? Type => ExternalIdMediaType.Movie;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "movie/{0}";
+        public string? UrlFormatString => TmdbUtils.BaseTmdbUrl + "movie/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item)
         public bool Supports(IHasProviderIds item)

+ 2 - 0
MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 2 - 0
MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 1 - 1
MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
         public ExternalIdMediaType? Type => ExternalIdMediaType.Person;
         public ExternalIdMediaType? Type => ExternalIdMediaType.Person;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "person/{0}";
+        public string? UrlFormatString => TmdbUtils.BaseTmdbUrl + "person/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item)
         public bool Supports(IHasProviderIds item)

+ 2 - 0
MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 2 - 0
MediaBrowser.Providers/Plugins/Tmdb/Plugin.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;

+ 2 - 0
MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 2 - 0
MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 1 - 1
MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs

@@ -33,7 +33,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
         {
         {
             var result = new MetadataResult<Season>();
             var result = new MetadataResult<Season>();
 
 
-            info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string seriesTmdbId);
+            info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string? seriesTmdbId);
 
 
             var seasonNumber = info.IndexNumber;
             var seasonNumber = info.IndexNumber;
 
 

+ 1 - 1
MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
         public ExternalIdMediaType? Type => ExternalIdMediaType.Series;
         public ExternalIdMediaType? Type => ExternalIdMediaType.Series;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "tv/{0}";
+        public string? UrlFormatString => TmdbUtils.BaseTmdbUrl + "tv/{0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item)
         public bool Supports(IHasProviderIds item)

+ 2 - 0
MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

+ 17 - 3
MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs

@@ -1,4 +1,6 @@
-using System;
+#nullable disable
+
+using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.Threading;
 using System.Threading;
@@ -55,11 +57,17 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
 
 
             await EnsureClientConfigAsync().ConfigureAwait(false);
             await EnsureClientConfigAsync().ConfigureAwait(false);
 
 
+            var extraMethods = MovieMethods.Credits | MovieMethods.Releases | MovieMethods.Images | MovieMethods.Videos;
+            if (!(Plugin.Instance?.Configuration.ExcludeTagsMovies).GetValueOrDefault())
+            {
+                extraMethods |= MovieMethods.Keywords;
+            }
+
             movie = await _tmDbClient.GetMovieAsync(
             movie = await _tmDbClient.GetMovieAsync(
                 tmdbId,
                 tmdbId,
                 TmdbUtils.NormalizeLanguage(language),
                 TmdbUtils.NormalizeLanguage(language),
                 imageLanguages,
                 imageLanguages,
-                MovieMethods.Credits | MovieMethods.Releases | MovieMethods.Images | MovieMethods.Keywords | MovieMethods.Videos,
+                extraMethods,
                 cancellationToken).ConfigureAwait(false);
                 cancellationToken).ConfigureAwait(false);
 
 
             if (movie != null)
             if (movie != null)
@@ -121,11 +129,17 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
 
 
             await EnsureClientConfigAsync().ConfigureAwait(false);
             await EnsureClientConfigAsync().ConfigureAwait(false);
 
 
+            var extraMethods = TvShowMethods.Credits | TvShowMethods.Images | TvShowMethods.ExternalIds | TvShowMethods.Videos | TvShowMethods.ContentRatings | TvShowMethods.EpisodeGroups;
+            if (!(Plugin.Instance?.Configuration.ExcludeTagsSeries).GetValueOrDefault())
+            {
+                extraMethods |= TvShowMethods.Keywords;
+            }
+
             series = await _tmDbClient.GetTvShowAsync(
             series = await _tmDbClient.GetTvShowAsync(
                 tmdbId,
                 tmdbId,
                 language: TmdbUtils.NormalizeLanguage(language),
                 language: TmdbUtils.NormalizeLanguage(language),
                 includeImageLanguage: imageLanguages,
                 includeImageLanguage: imageLanguages,
-                extraMethods: TvShowMethods.Credits | TvShowMethods.Images | TvShowMethods.Keywords | TvShowMethods.ExternalIds | TvShowMethods.Videos | TvShowMethods.ContentRatings | TvShowMethods.EpisodeGroups,
+                extraMethods: extraMethods,
                 cancellationToken: cancellationToken).ConfigureAwait(false);
                 cancellationToken: cancellationToken).ConfigureAwait(false);
 
 
             if (series != null)
             if (series != null)

+ 0 - 2
MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs

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

+ 2 - 0
MediaBrowser.Providers/Studios/StudiosImageProvider.cs

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;

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

@@ -1,3 +1,5 @@
+#nullable disable
+
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System;
 using System;
@@ -21,7 +23,6 @@ using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Providers;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
-using static MediaBrowser.Model.IO.IODefaults;
 
 
 namespace MediaBrowser.Providers.Subtitles
 namespace MediaBrowser.Providers.Subtitles
 {
 {

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

@@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.TV
                 if (!string.Equals(item.Name, seasonZeroDisplayName, StringComparison.OrdinalIgnoreCase))
                 if (!string.Equals(item.Name, seasonZeroDisplayName, StringComparison.OrdinalIgnoreCase))
                 {
                 {
                     item.Name = seasonZeroDisplayName;
                     item.Name = seasonZeroDisplayName;
-                    updatedType = updatedType | ItemUpdateType.MetadataEdit;
+                    updatedType |= ItemUpdateType.MetadataEdit;
                 }
                 }
             }
             }
 
 

+ 2 - 0
MediaBrowser.Providers/TV/SeriesMetadataService.cs

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

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

@@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.TV
         public ExternalIdMediaType? Type => null;
         public ExternalIdMediaType? Type => null;
 
 
         /// <inheritdoc />
         /// <inheritdoc />
-        public string UrlFormatString => "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}";
+        public string? UrlFormatString => "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}";
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public bool Supports(IHasProviderIds item) => item is Series;
         public bool Supports(IHasProviderIds item) => item is Series;

+ 1 - 1
deployment/unraid/docker-templates/README.md

@@ -8,7 +8,7 @@ Click on the Docker tab
 
 
 Add the following line under "Template Repositories" 
 Add the following line under "Template Repositories" 
 
 
-https://github.com/jellyfin/jellyfin/blob/master/deployment/unraid/docker-templates
+https://github.com/jellyfin/jellyfin/tree/master/deployment/unraid/docker-templates
 
 
 Click save than click on Add Container and select jellyfin.
 Click save than click on Add Container and select jellyfin.
 
 

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

@@ -17,7 +17,7 @@
     <PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
     <PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
     <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0-rc.2*" />
     <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0-rc.2*" />
     <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0-rc.2*" />
     <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0-rc.2*" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
     <PackageReference Include="coverlet.collector" Version="3.1.0" />
     <PackageReference Include="coverlet.collector" Version="3.1.0" />

+ 1 - 1
tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj

@@ -12,7 +12,7 @@
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
     <PackageReference Include="coverlet.collector" Version="3.1.0" />
     <PackageReference Include="coverlet.collector" Version="3.1.0" />

+ 1 - 1
tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj

@@ -12,7 +12,7 @@
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
     <PackageReference Include="Moq" Version="4.16.1" />
     <PackageReference Include="Moq" Version="4.16.1" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />

+ 1 - 1
tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj

@@ -7,7 +7,7 @@
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
     <PackageReference Include="Moq" Version="4.16.1" />
     <PackageReference Include="Moq" Version="4.16.1" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />

+ 1 - 1
tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj

@@ -7,7 +7,7 @@
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

+ 1 - 1
tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj

@@ -22,7 +22,7 @@
     <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
     <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
     <PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
     <PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
     <PackageReference Include="coverlet.collector" Version="3.1.0" />
     <PackageReference Include="coverlet.collector" Version="3.1.0" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
     <PackageReference Include="Moq" Version="4.16.1" />
     <PackageReference Include="Moq" Version="4.16.1" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />

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