Преглед на файлове

Merge pull request #4853 from Ullmie02/servicestack-json

Bond-009 преди 4 години
родител
ревизия
054adf6379
променени са 31 файла, в които са добавени 335 реда и са изтрити 554 реда
  1. 5 5
      Emby.Dlna/DlnaManager.cs
  2. 6 8
      Emby.Server.Implementations/ApplicationHost.cs
  3. 20 15
      Emby.Server.Implementations/Channels/ChannelManager.cs
  4. 0 1
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  5. 8 6
      Emby.Server.Implementations/Library/LiveStreamHelper.cs
  6. 10 9
      Emby.Server.Implementations/Library/MediaSourceManager.cs
  7. 4 8
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  8. 6 10
      Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
  9. 9 6
      Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
  10. 2 2
      Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
  11. 2 3
      Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
  12. 11 12
      Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
  13. 10 7
      Emby.Server.Implementations/Localization/LocalizationManager.cs
  14. 24 18
      Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
  15. 2 6
      Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
  16. 0 281
      Emby.Server.Implementations/Serialization/JsonSerializer.cs
  17. 35 0
      MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs
  18. 35 0
      MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStructConverter.cs
  19. 4 3
      MediaBrowser.Controller/Entities/CollectionFolder.cs
  20. 0 102
      MediaBrowser.Model/Serialization/IJsonSerializer.cs
  21. 7 4
      MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
  22. 6 4
      MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
  23. 7 4
      MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
  24. 6 4
      MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
  25. 2 6
      MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
  26. 3 6
      MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
  27. 12 8
      MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
  28. 28 16
      MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
  29. 2 0
      MediaBrowser.Providers/Properties/AssemblyInfo.cs
  30. 1 0
      tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
  31. 68 0
      tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs

+ 5 - 5
Emby.Dlna/DlnaManager.cs

@@ -7,12 +7,14 @@ using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
 using System.Text;
 using System.Text;
+using System.Text.Json;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Emby.Dlna.Profiles;
 using Emby.Dlna.Profiles;
 using Emby.Dlna.Server;
 using Emby.Dlna.Server;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Drawing;
@@ -32,9 +34,9 @@ namespace Emby.Dlna
         private readonly IXmlSerializer _xmlSerializer;
         private readonly IXmlSerializer _xmlSerializer;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly ILogger<DlnaManager> _logger;
         private readonly ILogger<DlnaManager> _logger;
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly IServerApplicationHost _appHost;
         private readonly IServerApplicationHost _appHost;
         private static readonly Assembly _assembly = typeof(DlnaManager).Assembly;
         private static readonly Assembly _assembly = typeof(DlnaManager).Assembly;
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
 
 
         private readonly Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>> _profiles = new Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>>(StringComparer.Ordinal);
         private readonly Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>> _profiles = new Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>>(StringComparer.Ordinal);
 
 
@@ -43,14 +45,12 @@ namespace Emby.Dlna
             IFileSystem fileSystem,
             IFileSystem fileSystem,
             IApplicationPaths appPaths,
             IApplicationPaths appPaths,
             ILoggerFactory loggerFactory,
             ILoggerFactory loggerFactory,
-            IJsonSerializer jsonSerializer,
             IServerApplicationHost appHost)
             IServerApplicationHost appHost)
         {
         {
             _xmlSerializer = xmlSerializer;
             _xmlSerializer = xmlSerializer;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _appPaths = appPaths;
             _appPaths = appPaths;
             _logger = loggerFactory.CreateLogger<DlnaManager>();
             _logger = loggerFactory.CreateLogger<DlnaManager>();
-            _jsonSerializer = jsonSerializer;
             _appHost = appHost;
             _appHost = appHost;
         }
         }
 
 
@@ -495,9 +495,9 @@ namespace Emby.Dlna
                 return profile;
                 return profile;
             }
             }
 
 
-            var json = _jsonSerializer.SerializeToString(profile);
+            var json = JsonSerializer.Serialize(profile, _jsonOptions);
 
 
-            return _jsonSerializer.DeserializeFromString<DeviceProfile>(json);
+            return JsonSerializer.Deserialize<DeviceProfile>(json, _jsonOptions);
         }
         }
 
 
         public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
         public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)

+ 6 - 8
Emby.Server.Implementations/ApplicationHost.cs

@@ -7,11 +7,11 @@ using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Net;
 using System.Net;
-using System.Net.Http;
 using System.Reflection;
 using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 using System.Security.Cryptography.X509Certificates;
 using System.Security.Cryptography.X509Certificates;
 using System.Text;
 using System.Text;
+using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Emby.Dlna;
 using Emby.Dlna;
@@ -50,6 +50,7 @@ using Jellyfin.Networking.Manager;
 using MediaBrowser.Common;
 using MediaBrowser.Common;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Events;
 using MediaBrowser.Common.Events;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Common.Updates;
 using MediaBrowser.Common.Updates;
@@ -118,12 +119,12 @@ namespace Emby.Server.Implementations
 
 
         private readonly IFileSystem _fileSystemManager;
         private readonly IFileSystem _fileSystemManager;
         private readonly IXmlSerializer _xmlSerializer;
         private readonly IXmlSerializer _xmlSerializer;
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly IStartupOptions _startupOptions;
         private readonly IStartupOptions _startupOptions;
 
 
         private IMediaEncoder _mediaEncoder;
         private IMediaEncoder _mediaEncoder;
         private ISessionManager _sessionManager;
         private ISessionManager _sessionManager;
         private string[] _urlPrefixes;
         private string[] _urlPrefixes;
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
 
 
         /// <summary>
         /// <summary>
         /// Gets a value indicating whether this instance can self restart.
         /// Gets a value indicating whether this instance can self restart.
@@ -257,7 +258,6 @@ namespace Emby.Server.Implementations
             IServiceCollection serviceCollection)
             IServiceCollection serviceCollection)
         {
         {
             _xmlSerializer = new MyXmlSerializer();
             _xmlSerializer = new MyXmlSerializer();
-            _jsonSerializer = new JsonSerializer();
 
 
             ServiceCollection = serviceCollection;
             ServiceCollection = serviceCollection;
 
 
@@ -528,8 +528,6 @@ namespace Emby.Server.Implementations
 
 
             ServiceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
             ServiceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
 
 
-            ServiceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
-
             ServiceCollection.AddSingleton(_fileSystemManager);
             ServiceCollection.AddSingleton(_fileSystemManager);
             ServiceCollection.AddSingleton<TmdbClientManager>();
             ServiceCollection.AddSingleton<TmdbClientManager>();
 
 
@@ -754,7 +752,6 @@ namespace Emby.Server.Implementations
             UserView.CollectionManager = Resolve<ICollectionManager>();
             UserView.CollectionManager = Resolve<ICollectionManager>();
             BaseItem.MediaSourceManager = Resolve<IMediaSourceManager>();
             BaseItem.MediaSourceManager = Resolve<IMediaSourceManager>();
             CollectionFolder.XmlSerializer = _xmlSerializer;
             CollectionFolder.XmlSerializer = _xmlSerializer;
-            CollectionFolder.JsonSerializer = Resolve<IJsonSerializer>();
             CollectionFolder.ApplicationHost = this;
             CollectionFolder.ApplicationHost = this;
         }
         }
 
 
@@ -967,7 +964,7 @@ namespace Emby.Server.Implementations
                 {
                 {
                     return true;
                     return true;
                 }
                 }
-                
+
                 throw new FileNotFoundException(
                 throw new FileNotFoundException(
                     string.Format(
                     string.Format(
                         CultureInfo.InvariantCulture,
                         CultureInfo.InvariantCulture,
@@ -1051,7 +1048,8 @@ namespace Emby.Server.Implementations
                     var metafile = Path.Combine(dir, "meta.json");
                     var metafile = Path.Combine(dir, "meta.json");
                     if (File.Exists(metafile))
                     if (File.Exists(metafile))
                     {
                     {
-                        var manifest = _jsonSerializer.DeserializeFromFile<PluginManifest>(metafile);
+                        var jsonString = File.ReadAllText(metafile, Encoding.UTF8);
+                        var manifest = JsonSerializer.Deserialize<PluginManifest>(jsonString, _jsonOptions);
 
 
                         if (!Version.TryParse(manifest.TargetAbi, out var targetAbi))
                         if (!Version.TryParse(manifest.TargetAbi, out var targetAbi))
                         {
                         {

+ 20 - 15
Emby.Server.Implementations/Channels/ChannelManager.cs

@@ -3,11 +3,14 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Text;
+using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
 using Jellyfin.Data.Enums;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
@@ -21,7 +24,6 @@ using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
 using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 using Episode = MediaBrowser.Controller.Entities.TV.Episode;
 using Episode = MediaBrowser.Controller.Entities.TV.Episode;
@@ -44,10 +46,10 @@ namespace Emby.Server.Implementations.Channels
         private readonly ILogger<ChannelManager> _logger;
         private readonly ILogger<ChannelManager> _logger;
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly IProviderManager _providerManager;
         private readonly IProviderManager _providerManager;
         private readonly IMemoryCache _memoryCache;
         private readonly IMemoryCache _memoryCache;
         private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
         private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="ChannelManager"/> class.
         /// Initializes a new instance of the <see cref="ChannelManager"/> class.
@@ -59,7 +61,6 @@ namespace Emby.Server.Implementations.Channels
         /// <param name="config">The server configuration manager.</param>
         /// <param name="config">The server configuration manager.</param>
         /// <param name="fileSystem">The filesystem.</param>
         /// <param name="fileSystem">The filesystem.</param>
         /// <param name="userDataManager">The user data manager.</param>
         /// <param name="userDataManager">The user data manager.</param>
-        /// <param name="jsonSerializer">The JSON serializer.</param>
         /// <param name="providerManager">The provider manager.</param>
         /// <param name="providerManager">The provider manager.</param>
         /// <param name="memoryCache">The memory cache.</param>
         /// <param name="memoryCache">The memory cache.</param>
         public ChannelManager(
         public ChannelManager(
@@ -70,7 +71,6 @@ namespace Emby.Server.Implementations.Channels
             IServerConfigurationManager config,
             IServerConfigurationManager config,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
             IUserDataManager userDataManager,
             IUserDataManager userDataManager,
-            IJsonSerializer jsonSerializer,
             IProviderManager providerManager,
             IProviderManager providerManager,
             IMemoryCache memoryCache)
             IMemoryCache memoryCache)
         {
         {
@@ -81,7 +81,6 @@ namespace Emby.Server.Implementations.Channels
             _config = config;
             _config = config;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _userDataManager = userDataManager;
             _userDataManager = userDataManager;
-            _jsonSerializer = jsonSerializer;
             _providerManager = providerManager;
             _providerManager = providerManager;
             _memoryCache = memoryCache;
             _memoryCache = memoryCache;
         }
         }
@@ -343,7 +342,9 @@ namespace Emby.Server.Implementations.Channels
 
 
             try
             try
             {
             {
-                return _jsonSerializer.DeserializeFromFile<List<MediaSourceInfo>>(path) ?? new List<MediaSourceInfo>();
+                var jsonString = File.ReadAllText(path, Encoding.UTF8);
+                return JsonSerializer.Deserialize<List<MediaSourceInfo>>(jsonString, _jsonOptions)
+                    ?? new List<MediaSourceInfo>();
             }
             }
             catch
             catch
             {
             {
@@ -351,7 +352,7 @@ namespace Emby.Server.Implementations.Channels
             }
             }
         }
         }
 
 
-        private void SaveMediaSources(BaseItem item, List<MediaSourceInfo> mediaSources)
+        private async Task SaveMediaSources(BaseItem item, List<MediaSourceInfo> mediaSources)
         {
         {
             var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasourceinfos.json");
             var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasourceinfos.json");
 
 
@@ -370,7 +371,8 @@ namespace Emby.Server.Implementations.Channels
 
 
             Directory.CreateDirectory(Path.GetDirectoryName(path));
             Directory.CreateDirectory(Path.GetDirectoryName(path));
 
 
-            _jsonSerializer.SerializeToFile(mediaSources, path);
+            await using FileStream createStream = File.Create(path);
+            await JsonSerializer.SerializeAsync(createStream, mediaSources, _jsonOptions).ConfigureAwait(false);
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
@@ -812,7 +814,8 @@ namespace Emby.Server.Implementations.Channels
             {
             {
                 if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
                 if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
                 {
                 {
-                    var cachedResult = _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
+                    await using FileStream jsonStream = File.OpenRead(cachePath);
+                    var cachedResult = await JsonSerializer.DeserializeAsync<ChannelItemResult>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
                     if (cachedResult != null)
                     if (cachedResult != null)
                     {
                     {
                         return null;
                         return null;
@@ -834,7 +837,8 @@ namespace Emby.Server.Implementations.Channels
                 {
                 {
                     if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
                     if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
                     {
                     {
-                        var cachedResult = _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
+                        await using FileStream jsonStream = File.OpenRead(cachePath);
+                        var cachedResult = await JsonSerializer.DeserializeAsync<ChannelItemResult>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
                         if (cachedResult != null)
                         if (cachedResult != null)
                         {
                         {
                             return null;
                             return null;
@@ -865,7 +869,7 @@ namespace Emby.Server.Implementations.Channels
                     throw new InvalidOperationException("Channel returned a null result from GetChannelItems");
                     throw new InvalidOperationException("Channel returned a null result from GetChannelItems");
                 }
                 }
 
 
-                CacheResponse(result, cachePath);
+                await CacheResponse(result, cachePath);
 
 
                 return result;
                 return result;
             }
             }
@@ -875,13 +879,14 @@ namespace Emby.Server.Implementations.Channels
             }
             }
         }
         }
 
 
-        private void CacheResponse(object result, string path)
+        private async Task CacheResponse(object result, string path)
         {
         {
             try
             try
             {
             {
                 Directory.CreateDirectory(Path.GetDirectoryName(path));
                 Directory.CreateDirectory(Path.GetDirectoryName(path));
 
 
-                _jsonSerializer.SerializeToFile(result, path);
+                await using FileStream createStream = File.Create(path);
+                await JsonSerializer.SerializeAsync(createStream, result, _jsonOptions).ConfigureAwait(false);
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
@@ -1176,11 +1181,11 @@ namespace Emby.Server.Implementations.Channels
             {
             {
                 if (enableMediaProbe && !info.IsLiveStream && item.HasPathProtocol)
                 if (enableMediaProbe && !info.IsLiveStream && item.HasPathProtocol)
                 {
                 {
-                    SaveMediaSources(item, new List<MediaSourceInfo>());
+                    await SaveMediaSources(item, new List<MediaSourceInfo>()).ConfigureAwait(false);
                 }
                 }
                 else
                 else
                 {
                 {
-                    SaveMediaSources(item, info.MediaSources);
+                    await SaveMediaSources(item, info.MediaSources).ConfigureAwait(false);
                 }
                 }
             }
             }
 
 

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

@@ -29,7 +29,6 @@
     <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
     <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
     <PackageReference Include="Mono.Nat" Version="3.0.1" />
     <PackageReference Include="Mono.Nat" Version="3.0.1" />
     <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.1" />
     <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.1" />
-    <PackageReference Include="ServiceStack.Text.Core" Version="5.10.2" />
     <PackageReference Include="sharpcompress" Version="0.26.0" />
     <PackageReference Include="sharpcompress" Version="0.26.0" />
     <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
     <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
     <PackageReference Include="DotNet.Glob" Version="3.1.0" />
     <PackageReference Include="DotNet.Glob" Version="3.1.0" />

+ 8 - 6
Emby.Server.Implementations/Library/LiveStreamHelper.cs

@@ -5,16 +5,17 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Emby.Server.Implementations.Library
 namespace Emby.Server.Implementations.Library
@@ -23,14 +24,13 @@ namespace Emby.Server.Implementations.Library
     {
     {
         private readonly IMediaEncoder _mediaEncoder;
         private readonly IMediaEncoder _mediaEncoder;
         private readonly ILogger _logger;
         private readonly ILogger _logger;
-        private readonly IJsonSerializer _json;
         private readonly IApplicationPaths _appPaths;
         private readonly IApplicationPaths _appPaths;
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
 
 
-        public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger, IJsonSerializer json, IApplicationPaths appPaths)
+        public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger, IApplicationPaths appPaths)
         {
         {
             _mediaEncoder = mediaEncoder;
             _mediaEncoder = mediaEncoder;
             _logger = logger;
             _logger = logger;
-            _json = json;
             _appPaths = appPaths;
             _appPaths = appPaths;
         }
         }
 
 
@@ -47,7 +47,8 @@ namespace Emby.Server.Implementations.Library
             {
             {
                 try
                 try
                 {
                 {
-                    mediaInfo = _json.DeserializeFromFile<MediaInfo>(cacheFilePath);
+                    await using FileStream jsonStream = File.OpenRead(cacheFilePath);
+                    mediaInfo = await JsonSerializer.DeserializeAsync<MediaInfo>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
 
                     // _logger.LogDebug("Found cached media info");
                     // _logger.LogDebug("Found cached media info");
                 }
                 }
@@ -83,7 +84,8 @@ namespace Emby.Server.Implementations.Library
                 if (cacheFilePath != null)
                 if (cacheFilePath != null)
                 {
                 {
                     Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
                     Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
-                    _json.SerializeToFile(mediaInfo, cacheFilePath);
+                    await using FileStream createStream = File.OpenWrite(cacheFilePath);
+                    await JsonSerializer.SerializeAsync(createStream, mediaInfo, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
 
                     // _logger.LogDebug("Saved media info to {0}", cacheFilePath);
                     // _logger.LogDebug("Saved media info to {0}", cacheFilePath);
                 }
                 }

+ 10 - 9
Emby.Server.Implementations/Library/MediaSourceManager.cs

@@ -6,12 +6,14 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
 using Jellyfin.Data.Enums;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.MediaEncoding;
@@ -23,7 +25,6 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Emby.Server.Implementations.Library
 namespace Emby.Server.Implementations.Library
@@ -36,7 +37,6 @@ namespace Emby.Server.Implementations.Library
         private readonly IItemRepository _itemRepo;
         private readonly IItemRepository _itemRepo;
         private readonly IUserManager _userManager;
         private readonly IUserManager _userManager;
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly ILogger<MediaSourceManager> _logger;
         private readonly ILogger<MediaSourceManager> _logger;
         private readonly IUserDataManager _userDataManager;
         private readonly IUserDataManager _userDataManager;
@@ -46,6 +46,7 @@ namespace Emby.Server.Implementations.Library
 
 
         private readonly ConcurrentDictionary<string, ILiveStream> _openStreams = new ConcurrentDictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
         private readonly ConcurrentDictionary<string, ILiveStream> _openStreams = new ConcurrentDictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
         private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
         private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
 
 
         private IMediaSourceProvider[] _providers;
         private IMediaSourceProvider[] _providers;
 
 
@@ -56,7 +57,6 @@ namespace Emby.Server.Implementations.Library
             IUserManager userManager,
             IUserManager userManager,
             ILibraryManager libraryManager,
             ILibraryManager libraryManager,
             ILogger<MediaSourceManager> logger,
             ILogger<MediaSourceManager> logger,
-            IJsonSerializer jsonSerializer,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
             IUserDataManager userDataManager,
             IUserDataManager userDataManager,
             IMediaEncoder mediaEncoder)
             IMediaEncoder mediaEncoder)
@@ -65,7 +65,6 @@ namespace Emby.Server.Implementations.Library
             _userManager = userManager;
             _userManager = userManager;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _logger = logger;
             _logger = logger;
-            _jsonSerializer = jsonSerializer;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _userDataManager = userDataManager;
             _userDataManager = userDataManager;
             _mediaEncoder = mediaEncoder;
             _mediaEncoder = mediaEncoder;
@@ -504,7 +503,7 @@ namespace Emby.Server.Implementations.Library
                     // hack - these two values were taken from LiveTVMediaSourceProvider
                     // hack - these two values were taken from LiveTVMediaSourceProvider
                     string cacheKey = request.OpenToken;
                     string cacheKey = request.OpenToken;
 
 
-                    await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _appPaths)
+                    await new LiveStreamHelper(_mediaEncoder, _logger, _appPaths)
                         .AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken)
                         .AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken)
                         .ConfigureAwait(false);
                         .ConfigureAwait(false);
                 }
                 }
@@ -516,9 +515,9 @@ namespace Emby.Server.Implementations.Library
             }
             }
 
 
             // TODO: @bond Fix
             // TODO: @bond Fix
-            var json = _jsonSerializer.SerializeToString(mediaSource);
+            var json = JsonSerializer.Serialize(mediaSource, _jsonOptions);
             _logger.LogInformation("Live stream opened: " + json);
             _logger.LogInformation("Live stream opened: " + json);
-            var clone = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
+            var clone = JsonSerializer.Deserialize<MediaSourceInfo>(json, _jsonOptions);
 
 
             if (!request.UserId.Equals(Guid.Empty))
             if (!request.UserId.Equals(Guid.Empty))
             {
             {
@@ -643,7 +642,8 @@ namespace Emby.Server.Implementations.Library
             {
             {
                 try
                 try
                 {
                 {
-                    mediaInfo = _jsonSerializer.DeserializeFromFile<MediaInfo>(cacheFilePath);
+                    await using FileStream jsonStream = File.OpenRead(cacheFilePath);
+                    mediaInfo = await JsonSerializer.DeserializeAsync<MediaInfo>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
 
                     // _logger.LogDebug("Found cached media info");
                     // _logger.LogDebug("Found cached media info");
                 }
                 }
@@ -679,7 +679,8 @@ namespace Emby.Server.Implementations.Library
                 if (cacheFilePath != null)
                 if (cacheFilePath != null)
                 {
                 {
                     Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
                     Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
-                    _jsonSerializer.SerializeToFile(mediaInfo, cacheFilePath);
+                    await using FileStream createStream = File.Create(cacheFilePath);
+                    await JsonSerializer.SerializeAsync(createStream, mediaInfo, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
 
                     // _logger.LogDebug("Saved media info to {0}", cacheFilePath);
                     // _logger.LogDebug("Saved media info to {0}", cacheFilePath);
                 }
                 }

+ 4 - 8
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -36,7 +36,6 @@ using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Emby.Server.Implementations.LiveTv.EmbyTV
 namespace Emby.Server.Implementations.LiveTv.EmbyTV
@@ -51,7 +50,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         private readonly ILogger<EmbyTV> _logger;
         private readonly ILogger<EmbyTV> _logger;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
-        private readonly IJsonSerializer _jsonSerializer;
 
 
         private readonly ItemDataProvider<SeriesTimerInfo> _seriesTimerProvider;
         private readonly ItemDataProvider<SeriesTimerInfo> _seriesTimerProvider;
         private readonly TimerManager _timerProvider;
         private readonly TimerManager _timerProvider;
@@ -81,7 +79,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             IStreamHelper streamHelper,
             IStreamHelper streamHelper,
             IMediaSourceManager mediaSourceManager,
             IMediaSourceManager mediaSourceManager,
             ILogger<EmbyTV> logger,
             ILogger<EmbyTV> logger,
-            IJsonSerializer jsonSerializer,
             IHttpClientFactory httpClientFactory,
             IHttpClientFactory httpClientFactory,
             IServerConfigurationManager config,
             IServerConfigurationManager config,
             ILiveTvManager liveTvManager,
             ILiveTvManager liveTvManager,
@@ -103,12 +100,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             _providerManager = providerManager;
             _providerManager = providerManager;
             _mediaEncoder = mediaEncoder;
             _mediaEncoder = mediaEncoder;
             _liveTvManager = (LiveTvManager)liveTvManager;
             _liveTvManager = (LiveTvManager)liveTvManager;
-            _jsonSerializer = jsonSerializer;
             _mediaSourceManager = mediaSourceManager;
             _mediaSourceManager = mediaSourceManager;
             _streamHelper = streamHelper;
             _streamHelper = streamHelper;
 
 
-            _seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers.json"));
-            _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers.json"));
+            _seriesTimerProvider = new SeriesTimerManager(_logger, Path.Combine(DataPath, "seriestimers.json"));
+            _timerProvider = new TimerManager(_logger, Path.Combine(DataPath, "timers.json"));
             _timerProvider.TimerFired += OnTimerProviderTimerFired;
             _timerProvider.TimerFired += OnTimerProviderTimerFired;
 
 
             _config.NamedConfigurationUpdated += OnNamedConfigurationUpdated;
             _config.NamedConfigurationUpdated += OnNamedConfigurationUpdated;
@@ -1052,7 +1048,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 IgnoreIndex = true
                 IgnoreIndex = true
             };
             };
 
 
-            await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _config.CommonApplicationPaths)
+            await new LiveStreamHelper(_mediaEncoder, _logger, _config.CommonApplicationPaths)
                 .AddMediaInfoWithProbe(stream, false, false, cancellationToken).ConfigureAwait(false);
                 .AddMediaInfoWithProbe(stream, false, false, cancellationToken).ConfigureAwait(false);
 
 
             return new List<MediaSourceInfo>
             return new List<MediaSourceInfo>
@@ -1635,7 +1631,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         {
         {
             if (mediaSource.RequiresLooping || !(mediaSource.Container ?? string.Empty).EndsWith("ts", StringComparison.OrdinalIgnoreCase) || (mediaSource.Protocol != MediaProtocol.File && mediaSource.Protocol != MediaProtocol.Http))
             if (mediaSource.RequiresLooping || !(mediaSource.Container ?? string.Empty).EndsWith("ts", StringComparison.OrdinalIgnoreCase) || (mediaSource.Protocol != MediaProtocol.File && mediaSource.Protocol != MediaProtocol.Http))
             {
             {
-                return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _config);
+                return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _config);
             }
             }
 
 
             return new DirectRecorder(_logger, _httpClientFactory, _streamHelper);
             return new DirectRecorder(_logger, _httpClientFactory, _streamHelper);

+ 6 - 10
Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -6,16 +6,17 @@ using System.Diagnostics;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Text;
 using System.Text;
+using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Emby.Server.Implementations.LiveTv.EmbyTV
 namespace Emby.Server.Implementations.LiveTv.EmbyTV
@@ -25,10 +26,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IMediaEncoder _mediaEncoder;
         private readonly IMediaEncoder _mediaEncoder;
         private readonly IServerApplicationPaths _appPaths;
         private readonly IServerApplicationPaths _appPaths;
-        private readonly IJsonSerializer _json;
         private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
         private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
         private readonly IServerConfigurationManager _serverConfigurationManager;
         private readonly IServerConfigurationManager _serverConfigurationManager;
-
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
         private bool _hasExited;
         private bool _hasExited;
         private Stream _logFileStream;
         private Stream _logFileStream;
         private string _targetPath;
         private string _targetPath;
@@ -38,13 +38,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             ILogger logger,
             ILogger logger,
             IMediaEncoder mediaEncoder,
             IMediaEncoder mediaEncoder,
             IServerApplicationPaths appPaths,
             IServerApplicationPaths appPaths,
-            IJsonSerializer json,
             IServerConfigurationManager serverConfigurationManager)
             IServerConfigurationManager serverConfigurationManager)
         {
         {
             _logger = logger;
             _logger = logger;
             _mediaEncoder = mediaEncoder;
             _mediaEncoder = mediaEncoder;
             _appPaths = appPaths;
             _appPaths = appPaths;
-            _json = json;
             _serverConfigurationManager = serverConfigurationManager;
             _serverConfigurationManager = serverConfigurationManager;
         }
         }
 
 
@@ -66,7 +64,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             _logger.LogInformation("Recording completed to file {0}", targetFile);
             _logger.LogInformation("Recording completed to file {0}", targetFile);
         }
         }
 
 
-        private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
+        private async Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
         {
         {
             _targetPath = targetFile;
             _targetPath = targetFile;
             Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
             Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
@@ -95,8 +93,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
             // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
             _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
             _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
 
 
-            var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
-            _logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);
+            await JsonSerializer.SerializeAsync(_logFileStream, mediaSource, _jsonOptions, cancellationToken).ConfigureAwait(false);
+            await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false);
 
 
             _process = new Process
             _process = new Process
             {
             {
@@ -115,8 +113,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             _ = StartStreamingLog(_process.StandardError.BaseStream, _logFileStream);
             _ = StartStreamingLog(_process.StandardError.BaseStream, _logFileStream);
 
 
             _logger.LogInformation("ffmpeg recording process started for {0}", _targetPath);
             _logger.LogInformation("ffmpeg recording process started for {0}", _targetPath);
-
-            return _taskCompletionSource.Task;
         }
         }
 
 
         private string GetCommandLineArgs(MediaSourceInfo mediaSource, string inputTempFile, string targetFile, TimeSpan duration)
         private string GetCommandLineArgs(MediaSourceInfo mediaSource, string inputTempFile, string targetFile, TimeSpan duration)

+ 9 - 6
Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs

@@ -4,7 +4,10 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
-using MediaBrowser.Model.Serialization;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Json;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Emby.Server.Implementations.LiveTv.EmbyTV
 namespace Emby.Server.Implementations.LiveTv.EmbyTV
@@ -12,18 +15,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
     public class ItemDataProvider<T>
     public class ItemDataProvider<T>
         where T : class
         where T : class
     {
     {
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly string _dataPath;
         private readonly string _dataPath;
         private readonly object _fileDataLock = new object();
         private readonly object _fileDataLock = new object();
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
         private T[] _items;
         private T[] _items;
 
 
         public ItemDataProvider(
         public ItemDataProvider(
-            IJsonSerializer jsonSerializer,
             ILogger logger,
             ILogger logger,
             string dataPath,
             string dataPath,
             Func<T, T, bool> equalityComparer)
             Func<T, T, bool> equalityComparer)
         {
         {
-            _jsonSerializer = jsonSerializer;
             Logger = logger;
             Logger = logger;
             _dataPath = dataPath;
             _dataPath = dataPath;
             EqualityComparer = equalityComparer;
             EqualityComparer = equalityComparer;
@@ -46,7 +47,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
                 try
                 try
                 {
                 {
-                    _items = _jsonSerializer.DeserializeFromFile<T[]>(_dataPath);
+                    var jsonString = File.ReadAllText(_dataPath, Encoding.UTF8);
+                    _items = JsonSerializer.Deserialize<T[]>(jsonString, _jsonOptions);
                     return;
                     return;
                 }
                 }
                 catch (Exception ex)
                 catch (Exception ex)
@@ -61,7 +63,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         private void SaveList()
         private void SaveList()
         {
         {
             Directory.CreateDirectory(Path.GetDirectoryName(_dataPath));
             Directory.CreateDirectory(Path.GetDirectoryName(_dataPath));
-            _jsonSerializer.SerializeToFile(_items, _dataPath);
+            var jsonString = JsonSerializer.Serialize(_items, _jsonOptions);
+            File.WriteAllText(_dataPath, jsonString);
         }
         }
 
 
         public IReadOnlyList<T> GetAll()
         public IReadOnlyList<T> GetAll()

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

@@ -9,8 +9,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 {
 {
     public class SeriesTimerManager : ItemDataProvider<SeriesTimerInfo>
     public class SeriesTimerManager : ItemDataProvider<SeriesTimerInfo>
     {
     {
-        public SeriesTimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath)
-            : base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
+        public SeriesTimerManager(ILogger logger, string dataPath)
+            : base(logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
         {
         {
         }
         }
 
 

+ 2 - 3
Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs

@@ -8,7 +8,6 @@ using System.Threading;
 using Jellyfin.Data.Events;
 using Jellyfin.Data.Events;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Serialization;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Emby.Server.Implementations.LiveTv.EmbyTV
 namespace Emby.Server.Implementations.LiveTv.EmbyTV
@@ -17,8 +16,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
     {
     {
         private readonly ConcurrentDictionary<string, Timer> _timers = new ConcurrentDictionary<string, Timer>(StringComparer.OrdinalIgnoreCase);
         private readonly ConcurrentDictionary<string, Timer> _timers = new ConcurrentDictionary<string, Timer>(StringComparer.OrdinalIgnoreCase);
 
 
-        public TimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath)
-            : base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
+        public TimerManager(ILogger logger, string dataPath)
+            : base(logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
         {
         {
         }
         }
 
 

+ 11 - 12
Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -9,16 +9,17 @@ using System.Net;
 using System.Net.Http;
 using System.Net.Http;
 using System.Net.Mime;
 using System.Net.Mime;
 using System.Text;
 using System.Text;
+using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common;
 using MediaBrowser.Common;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Model.Cryptography;
 using MediaBrowser.Model.Cryptography;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Serialization;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
 namespace Emby.Server.Implementations.LiveTv.Listings
 namespace Emby.Server.Implementations.LiveTv.Listings
@@ -28,7 +29,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
         private const string ApiUrl = "https://json.schedulesdirect.org/20141201";
         private const string ApiUrl = "https://json.schedulesdirect.org/20141201";
 
 
         private readonly ILogger<SchedulesDirect> _logger;
         private readonly ILogger<SchedulesDirect> _logger;
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1);
         private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1);
         private readonly IApplicationHost _appHost;
         private readonly IApplicationHost _appHost;
@@ -36,16 +36,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
 
         private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
         private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
         private DateTime _lastErrorResponse;
         private DateTime _lastErrorResponse;
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
 
 
         public SchedulesDirect(
         public SchedulesDirect(
             ILogger<SchedulesDirect> logger,
             ILogger<SchedulesDirect> logger,
-            IJsonSerializer jsonSerializer,
             IHttpClientFactory httpClientFactory,
             IHttpClientFactory httpClientFactory,
             IApplicationHost appHost,
             IApplicationHost appHost,
             ICryptoProvider cryptoProvider)
             ICryptoProvider cryptoProvider)
         {
         {
             _logger = logger;
             _logger = logger;
-            _jsonSerializer = jsonSerializer;
             _httpClientFactory = httpClientFactory;
             _httpClientFactory = httpClientFactory;
             _appHost = appHost;
             _appHost = appHost;
             _cryptoProvider = cryptoProvider;
             _cryptoProvider = cryptoProvider;
@@ -104,7 +103,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                     }
                     }
                 };
                 };
 
 
-            var requestString = _jsonSerializer.SerializeToString(requestList);
+            var requestString = JsonSerializer.Serialize(requestList, _jsonOptions);
             _logger.LogDebug("Request string for schedules is: {RequestString}", requestString);
             _logger.LogDebug("Request string for schedules is: {RequestString}", requestString);
 
 
             using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/schedules");
             using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/schedules");
@@ -112,7 +111,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             options.Headers.TryAddWithoutValidation("token", token);
             options.Headers.TryAddWithoutValidation("token", token);
             using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
             using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
             await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
             await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-            var dailySchedules = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.Day>>(responseStream).ConfigureAwait(false);
+            var dailySchedules = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.Day>>(responseStream, _jsonOptions).ConfigureAwait(false);
             _logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
             _logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
 
 
             using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs");
             using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs");
@@ -123,7 +122,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
 
             using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
             using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
             await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
             await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-            var programDetails = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ProgramDetails>>(innerResponseStream).ConfigureAwait(false);
+            var programDetails = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.ProgramDetails>>(innerResponseStream, _jsonOptions).ConfigureAwait(false);
             var programDict = programDetails.ToDictionary(p => p.programID, y => y);
             var programDict = programDetails.ToDictionary(p => p.programID, y => y);
 
 
             var programIdsWithImages =
             var programIdsWithImages =
@@ -479,7 +478,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             {
             {
                 using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false);
                 using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false);
                 await using var response = await innerResponse2.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
                 await using var response = await innerResponse2.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-                return await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ShowImages>>(response).ConfigureAwait(false);
+                return await JsonSerializer.DeserializeAsync<List<ScheduleDirect.ShowImages>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
@@ -508,7 +507,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 using var httpResponse = await Send(options, false, info, cancellationToken).ConfigureAwait(false);
                 using var httpResponse = await Send(options, false, info, cancellationToken).ConfigureAwait(false);
                 await using var response = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
                 await using var response = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
 
 
-                var root = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.Headends>>(response).ConfigureAwait(false);
+                var root = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.Headends>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
 
                 if (root != null)
                 if (root != null)
                 {
                 {
@@ -649,7 +648,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false);
             using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false);
             response.EnsureSuccessStatusCode();
             response.EnsureSuccessStatusCode();
             await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
             await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-            var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Token>(stream).ConfigureAwait(false);
+            var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Token>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
             if (string.Equals(root.message, "OK", StringComparison.Ordinal))
             if (string.Equals(root.message, "OK", StringComparison.Ordinal))
             {
             {
                 _logger.LogInformation("Authenticated with Schedules Direct token: " + root.token);
                 _logger.LogInformation("Authenticated with Schedules Direct token: " + root.token);
@@ -705,7 +704,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 httpResponse.EnsureSuccessStatusCode();
                 httpResponse.EnsureSuccessStatusCode();
                 await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
                 await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
                 using var response = httpResponse.Content;
                 using var response = httpResponse.Content;
-                var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Lineups>(stream).ConfigureAwait(false);
+                var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Lineups>(stream, _jsonOptions).ConfigureAwait(false);
 
 
                 return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
                 return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
             }
             }
@@ -777,7 +776,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
 
             using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
             using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
             await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
             await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-            var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Channel>(stream).ConfigureAwait(false);
+            var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Channel>(stream, _jsonOptions).ConfigureAwait(false);
             _logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count);
             _logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count);
             _logger.LogInformation("Mapping Stations to Channel");
             _logger.LogInformation("Mapping Stations to Channel");
 
 

+ 10 - 7
Emby.Server.Implementations/Localization/LocalizationManager.cs

@@ -5,7 +5,9 @@ using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
+using System.Text.Json;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
@@ -24,7 +26,6 @@ namespace Emby.Server.Implementations.Localization
         private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
         private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
 
 
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IServerConfigurationManager _configurationManager;
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly ILogger<LocalizationManager> _logger;
         private readonly ILogger<LocalizationManager> _logger;
 
 
         private readonly Dictionary<string, Dictionary<string, ParentalRating>> _allParentalRatings =
         private readonly Dictionary<string, Dictionary<string, ParentalRating>> _allParentalRatings =
@@ -35,19 +36,18 @@ namespace Emby.Server.Implementations.Localization
 
 
         private List<CultureDto> _cultures;
         private List<CultureDto> _cultures;
 
 
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
+
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="LocalizationManager" /> class.
         /// Initializes a new instance of the <see cref="LocalizationManager" /> class.
         /// </summary>
         /// </summary>
         /// <param name="configurationManager">The configuration manager.</param>
         /// <param name="configurationManager">The configuration manager.</param>
-        /// <param name="jsonSerializer">The json serializer.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="logger">The logger.</param>
         public LocalizationManager(
         public LocalizationManager(
             IServerConfigurationManager configurationManager,
             IServerConfigurationManager configurationManager,
-            IJsonSerializer jsonSerializer,
             ILogger<LocalizationManager> logger)
             ILogger<LocalizationManager> logger)
         {
         {
             _configurationManager = configurationManager;
             _configurationManager = configurationManager;
-            _jsonSerializer = jsonSerializer;
             _logger = logger;
             _logger = logger;
         }
         }
 
 
@@ -179,8 +179,11 @@ namespace Emby.Server.Implementations.Localization
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public IEnumerable<CountryInfo> GetCountries()
         public IEnumerable<CountryInfo> GetCountries()
-            => _jsonSerializer.DeserializeFromStream<IEnumerable<CountryInfo>>(
-                    _assembly.GetManifestResourceStream("Emby.Server.Implementations.Localization.countries.json"));
+        {
+            StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream("Emby.Server.Implementations.Localization.countries.json"));
+
+            return JsonSerializer.Deserialize<IEnumerable<CountryInfo>>(reader.ReadToEnd(), _jsonOptions);
+        }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
         public IEnumerable<ParentalRating> GetParentalRatings()
         public IEnumerable<ParentalRating> GetParentalRatings()
@@ -344,7 +347,7 @@ namespace Emby.Server.Implementations.Localization
                 // If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain
                 // If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain
                 if (stream != null)
                 if (stream != null)
                 {
                 {
-                    var dict = await _jsonSerializer.DeserializeFromStreamAsync<Dictionary<string, string>>(stream).ConfigureAwait(false);
+                    var dict = await JsonSerializer.DeserializeAsync<Dictionary<string, string>>(stream, _jsonOptions).ConfigureAwait(false);
 
 
                     foreach (var key in dict.Keys)
                     foreach (var key in dict.Keys)
                     {
                     {

+ 24 - 18
Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs

@@ -4,13 +4,15 @@ using System;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Text;
+using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Jellyfin.Data.Events;
 using Jellyfin.Data.Events;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Common.Progress;
-using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Tasks;
 using MediaBrowser.Model.Tasks;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
@@ -21,11 +23,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
     /// </summary>
     /// </summary>
     public class ScheduledTaskWorker : IScheduledTaskWorker
     public class ScheduledTaskWorker : IScheduledTaskWorker
     {
     {
-        /// <summary>
-        /// Gets or sets the json serializer.
-        /// </summary>
-        /// <value>The json serializer.</value>
-        private readonly IJsonSerializer _jsonSerializer;
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets the application paths.
         /// Gets or sets the application paths.
@@ -69,13 +66,17 @@ namespace Emby.Server.Implementations.ScheduledTasks
         /// </summary>
         /// </summary>
         private string _id;
         private string _id;
 
 
+        /// <summary>
+        /// The options for the json Serializer.
+        /// </summary>
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
+
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
         /// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
         /// </summary>
         /// </summary>
         /// <param name="scheduledTask">The scheduled task.</param>
         /// <param name="scheduledTask">The scheduled task.</param>
         /// <param name="applicationPaths">The application paths.</param>
         /// <param name="applicationPaths">The application paths.</param>
         /// <param name="taskManager">The task manager.</param>
         /// <param name="taskManager">The task manager.</param>
-        /// <param name="jsonSerializer">The json serializer.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="logger">The logger.</param>
         /// <exception cref="ArgumentNullException">
         /// <exception cref="ArgumentNullException">
         /// scheduledTask
         /// scheduledTask
@@ -88,7 +89,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
         /// or
         /// or
         /// logger.
         /// logger.
         /// </exception>
         /// </exception>
-        public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger)
+        public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, ILogger logger)
         {
         {
             if (scheduledTask == null)
             if (scheduledTask == null)
             {
             {
@@ -105,11 +106,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
                 throw new ArgumentNullException(nameof(taskManager));
                 throw new ArgumentNullException(nameof(taskManager));
             }
             }
 
 
-            if (jsonSerializer == null)
-            {
-                throw new ArgumentNullException(nameof(jsonSerializer));
-            }
-
             if (logger == null)
             if (logger == null)
             {
             {
                 throw new ArgumentNullException(nameof(logger));
                 throw new ArgumentNullException(nameof(logger));
@@ -118,7 +114,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
             ScheduledTask = scheduledTask;
             ScheduledTask = scheduledTask;
             _applicationPaths = applicationPaths;
             _applicationPaths = applicationPaths;
             _taskManager = taskManager;
             _taskManager = taskManager;
-            _jsonSerializer = jsonSerializer;
             _logger = logger;
             _logger = logger;
 
 
             InitTriggerEvents();
             InitTriggerEvents();
@@ -150,7 +145,15 @@ namespace Emby.Server.Implementations.ScheduledTasks
                         {
                         {
                             try
                             try
                             {
                             {
-                                _lastExecutionResult = _jsonSerializer.DeserializeFromFile<TaskResult>(path);
+                                var jsonString = File.ReadAllText(path, Encoding.UTF8);
+                                if (!string.IsNullOrWhiteSpace(jsonString))
+                                {
+                                    _lastExecutionResult = JsonSerializer.Deserialize<TaskResult>(jsonString, _jsonOptions);
+                                }
+                                else
+                                {
+                                    _logger.LogDebug("Scheduled Task history file {Path} is empty. Skipping deserialization.", path);
+                                }
                             }
                             }
                             catch (Exception ex)
                             catch (Exception ex)
                             {
                             {
@@ -174,7 +177,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
 
 
                 lock (_lastExecutionResultSyncLock)
                 lock (_lastExecutionResultSyncLock)
                 {
                 {
-                    _jsonSerializer.SerializeToFile(value, path);
+                    using FileStream createStream = File.OpenWrite(path);
+                    JsonSerializer.SerializeAsync(createStream, value, _jsonOptions);
                 }
                 }
             }
             }
         }
         }
@@ -537,7 +541,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
             TaskTriggerInfo[] list = null;
             TaskTriggerInfo[] list = null;
             if (File.Exists(path))
             if (File.Exists(path))
             {
             {
-                list = _jsonSerializer.DeserializeFromFile<TaskTriggerInfo[]>(path);
+                var jsonString = File.ReadAllText(path, Encoding.UTF8);
+                list = JsonSerializer.Deserialize<TaskTriggerInfo[]>(jsonString, _jsonOptions);
             }
             }
 
 
             // Return defaults if file doesn't exist.
             // Return defaults if file doesn't exist.
@@ -573,7 +578,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
 
 
             Directory.CreateDirectory(Path.GetDirectoryName(path));
             Directory.CreateDirectory(Path.GetDirectoryName(path));
 
 
-            _jsonSerializer.SerializeToFile(triggers, path);
+            var json = JsonSerializer.Serialize(triggers, _jsonOptions);
+            File.WriteAllText(path, json, Encoding.UTF8);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 2 - 6
Emby.Server.Implementations/ScheduledTasks/TaskManager.cs

@@ -7,7 +7,6 @@ using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Jellyfin.Data.Events;
 using Jellyfin.Data.Events;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Tasks;
 using MediaBrowser.Model.Tasks;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 
 
@@ -19,6 +18,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
     public class TaskManager : ITaskManager
     public class TaskManager : ITaskManager
     {
     {
         public event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting;
         public event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting;
+
         public event EventHandler<TaskCompletionEventArgs> TaskCompleted;
         public event EventHandler<TaskCompletionEventArgs> TaskCompleted;
 
 
         /// <summary>
         /// <summary>
@@ -33,7 +33,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
         private readonly ConcurrentQueue<Tuple<Type, TaskOptions>> _taskQueue =
         private readonly ConcurrentQueue<Tuple<Type, TaskOptions>> _taskQueue =
             new ConcurrentQueue<Tuple<Type, TaskOptions>>();
             new ConcurrentQueue<Tuple<Type, TaskOptions>>();
 
 
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly IApplicationPaths _applicationPaths;
         private readonly IApplicationPaths _applicationPaths;
         private readonly ILogger<TaskManager> _logger;
         private readonly ILogger<TaskManager> _logger;
 
 
@@ -41,15 +40,12 @@ namespace Emby.Server.Implementations.ScheduledTasks
         /// Initializes a new instance of the <see cref="TaskManager" /> class.
         /// Initializes a new instance of the <see cref="TaskManager" /> class.
         /// </summary>
         /// </summary>
         /// <param name="applicationPaths">The application paths.</param>
         /// <param name="applicationPaths">The application paths.</param>
-        /// <param name="jsonSerializer">The json serializer.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="logger">The logger.</param>
         public TaskManager(
         public TaskManager(
             IApplicationPaths applicationPaths,
             IApplicationPaths applicationPaths,
-            IJsonSerializer jsonSerializer,
             ILogger<TaskManager> logger)
             ILogger<TaskManager> logger)
         {
         {
             _applicationPaths = applicationPaths;
             _applicationPaths = applicationPaths;
-            _jsonSerializer = jsonSerializer;
             _logger = logger;
             _logger = logger;
 
 
             ScheduledTasks = Array.Empty<IScheduledTaskWorker>();
             ScheduledTasks = Array.Empty<IScheduledTaskWorker>();
@@ -196,7 +192,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
         /// <param name="tasks">The tasks.</param>
         /// <param name="tasks">The tasks.</param>
         public void AddTasks(IEnumerable<IScheduledTask> tasks)
         public void AddTasks(IEnumerable<IScheduledTask> tasks)
         {
         {
-            var list = tasks.Select(t => new ScheduledTaskWorker(t, _applicationPaths, this, _jsonSerializer, _logger));
+            var list = tasks.Select(t => new ScheduledTaskWorker(t, _applicationPaths, this, _logger));
 
 
             ScheduledTasks = ScheduledTasks.Concat(list).ToArray();
             ScheduledTasks = ScheduledTasks.Concat(list).ToArray();
         }
         }

+ 0 - 281
Emby.Server.Implementations/Serialization/JsonSerializer.cs

@@ -1,281 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Globalization;
-using System.IO;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Serialization;
-
-namespace Emby.Server.Implementations.Serialization
-{
-    /// <summary>
-    /// Provides a wrapper around third party json serialization.
-    /// </summary>
-    public class JsonSerializer : IJsonSerializer
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="JsonSerializer" /> class.
-        /// </summary>
-        public JsonSerializer()
-        {
-            ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.DateHandler.ISO8601;
-            ServiceStack.Text.JsConfig.ExcludeTypeInfo = true;
-            ServiceStack.Text.JsConfig.IncludeNullValues = false;
-            ServiceStack.Text.JsConfig.AlwaysUseUtc = true;
-            ServiceStack.Text.JsConfig.AssumeUtc = true;
-
-            ServiceStack.Text.JsConfig<Guid>.SerializeFn = SerializeGuid;
-        }
-
-        /// <summary>
-        /// Serializes to stream.
-        /// </summary>
-        /// <param name="obj">The obj.</param>
-        /// <param name="stream">The stream.</param>
-        /// <exception cref="ArgumentNullException">obj</exception>
-        public void SerializeToStream(object obj, Stream stream)
-        {
-            if (obj == null)
-            {
-                throw new ArgumentNullException(nameof(obj));
-            }
-
-            if (stream == null)
-            {
-                throw new ArgumentNullException(nameof(stream));
-            }
-
-            ServiceStack.Text.JsonSerializer.SerializeToStream(obj, obj.GetType(), stream);
-        }
-
-        /// <summary>
-        /// Serializes to stream.
-        /// </summary>
-        /// <param name="obj">The obj.</param>
-        /// <param name="stream">The stream.</param>
-        /// <exception cref="ArgumentNullException">obj</exception>
-        public void SerializeToStream<T>(T obj, Stream stream)
-        {
-            if (obj == null)
-            {
-                throw new ArgumentNullException(nameof(obj));
-            }
-
-            if (stream == null)
-            {
-                throw new ArgumentNullException(nameof(stream));
-            }
-
-            ServiceStack.Text.JsonSerializer.SerializeToStream<T>(obj, stream);
-        }
-
-        /// <summary>
-        /// Serializes to file.
-        /// </summary>
-        /// <param name="obj">The obj.</param>
-        /// <param name="file">The file.</param>
-        /// <exception cref="ArgumentNullException">obj</exception>
-        public void SerializeToFile(object obj, string file)
-        {
-            if (obj == null)
-            {
-                throw new ArgumentNullException(nameof(obj));
-            }
-
-            if (string.IsNullOrEmpty(file))
-            {
-                throw new ArgumentNullException(nameof(file));
-            }
-
-            using (var stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read))
-            {
-                SerializeToStream(obj, stream);
-            }
-        }
-
-        private static Stream OpenFile(string path)
-        {
-            return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072);
-        }
-
-        /// <summary>
-        /// Deserializes from file.
-        /// </summary>
-        /// <param name="type">The type.</param>
-        /// <param name="file">The file.</param>
-        /// <returns>System.Object.</returns>
-        /// <exception cref="ArgumentNullException">type</exception>
-        public object DeserializeFromFile(Type type, string file)
-        {
-            if (type == null)
-            {
-                throw new ArgumentNullException(nameof(type));
-            }
-
-            if (string.IsNullOrEmpty(file))
-            {
-                throw new ArgumentNullException(nameof(file));
-            }
-
-            using (var stream = OpenFile(file))
-            {
-                return DeserializeFromStream(stream, type);
-            }
-        }
-
-        /// <summary>
-        /// Deserializes from file.
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        /// <param name="file">The file.</param>
-        /// <returns>``0.</returns>
-        /// <exception cref="ArgumentNullException">file</exception>
-        public T DeserializeFromFile<T>(string file)
-            where T : class
-        {
-            if (string.IsNullOrEmpty(file))
-            {
-                throw new ArgumentNullException(nameof(file));
-            }
-
-            using (var stream = OpenFile(file))
-            {
-                return DeserializeFromStream<T>(stream);
-            }
-        }
-
-        /// <summary>
-        /// Deserializes from stream.
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        /// <param name="stream">The stream.</param>
-        /// <returns>``0.</returns>
-        /// <exception cref="ArgumentNullException">stream</exception>
-        public T DeserializeFromStream<T>(Stream stream)
-        {
-            if (stream == null)
-            {
-                throw new ArgumentNullException(nameof(stream));
-            }
-
-            return ServiceStack.Text.JsonSerializer.DeserializeFromStream<T>(stream);
-        }
-
-        public Task<T> DeserializeFromStreamAsync<T>(Stream stream)
-        {
-            if (stream == null)
-            {
-                throw new ArgumentNullException(nameof(stream));
-            }
-
-            return ServiceStack.Text.JsonSerializer.DeserializeFromStreamAsync<T>(stream);
-        }
-
-        /// <summary>
-        /// Deserializes from string.
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        /// <param name="text">The text.</param>
-        /// <returns>``0.</returns>
-        /// <exception cref="ArgumentNullException">text</exception>
-        public T DeserializeFromString<T>(string text)
-        {
-            if (string.IsNullOrEmpty(text))
-            {
-                throw new ArgumentNullException(nameof(text));
-            }
-
-            return ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(text);
-        }
-
-        /// <summary>
-        /// Deserializes from stream.
-        /// </summary>
-        /// <param name="stream">The stream.</param>
-        /// <param name="type">The type.</param>
-        /// <returns>System.Object.</returns>
-        /// <exception cref="ArgumentNullException">stream</exception>
-        public object DeserializeFromStream(Stream stream, Type type)
-        {
-            if (stream == null)
-            {
-                throw new ArgumentNullException(nameof(stream));
-            }
-
-            if (type == null)
-            {
-                throw new ArgumentNullException(nameof(type));
-            }
-
-            return ServiceStack.Text.JsonSerializer.DeserializeFromStream(type, stream);
-        }
-
-        public async Task<object> DeserializeFromStreamAsync(Stream stream, Type type)
-        {
-            if (stream == null)
-            {
-                throw new ArgumentNullException(nameof(stream));
-            }
-
-            if (type == null)
-            {
-                throw new ArgumentNullException(nameof(type));
-            }
-
-            using (var reader = new StreamReader(stream))
-            {
-                var json = await reader.ReadToEndAsync().ConfigureAwait(false);
-
-                return ServiceStack.Text.JsonSerializer.DeserializeFromString(json, type);
-            }
-        }
-
-        private static string SerializeGuid(Guid guid)
-        {
-            if (guid.Equals(Guid.Empty))
-            {
-                return null;
-            }
-
-            return guid.ToString("N", CultureInfo.InvariantCulture);
-        }
-
-        /// <summary>
-        /// Deserializes from string.
-        /// </summary>
-        /// <param name="json">The json.</param>
-        /// <param name="type">The type.</param>
-        /// <returns>System.Object.</returns>
-        /// <exception cref="ArgumentNullException">json</exception>
-        public object DeserializeFromString(string json, Type type)
-        {
-            if (string.IsNullOrEmpty(json))
-            {
-                throw new ArgumentNullException(nameof(json));
-            }
-
-            if (type == null)
-            {
-                throw new ArgumentNullException(nameof(type));
-            }
-
-            return ServiceStack.Text.JsonSerializer.DeserializeFromString(json, type);
-        }
-
-        /// <summary>
-        /// Serializes to string.
-        /// </summary>
-        /// <param name="obj">The obj.</param>
-        /// <returns>System.String.</returns>
-        /// <exception cref="ArgumentNullException">obj</exception>
-        public string SerializeToString(object obj)
-        {
-            if (obj == null)
-            {
-                throw new ArgumentNullException(nameof(obj));
-            }
-
-            return ServiceStack.Text.JsonSerializer.SerializeToString(obj, obj.GetType());
-        }
-    }
-}

+ 35 - 0
MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs

@@ -0,0 +1,35 @@
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace MediaBrowser.Common.Json.Converters
+{
+    /// <summary>
+    /// Converts a string <c>N/A</c> to <c>string.Empty</c>.
+    /// </summary>
+    public class JsonOmdbNotAvailableStringConverter : JsonConverter<string>
+    {
+        /// <inheritdoc />
+        public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+        {
+            if (reader.TokenType == JsonTokenType.String)
+            {
+                var str = reader.GetString();
+                if (str != null && str.Equals("N/A", StringComparison.OrdinalIgnoreCase))
+                {
+                    return null;
+                }
+
+                return str;
+            }
+
+            return JsonSerializer.Deserialize<string>(ref reader, options);
+        }
+
+        /// <inheritdoc />
+        public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
+        {
+            JsonSerializer.Serialize(value, options);
+        }
+    }
+}

+ 35 - 0
MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStructConverter.cs

@@ -0,0 +1,35 @@
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace MediaBrowser.Common.Json.Converters
+{
+    /// <summary>
+    /// Converts a string <c>N/A</c> to <c>string.Empty</c>.
+    /// </summary>
+    /// <typeparam name="T">The resulting type.</typeparam>
+    public class JsonOmdbNotAvailableStructConverter<T> : JsonConverter<T?>
+        where T : struct
+    {
+        /// <inheritdoc />
+        public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+        {
+            if (reader.TokenType == JsonTokenType.String)
+            {
+                var str = reader.GetString();
+                if (str != null && str.Equals("N/A", StringComparison.OrdinalIgnoreCase))
+                {
+                    return null;
+                }
+            }
+
+            return JsonSerializer.Deserialize<T>(ref reader, options);
+        }
+
+        /// <inheritdoc />
+        public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
+        {
+            JsonSerializer.Serialize(value, options);
+        }
+    }
+}

+ 4 - 3
MediaBrowser.Controller/Entities/CollectionFolder.cs

@@ -4,9 +4,11 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Text.Json;
 using System.Text.Json.Serialization;
 using System.Text.Json.Serialization;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
@@ -24,10 +26,9 @@ namespace MediaBrowser.Controller.Entities
     /// </summary>
     /// </summary>
     public class CollectionFolder : Folder, ICollectionFolder
     public class CollectionFolder : Folder, ICollectionFolder
     {
     {
+        private static readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
         public static IXmlSerializer XmlSerializer { get; set; }
         public static IXmlSerializer XmlSerializer { get; set; }
 
 
-        public static IJsonSerializer JsonSerializer { get; set; }
-
         public static IServerApplicationHost ApplicationHost { get; set; }
         public static IServerApplicationHost ApplicationHost { get; set; }
 
 
         public CollectionFolder()
         public CollectionFolder()
@@ -122,7 +123,7 @@ namespace MediaBrowser.Controller.Entities
             {
             {
                 LibraryOptions[path] = options;
                 LibraryOptions[path] = options;
 
 
-                var clone = JsonSerializer.DeserializeFromString<LibraryOptions>(JsonSerializer.SerializeToString(options));
+                var clone = JsonSerializer.Deserialize<LibraryOptions>(JsonSerializer.Serialize(options, _jsonOptions), _jsonOptions);
                 foreach (var mediaPath in clone.PathInfos)
                 foreach (var mediaPath in clone.PathInfos)
                 {
                 {
                     if (!string.IsNullOrEmpty(mediaPath.Path))
                     if (!string.IsNullOrEmpty(mediaPath.Path))

+ 0 - 102
MediaBrowser.Model/Serialization/IJsonSerializer.cs

@@ -1,102 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.Serialization
-{
-    public interface IJsonSerializer
-    {
-        /// <summary>
-        /// Serializes to stream.
-        /// </summary>
-        /// <param name="obj">The obj.</param>
-        /// <param name="stream">The stream.</param>
-        /// <exception cref="ArgumentNullException">obj</exception>
-        void SerializeToStream(object obj, Stream stream);
-
-        /// <summary>
-        /// Serializes to stream.
-        /// </summary>
-        /// <param name="obj">The obj.</param>
-        /// <param name="stream">The stream.</param>
-        /// <exception cref="ArgumentNullException">obj</exception>
-        void SerializeToStream<T>(T obj, Stream stream);
-
-        /// <summary>
-        /// Serializes to file.
-        /// </summary>
-        /// <param name="obj">The obj.</param>
-        /// <param name="file">The file.</param>
-        /// <exception cref="ArgumentNullException">obj</exception>
-        void SerializeToFile(object obj, string file);
-
-        /// <summary>
-        /// Deserializes from file.
-        /// </summary>
-        /// <param name="type">The type.</param>
-        /// <param name="file">The file.</param>
-        /// <returns>System.Object.</returns>
-        /// <exception cref="ArgumentNullException">type</exception>
-        object DeserializeFromFile(Type type, string file);
-
-        /// <summary>
-        /// Deserializes from file.
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        /// <param name="file">The file.</param>
-        /// <returns>``0.</returns>
-        /// <exception cref="ArgumentNullException">file</exception>
-        T DeserializeFromFile<T>(string file)
-            where T : class;
-
-        /// <summary>
-        /// Deserializes from stream.
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        /// <param name="stream">The stream.</param>
-        /// <returns>``0.</returns>
-        /// <exception cref="ArgumentNullException">stream</exception>
-        T DeserializeFromStream<T>(Stream stream);
-
-        /// <summary>
-        /// Deserializes from string.
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        /// <param name="text">The text.</param>
-        /// <returns>``0.</returns>
-        /// <exception cref="ArgumentNullException">text</exception>
-        T DeserializeFromString<T>(string text);
-
-        /// <summary>
-        /// Deserializes from stream.
-        /// </summary>
-        /// <param name="stream">The stream.</param>
-        /// <param name="type">The type.</param>
-        /// <returns>System.Object.</returns>
-        /// <exception cref="ArgumentNullException">stream</exception>
-        object DeserializeFromStream(Stream stream, Type type);
-
-        /// <summary>
-        /// Deserializes from string.
-        /// </summary>
-        /// <param name="json">The json.</param>
-        /// <param name="type">The type.</param>
-        /// <returns>System.Object.</returns>
-        /// <exception cref="ArgumentNullException">json</exception>
-        object DeserializeFromString(string json, Type type);
-
-        /// <summary>
-        /// Serializes to string.
-        /// </summary>
-        /// <param name="obj">The obj.</param>
-        /// <returns>System.String.</returns>
-        /// <exception cref="ArgumentNullException">obj</exception>
-        string SerializeToString(object obj);
-
-        Task<object> DeserializeFromStreamAsync(Stream stream, Type type);
-        Task<T> DeserializeFromStreamAsync<T>(Stream stream);
-    }
-}

+ 7 - 4
MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs

@@ -1,9 +1,12 @@
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.IO;
 using System.Net.Http;
 using System.Net.Http;
+using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
@@ -19,13 +22,12 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
     {
     {
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly IHttpClientFactory _httpClientFactory;
-        private readonly IJsonSerializer _json;
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
 
 
-        public AudioDbAlbumImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory, IJsonSerializer json)
+        public AudioDbAlbumImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory)
         {
         {
             _config = config;
             _config = config;
             _httpClientFactory = httpClientFactory;
             _httpClientFactory = httpClientFactory;
-            _json = json;
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
@@ -56,7 +58,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
 
 
                 var path = AudioDbAlbumProvider.GetAlbumInfoPath(_config.ApplicationPaths, id);
                 var path = AudioDbAlbumProvider.GetAlbumInfoPath(_config.ApplicationPaths, id);
 
 
-                var obj = _json.DeserializeFromFile<AudioDbAlbumProvider.RootObject>(path);
+                await using FileStream jsonStream = File.OpenRead(path);
+                var obj = await JsonSerializer.DeserializeAsync<AudioDbAlbumProvider.RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
 
                 if (obj != null && obj.album != null && obj.album.Count > 0)
                 if (obj != null && obj.album != null && obj.album.Count > 0)
                 {
                 {

+ 6 - 4
MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs

@@ -6,10 +6,12 @@ using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Net.Http;
 using System.Net.Http;
+using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Audio;
@@ -27,16 +29,15 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly IHttpClientFactory _httpClientFactory;
-        private readonly IJsonSerializer _json;
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
 
 
         public static AudioDbAlbumProvider Current;
         public static AudioDbAlbumProvider Current;
 
 
-        public AudioDbAlbumProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json)
+        public AudioDbAlbumProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory)
         {
         {
             _config = config;
             _config = config;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _httpClientFactory = httpClientFactory;
             _httpClientFactory = httpClientFactory;
-            _json = json;
 
 
             Current = this;
             Current = this;
         }
         }
@@ -64,7 +65,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
 
 
                 var path = GetAlbumInfoPath(_config.ApplicationPaths, id);
                 var path = GetAlbumInfoPath(_config.ApplicationPaths, id);
 
 
-                var obj = _json.DeserializeFromFile<RootObject>(path);
+                await using FileStream jsonStream = File.OpenRead(path);
+                var obj = await JsonSerializer.DeserializeAsync<RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
 
                 if (obj != null && obj.album != null && obj.album.Count > 0)
                 if (obj != null && obj.album != null && obj.album.Count > 0)
                 {
                 {

+ 7 - 4
MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs

@@ -1,9 +1,12 @@
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.IO;
 using System.Net.Http;
 using System.Net.Http;
+using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
@@ -19,12 +22,11 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
     {
     {
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly IHttpClientFactory _httpClientFactory;
-        private readonly IJsonSerializer _json;
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
 
 
-        public AudioDbArtistImageProvider(IServerConfigurationManager config, IJsonSerializer json, IHttpClientFactory httpClientFactory)
+        public AudioDbArtistImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory)
         {
         {
             _config = config;
             _config = config;
-            _json = json;
             _httpClientFactory = httpClientFactory;
             _httpClientFactory = httpClientFactory;
         }
         }
 
 
@@ -58,7 +60,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
 
 
                 var path = AudioDbArtistProvider.GetArtistInfoPath(_config.ApplicationPaths, id);
                 var path = AudioDbArtistProvider.GetArtistInfoPath(_config.ApplicationPaths, id);
 
 
-                var obj = _json.DeserializeFromFile<AudioDbArtistProvider.RootObject>(path);
+                await using FileStream jsonStream = File.OpenRead(path);
+                var obj = await JsonSerializer.DeserializeAsync<AudioDbArtistProvider.RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
 
                 if (obj != null && obj.artists != null && obj.artists.Count > 0)
                 if (obj != null && obj.artists != null && obj.artists.Count > 0)
                 {
                 {

+ 6 - 4
MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs

@@ -5,10 +5,12 @@ using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Net.Http;
 using System.Net.Http;
+using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Json;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Audio;
@@ -29,14 +31,13 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly IHttpClientFactory _httpClientFactory;
-        private readonly IJsonSerializer _json;
+        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
 
 
-        public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json)
+        public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory)
         {
         {
             _config = config;
             _config = config;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _httpClientFactory = httpClientFactory;
             _httpClientFactory = httpClientFactory;
-            _json = json;
             Current = this;
             Current = this;
         }
         }
 
 
@@ -65,7 +66,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
 
 
                 var path = GetArtistInfoPath(_config.ApplicationPaths, id);
                 var path = GetArtistInfoPath(_config.ApplicationPaths, id);
 
 
-                var obj = _json.DeserializeFromFile<RootObject>(path);
+                await using FileStream jsonStream = File.OpenRead(path);
+                var obj = await JsonSerializer.DeserializeAsync<RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
 
                 if (obj != null && obj.artists != null && obj.artists.Count > 0)
                 if (obj != null && obj.artists != null && obj.artists.Count > 0)
                 {
                 {

+ 2 - 6
MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs

@@ -12,13 +12,11 @@ using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
 
 
 namespace MediaBrowser.Providers.Plugins.Omdb
 namespace MediaBrowser.Providers.Plugins.Omdb
 {
 {
     public class OmdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder
     public class OmdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder
     {
     {
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly OmdbItemProvider _itemProvider;
         private readonly OmdbItemProvider _itemProvider;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
@@ -26,19 +24,17 @@ namespace MediaBrowser.Providers.Plugins.Omdb
         private readonly IApplicationHost _appHost;
         private readonly IApplicationHost _appHost;
 
 
         public OmdbEpisodeProvider(
         public OmdbEpisodeProvider(
-            IJsonSerializer jsonSerializer,
             IApplicationHost appHost,
             IApplicationHost appHost,
             IHttpClientFactory httpClientFactory,
             IHttpClientFactory httpClientFactory,
             ILibraryManager libraryManager,
             ILibraryManager libraryManager,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
             IServerConfigurationManager configurationManager)
             IServerConfigurationManager configurationManager)
         {
         {
-            _jsonSerializer = jsonSerializer;
             _httpClientFactory = httpClientFactory;
             _httpClientFactory = httpClientFactory;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _configurationManager = configurationManager;
             _configurationManager = configurationManager;
             _appHost = appHost;
             _appHost = appHost;
-            _itemProvider = new OmdbItemProvider(jsonSerializer, _appHost, httpClientFactory, libraryManager, fileSystem, configurationManager);
+            _itemProvider = new OmdbItemProvider(_appHost, httpClientFactory, libraryManager, fileSystem, configurationManager);
         }
         }
 
 
         // After TheTvDb
         // After TheTvDb
@@ -69,7 +65,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
             {
             {
                 if (info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue)
                 if (info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue)
                 {
                 {
-                    result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager)
+                    result.HasMetadata = await new OmdbProvider(_httpClientFactory, _fileSystem, _appHost, _configurationManager)
                         .FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProvider.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
                         .FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProvider.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
                 }
                 }
             }
             }

+ 3 - 6
MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs

@@ -1,8 +1,8 @@
 #pragma warning disable CS1591
 #pragma warning disable CS1591
 
 
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Net.Http;
 using System.Globalization;
 using System.Globalization;
+using System.Net.Http;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common;
 using MediaBrowser.Common;
@@ -15,21 +15,18 @@ using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
 
 
 namespace MediaBrowser.Providers.Plugins.Omdb
 namespace MediaBrowser.Providers.Plugins.Omdb
 {
 {
     public class OmdbImageProvider : IRemoteImageProvider, IHasOrder
     public class OmdbImageProvider : IRemoteImageProvider, IHasOrder
     {
     {
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly IHttpClientFactory _httpClientFactory;
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IApplicationHost _appHost;
         private readonly IApplicationHost _appHost;
 
 
-        public OmdbImageProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
+        public OmdbImageProvider(IApplicationHost appHost, IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
         {
         {
-            _jsonSerializer = jsonSerializer;
             _httpClientFactory = httpClientFactory;
             _httpClientFactory = httpClientFactory;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _configurationManager = configurationManager;
             _configurationManager = configurationManager;
@@ -56,7 +53,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
 
 
             var list = new List<RemoteImageInfo>();
             var list = new List<RemoteImageInfo>();
 
 
-            var provider = new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager);
+            var provider = new OmdbProvider(_httpClientFactory, _fileSystem, _appHost, _configurationManager);
 
 
             if (!string.IsNullOrWhiteSpace(imdbId))
             if (!string.IsNullOrWhiteSpace(imdbId))
             {
             {

+ 12 - 8
MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs

@@ -6,9 +6,12 @@ using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Net;
 using System.Net;
 using System.Net.Http;
 using System.Net.Http;
+using System.Text.Json;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common;
 using MediaBrowser.Common;
+using MediaBrowser.Common.Json;
+using MediaBrowser.Common.Json.Converters;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
@@ -19,34 +22,35 @@ using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
 
 
 namespace MediaBrowser.Providers.Plugins.Omdb
 namespace MediaBrowser.Providers.Plugins.Omdb
 {
 {
     public class OmdbItemProvider : IRemoteMetadataProvider<Series, SeriesInfo>,
     public class OmdbItemProvider : IRemoteMetadataProvider<Series, SeriesInfo>,
         IRemoteMetadataProvider<Movie, MovieInfo>, IRemoteMetadataProvider<Trailer, TrailerInfo>, IHasOrder
         IRemoteMetadataProvider<Movie, MovieInfo>, IRemoteMetadataProvider<Trailer, TrailerInfo>, IHasOrder
     {
     {
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IApplicationHost _appHost;
         private readonly IApplicationHost _appHost;
+        private readonly JsonSerializerOptions _jsonOptions;
 
 
         public OmdbItemProvider(
         public OmdbItemProvider(
-            IJsonSerializer jsonSerializer,
             IApplicationHost appHost,
             IApplicationHost appHost,
             IHttpClientFactory httpClientFactory,
             IHttpClientFactory httpClientFactory,
             ILibraryManager libraryManager,
             ILibraryManager libraryManager,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
             IServerConfigurationManager configurationManager)
             IServerConfigurationManager configurationManager)
         {
         {
-            _jsonSerializer = jsonSerializer;
             _httpClientFactory = httpClientFactory;
             _httpClientFactory = httpClientFactory;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _configurationManager = configurationManager;
             _configurationManager = configurationManager;
             _appHost = appHost;
             _appHost = appHost;
+
+            _jsonOptions = new JsonSerializerOptions(JsonDefaults.GetOptions());
+            _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStringConverter());
+            _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStructConverter<int>());
         }
         }
 
 
         public string Name => "The Open Movie Database";
         public string Name => "The Open Movie Database";
@@ -138,7 +142,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
 
 
             if (isSearch)
             if (isSearch)
             {
             {
-                var searchResultList = await _jsonSerializer.DeserializeFromStreamAsync<SearchResultList>(stream).ConfigureAwait(false);
+                var searchResultList = await JsonSerializer.DeserializeAsync<SearchResultList>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
                 if (searchResultList != null && searchResultList.Search != null)
                 if (searchResultList != null && searchResultList.Search != null)
                 {
                 {
                     resultList.AddRange(searchResultList.Search);
                     resultList.AddRange(searchResultList.Search);
@@ -146,7 +150,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
             }
             }
             else
             else
             {
             {
-                var result = await _jsonSerializer.DeserializeFromStreamAsync<SearchResult>(stream).ConfigureAwait(false);
+                var result = await JsonSerializer.DeserializeAsync<SearchResult>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
                 if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase))
                 if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase))
                 {
                 {
                     resultList.Add(result);
                     resultList.Add(result);
@@ -221,7 +225,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                 result.Item.SetProviderId(MetadataProvider.Imdb, imdbId);
                 result.Item.SetProviderId(MetadataProvider.Imdb, imdbId);
                 result.HasMetadata = true;
                 result.HasMetadata = true;
 
 
-                await new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+                await new OmdbProvider(_httpClientFactory, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
             }
             }
 
 
             return result;
             return result;
@@ -253,7 +257,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                 result.Item.SetProviderId(MetadataProvider.Imdb, imdbId);
                 result.Item.SetProviderId(MetadataProvider.Imdb, imdbId);
                 result.HasMetadata = true;
                 result.HasMetadata = true;
 
 
-                await new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+                await new OmdbProvider(_httpClientFactory, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
             }
             }
 
 
             return result;
             return result;

+ 28 - 16
MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs

@@ -7,35 +7,41 @@ using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Net.Http;
 using System.Net.Http;
 using System.Text;
 using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common;
 using MediaBrowser.Common;
+using MediaBrowser.Common.Json;
+using MediaBrowser.Common.Json.Converters;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
 
 
 namespace MediaBrowser.Providers.Plugins.Omdb
 namespace MediaBrowser.Providers.Plugins.Omdb
 {
 {
     public class OmdbProvider
     public class OmdbProvider
     {
     {
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
         private readonly IApplicationHost _appHost;
         private readonly IApplicationHost _appHost;
+        private readonly JsonSerializerOptions _jsonOptions;
 
 
-        public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager)
+        public OmdbProvider(IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager)
         {
         {
-            _jsonSerializer = jsonSerializer;
             _httpClientFactory = httpClientFactory;
             _httpClientFactory = httpClientFactory;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _configurationManager = configurationManager;
             _configurationManager = configurationManager;
             _appHost = appHost;
             _appHost = appHost;
+
+            _jsonOptions = new JsonSerializerOptions(JsonDefaults.GetOptions());
+            _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStringConverter());
+            _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStructConverter<int>());
         }
         }
 
 
         public async Task Fetch<T>(MetadataResult<T> itemResult, string imdbId, string language, string country, CancellationToken cancellationToken)
         public async Task Fetch<T>(MetadataResult<T> itemResult, string imdbId, string language, string country, CancellationToken cancellationToken)
@@ -220,7 +226,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                 }
                 }
             }
             }
 
 
-            var result = _jsonSerializer.DeserializeFromString<RootObject>(resultString);
+            var result = JsonSerializer.Deserialize<RootObject>(resultString, _jsonOptions);
             return result;
             return result;
         }
         }
 
 
@@ -239,7 +245,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                 }
                 }
             }
             }
 
 
-            var result = _jsonSerializer.DeserializeFromString<SeasonRootObject>(resultString);
+            var result = JsonSerializer.Deserialize<SeasonRootObject>(resultString, _jsonOptions);
             return result;
             return result;
         }
         }
 
 
@@ -297,11 +303,10 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                     "i={0}&plot=short&tomatoes=true&r=json",
                     "i={0}&plot=short&tomatoes=true&r=json",
                     imdbParam));
                     imdbParam));
 
 
-            using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
-            await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-            var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(stream).ConfigureAwait(false);
+            var rootObject = await GetDeserializedOmdbResponse<RootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
             Directory.CreateDirectory(Path.GetDirectoryName(path));
             Directory.CreateDirectory(Path.GetDirectoryName(path));
-            _jsonSerializer.SerializeToFile(rootObject, path);
+            await using FileStream jsonFileStream = File.OpenWrite(path);
+            await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
 
             return path;
             return path;
         }
         }
@@ -335,15 +340,22 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                     imdbParam,
                     imdbParam,
                     seasonId));
                     seasonId));
 
 
-            using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
-            await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-            var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<SeasonRootObject>(stream).ConfigureAwait(false);
+            var rootObject = await GetDeserializedOmdbResponse<SeasonRootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
             Directory.CreateDirectory(Path.GetDirectoryName(path));
             Directory.CreateDirectory(Path.GetDirectoryName(path));
-            _jsonSerializer.SerializeToFile(rootObject, path);
+            await using FileStream jsonFileStream = File.OpenWrite(path);
+            await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
 
 
             return path;
             return path;
         }
         }
 
 
+        public async Task<T> GetDeserializedOmdbResponse<T>(HttpClient httpClient, string url, CancellationToken cancellationToken)
+        {
+            using var response = await GetOmdbResponse(httpClient, url, cancellationToken).ConfigureAwait(false);
+            await using Stream content = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
+
+            return await JsonSerializer.DeserializeAsync<T>(content, _jsonOptions, cancellationToken).ConfigureAwait(false);
+        }
+
         public static Task<HttpResponseMessage> GetOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken)
         public static Task<HttpResponseMessage> GetOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken)
         {
         {
             return httpClient.GetAsync(url, cancellationToken);
             return httpClient.GetAsync(url, cancellationToken);
@@ -465,7 +477,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
 
 
             public string seriesID { get; set; }
             public string seriesID { get; set; }
 
 
-            public int Season { get; set; }
+            public int? Season { get; set; }
 
 
             public int? totalSeasons { get; set; }
             public int? totalSeasons { get; set; }
 
 
@@ -526,7 +538,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
 
 
             public string Response { get; set; }
             public string Response { get; set; }
 
 
-            public int Episode { get; set; }
+            public int? Episode { get; set; }
 
 
             public float? GetRottenTomatoScore()
             public float? GetRottenTomatoScore()
             {
             {

+ 2 - 0
MediaBrowser.Providers/Properties/AssemblyInfo.cs

@@ -1,5 +1,6 @@
 using System.Reflection;
 using System.Reflection;
 using System.Resources;
 using System.Resources;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 
 
 // General Information about an assembly is controlled through the following
 // General Information about an assembly is controlled through the following
@@ -14,6 +15,7 @@ using System.Runtime.InteropServices;
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyCulture("")]
 [assembly: AssemblyCulture("")]
 [assembly: NeutralResourcesLanguage("en")]
 [assembly: NeutralResourcesLanguage("en")]
+[assembly: InternalsVisibleTo("Jellyfin.Common.Tests")]
 
 
 // Setting ComVisible to false makes the types in this assembly not visible
 // Setting ComVisible to false makes the types in this assembly not visible
 // to COM components.  If you need to access a type in this assembly from
 // to COM components.  If you need to access a type in this assembly from

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

@@ -29,6 +29,7 @@
 
 
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="../../MediaBrowser.Common/MediaBrowser.Common.csproj" />
     <ProjectReference Include="../../MediaBrowser.Common/MediaBrowser.Common.csproj" />
+    <ProjectReference Include="../../MediaBrowser.Providers/MediaBrowser.Providers.csproj" />
   </ItemGroup>
   </ItemGroup>
 
 
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

+ 68 - 0
tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs

@@ -0,0 +1,68 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using MediaBrowser.Common.Json.Converters;
+using MediaBrowser.Providers.Plugins.Omdb;
+using Xunit;
+
+namespace Jellyfin.Common.Tests.Json
+{
+    public class JsonOmdbConverterTests
+    {
+        private readonly JsonSerializerOptions _options;
+
+        public JsonOmdbConverterTests()
+        {
+            _options = new JsonSerializerOptions();
+            _options.Converters.Add(new JsonOmdbNotAvailableStringConverter());
+            _options.Converters.Add(new JsonOmdbNotAvailableStructConverter<int>());
+            _options.NumberHandling = JsonNumberHandling.AllowReadingFromString;
+        }
+
+        [Fact]
+        public void Deserialize_Omdb_Response_Not_Available_Success()
+        {
+            const string Input = "{\"Title\":\"Chapter 1\",\"Year\":\"2013\",\"Rated\":\"TV-MA\",\"Released\":\"01 Feb 2013\",\"Season\":\"N/A\",\"Episode\":\"N/A\",\"Runtime\":\"55 min\",\"Genre\":\"Drama\",\"Director\":\"David Fincher\",\"Writer\":\"Michael Dobbs (based on the novels by), Andrew Davies (based on the mini-series by), Beau Willimon (created for television by), Beau Willimon, Sam Forman (staff writer)\",\"Actors\":\"Kevin Spacey, Robin Wright, Kate Mara, Corey Stoll\",\"Plot\":\"Congressman Francis Underwood has been declined the chair for Secretary of State. He's now gathering his own team to plot his revenge. Zoe Barnes, a reporter for the Washington Herald, will do anything to get her big break.\",\"Language\":\"English\",\"Country\":\"USA\",\"Awards\":\"N/A\",\"Poster\":\"https://m.media-amazon.com/images/M/MV5BMTY5MTU4NDQzNV5BMl5BanBnXkFtZTgwMzk2ODcxMzE@._V1_SX300.jpg\",\"Ratings\":[{\"Source\":\"Internet Movie Database\",\"Value\":\"8.7/10\"}],\"Metascore\":\"N/A\",\"imdbRating\":\"8.7\",\"imdbVotes\":\"6736\",\"imdbID\":\"tt2161930\",\"seriesID\":\"N/A\",\"Type\":\"episode\",\"Response\":\"True\"}";
+            var seasonRootObject = JsonSerializer.Deserialize<OmdbProvider.RootObject>(Input, _options);
+            Assert.NotNull(seasonRootObject);
+            Assert.Null(seasonRootObject?.Awards);
+            Assert.Null(seasonRootObject?.Episode);
+            Assert.Null(seasonRootObject?.Metascore);
+        }
+
+        [Theory]
+        [InlineData("\"N/A\"")]
+        [InlineData("null")]
+        public void Deserialization_To_Nullable_Int_Shoud_Be_Null(string input)
+        {
+            var result = JsonSerializer.Deserialize<int?>(input, _options);
+            Assert.Null(result);
+        }
+
+        [Theory]
+        [InlineData("\"N/A\"")]
+        [InlineData("null")]
+        public void Deserialization_To_Nullable_String_Shoud_Be_Null(string input)
+        {
+            var result = JsonSerializer.Deserialize<string?>(input, _options);
+            Assert.Null(result);
+        }
+
+        [Theory]
+        [InlineData("\"8\"", 8)]
+        [InlineData("8", 8)]
+        public void Deserialize_Int_Success(string input, int expected)
+        {
+            var result = JsonSerializer.Deserialize<int>(input, _options);
+            Assert.Equal(result, expected);
+        }
+
+        [Fact]
+        public void Deserialize_Normal_String_Success()
+        {
+            const string Input = "\"Jellyfin\"";
+            const string Expected = "Jellyfin";
+            var result = JsonSerializer.Deserialize<string>(Input, _options);
+            Assert.Equal(Expected, result);
+        }
+    }
+}