From d69f2d7d7fd4209652878d84f811051e69fb45b2 Mon Sep 17 00:00:00 2001 From: Greenback Date: Mon, 7 Dec 2020 22:06:28 +0000 Subject: [PATCH 01/52] DNLA over HTTP only --- Emby.Dlna/Main/DlnaEntryPoint.cs | 23 +++++++++++++++++-- .../ApplicationHost.cs | 6 ++--- .../Configuration/NetworkConfiguration.cs | 6 ++--- Jellyfin.Networking/Manager/NetworkManager.cs | 2 +- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index fb4454a343..ced56a7182 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Emby.Dlna.PlayTo; using Emby.Dlna.Ssdp; +using Jellyfin.Networking.Configuration; using Jellyfin.Networking.Manager; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; @@ -52,6 +53,8 @@ namespace Emby.Dlna.Main private readonly ISocketFactory _socketFactory; private readonly INetworkManager _networkManager; private readonly object _syncLock = new object(); + private readonly NetworkConfiguration _netConfig; + private readonly bool _disabled; private PlayToManager _manager; private SsdpDevicePublisher _publisher; @@ -122,6 +125,13 @@ namespace Emby.Dlna.Main httpClientFactory, config); Current = this; + + _netConfig = config.GetConfiguration("network"); + _disabled = appHost.ListenWithHttps && _netConfig.RequireHttps; + if (_disabled) + { + _logger.LogError("The DLNA specification does not support HTTPS."); + } } public static DlnaEntryPoint Current { get; private set; } @@ -136,6 +146,12 @@ namespace Emby.Dlna.Main { await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false); + if (_disabled) + { + // No use starting as dlna won't work, as we're running purely on HTTPS. + return; + } + ReloadComponents(); _config.NamedConfigurationUpdated += OnNamedConfigurationUpdated; @@ -290,12 +306,15 @@ namespace Emby.Dlna.Main _logger.LogInformation("Registering publisher for {0} on {1}", fullService, address); - var uri = new Uri(_appHost.GetSmartApiUrl(address.Address) + descriptorUri); + var uri = new UriBuilder(_appHost.GetSmartApiUrl(address.Address) + descriptorUri); + // DLNA will only work over http, so we must reset to http:// : {port} + uri.Scheme = "http://"; + uri.Port = _netConfig.PublicPort; var device = new SsdpRootDevice { CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info. - Location = uri, // Must point to the URL that serves your devices UPnP description document. + Location = uri.Uri, // Must point to the URL that serves your devices UPnP description document. Address = address.Address, PrefixLength = address.PrefixLength, FriendlyName = "Jellyfin", diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index d74ea03520..b6f63e5659 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -496,7 +496,7 @@ namespace Emby.Server.Implementations { var networkConfiguration = ServerConfigurationManager.GetNetworkConfiguration(); HttpPort = networkConfiguration.HttpServerPortNumber; - HttpsPort = networkConfiguration.HttpsPortNumber; + HttpsPort = networkConfiguration.HttpsServerPortNumber; // Safeguard against invalid configuration if (HttpPort == HttpsPort) @@ -714,7 +714,7 @@ namespace Emby.Server.Implementations // Don't use an empty string password var password = string.IsNullOrWhiteSpace(info.Password) ? null : info.Password; - var localCert = new X509Certificate2(certificateLocation, password); + var localCert = new X509Certificate2(certificateLocation, password, X509KeyStorageFlags.UserKeySet); // localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA; if (!localCert.HasPrivateKey) { @@ -919,7 +919,7 @@ namespace Emby.Server.Implementations var networkConfiguration = ServerConfigurationManager.GetNetworkConfiguration(); // Need to restart if ports have changed if (networkConfiguration.HttpServerPortNumber != HttpPort || - networkConfiguration.HttpsPortNumber != HttpsPort) + networkConfiguration.HttpsServerPortNumber != HttpsPort) { if (ServerConfigurationManager.Configuration.IsPortAuthorized) { diff --git a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs index df420f48a2..8db3d3a9c1 100644 --- a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs +++ b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs @@ -16,7 +16,7 @@ namespace Jellyfin.Networking.Configuration public const int DefaultHttpPort = 8096; /// - /// The default value for and . + /// The default value for and . /// public const int DefaultHttpsPort = 8920; @@ -76,7 +76,7 @@ namespace Jellyfin.Networking.Configuration /// Gets or sets the HTTPS server port number. /// /// The HTTPS server port number. - public int HttpsPortNumber { get; set; } = DefaultHttpsPort; + public int HttpsServerPortNumber { get; set; } = DefaultHttpsPort; /// /// Gets or sets a value indicating whether to use HTTPS. @@ -88,7 +88,7 @@ namespace Jellyfin.Networking.Configuration public bool EnableHttps { get; set; } /// - /// Gets or sets the public mapped port. + /// Gets or sets the Upublic mapped port. /// /// The public mapped port. public int PublicPort { get; set; } = DefaultHttpPort; diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 85da927fb4..a76ba49b6a 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -784,7 +784,7 @@ namespace Jellyfin.Networking.Manager } else { - _logger.LogDebug("Invalid or unknown network {Token}.", token); + _logger.LogDebug("Invalid or unknown object {Token}.", token); } } From 57cb3a67bc12d6a7aa38e5a8b4df68bf631b5ffe Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Dec 2020 22:12:43 +0000 Subject: [PATCH 02/52] Update ApplicationHost.cs Removed previous change, use for testing. --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index b6f63e5659..086e6d7f94 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -714,7 +714,7 @@ namespace Emby.Server.Implementations // Don't use an empty string password var password = string.IsNullOrWhiteSpace(info.Password) ? null : info.Password; - var localCert = new X509Certificate2(certificateLocation, password, X509KeyStorageFlags.UserKeySet); + var localCert = new X509Certificate2(certificateLocation, password); // localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA; if (!localCert.HasPrivateKey) { From e34adc12d9ab15fa5126e0c0e3a625cea7940e7f Mon Sep 17 00:00:00 2001 From: Greenback Date: Mon, 7 Dec 2020 22:15:30 +0000 Subject: [PATCH 03/52] reverted change. --- Emby.Server.Implementations/ApplicationHost.cs | 4 ++-- Jellyfin.Networking/Configuration/NetworkConfiguration.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 086e6d7f94..d74ea03520 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -496,7 +496,7 @@ namespace Emby.Server.Implementations { var networkConfiguration = ServerConfigurationManager.GetNetworkConfiguration(); HttpPort = networkConfiguration.HttpServerPortNumber; - HttpsPort = networkConfiguration.HttpsServerPortNumber; + HttpsPort = networkConfiguration.HttpsPortNumber; // Safeguard against invalid configuration if (HttpPort == HttpsPort) @@ -919,7 +919,7 @@ namespace Emby.Server.Implementations var networkConfiguration = ServerConfigurationManager.GetNetworkConfiguration(); // Need to restart if ports have changed if (networkConfiguration.HttpServerPortNumber != HttpPort || - networkConfiguration.HttpsServerPortNumber != HttpsPort) + networkConfiguration.HttpsPortNumber != HttpsPort) { if (ServerConfigurationManager.Configuration.IsPortAuthorized) { diff --git a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs index 8db3d3a9c1..730fc498cd 100644 --- a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs +++ b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs @@ -16,7 +16,7 @@ namespace Jellyfin.Networking.Configuration public const int DefaultHttpPort = 8096; /// - /// The default value for and . + /// The default value for and . /// public const int DefaultHttpsPort = 8920; @@ -76,7 +76,7 @@ namespace Jellyfin.Networking.Configuration /// Gets or sets the HTTPS server port number. /// /// The HTTPS server port number. - public int HttpsServerPortNumber { get; set; } = DefaultHttpsPort; + public int HttpsPortNumber { get; set; } = DefaultHttpsPort; /// /// Gets or sets a value indicating whether to use HTTPS. From acc45b7307d9b01a152ebc05e84f81e3672f2169 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Dec 2020 22:29:19 +0000 Subject: [PATCH 04/52] Update Jellyfin.Networking/Configuration/NetworkConfiguration.cs Co-authored-by: Cody Robibero --- Jellyfin.Networking/Configuration/NetworkConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs index 730fc498cd..df420f48a2 100644 --- a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs +++ b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs @@ -88,7 +88,7 @@ namespace Jellyfin.Networking.Configuration public bool EnableHttps { get; set; } /// - /// Gets or sets the Upublic mapped port. + /// Gets or sets the public mapped port. /// /// The public mapped port. public int PublicPort { get; set; } = DefaultHttpPort; From e09d3ba9efa7505a14d57d344707fe7ce71119b2 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 23 Dec 2020 13:06:29 +0100 Subject: [PATCH 05/52] Remove custom Json serializer from Providers --- .../Plugins/AudioDb/AlbumImageProvider.cs | 10 +++++---- .../Plugins/AudioDb/AlbumProvider.cs | 9 ++++---- .../Plugins/AudioDb/ArtistImageProvider.cs | 10 +++++---- .../Plugins/AudioDb/ArtistProvider.cs | 9 ++++---- .../Plugins/Omdb/OmdbEpisodeProvider.cs | 8 ++----- .../Plugins/Omdb/OmdbImageProvider.cs | 9 +++----- .../Plugins/Omdb/OmdbItemProvider.cs | 14 ++++++------- .../Plugins/Omdb/OmdbProvider.cs | 21 ++++++++++--------- 8 files changed, 44 insertions(+), 46 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs index 293087da7a..605b93788b 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs @@ -1,9 +1,12 @@ #pragma warning disable CS1591 using System.Collections.Generic; +using System.IO; using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -19,13 +22,11 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { private readonly IServerConfigurationManager _config; private readonly IHttpClientFactory _httpClientFactory; - private readonly IJsonSerializer _json; - public AudioDbAlbumImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory, IJsonSerializer json) + public AudioDbAlbumImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory) { _config = config; _httpClientFactory = httpClientFactory; - _json = json; } /// @@ -56,7 +57,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = AudioDbAlbumProvider.GetAlbumInfoPath(_config.ApplicationPaths, id); - var obj = _json.DeserializeFromFile(path); + var jsonString = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false); + var obj = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); if (obj != null && obj.album != null && obj.album.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs index 97bba10baa..32ef135475 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs @@ -6,10 +6,12 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Audio; @@ -27,16 +29,14 @@ namespace MediaBrowser.Providers.Plugins.AudioDb private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; private readonly IHttpClientFactory _httpClientFactory; - private readonly IJsonSerializer _json; public static AudioDbAlbumProvider Current; - public AudioDbAlbumProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json) + public AudioDbAlbumProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory) { _config = config; _fileSystem = fileSystem; _httpClientFactory = httpClientFactory; - _json = json; Current = this; } @@ -64,7 +64,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = GetAlbumInfoPath(_config.ApplicationPaths, id); - var obj = _json.DeserializeFromFile(path); + var jsonString = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false); + var obj = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); if (obj != null && obj.album != null && obj.album.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs index d250acfa84..f0b8e00f3f 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs @@ -1,9 +1,12 @@ #pragma warning disable CS1591 using System.Collections.Generic; +using System.IO; using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -19,12 +22,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { private readonly IServerConfigurationManager _config; private readonly IHttpClientFactory _httpClientFactory; - private readonly IJsonSerializer _json; - public AudioDbArtistImageProvider(IServerConfigurationManager config, IJsonSerializer json, IHttpClientFactory httpClientFactory) + public AudioDbArtistImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory) { _config = config; - _json = json; _httpClientFactory = httpClientFactory; } @@ -58,7 +59,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = AudioDbArtistProvider.GetArtistInfoPath(_config.ApplicationPaths, id); - var obj = _json.DeserializeFromFile(path); + var jsonString = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false); + var obj = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); if (obj != null && obj.artists != null && obj.artists.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs index a2a03e1f9a..2476c86715 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs @@ -5,10 +5,12 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Audio; @@ -29,14 +31,12 @@ namespace MediaBrowser.Providers.Plugins.AudioDb private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; private readonly IHttpClientFactory _httpClientFactory; - private readonly IJsonSerializer _json; - public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json) + public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory) { _config = config; _fileSystem = fileSystem; _httpClientFactory = httpClientFactory; - _json = json; Current = this; } @@ -65,7 +65,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = GetArtistInfoPath(_config.ApplicationPaths, id); - var obj = _json.DeserializeFromFile(path); + var jsonString = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false); + var obj = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); if (obj != null && obj.artists != null && obj.artists.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs index bfc840ea59..24ef80a35d 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs @@ -12,13 +12,11 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.Plugins.Omdb { public class OmdbEpisodeProvider : IRemoteMetadataProvider, IHasOrder { - private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClientFactory _httpClientFactory; private readonly OmdbItemProvider _itemProvider; private readonly IFileSystem _fileSystem; @@ -26,19 +24,17 @@ namespace MediaBrowser.Providers.Plugins.Omdb private readonly IApplicationHost _appHost; public OmdbEpisodeProvider( - IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClientFactory httpClientFactory, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { - _jsonSerializer = jsonSerializer; _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; _configurationManager = configurationManager; _appHost = appHost; - _itemProvider = new OmdbItemProvider(jsonSerializer, _appHost, httpClientFactory, libraryManager, fileSystem, configurationManager); + _itemProvider = new OmdbItemProvider(_appHost, httpClientFactory, libraryManager, fileSystem, configurationManager); } // After TheTvDb @@ -69,7 +65,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb { 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); } } diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs index 8f4240dc11..df67aff31c 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs @@ -1,8 +1,8 @@ #pragma warning disable CS1591 using System.Collections.Generic; -using System.Net.Http; using System.Globalization; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; @@ -15,21 +15,18 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.Plugins.Omdb { public class OmdbImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClientFactory _httpClientFactory; - private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; 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; _fileSystem = fileSystem; _configurationManager = configurationManager; @@ -56,7 +53,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var list = new List(); - var provider = new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager); + var provider = new OmdbProvider(_httpClientFactory, _fileSystem, _appHost, _configurationManager); if (!string.IsNullOrWhiteSpace(imdbId)) { diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs index 43d8af75f0..7e9ef974e2 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs @@ -6,9 +6,11 @@ using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; +using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -19,14 +21,12 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.Plugins.Omdb { public class OmdbItemProvider : IRemoteMetadataProvider, IRemoteMetadataProvider, IRemoteMetadataProvider, IHasOrder { - private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; private readonly IFileSystem _fileSystem; @@ -34,14 +34,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb private readonly IApplicationHost _appHost; public OmdbItemProvider( - IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClientFactory httpClientFactory, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { - _jsonSerializer = jsonSerializer; _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; _fileSystem = fileSystem; @@ -138,7 +136,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb if (isSearch) { - var searchResultList = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + var searchResultList = await JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); if (searchResultList != null && searchResultList.Search != null) { resultList.AddRange(searchResultList.Search); @@ -146,7 +144,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb } else { - var result = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + var result = await JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase)) { resultList.Add(result); @@ -221,7 +219,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb result.Item.SetProviderId(MetadataProvider.Imdb, imdbId); 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; @@ -253,7 +251,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb result.Item.SetProviderId(MetadataProvider.Imdb, imdbId); 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; diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index e6c6050720..e73085460d 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -7,31 +7,30 @@ using System.IO; using System.Linq; using System.Net.Http; using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; +using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.Plugins.Omdb { public class OmdbProvider { - private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; private readonly IHttpClientFactory _httpClientFactory; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IApplicationHost _appHost; - 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; _fileSystem = fileSystem; _configurationManager = configurationManager; @@ -220,7 +219,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb } } - var result = _jsonSerializer.DeserializeFromString(resultString); + var result = JsonSerializer.Deserialize(resultString, JsonDefaults.GetOptions()); return result; } @@ -239,7 +238,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb } } - var result = _jsonSerializer.DeserializeFromString(resultString); + var result = JsonSerializer.Deserialize(resultString, JsonDefaults.GetOptions()); return result; } @@ -299,9 +298,10 @@ namespace MediaBrowser.Providers.Plugins.Omdb 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(stream).ConfigureAwait(false); + var rootObject = await JsonSerializer.DeserializeAsync(stream, cancellationToken: cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(path)); - _jsonSerializer.SerializeToFile(rootObject, path); + await using FileStream jsonFileStream = File.Create(path); + await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); return path; } @@ -337,9 +337,10 @@ namespace MediaBrowser.Providers.Plugins.Omdb 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(stream).ConfigureAwait(false); + var rootObject = await JsonSerializer.DeserializeAsync(stream, cancellationToken: cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(path)); - _jsonSerializer.SerializeToFile(rootObject, path); + await using FileStream jsonFileStream = File.Create(path); + await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); return path; } From bc0976ceac32b5f7e0ef234ec01a0b17477b9086 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 23 Dec 2020 13:12:06 +0100 Subject: [PATCH 06/52] Remove custom Json serializer from Dlna --- Emby.Dlna/DlnaManager.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index fedd20b68a..df69dd5162 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -7,12 +7,14 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; using Emby.Dlna.Profiles; using Emby.Dlna.Server; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Json; using MediaBrowser.Controller; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; @@ -32,7 +34,6 @@ namespace Emby.Dlna private readonly IXmlSerializer _xmlSerializer; private readonly IFileSystem _fileSystem; private readonly ILogger _logger; - private readonly IJsonSerializer _jsonSerializer; private readonly IServerApplicationHost _appHost; private static readonly Assembly _assembly = typeof(DlnaManager).Assembly; @@ -43,14 +44,12 @@ namespace Emby.Dlna IFileSystem fileSystem, IApplicationPaths appPaths, ILoggerFactory loggerFactory, - IJsonSerializer jsonSerializer, IServerApplicationHost appHost) { _xmlSerializer = xmlSerializer; _fileSystem = fileSystem; _appPaths = appPaths; _logger = loggerFactory.CreateLogger(); - _jsonSerializer = jsonSerializer; _appHost = appHost; } @@ -495,9 +494,9 @@ namespace Emby.Dlna return profile; } - var json = _jsonSerializer.SerializeToString(profile); + var json = JsonSerializer.Serialize(profile, JsonDefaults.GetOptions()); - return _jsonSerializer.DeserializeFromString(json); + return JsonSerializer.Deserialize(json, options: JsonDefaults.GetOptions()); } public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress) From 196388d607a856050221d1b55192360dd3ea8be5 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 23 Dec 2020 13:12:40 +0100 Subject: [PATCH 07/52] Remove custom Json serializer from Emby.Server.Implementations --- .../ApplicationHost.cs | 14 +++----- .../Channels/ChannelManager.cs | 32 +++++++++-------- .../Library/LiveStreamHelper.cs | 13 +++---- .../Library/MediaSourceManager.cs | 18 +++++----- .../LiveTv/EmbyTV/EmbyTV.cs | 12 +++---- .../LiveTv/EmbyTV/EncodedRecorder.cs | 8 ++--- .../LiveTv/EmbyTV/ItemDataProvider.cs | 12 +++---- .../LiveTv/EmbyTV/SeriesTimerManager.cs | 4 +-- .../LiveTv/EmbyTV/TimerManager.cs | 5 ++- .../LiveTv/Listings/SchedulesDirect.cs | 24 ++++++------- .../Localization/LocalizationManager.cs | 14 ++++---- .../ScheduledTasks/ScheduledTaskWorker.cs | 35 ++++++++++--------- .../ScheduledTasks/TaskManager.cs | 7 ++-- 13 files changed, 95 insertions(+), 103 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 50ef71a46d..fcc156e9c5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -7,11 +7,9 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; -using System.Net.Http; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; -using System.Text; using System.Threading; using System.Threading.Tasks; using Emby.Dlna; @@ -50,6 +48,7 @@ using Jellyfin.Networking.Manager; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; +using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; @@ -101,6 +100,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Prometheus.DotNetRuntime; +using JsonSerializer = System.Text.Json.JsonSerializer; using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager; @@ -118,7 +118,6 @@ namespace Emby.Server.Implementations private readonly IFileSystem _fileSystemManager; private readonly IXmlSerializer _xmlSerializer; - private readonly IJsonSerializer _jsonSerializer; private readonly IStartupOptions _startupOptions; private IMediaEncoder _mediaEncoder; @@ -257,7 +256,6 @@ namespace Emby.Server.Implementations IServiceCollection serviceCollection) { _xmlSerializer = new MyXmlSerializer(); - _jsonSerializer = new JsonSerializer(); ServiceCollection = serviceCollection; @@ -528,8 +526,6 @@ namespace Emby.Server.Implementations ServiceCollection.AddSingleton(ApplicationPaths); - ServiceCollection.AddSingleton(); - ServiceCollection.AddSingleton(_fileSystemManager); ServiceCollection.AddSingleton(); @@ -754,7 +750,6 @@ namespace Emby.Server.Implementations UserView.CollectionManager = Resolve(); BaseItem.MediaSourceManager = Resolve(); CollectionFolder.XmlSerializer = _xmlSerializer; - CollectionFolder.JsonSerializer = Resolve(); CollectionFolder.ApplicationHost = this; } @@ -967,7 +962,7 @@ namespace Emby.Server.Implementations { return true; } - + throw new FileNotFoundException( string.Format( CultureInfo.InvariantCulture, @@ -1051,7 +1046,8 @@ namespace Emby.Server.Implementations var metafile = Path.Combine(dir, "meta.json"); if (File.Exists(metafile)) { - var manifest = _jsonSerializer.DeserializeFromFile(metafile); + var jsonString = File.ReadAllText(metafile); + var manifest = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); if (!Version.TryParse(manifest.TargetAbi, out var targetAbi)) { diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 57684a4298..0966c252ba 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Json; using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; @@ -21,10 +22,10 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Episode = MediaBrowser.Controller.Entities.TV.Episode; +using JsonSerializer = System.Text.Json.JsonSerializer; using Movie = MediaBrowser.Controller.Entities.Movies.Movie; using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum; using Season = MediaBrowser.Controller.Entities.TV.Season; @@ -44,7 +45,6 @@ namespace Emby.Server.Implementations.Channels private readonly ILogger _logger; private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; - private readonly IJsonSerializer _jsonSerializer; private readonly IProviderManager _providerManager; private readonly IMemoryCache _memoryCache; private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1); @@ -70,7 +70,6 @@ namespace Emby.Server.Implementations.Channels IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager, - IJsonSerializer jsonSerializer, IProviderManager providerManager, IMemoryCache memoryCache) { @@ -81,7 +80,6 @@ namespace Emby.Server.Implementations.Channels _config = config; _fileSystem = fileSystem; _userDataManager = userDataManager; - _jsonSerializer = jsonSerializer; _providerManager = providerManager; _memoryCache = memoryCache; } @@ -343,7 +341,8 @@ namespace Emby.Server.Implementations.Channels try { - return _jsonSerializer.DeserializeFromFile>(path) ?? new List(); + var jsonString = File.ReadAllText(path); + return JsonSerializer.Deserialize>(jsonString, JsonDefaults.GetOptions()) ?? new List(); } catch { @@ -351,7 +350,7 @@ namespace Emby.Server.Implementations.Channels } } - private void SaveMediaSources(BaseItem item, List mediaSources) + private async Task SaveMediaSources(BaseItem item, List mediaSources) { var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasourceinfos.json"); @@ -369,8 +368,8 @@ namespace Emby.Server.Implementations.Channels } Directory.CreateDirectory(Path.GetDirectoryName(path)); - - _jsonSerializer.SerializeToFile(mediaSources, path); + await using FileStream createStream = File.Create(path); + await JsonSerializer.SerializeAsync(createStream, mediaSources, JsonDefaults.GetOptions()).ConfigureAwait(false); } /// @@ -812,7 +811,8 @@ namespace Emby.Server.Implementations.Channels { if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) { - var cachedResult = _jsonSerializer.DeserializeFromFile(cachePath); + var jsonString = await File.ReadAllTextAsync(cachePath, cancellationToken); + var cachedResult = JsonSerializer.Deserialize(jsonString); if (cachedResult != null) { return null; @@ -834,7 +834,8 @@ namespace Emby.Server.Implementations.Channels { if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) { - var cachedResult = _jsonSerializer.DeserializeFromFile(cachePath); + var jsonString = await File.ReadAllTextAsync(cachePath, cancellationToken); + var cachedResult = JsonSerializer.Deserialize(jsonString); if (cachedResult != null) { return null; @@ -865,7 +866,7 @@ namespace Emby.Server.Implementations.Channels throw new InvalidOperationException("Channel returned a null result from GetChannelItems"); } - CacheResponse(result, cachePath); + await CacheResponse(result, cachePath); return result; } @@ -875,13 +876,14 @@ namespace Emby.Server.Implementations.Channels } } - private void CacheResponse(object result, string path) + private async Task CacheResponse(object result, string path) { try { Directory.CreateDirectory(Path.GetDirectoryName(path)); - _jsonSerializer.SerializeToFile(result, path); + await using FileStream createStream = File.Create(path); + await JsonSerializer.SerializeAsync(createStream, result, JsonDefaults.GetOptions()).ConfigureAwait(false); } catch (Exception ex) { @@ -1176,11 +1178,11 @@ namespace Emby.Server.Implementations.Channels { if (enableMediaProbe && !info.IsLiveStream && item.HasPathProtocol) { - SaveMediaSources(item, new List()); + await SaveMediaSources(item, new List()); } else { - SaveMediaSources(item, info.MediaSources); + await SaveMediaSources(item, info.MediaSources); } } diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index 041619d1e5..ad7988fb15 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -5,16 +5,17 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Json; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Library @@ -23,14 +24,12 @@ namespace Emby.Server.Implementations.Library { private readonly IMediaEncoder _mediaEncoder; private readonly ILogger _logger; - private readonly IJsonSerializer _json; private readonly IApplicationPaths _appPaths; - public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger, IJsonSerializer json, IApplicationPaths appPaths) + public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger, IApplicationPaths appPaths) { _mediaEncoder = mediaEncoder; _logger = logger; - _json = json; _appPaths = appPaths; } @@ -47,7 +46,8 @@ namespace Emby.Server.Implementations.Library { try { - mediaInfo = _json.DeserializeFromFile(cacheFilePath); + var jsonString = await File.ReadAllTextAsync(cacheFilePath, cancellationToken).ConfigureAwait(false); + JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); // _logger.LogDebug("Found cached media info"); } @@ -83,7 +83,8 @@ namespace Emby.Server.Implementations.Library if (cacheFilePath != null) { Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); - _json.SerializeToFile(mediaInfo, cacheFilePath); + await using FileStream createStream = File.Create(cacheFilePath); + await JsonSerializer.SerializeAsync(createStream, mediaInfo, cancellationToken: cancellationToken).ConfigureAwait(false); // _logger.LogDebug("Saved media info to {0}", cacheFilePath); } diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 928f5f88e4..264390dd33 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -6,12 +6,14 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Json; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; @@ -23,7 +25,6 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Library @@ -36,7 +37,6 @@ namespace Emby.Server.Implementations.Library private readonly IItemRepository _itemRepo; private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; - private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly ILogger _logger; private readonly IUserDataManager _userDataManager; @@ -56,7 +56,6 @@ namespace Emby.Server.Implementations.Library IUserManager userManager, ILibraryManager libraryManager, ILogger logger, - IJsonSerializer jsonSerializer, IFileSystem fileSystem, IUserDataManager userDataManager, IMediaEncoder mediaEncoder) @@ -65,7 +64,6 @@ namespace Emby.Server.Implementations.Library _userManager = userManager; _libraryManager = libraryManager; _logger = logger; - _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; _userDataManager = userDataManager; _mediaEncoder = mediaEncoder; @@ -504,7 +502,7 @@ namespace Emby.Server.Implementations.Library // hack - these two values were taken from LiveTVMediaSourceProvider string cacheKey = request.OpenToken; - await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _appPaths) + await new LiveStreamHelper(_mediaEncoder, _logger, _appPaths) .AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken) .ConfigureAwait(false); } @@ -516,9 +514,9 @@ namespace Emby.Server.Implementations.Library } // TODO: @bond Fix - var json = _jsonSerializer.SerializeToString(mediaSource); + var json = JsonSerializer.Serialize(mediaSource, JsonDefaults.GetOptions()); _logger.LogInformation("Live stream opened: " + json); - var clone = _jsonSerializer.DeserializeFromString(json); + var clone = JsonSerializer.Deserialize(json, JsonDefaults.GetOptions()); if (!request.UserId.Equals(Guid.Empty)) { @@ -643,7 +641,8 @@ namespace Emby.Server.Implementations.Library { try { - mediaInfo = _jsonSerializer.DeserializeFromFile(cacheFilePath); + var json = await File.ReadAllTextAsync(cacheFilePath, cancellationToken).ConfigureAwait(false); + mediaInfo = JsonSerializer.Deserialize(json, JsonDefaults.GetOptions()); // _logger.LogDebug("Found cached media info"); } @@ -679,7 +678,8 @@ namespace Emby.Server.Implementations.Library if (cacheFilePath != null) { Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); - _jsonSerializer.SerializeToFile(mediaInfo, cacheFilePath); + await using FileStream createStream = File.Create(cacheFilePath); + await JsonSerializer.SerializeAsync(createStream, mediaInfo, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); // _logger.LogDebug("Saved media info to {0}", cacheFilePath); } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 0dc045ee6f..2c0de661df 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -36,7 +36,6 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.EmbyTV @@ -51,7 +50,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private readonly ILogger _logger; private readonly IHttpClientFactory _httpClientFactory; private readonly IServerConfigurationManager _config; - private readonly IJsonSerializer _jsonSerializer; private readonly ItemDataProvider _seriesTimerProvider; private readonly TimerManager _timerProvider; @@ -81,7 +79,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV IStreamHelper streamHelper, IMediaSourceManager mediaSourceManager, ILogger logger, - IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory, IServerConfigurationManager config, ILiveTvManager liveTvManager, @@ -103,12 +100,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _providerManager = providerManager; _mediaEncoder = mediaEncoder; _liveTvManager = (LiveTvManager)liveTvManager; - _jsonSerializer = jsonSerializer; _mediaSourceManager = mediaSourceManager; _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; _config.NamedConfigurationUpdated += OnNamedConfigurationUpdated; @@ -1052,7 +1048,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV IgnoreIndex = true }; - await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _config.CommonApplicationPaths) + await new LiveStreamHelper(_mediaEncoder, _logger, _config.CommonApplicationPaths) .AddMediaInfoWithProbe(stream, false, false, cancellationToken).ConfigureAwait(false); return new List @@ -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)) { - return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _config); + return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _config); } return new DirectRecorder(_logger, _httpClientFactory, _streamHelper); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index e6ee9819eb..9609f28816 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -6,16 +6,17 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Json; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.EmbyTV @@ -25,7 +26,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private readonly ILogger _logger; private readonly IMediaEncoder _mediaEncoder; private readonly IServerApplicationPaths _appPaths; - private readonly IJsonSerializer _json; private readonly TaskCompletionSource _taskCompletionSource = new TaskCompletionSource(); private readonly IServerConfigurationManager _serverConfigurationManager; @@ -38,13 +38,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV ILogger logger, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, - IJsonSerializer json, IServerConfigurationManager serverConfigurationManager) { _logger = logger; _mediaEncoder = mediaEncoder; _appPaths = appPaths; - _json = json; _serverConfigurationManager = serverConfigurationManager; } @@ -95,7 +93,7 @@ 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. _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); + var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(mediaSource, JsonDefaults.GetOptions()) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine); _logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length); _process = new Process diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index fc543dc551..5256a0ee72 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -4,7 +4,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using MediaBrowser.Model.Serialization; +using System.Text.Json; +using MediaBrowser.Common.Json; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.EmbyTV @@ -12,18 +13,15 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public class ItemDataProvider where T : class { - private readonly IJsonSerializer _jsonSerializer; private readonly string _dataPath; private readonly object _fileDataLock = new object(); private T[] _items; public ItemDataProvider( - IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func equalityComparer) { - _jsonSerializer = jsonSerializer; Logger = logger; _dataPath = dataPath; EqualityComparer = equalityComparer; @@ -46,7 +44,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV try { - _items = _jsonSerializer.DeserializeFromFile(_dataPath); + var json = File.ReadAllText(_dataPath); + _items = JsonSerializer.Deserialize(json, JsonDefaults.GetOptions()); return; } catch (Exception ex) @@ -61,7 +60,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private void SaveList() { Directory.CreateDirectory(Path.GetDirectoryName(_dataPath)); - _jsonSerializer.SerializeToFile(_items, _dataPath); + using FileStream stream = File.OpenWrite(_dataPath); + JsonSerializer.SerializeAsync(stream, _items, JsonDefaults.GetOptions()); } public IReadOnlyList GetAll() diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs index 194e4606de..da707fec63 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs @@ -9,8 +9,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { public class SeriesTimerManager : ItemDataProvider { - 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)) { } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs index dd479b7d1b..1efa90e256 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs @@ -8,7 +8,6 @@ using System.Threading; using Jellyfin.Data.Events; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.LiveTv; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.EmbyTV @@ -17,8 +16,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { private readonly ConcurrentDictionary _timers = new ConcurrentDictionary(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)) { } diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 90e6cc9668..198d196c23 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -9,16 +9,17 @@ using System.Net; using System.Net.Http; using System.Net.Mime; using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; +using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; 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 readonly ILogger _logger; - private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClientFactory _httpClientFactory; private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1); private readonly IApplicationHost _appHost; @@ -39,13 +39,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings public SchedulesDirect( ILogger logger, - IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory, IApplicationHost appHost, ICryptoProvider cryptoProvider) { _logger = logger; - _jsonSerializer = jsonSerializer; _httpClientFactory = httpClientFactory; _appHost = appHost; _cryptoProvider = cryptoProvider; @@ -104,7 +102,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings } }; - var requestString = _jsonSerializer.SerializeToString(requestList); + var jsonOptions = JsonDefaults.GetOptions(); + + var requestString = JsonSerializer.Serialize(requestList, jsonOptions); _logger.LogDebug("Request string for schedules is: {RequestString}", requestString); using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/schedules"); @@ -112,7 +112,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings options.Headers.TryAddWithoutValidation("token", token); using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false); await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - var dailySchedules = await _jsonSerializer.DeserializeFromStreamAsync>(responseStream).ConfigureAwait(false); + var dailySchedules = await JsonSerializer.DeserializeAsync>(responseStream, jsonOptions).ConfigureAwait(false); _logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId); using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs"); @@ -123,7 +123,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false); await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - var programDetails = await _jsonSerializer.DeserializeFromStreamAsync>(innerResponseStream).ConfigureAwait(false); + var programDetails = await JsonSerializer.DeserializeAsync>(innerResponseStream, jsonOptions).ConfigureAwait(false); var programDict = programDetails.ToDictionary(p => p.programID, y => y); var programIdsWithImages = @@ -479,7 +479,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false); await using var response = await innerResponse2.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - return await _jsonSerializer.DeserializeFromStreamAsync>(response).ConfigureAwait(false); + return await JsonSerializer.DeserializeAsync>(response, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -508,7 +508,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings using var httpResponse = await Send(options, false, info, cancellationToken).ConfigureAwait(false); await using var response = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - var root = await _jsonSerializer.DeserializeFromStreamAsync>(response).ConfigureAwait(false); + var root = await JsonSerializer.DeserializeAsync>(response, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); if (root != null) { @@ -649,7 +649,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - var root = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + var root = await JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); if (string.Equals(root.message, "OK", StringComparison.Ordinal)) { _logger.LogInformation("Authenticated with Schedules Direct token: " + root.token); @@ -705,7 +705,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings httpResponse.EnsureSuccessStatusCode(); await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); using var response = httpResponse.Content; - var root = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + var root = await JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions()).ConfigureAwait(false); return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase)); } @@ -777,7 +777,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false); await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - var root = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + var root = await JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions()).ConfigureAwait(false); _logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count); _logger.LogInformation("Mapping Stations to Channel"); diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 30aaf3a058..11355872f3 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -5,7 +5,9 @@ using System.Globalization; using System.IO; using System.Linq; using System.Reflection; +using System.Text.Json; using System.Threading.Tasks; +using MediaBrowser.Common.Json; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; @@ -24,7 +26,6 @@ namespace Emby.Server.Implementations.Localization private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; private readonly IServerConfigurationManager _configurationManager; - private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; private readonly Dictionary> _allParentalRatings = @@ -43,11 +44,9 @@ namespace Emby.Server.Implementations.Localization /// The logger. public LocalizationManager( IServerConfigurationManager configurationManager, - IJsonSerializer jsonSerializer, ILogger logger) { _configurationManager = configurationManager; - _jsonSerializer = jsonSerializer; _logger = logger; } @@ -179,8 +178,11 @@ namespace Emby.Server.Implementations.Localization /// public IEnumerable GetCountries() - => _jsonSerializer.DeserializeFromStream>( - _assembly.GetManifestResourceStream("Emby.Server.Implementations.Localization.countries.json")); + { + StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream("Emby.Server.Implementations.Localization.countries.json")); + + return JsonSerializer.Deserialize>(reader.ReadToEnd(), JsonDefaults.GetOptions()); + } /// public IEnumerable GetParentalRatings() @@ -344,7 +346,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 (stream != null) { - var dict = await _jsonSerializer.DeserializeFromStreamAsync>(stream).ConfigureAwait(false); + var dict = await JsonSerializer.DeserializeAsync>(stream, JsonDefaults.GetOptions()).ConfigureAwait(false); foreach (var key in dict.Keys) { diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 3a9e284583..4ac4443cd6 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -4,13 +4,14 @@ using System; using System.Globalization; using System.IO; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Events; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Json; using MediaBrowser.Common.Progress; -using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; @@ -21,11 +22,6 @@ namespace Emby.Server.Implementations.ScheduledTasks /// public class ScheduledTaskWorker : IScheduledTaskWorker { - /// - /// Gets or sets the json serializer. - /// - /// The json serializer. - private readonly IJsonSerializer _jsonSerializer; /// /// Gets or sets the application paths. @@ -88,7 +84,7 @@ namespace Emby.Server.Implementations.ScheduledTasks /// or /// logger. /// - 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) { @@ -105,11 +101,6 @@ namespace Emby.Server.Implementations.ScheduledTasks throw new ArgumentNullException(nameof(taskManager)); } - if (jsonSerializer == null) - { - throw new ArgumentNullException(nameof(jsonSerializer)); - } - if (logger == null) { throw new ArgumentNullException(nameof(logger)); @@ -118,7 +109,6 @@ namespace Emby.Server.Implementations.ScheduledTasks ScheduledTask = scheduledTask; _applicationPaths = applicationPaths; _taskManager = taskManager; - _jsonSerializer = jsonSerializer; _logger = logger; InitTriggerEvents(); @@ -150,7 +140,15 @@ namespace Emby.Server.Implementations.ScheduledTasks { try { - _lastExecutionResult = _jsonSerializer.DeserializeFromFile(path); + var jsonString = File.ReadAllText(path); + if (!string.IsNullOrWhiteSpace(jsonString)) + { + _lastExecutionResult = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); + } + else + { + _logger.LogDebug("Scheduled Task history file {path} is empty. Skipping deserialization.", path); + } } catch (Exception ex) { @@ -174,7 +172,8 @@ namespace Emby.Server.Implementations.ScheduledTasks lock (_lastExecutionResultSyncLock) { - _jsonSerializer.SerializeToFile(value, path); + using FileStream createStream = File.OpenWrite(path); + JsonSerializer.SerializeAsync(createStream, value, JsonDefaults.GetOptions()); } } } @@ -537,7 +536,8 @@ namespace Emby.Server.Implementations.ScheduledTasks TaskTriggerInfo[] list = null; if (File.Exists(path)) { - list = _jsonSerializer.DeserializeFromFile(path); + var jsonString = File.ReadAllText(path); + list = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); } // Return defaults if file doesn't exist. @@ -573,7 +573,8 @@ namespace Emby.Server.Implementations.ScheduledTasks Directory.CreateDirectory(Path.GetDirectoryName(path)); - _jsonSerializer.SerializeToFile(triggers, path); + using FileStream stream = File.OpenWrite(path); + JsonSerializer.SerializeAsync(stream, triggers, JsonDefaults.GetOptions()); } /// diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs index cfbf03ddc0..197a5ed5ef 100644 --- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs +++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Threading.Tasks; using Jellyfin.Data.Events; using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; @@ -19,6 +18,7 @@ namespace Emby.Server.Implementations.ScheduledTasks public class TaskManager : ITaskManager { public event EventHandler> TaskExecuting; + public event EventHandler TaskCompleted; /// @@ -33,7 +33,6 @@ namespace Emby.Server.Implementations.ScheduledTasks private readonly ConcurrentQueue> _taskQueue = new ConcurrentQueue>(); - private readonly IJsonSerializer _jsonSerializer; private readonly IApplicationPaths _applicationPaths; private readonly ILogger _logger; @@ -45,11 +44,9 @@ namespace Emby.Server.Implementations.ScheduledTasks /// The logger. public TaskManager( IApplicationPaths applicationPaths, - IJsonSerializer jsonSerializer, ILogger logger) { _applicationPaths = applicationPaths; - _jsonSerializer = jsonSerializer; _logger = logger; ScheduledTasks = Array.Empty(); @@ -196,7 +193,7 @@ namespace Emby.Server.Implementations.ScheduledTasks /// The tasks. public void AddTasks(IEnumerable 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(); } From e9902e9d35978b0f0d56b35612fa092daf6145c8 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 23 Dec 2020 13:13:28 +0100 Subject: [PATCH 08/52] Remove custom Json serializer --- .../Serialization/JsonSerializer.cs | 281 ------------------ .../Serialization/IJsonSerializer.cs | 102 ------- 2 files changed, 383 deletions(-) delete mode 100644 Emby.Server.Implementations/Serialization/JsonSerializer.cs delete mode 100644 MediaBrowser.Model/Serialization/IJsonSerializer.cs diff --git a/Emby.Server.Implementations/Serialization/JsonSerializer.cs b/Emby.Server.Implementations/Serialization/JsonSerializer.cs deleted file mode 100644 index 5ec3a735a6..0000000000 --- a/Emby.Server.Implementations/Serialization/JsonSerializer.cs +++ /dev/null @@ -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 -{ - /// - /// Provides a wrapper around third party json serialization. - /// - public class JsonSerializer : IJsonSerializer - { - /// - /// Initializes a new instance of the class. - /// - 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.SerializeFn = SerializeGuid; - } - - /// - /// Serializes to stream. - /// - /// The obj. - /// The stream. - /// obj - 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); - } - - /// - /// Serializes to stream. - /// - /// The obj. - /// The stream. - /// obj - public void SerializeToStream(T obj, Stream stream) - { - if (obj == null) - { - throw new ArgumentNullException(nameof(obj)); - } - - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - ServiceStack.Text.JsonSerializer.SerializeToStream(obj, stream); - } - - /// - /// Serializes to file. - /// - /// The obj. - /// The file. - /// obj - 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); - } - - /// - /// Deserializes from file. - /// - /// The type. - /// The file. - /// System.Object. - /// type - 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); - } - } - - /// - /// Deserializes from file. - /// - /// - /// The file. - /// ``0. - /// file - public T DeserializeFromFile(string file) - where T : class - { - if (string.IsNullOrEmpty(file)) - { - throw new ArgumentNullException(nameof(file)); - } - - using (var stream = OpenFile(file)) - { - return DeserializeFromStream(stream); - } - } - - /// - /// Deserializes from stream. - /// - /// - /// The stream. - /// ``0. - /// stream - public T DeserializeFromStream(Stream stream) - { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - return ServiceStack.Text.JsonSerializer.DeserializeFromStream(stream); - } - - public Task DeserializeFromStreamAsync(Stream stream) - { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - return ServiceStack.Text.JsonSerializer.DeserializeFromStreamAsync(stream); - } - - /// - /// Deserializes from string. - /// - /// - /// The text. - /// ``0. - /// text - public T DeserializeFromString(string text) - { - if (string.IsNullOrEmpty(text)) - { - throw new ArgumentNullException(nameof(text)); - } - - return ServiceStack.Text.JsonSerializer.DeserializeFromString(text); - } - - /// - /// Deserializes from stream. - /// - /// The stream. - /// The type. - /// System.Object. - /// stream - 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 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); - } - - /// - /// Deserializes from string. - /// - /// The json. - /// The type. - /// System.Object. - /// json - 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); - } - - /// - /// Serializes to string. - /// - /// The obj. - /// System.String. - /// obj - public string SerializeToString(object obj) - { - if (obj == null) - { - throw new ArgumentNullException(nameof(obj)); - } - - return ServiceStack.Text.JsonSerializer.SerializeToString(obj, obj.GetType()); - } - } -} diff --git a/MediaBrowser.Model/Serialization/IJsonSerializer.cs b/MediaBrowser.Model/Serialization/IJsonSerializer.cs deleted file mode 100644 index 09b6ff9b5a..0000000000 --- a/MediaBrowser.Model/Serialization/IJsonSerializer.cs +++ /dev/null @@ -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 - { - /// - /// Serializes to stream. - /// - /// The obj. - /// The stream. - /// obj - void SerializeToStream(object obj, Stream stream); - - /// - /// Serializes to stream. - /// - /// The obj. - /// The stream. - /// obj - void SerializeToStream(T obj, Stream stream); - - /// - /// Serializes to file. - /// - /// The obj. - /// The file. - /// obj - void SerializeToFile(object obj, string file); - - /// - /// Deserializes from file. - /// - /// The type. - /// The file. - /// System.Object. - /// type - object DeserializeFromFile(Type type, string file); - - /// - /// Deserializes from file. - /// - /// - /// The file. - /// ``0. - /// file - T DeserializeFromFile(string file) - where T : class; - - /// - /// Deserializes from stream. - /// - /// - /// The stream. - /// ``0. - /// stream - T DeserializeFromStream(Stream stream); - - /// - /// Deserializes from string. - /// - /// - /// The text. - /// ``0. - /// text - T DeserializeFromString(string text); - - /// - /// Deserializes from stream. - /// - /// The stream. - /// The type. - /// System.Object. - /// stream - object DeserializeFromStream(Stream stream, Type type); - - /// - /// Deserializes from string. - /// - /// The json. - /// The type. - /// System.Object. - /// json - object DeserializeFromString(string json, Type type); - - /// - /// Serializes to string. - /// - /// The obj. - /// System.String. - /// obj - string SerializeToString(object obj); - - Task DeserializeFromStreamAsync(Stream stream, Type type); - Task DeserializeFromStreamAsync(Stream stream); - } -} From b9dbdc7e542af6e84de75e5548f4185646383d6d Mon Sep 17 00:00:00 2001 From: David Date: Wed, 23 Dec 2020 13:24:14 +0100 Subject: [PATCH 09/52] Remove custom Json serializer from MediaBrowser.Controller --- MediaBrowser.Controller/Entities/CollectionFolder.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index d25545a2fc..b960278b85 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -4,9 +4,11 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Json; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -26,8 +28,6 @@ namespace MediaBrowser.Controller.Entities { public static IXmlSerializer XmlSerializer { get; set; } - public static IJsonSerializer JsonSerializer { get; set; } - public static IServerApplicationHost ApplicationHost { get; set; } public CollectionFolder() @@ -122,7 +122,8 @@ namespace MediaBrowser.Controller.Entities { LibraryOptions[path] = options; - var clone = JsonSerializer.DeserializeFromString(JsonSerializer.SerializeToString(options)); + var jsonOptions = JsonDefaults.GetOptions(); + var clone = JsonSerializer.Deserialize(JsonSerializer.Serialize(options, jsonOptions), jsonOptions); foreach (var mediaPath in clone.PathInfos) { if (!string.IsNullOrEmpty(mediaPath.Path)) From 62fcc84bf4ba3affeb7ab853cfb6880417fd1a9e Mon Sep 17 00:00:00 2001 From: David Date: Wed, 23 Dec 2020 13:35:49 +0100 Subject: [PATCH 10/52] Remove nuget reference --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 9e9452f32c..1416ffa261 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -37,7 +37,6 @@ - From 276b219fbdaaf14e16bc9e3435d1d9c571ed1cf8 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 23 Dec 2020 13:43:42 +0100 Subject: [PATCH 11/52] Add missing options --- MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index e73085460d..374b154e1e 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -298,10 +298,11 @@ namespace MediaBrowser.Providers.Plugins.Omdb 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.DeserializeAsync(stream, cancellationToken: cancellationToken).ConfigureAwait(false); + var jsonOptions = JsonDefaults.GetOptions(); + var rootObject = await JsonSerializer.DeserializeAsync(stream, jsonOptions, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(path)); await using FileStream jsonFileStream = File.Create(path); - await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, jsonOptions, cancellationToken).ConfigureAwait(false); return path; } @@ -337,10 +338,11 @@ namespace MediaBrowser.Providers.Plugins.Omdb 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.DeserializeAsync(stream, cancellationToken: cancellationToken).ConfigureAwait(false); + var jsonOptions = JsonDefaults.GetOptions(); + var rootObject = await JsonSerializer.DeserializeAsync(stream, jsonOptions, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(path)); await using FileStream jsonFileStream = File.Create(path); - await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, jsonOptions, cancellationToken).ConfigureAwait(false); return path; } From e2d338412fea5489aadb0e75af96cb36e4f13317 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 23 Dec 2020 14:45:05 +0100 Subject: [PATCH 12/52] Fix OMDb "N/A" --- .../Plugins/Omdb/OmdbProvider.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 374b154e1e..bbedffbc7d 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -296,10 +296,8 @@ namespace MediaBrowser.Providers.Plugins.Omdb "i={0}&plot=short&tomatoes=true&r=json", 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 jsonOptions = JsonDefaults.GetOptions(); - var rootObject = await JsonSerializer.DeserializeAsync(stream, jsonOptions, cancellationToken).ConfigureAwait(false); + var rootObject = await GetDeserializedOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, jsonOptions, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(path)); await using FileStream jsonFileStream = File.Create(path); await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, jsonOptions, cancellationToken).ConfigureAwait(false); @@ -336,10 +334,8 @@ namespace MediaBrowser.Providers.Plugins.Omdb imdbParam, 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 jsonOptions = JsonDefaults.GetOptions(); - var rootObject = await JsonSerializer.DeserializeAsync(stream, jsonOptions, cancellationToken).ConfigureAwait(false); + var rootObject = await GetDeserializedOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, jsonOptions, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(path)); await using FileStream jsonFileStream = File.Create(path); await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, jsonOptions, cancellationToken).ConfigureAwait(false); @@ -347,6 +343,16 @@ namespace MediaBrowser.Providers.Plugins.Omdb return path; } + public static async Task GetDeserializedOmdbResponse(HttpClient httpClient, string url, JsonSerializerOptions jsonOptions, CancellationToken cancellationToken) + { + using var response = await GetOmdbResponse(httpClient, url, cancellationToken).ConfigureAwait(false); + var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + + // OMDb is sending "N/A" for no empty number fields + content = content.Replace("\"N/A\"", "\"0\"", StringComparison.InvariantCulture); + return JsonSerializer.Deserialize(content, jsonOptions); + } + public static Task GetOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken) { return httpClient.GetAsync(url, cancellationToken); From f38970cbd32c845d1740828450aac8de16fec788 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 23 Dec 2020 15:03:14 +0100 Subject: [PATCH 13/52] Remove xml docs --- Emby.Server.Implementations/Channels/ChannelManager.cs | 1 - Emby.Server.Implementations/Localization/LocalizationManager.cs | 1 - .../ScheduledTasks/ScheduledTaskWorker.cs | 1 - Emby.Server.Implementations/ScheduledTasks/TaskManager.cs | 1 - 4 files changed, 4 deletions(-) diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 0966c252ba..4e4fbf82c6 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -59,7 +59,6 @@ namespace Emby.Server.Implementations.Channels /// The server configuration manager. /// The filesystem. /// The user data manager. - /// The JSON serializer. /// The provider manager. /// The memory cache. public ChannelManager( diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 11355872f3..2d6fb252d2 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -40,7 +40,6 @@ namespace Emby.Server.Implementations.Localization /// Initializes a new instance of the class. /// /// The configuration manager. - /// The json serializer. /// The logger. public LocalizationManager( IServerConfigurationManager configurationManager, diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 4ac4443cd6..eee7aeea30 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -71,7 +71,6 @@ namespace Emby.Server.Implementations.ScheduledTasks /// The scheduled task. /// The application paths. /// The task manager. - /// The json serializer. /// The logger. /// /// scheduledTask diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs index 197a5ed5ef..af316e1083 100644 --- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs +++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs @@ -40,7 +40,6 @@ namespace Emby.Server.Implementations.ScheduledTasks /// Initializes a new instance of the class. /// /// The application paths. - /// The json serializer. /// The logger. public TaskManager( IApplicationPaths applicationPaths, From b61541b6f749a189a69011f0888496fd7fcd99a7 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 24 Dec 2020 01:32:23 +0800 Subject: [PATCH 14/52] fix some profiles for H264 AMF encoder --- .../MediaEncoding/EncodingHelper.cs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 73ede7c5a4..6667c1c3bc 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1131,8 +1131,8 @@ namespace MediaBrowser.Controller.MediaEncoding profile = Regex.Replace(profile, @"\s+", string.Empty); // We only transcode to HEVC 8-bit for now, force Main Profile. - if (profile.Contains("main 10", StringComparison.OrdinalIgnoreCase) - || profile.Contains("main still", StringComparison.OrdinalIgnoreCase)) + if (profile.Contains("main10", StringComparison.OrdinalIgnoreCase) + || profile.Contains("mainstill", StringComparison.OrdinalIgnoreCase)) { profile = "main"; } @@ -1145,7 +1145,7 @@ namespace MediaBrowser.Controller.MediaEncoding // Only libx264 support encoding H264 High 10 Profile, otherwise force High Profile. if (!string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) - && profile.Contains("high 10", StringComparison.OrdinalIgnoreCase)) + && profile.Contains("high10", StringComparison.OrdinalIgnoreCase)) { profile = "high"; } @@ -1177,9 +1177,21 @@ namespace MediaBrowser.Controller.MediaEncoding profile = "high"; } + if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) + && profile.Contains("constrainedbaseline", StringComparison.OrdinalIgnoreCase)) + { + profile = "constrained_baseline"; + } + + if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) + && profile.Contains("constrainedhigh", StringComparison.OrdinalIgnoreCase)) + { + profile = "constrained_high"; + } + // Currently hevc_amf only support encoding HEVC Main Profile, otherwise force Main Profile. if (string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase) - && profile.Contains("main 10", StringComparison.OrdinalIgnoreCase)) + && profile.Contains("main10", StringComparison.OrdinalIgnoreCase)) { profile = "main"; } From 2a574914eaca486f6216db400d9fdf88ad88636e Mon Sep 17 00:00:00 2001 From: David Date: Wed, 23 Dec 2020 19:24:58 +0100 Subject: [PATCH 15/52] Use streams instead of strings --- Emby.Server.Implementations/ApplicationHost.cs | 5 +++-- .../Channels/ChannelManager.cs | 4 ++-- .../Library/LiveStreamHelper.cs | 4 ++-- .../Library/MediaSourceManager.cs | 4 ++-- .../LiveTv/EmbyTV/ItemDataProvider.cs | 4 ++-- .../ScheduledTasks/ScheduledTaskWorker.cs | 11 ++--------- .../Plugins/AudioDb/AlbumImageProvider.cs | 4 ++-- .../Plugins/AudioDb/AlbumProvider.cs | 4 ++-- .../Plugins/AudioDb/ArtistImageProvider.cs | 4 ++-- .../Plugins/AudioDb/ArtistProvider.cs | 4 ++-- MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs | 6 +++--- 11 files changed, 24 insertions(+), 30 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index fcc156e9c5..99cd3b6cc1 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1038,6 +1038,7 @@ namespace Emby.Server.Implementations } var directories = Directory.EnumerateDirectories(path, "*.*", SearchOption.TopDirectoryOnly); + var jsonOptions = JsonDefaults.GetOptions(); foreach (var dir in directories) { @@ -1046,8 +1047,8 @@ namespace Emby.Server.Implementations var metafile = Path.Combine(dir, "meta.json"); if (File.Exists(metafile)) { - var jsonString = File.ReadAllText(metafile); - var manifest = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); + using FileStream jsonStream = File.OpenRead(metafile); + var manifest = JsonSerializer.DeserializeAsync(jsonStream, jsonOptions).GetAwaiter().GetResult(); if (!Version.TryParse(manifest.TargetAbi, out var targetAbi)) { diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 4e4fbf82c6..b462d7bdcd 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -340,8 +340,8 @@ namespace Emby.Server.Implementations.Channels try { - var jsonString = File.ReadAllText(path); - return JsonSerializer.Deserialize>(jsonString, JsonDefaults.GetOptions()) ?? new List(); + using FileStream jsonStream = File.OpenRead(path); + return JsonSerializer.DeserializeAsync>(jsonStream, JsonDefaults.GetOptions()).GetAwaiter().GetResult(); } catch { diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index ad7988fb15..3ceba0fe94 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -46,8 +46,8 @@ namespace Emby.Server.Implementations.Library { try { - var jsonString = await File.ReadAllTextAsync(cacheFilePath, cancellationToken).ConfigureAwait(false); - JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); + await using FileStream jsonStream = File.OpenRead(cacheFilePath); + await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); // _logger.LogDebug("Found cached media info"); } diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 264390dd33..b3900e4aa1 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -641,8 +641,8 @@ namespace Emby.Server.Implementations.Library { try { - var json = await File.ReadAllTextAsync(cacheFilePath, cancellationToken).ConfigureAwait(false); - mediaInfo = JsonSerializer.Deserialize(json, JsonDefaults.GetOptions()); + await using FileStream jsonStream = File.OpenRead(cacheFilePath); + mediaInfo = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); // _logger.LogDebug("Found cached media info"); } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index 5256a0ee72..1b9abaa78e 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -44,8 +44,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV try { - var json = File.ReadAllText(_dataPath); - _items = JsonSerializer.Deserialize(json, JsonDefaults.GetOptions()); + using FileStream jsonStream = File.OpenRead(_dataPath); + _items = JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions()).GetAwaiter().GetResult(); return; } catch (Exception ex) diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index eee7aeea30..a9f4599864 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -139,15 +139,8 @@ namespace Emby.Server.Implementations.ScheduledTasks { try { - var jsonString = File.ReadAllText(path); - if (!string.IsNullOrWhiteSpace(jsonString)) - { - _lastExecutionResult = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); - } - else - { - _logger.LogDebug("Scheduled Task history file {path} is empty. Skipping deserialization.", path); - } + using FileStream jsonStream = File.OpenRead(path); + _lastExecutionResult = JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions()).GetAwaiter().GetResult(); } catch (Exception ex) { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs index 605b93788b..a5aeba7d2d 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs @@ -57,8 +57,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = AudioDbAlbumProvider.GetAlbumInfoPath(_config.ApplicationPaths, id); - var jsonString = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false); - var obj = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); + await using FileStream jsonStream = File.OpenRead(path); + var obj = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); if (obj != null && obj.album != null && obj.album.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs index 32ef135475..9a61abc1ac 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs @@ -64,8 +64,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = GetAlbumInfoPath(_config.ApplicationPaths, id); - var jsonString = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false); - var obj = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); + await using FileStream jsonStream = File.OpenRead(path); + var obj = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); if (obj != null && obj.album != null && obj.album.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs index f0b8e00f3f..639bbe8595 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs @@ -59,8 +59,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = AudioDbArtistProvider.GetArtistInfoPath(_config.ApplicationPaths, id); - var jsonString = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false); - var obj = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); + await using FileStream jsonStream = File.OpenRead(path); + var obj = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); if (obj != null && obj.artists != null && obj.artists.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs index 2476c86715..71efd194e3 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs @@ -65,8 +65,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = GetArtistInfoPath(_config.ApplicationPaths, id); - var jsonString = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false); - var obj = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); + await using FileStream jsonStream = File.OpenRead(path); + var obj = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); if (obj != null && obj.artists != null && obj.artists.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index bbedffbc7d..fa75e4ec49 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -299,7 +299,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var jsonOptions = JsonDefaults.GetOptions(); var rootObject = await GetDeserializedOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, jsonOptions, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(path)); - await using FileStream jsonFileStream = File.Create(path); + await using FileStream jsonFileStream = File.OpenWrite(path); await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, jsonOptions, cancellationToken).ConfigureAwait(false); return path; @@ -337,7 +337,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var jsonOptions = JsonDefaults.GetOptions(); var rootObject = await GetDeserializedOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, jsonOptions, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(path)); - await using FileStream jsonFileStream = File.Create(path); + await using FileStream jsonFileStream = File.OpenWrite(path); await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, jsonOptions, cancellationToken).ConfigureAwait(false); return path; @@ -349,7 +349,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); // OMDb is sending "N/A" for no empty number fields - content = content.Replace("\"N/A\"", "\"0\"", StringComparison.InvariantCulture); + content = content.Replace("\"N/A\"", "\"\"", StringComparison.InvariantCulture); return JsonSerializer.Deserialize(content, jsonOptions); } From 79cf57ce16c969963a88ba26baa838bea7465a2c Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 23 Dec 2020 12:33:50 -0700 Subject: [PATCH 16/52] Remove SQLitePCLRaw.provider.sqlite3.netstandard11 --- Jellyfin.Server/Jellyfin.Server.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 97fb56ba1e..bc000fd458 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -52,7 +52,6 @@ - From 570b4dc0d04d018a4e6aeeb161a17706da3bda21 Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 23 Dec 2020 12:34:07 -0700 Subject: [PATCH 17/52] Explicity reference Newtonsoft.Json --- MediaBrowser.Providers/MediaBrowser.Providers.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index fc8eb8c4e4..071a149db9 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -19,6 +19,7 @@ + From a714008b596b108a44020f61ca384b30263df984 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 23 Dec 2020 21:00:50 +0100 Subject: [PATCH 18/52] Add missing FileStreams --- .../Channels/ChannelManager.cs | 11 ++++++----- .../Library/LiveStreamHelper.cs | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index b462d7bdcd..3663e5094b 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -341,7 +341,8 @@ namespace Emby.Server.Implementations.Channels try { using FileStream jsonStream = File.OpenRead(path); - return JsonSerializer.DeserializeAsync>(jsonStream, JsonDefaults.GetOptions()).GetAwaiter().GetResult(); + return JsonSerializer.DeserializeAsync>(jsonStream, JsonDefaults.GetOptions()).GetAwaiter().GetResult() + ?? new List(); } catch { @@ -810,8 +811,8 @@ namespace Emby.Server.Implementations.Channels { if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) { - var jsonString = await File.ReadAllTextAsync(cachePath, cancellationToken); - var cachedResult = JsonSerializer.Deserialize(jsonString); + await using FileStream jsonStream = File.OpenRead(cachePath); + var cachedResult = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); if (cachedResult != null) { return null; @@ -833,8 +834,8 @@ namespace Emby.Server.Implementations.Channels { if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) { - var jsonString = await File.ReadAllTextAsync(cachePath, cancellationToken); - var cachedResult = JsonSerializer.Deserialize(jsonString); + await using FileStream jsonStream = File.OpenRead(cachePath); + var cachedResult = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); if (cachedResult != null) { return null; diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index 3ceba0fe94..80729a280c 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.Library try { await using FileStream jsonStream = File.OpenRead(cacheFilePath); - await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + mediaInfo = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); // _logger.LogDebug("Found cached media info"); } @@ -83,8 +83,8 @@ namespace Emby.Server.Implementations.Library if (cacheFilePath != null) { Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); - await using FileStream createStream = File.Create(cacheFilePath); - await JsonSerializer.SerializeAsync(createStream, mediaInfo, cancellationToken: cancellationToken).ConfigureAwait(false); + await using FileStream createStream = File.OpenWrite(cacheFilePath); + await JsonSerializer.SerializeAsync(createStream, mediaInfo, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); // _logger.LogDebug("Saved media info to {0}", cacheFilePath); } From 20993412f20dd2fa11d25994f32c645dcdc1aae6 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 24 Dec 2020 10:18:32 +0100 Subject: [PATCH 19/52] Add unstable nuget condition --- .ci/azure-pipelines-package.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/azure-pipelines-package.yml b/.ci/azure-pipelines-package.yml index 6063851163..0a63b329ba 100644 --- a/.ci/azure-pipelines-package.yml +++ b/.ci/azure-pipelines-package.yml @@ -210,6 +210,7 @@ jobs: - task: DotNetCoreCLI@2 displayName: 'Build Unstable Nuget packages' + condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master') inputs: command: 'custom' projects: | From e835dfb27d4a25e2057047692fd58af439b15acc Mon Sep 17 00:00:00 2001 From: David Date: Thu, 24 Dec 2020 10:31:51 +0100 Subject: [PATCH 20/52] Use sync string instead of file --- Emby.Server.Implementations/ApplicationHost.cs | 6 +++--- .../Channels/ChannelManager.cs | 5 +++-- .../LiveTv/EmbyTV/ItemDataProvider.cs | 4 ++-- .../ScheduledTasks/ScheduledTaskWorker.cs | 11 +++++++++-- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 99cd3b6cc1..6e360ed852 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -10,6 +10,7 @@ using System.Net; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Emby.Dlna; @@ -100,7 +101,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Prometheus.DotNetRuntime; -using JsonSerializer = System.Text.Json.JsonSerializer; using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager; @@ -1047,8 +1047,8 @@ namespace Emby.Server.Implementations var metafile = Path.Combine(dir, "meta.json"); if (File.Exists(metafile)) { - using FileStream jsonStream = File.OpenRead(metafile); - var manifest = JsonSerializer.DeserializeAsync(jsonStream, jsonOptions).GetAwaiter().GetResult(); + var jsonString = File.ReadAllText(metafile); + var manifest = JsonSerializer.Deserialize(jsonString, jsonOptions); if (!Version.TryParse(manifest.TargetAbi, out var targetAbi)) { diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 3663e5094b..f06ad4436b 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -340,8 +340,8 @@ namespace Emby.Server.Implementations.Channels try { - using FileStream jsonStream = File.OpenRead(path); - return JsonSerializer.DeserializeAsync>(jsonStream, JsonDefaults.GetOptions()).GetAwaiter().GetResult() + var jsonString = File.ReadAllText(path); + return JsonSerializer.Deserialize>(jsonString, JsonDefaults.GetOptions()) ?? new List(); } catch @@ -368,6 +368,7 @@ namespace Emby.Server.Implementations.Channels } Directory.CreateDirectory(Path.GetDirectoryName(path)); + await using FileStream createStream = File.Create(path); await JsonSerializer.SerializeAsync(createStream, mediaSources, JsonDefaults.GetOptions()).ConfigureAwait(false); } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index 1b9abaa78e..bbe9b50dd5 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -44,8 +44,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV try { - using FileStream jsonStream = File.OpenRead(_dataPath); - _items = JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions()).GetAwaiter().GetResult(); + var jsonString = File.ReadAllText(_dataPath); + _items = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); return; } catch (Exception ex) diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index a9f4599864..eee7aeea30 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -139,8 +139,15 @@ namespace Emby.Server.Implementations.ScheduledTasks { try { - using FileStream jsonStream = File.OpenRead(path); - _lastExecutionResult = JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions()).GetAwaiter().GetResult(); + var jsonString = File.ReadAllText(path); + if (!string.IsNullOrWhiteSpace(jsonString)) + { + _lastExecutionResult = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); + } + else + { + _logger.LogDebug("Scheduled Task history file {path} is empty. Skipping deserialization.", path); + } } catch (Exception ex) { From 043d04544850bb82972bb7817510984c2ddea75a Mon Sep 17 00:00:00 2001 From: David Date: Thu, 24 Dec 2020 11:15:12 +0100 Subject: [PATCH 21/52] Put json serializer options in private field --- Emby.Dlna/DlnaManager.cs | 5 +++-- .../ApplicationHost.cs | 4 ++-- .../Channels/ChannelManager.cs | 13 +++++++------ .../Library/LiveStreamHelper.cs | 5 +++-- .../Library/MediaSourceManager.cs | 9 +++++---- .../LiveTv/EmbyTV/EncodedRecorder.cs | 4 ++-- .../LiveTv/EmbyTV/ItemDataProvider.cs | 5 +++-- .../LiveTv/Listings/SchedulesDirect.cs | 19 +++++++++---------- .../Localization/LocalizationManager.cs | 6 ++++-- .../ScheduledTasks/ScheduledTaskWorker.cs | 13 +++++++++---- .../Entities/CollectionFolder.cs | 4 ++-- .../Plugins/AudioDb/AlbumImageProvider.cs | 3 ++- .../Plugins/AudioDb/AlbumProvider.cs | 3 ++- .../Plugins/AudioDb/ArtistImageProvider.cs | 3 ++- .../Plugins/AudioDb/ArtistProvider.cs | 3 ++- .../Plugins/Omdb/OmdbItemProvider.cs | 5 +++-- .../Plugins/Omdb/OmdbProvider.cs | 19 +++++++++---------- 17 files changed, 69 insertions(+), 54 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index df69dd5162..21ba1c7552 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -36,6 +36,7 @@ namespace Emby.Dlna private readonly ILogger _logger; private readonly IServerApplicationHost _appHost; private static readonly Assembly _assembly = typeof(DlnaManager).Assembly; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); private readonly Dictionary> _profiles = new Dictionary>(StringComparer.Ordinal); @@ -494,9 +495,9 @@ namespace Emby.Dlna return profile; } - var json = JsonSerializer.Serialize(profile, JsonDefaults.GetOptions()); + var json = JsonSerializer.Serialize(profile, _jsonOptions); - return JsonSerializer.Deserialize(json, options: JsonDefaults.GetOptions()); + return JsonSerializer.Deserialize(json, _jsonOptions); } public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 6e360ed852..115d94b316 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -123,6 +123,7 @@ namespace Emby.Server.Implementations private IMediaEncoder _mediaEncoder; private ISessionManager _sessionManager; private string[] _urlPrefixes; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); /// /// Gets a value indicating whether this instance can self restart. @@ -1038,7 +1039,6 @@ namespace Emby.Server.Implementations } var directories = Directory.EnumerateDirectories(path, "*.*", SearchOption.TopDirectoryOnly); - var jsonOptions = JsonDefaults.GetOptions(); foreach (var dir in directories) { @@ -1048,7 +1048,7 @@ namespace Emby.Server.Implementations if (File.Exists(metafile)) { var jsonString = File.ReadAllText(metafile); - var manifest = JsonSerializer.Deserialize(jsonString, jsonOptions); + var manifest = JsonSerializer.Deserialize(jsonString, _jsonOptions); if (!Version.TryParse(manifest.TargetAbi, out var targetAbi)) { diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index f06ad4436b..cf6a87ecfd 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; @@ -25,7 +26,6 @@ using MediaBrowser.Model.Querying; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Episode = MediaBrowser.Controller.Entities.TV.Episode; -using JsonSerializer = System.Text.Json.JsonSerializer; using Movie = MediaBrowser.Controller.Entities.Movies.Movie; using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum; using Season = MediaBrowser.Controller.Entities.TV.Season; @@ -48,6 +48,7 @@ namespace Emby.Server.Implementations.Channels private readonly IProviderManager _providerManager; private readonly IMemoryCache _memoryCache; private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); /// /// Initializes a new instance of the class. @@ -341,7 +342,7 @@ namespace Emby.Server.Implementations.Channels try { var jsonString = File.ReadAllText(path); - return JsonSerializer.Deserialize>(jsonString, JsonDefaults.GetOptions()) + return JsonSerializer.Deserialize>(jsonString, _jsonOptions) ?? new List(); } catch @@ -370,7 +371,7 @@ namespace Emby.Server.Implementations.Channels Directory.CreateDirectory(Path.GetDirectoryName(path)); await using FileStream createStream = File.Create(path); - await JsonSerializer.SerializeAsync(createStream, mediaSources, JsonDefaults.GetOptions()).ConfigureAwait(false); + await JsonSerializer.SerializeAsync(createStream, mediaSources, _jsonOptions).ConfigureAwait(false); } /// @@ -813,7 +814,7 @@ namespace Emby.Server.Implementations.Channels if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) { await using FileStream jsonStream = File.OpenRead(cachePath); - var cachedResult = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + var cachedResult = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); if (cachedResult != null) { return null; @@ -836,7 +837,7 @@ namespace Emby.Server.Implementations.Channels if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) { await using FileStream jsonStream = File.OpenRead(cachePath); - var cachedResult = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + var cachedResult = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); if (cachedResult != null) { return null; @@ -884,7 +885,7 @@ namespace Emby.Server.Implementations.Channels Directory.CreateDirectory(Path.GetDirectoryName(path)); await using FileStream createStream = File.Create(path); - await JsonSerializer.SerializeAsync(createStream, result, JsonDefaults.GetOptions()).ConfigureAwait(false); + await JsonSerializer.SerializeAsync(createStream, result, _jsonOptions).ConfigureAwait(false); } catch (Exception ex) { diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index 80729a280c..2070df31e4 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -25,6 +25,7 @@ namespace Emby.Server.Implementations.Library private readonly IMediaEncoder _mediaEncoder; private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger, IApplicationPaths appPaths) { @@ -47,7 +48,7 @@ namespace Emby.Server.Implementations.Library try { await using FileStream jsonStream = File.OpenRead(cacheFilePath); - mediaInfo = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + mediaInfo = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); // _logger.LogDebug("Found cached media info"); } @@ -84,7 +85,7 @@ namespace Emby.Server.Implementations.Library { Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); await using FileStream createStream = File.OpenWrite(cacheFilePath); - await JsonSerializer.SerializeAsync(createStream, mediaInfo, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + await JsonSerializer.SerializeAsync(createStream, mediaInfo, _jsonOptions, cancellationToken).ConfigureAwait(false); // _logger.LogDebug("Saved media info to {0}", cacheFilePath); } diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index b3900e4aa1..660ec106bd 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -46,6 +46,7 @@ namespace Emby.Server.Implementations.Library private readonly ConcurrentDictionary _openStreams = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); private IMediaSourceProvider[] _providers; @@ -514,9 +515,9 @@ namespace Emby.Server.Implementations.Library } // TODO: @bond Fix - var json = JsonSerializer.Serialize(mediaSource, JsonDefaults.GetOptions()); + var json = JsonSerializer.Serialize(mediaSource, _jsonOptions); _logger.LogInformation("Live stream opened: " + json); - var clone = JsonSerializer.Deserialize(json, JsonDefaults.GetOptions()); + var clone = JsonSerializer.Deserialize(json, _jsonOptions); if (!request.UserId.Equals(Guid.Empty)) { @@ -642,7 +643,7 @@ namespace Emby.Server.Implementations.Library try { await using FileStream jsonStream = File.OpenRead(cacheFilePath); - mediaInfo = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + mediaInfo = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); // _logger.LogDebug("Found cached media info"); } @@ -679,7 +680,7 @@ namespace Emby.Server.Implementations.Library { Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); await using FileStream createStream = File.Create(cacheFilePath); - await JsonSerializer.SerializeAsync(createStream, mediaInfo, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + await JsonSerializer.SerializeAsync(createStream, mediaInfo, _jsonOptions, cancellationToken).ConfigureAwait(false); // _logger.LogDebug("Saved media info to {0}", cacheFilePath); } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 9609f28816..a70a72b74f 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private readonly IServerApplicationPaths _appPaths; private readonly TaskCompletionSource _taskCompletionSource = new TaskCompletionSource(); private readonly IServerConfigurationManager _serverConfigurationManager; - + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); private bool _hasExited; private Stream _logFileStream; private string _targetPath; @@ -93,7 +93,7 @@ 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. _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); - var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(mediaSource, JsonDefaults.GetOptions()) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine); + var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(mediaSource, _jsonOptions) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine); _logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length); _process = new Process diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index bbe9b50dd5..f16d96a592 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -15,6 +15,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { private readonly string _dataPath; private readonly object _fileDataLock = new object(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); private T[] _items; public ItemDataProvider( @@ -45,7 +46,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV try { var jsonString = File.ReadAllText(_dataPath); - _items = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); + _items = JsonSerializer.Deserialize(jsonString, _jsonOptions); return; } catch (Exception ex) @@ -61,7 +62,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { Directory.CreateDirectory(Path.GetDirectoryName(_dataPath)); using FileStream stream = File.OpenWrite(_dataPath); - JsonSerializer.SerializeAsync(stream, _items, JsonDefaults.GetOptions()); + JsonSerializer.SerializeAsync(stream, _items, _jsonOptions); } public IReadOnlyList GetAll() diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 198d196c23..2b8ccfb628 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -36,6 +36,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings private readonly ConcurrentDictionary _tokens = new ConcurrentDictionary(); private DateTime _lastErrorResponse; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); public SchedulesDirect( ILogger logger, @@ -102,9 +103,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings } }; - var jsonOptions = JsonDefaults.GetOptions(); - - var requestString = JsonSerializer.Serialize(requestList, jsonOptions); + var requestString = JsonSerializer.Serialize(requestList, _jsonOptions); _logger.LogDebug("Request string for schedules is: {RequestString}", requestString); using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/schedules"); @@ -112,7 +111,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings options.Headers.TryAddWithoutValidation("token", token); using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false); await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - var dailySchedules = await JsonSerializer.DeserializeAsync>(responseStream, jsonOptions).ConfigureAwait(false); + var dailySchedules = await JsonSerializer.DeserializeAsync>(responseStream, _jsonOptions).ConfigureAwait(false); _logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId); 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); await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - var programDetails = await JsonSerializer.DeserializeAsync>(innerResponseStream, jsonOptions).ConfigureAwait(false); + var programDetails = await JsonSerializer.DeserializeAsync>(innerResponseStream, _jsonOptions).ConfigureAwait(false); var programDict = programDetails.ToDictionary(p => p.programID, y => y); var programIdsWithImages = @@ -479,7 +478,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false); await using var response = await innerResponse2.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - return await JsonSerializer.DeserializeAsync>(response, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + return await JsonSerializer.DeserializeAsync>(response, _jsonOptions, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -508,7 +507,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings using var httpResponse = await Send(options, false, info, cancellationToken).ConfigureAwait(false); await using var response = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - var root = await JsonSerializer.DeserializeAsync>(response, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + var root = await JsonSerializer.DeserializeAsync>(response, _jsonOptions, cancellationToken).ConfigureAwait(false); if (root != null) { @@ -649,7 +648,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - var root = await JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + var root = await JsonSerializer.DeserializeAsync(stream, _jsonOptions, cancellationToken).ConfigureAwait(false); if (string.Equals(root.message, "OK", StringComparison.Ordinal)) { _logger.LogInformation("Authenticated with Schedules Direct token: " + root.token); @@ -705,7 +704,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings httpResponse.EnsureSuccessStatusCode(); await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); using var response = httpResponse.Content; - var root = await JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions()).ConfigureAwait(false); + var root = await JsonSerializer.DeserializeAsync(stream, _jsonOptions).ConfigureAwait(false); 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); await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - var root = await JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions()).ConfigureAwait(false); + var root = await JsonSerializer.DeserializeAsync(stream, _jsonOptions).ConfigureAwait(false); _logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count); _logger.LogInformation("Mapping Stations to Channel"); diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 2d6fb252d2..3f9e221066 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -36,6 +36,8 @@ namespace Emby.Server.Implementations.Localization private List _cultures; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + /// /// Initializes a new instance of the class. /// @@ -180,7 +182,7 @@ namespace Emby.Server.Implementations.Localization { StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream("Emby.Server.Implementations.Localization.countries.json")); - return JsonSerializer.Deserialize>(reader.ReadToEnd(), JsonDefaults.GetOptions()); + return JsonSerializer.Deserialize>(reader.ReadToEnd(), _jsonOptions); } /// @@ -345,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 (stream != null) { - var dict = await JsonSerializer.DeserializeAsync>(stream, JsonDefaults.GetOptions()).ConfigureAwait(false); + var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); foreach (var key in dict.Keys) { diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index eee7aeea30..3dca05bf98 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -65,6 +65,11 @@ namespace Emby.Server.Implementations.ScheduledTasks /// private string _id; + /// + /// The options for the json Serializer. + /// + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + /// /// Initializes a new instance of the class. /// @@ -142,7 +147,7 @@ namespace Emby.Server.Implementations.ScheduledTasks var jsonString = File.ReadAllText(path); if (!string.IsNullOrWhiteSpace(jsonString)) { - _lastExecutionResult = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); + _lastExecutionResult = JsonSerializer.Deserialize(jsonString, _jsonOptions); } else { @@ -172,7 +177,7 @@ namespace Emby.Server.Implementations.ScheduledTasks lock (_lastExecutionResultSyncLock) { using FileStream createStream = File.OpenWrite(path); - JsonSerializer.SerializeAsync(createStream, value, JsonDefaults.GetOptions()); + JsonSerializer.SerializeAsync(createStream, value, _jsonOptions); } } } @@ -536,7 +541,7 @@ namespace Emby.Server.Implementations.ScheduledTasks if (File.Exists(path)) { var jsonString = File.ReadAllText(path); - list = JsonSerializer.Deserialize(jsonString, JsonDefaults.GetOptions()); + list = JsonSerializer.Deserialize(jsonString, _jsonOptions); } // Return defaults if file doesn't exist. @@ -573,7 +578,7 @@ namespace Emby.Server.Implementations.ScheduledTasks Directory.CreateDirectory(Path.GetDirectoryName(path)); using FileStream stream = File.OpenWrite(path); - JsonSerializer.SerializeAsync(stream, triggers, JsonDefaults.GetOptions()); + JsonSerializer.SerializeAsync(stream, triggers, _jsonOptions); } /// diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index b960278b85..c3b6af76ea 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -26,6 +26,7 @@ namespace MediaBrowser.Controller.Entities /// public class CollectionFolder : Folder, ICollectionFolder { + private static readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); public static IXmlSerializer XmlSerializer { get; set; } public static IServerApplicationHost ApplicationHost { get; set; } @@ -122,8 +123,7 @@ namespace MediaBrowser.Controller.Entities { LibraryOptions[path] = options; - var jsonOptions = JsonDefaults.GetOptions(); - var clone = JsonSerializer.Deserialize(JsonSerializer.Serialize(options, jsonOptions), jsonOptions); + var clone = JsonSerializer.Deserialize(JsonSerializer.Serialize(options, _jsonOptions), _jsonOptions); foreach (var mediaPath in clone.PathInfos) { if (!string.IsNullOrEmpty(mediaPath.Path)) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs index a5aeba7d2d..cd9e477432 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs @@ -22,6 +22,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { private readonly IServerConfigurationManager _config; private readonly IHttpClientFactory _httpClientFactory; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); public AudioDbAlbumImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory) { @@ -58,7 +59,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = AudioDbAlbumProvider.GetAlbumInfoPath(_config.ApplicationPaths, id); await using FileStream jsonStream = File.OpenRead(path); - var obj = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + var obj = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); if (obj != null && obj.album != null && obj.album.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs index 9a61abc1ac..f463a3566b 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs @@ -29,6 +29,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; private readonly IHttpClientFactory _httpClientFactory; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); public static AudioDbAlbumProvider Current; @@ -65,7 +66,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = GetAlbumInfoPath(_config.ApplicationPaths, id); await using FileStream jsonStream = File.OpenRead(path); - var obj = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + var obj = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); if (obj != null && obj.album != null && obj.album.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs index 639bbe8595..36700d1917 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs @@ -22,6 +22,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { private readonly IServerConfigurationManager _config; private readonly IHttpClientFactory _httpClientFactory; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); public AudioDbArtistImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory) { @@ -60,7 +61,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = AudioDbArtistProvider.GetArtistInfoPath(_config.ApplicationPaths, id); await using FileStream jsonStream = File.OpenRead(path); - var obj = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + var obj = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); if (obj != null && obj.artists != null && obj.artists.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs index 71efd194e3..7a15adb8e8 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs @@ -31,6 +31,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; private readonly IHttpClientFactory _httpClientFactory; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory) { @@ -66,7 +67,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = GetArtistInfoPath(_config.ApplicationPaths, id); await using FileStream jsonStream = File.OpenRead(path); - var obj = await JsonSerializer.DeserializeAsync(jsonStream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + var obj = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); if (obj != null && obj.artists != null && obj.artists.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs index 7e9ef974e2..3ef404b535 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs @@ -32,6 +32,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; private readonly IApplicationHost _appHost; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); public OmdbItemProvider( IApplicationHost appHost, @@ -136,7 +137,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb if (isSearch) { - var searchResultList = await JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + var searchResultList = await JsonSerializer.DeserializeAsync(stream, _jsonOptions, cancellationToken).ConfigureAwait(false); if (searchResultList != null && searchResultList.Search != null) { resultList.AddRange(searchResultList.Search); @@ -144,7 +145,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb } else { - var result = await JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + var result = await JsonSerializer.DeserializeAsync(stream, _jsonOptions, cancellationToken).ConfigureAwait(false); if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase)) { resultList.Add(result); diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index fa75e4ec49..3c21629990 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -28,6 +28,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb private readonly IHttpClientFactory _httpClientFactory; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IApplicationHost _appHost; + private static readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); public OmdbProvider(IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager) { @@ -219,7 +220,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb } } - var result = JsonSerializer.Deserialize(resultString, JsonDefaults.GetOptions()); + var result = JsonSerializer.Deserialize(resultString, _jsonOptions); return result; } @@ -238,7 +239,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb } } - var result = JsonSerializer.Deserialize(resultString, JsonDefaults.GetOptions()); + var result = JsonSerializer.Deserialize(resultString, _jsonOptions); return result; } @@ -296,11 +297,10 @@ namespace MediaBrowser.Providers.Plugins.Omdb "i={0}&plot=short&tomatoes=true&r=json", imdbParam)); - var jsonOptions = JsonDefaults.GetOptions(); - var rootObject = await GetDeserializedOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, jsonOptions, cancellationToken).ConfigureAwait(false); + var rootObject = await GetDeserializedOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(path)); await using FileStream jsonFileStream = File.OpenWrite(path); - await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, jsonOptions, cancellationToken).ConfigureAwait(false); + await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false); return path; } @@ -334,23 +334,22 @@ namespace MediaBrowser.Providers.Plugins.Omdb imdbParam, seasonId)); - var jsonOptions = JsonDefaults.GetOptions(); - var rootObject = await GetDeserializedOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, jsonOptions, cancellationToken).ConfigureAwait(false); + var rootObject = await GetDeserializedOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(path)); await using FileStream jsonFileStream = File.OpenWrite(path); - await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, jsonOptions, cancellationToken).ConfigureAwait(false); + await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false); return path; } - public static async Task GetDeserializedOmdbResponse(HttpClient httpClient, string url, JsonSerializerOptions jsonOptions, CancellationToken cancellationToken) + public static async Task GetDeserializedOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken) { using var response = await GetOmdbResponse(httpClient, url, cancellationToken).ConfigureAwait(false); var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); // OMDb is sending "N/A" for no empty number fields content = content.Replace("\"N/A\"", "\"\"", StringComparison.InvariantCulture); - return JsonSerializer.Deserialize(content, jsonOptions); + return JsonSerializer.Deserialize(content, _jsonOptions); } public static Task GetOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken) From bc6ec08322a6d9111849dd4661bdda3746347835 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 24 Dec 2020 19:41:02 +0800 Subject: [PATCH 22/52] avoid transcoding to 3ch audio for HLS streaming --- .../MediaEncoding/EncodingHelper.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 6667c1c3bc..17201551a0 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1722,6 +1722,16 @@ namespace MediaBrowser.Controller.MediaEncoding : transcoderChannelLimit.Value; } + // Avoid transcoding to audio channels other than 1ch, 2ch, 6ch (5.1 layout) and 8ch (7.1 layout). + // https://developer.apple.com/documentation/http_live_streaming/hls_authoring_specification_for_apple_devices + if (isTranscodingAudio + && resultChannels.HasValue + && resultChannels.Value > 2 + && resultChannels.Value < 6) + { + resultChannels = 2; + } + return resultChannels; } From ae1187042af7bc9a144d184a31017b6bd32eefcd Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 24 Dec 2020 20:06:55 +0800 Subject: [PATCH 23/52] also avoid 7ch transcoding --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 17201551a0..29978a56c6 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1726,8 +1726,7 @@ namespace MediaBrowser.Controller.MediaEncoding // https://developer.apple.com/documentation/http_live_streaming/hls_authoring_specification_for_apple_devices if (isTranscodingAudio && resultChannels.HasValue - && resultChannels.Value > 2 - && resultChannels.Value < 6) + && (resultChannels.Value > 2 && resultChannels.Value < 6 || resultChannels.Value == 7)) { resultChannels = 2; } From c8a95e0926c3152af91ba07fd74d23a2ee76baf9 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 24 Dec 2020 10:05:06 -0700 Subject: [PATCH 24/52] Fix null reference when logging --- MediaBrowser.Model/Dlna/StreamBuilder.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 59c9810009..431cf0bafa 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -1033,9 +1033,9 @@ namespace MediaBrowser.Model.Dlna { _logger.LogInformation( "Profile: {0}, No video direct play profiles found for {1} with codec {2}", - profile.Name ?? "Unknown Profile", - mediaSource.Path ?? "Unknown path", - videoStream.Codec ?? "Unknown codec"); + profile?.Name ?? "Unknown Profile", + mediaSource?.Path ?? "Unknown path", + videoStream?.Codec ?? "Unknown codec"); return (null, GetTranscodeReasonsFromDirectPlayProfile(mediaSource, videoStream, audioStream, profile.DirectPlayProfiles)); } From 6ddbe8420f6bc61b2561c20def326fa1b4291c5e Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 24 Dec 2020 08:10:30 -0700 Subject: [PATCH 25/52] Add tests for special Omdb json --- .../JsonOmdbNotAvailableConverterFactory.cs | 36 +++++++++++++++++ .../JsonOmdbNotAvailableStringConverter.cs | 33 +++++++++++++++ .../JsonOmdbNotAvailableStructConverter.cs | 35 ++++++++++++++++ .../Plugins/Omdb/OmdbProvider.cs | 40 +++++++++++++++++-- .../Jellyfin.Common.Tests.csproj | 1 + .../Json/JsonOmdbConverterTests.cs | 21 ++++++++++ 6 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableConverterFactory.cs create mode 100644 MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs create mode 100644 MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStructConverter.cs create mode 100644 tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs diff --git a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableConverterFactory.cs new file mode 100644 index 0000000000..5994ce9226 --- /dev/null +++ b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableConverterFactory.cs @@ -0,0 +1,36 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Common.Json.Converters +{ + /// + /// Json Omdb converter factory. + /// + /// + /// Remove when Omdb is moved to plugin. + /// + public class JsonOmdbNotAvailableConverterFactory : JsonConverterFactory + { + /// + public override bool CanConvert(Type typeToConvert) + { + return (typeToConvert.IsGenericType + && typeToConvert.GetGenericTypeDefinition() == typeof(Nullable<>) + && typeToConvert.GenericTypeArguments[0].IsValueType) + || typeToConvert == typeof(string); + } + + /// + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + if (typeToConvert == typeof(string)) + { + return (JsonConverter)Activator.CreateInstance(typeof(JsonOmdbNotAvailableStringConverter)); + } + + var structType = typeToConvert.GenericTypeArguments[0]; + return (JsonConverter)Activator.CreateInstance(typeof(JsonOmdbNotAvailableStructConverter<>).MakeGenericType(structType)); + } + } +} diff --git a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs new file mode 100644 index 0000000000..2b343a5059 --- /dev/null +++ b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs @@ -0,0 +1,33 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Common.Json.Converters +{ + /// + /// Converts a string N/A to string.Empty. + /// + public class JsonOmdbNotAvailableStringConverter : JsonConverter + { + /// + 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 JsonSerializer.Deserialize(ref reader, options); + } + + /// + public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(value, options); + } + } +} diff --git a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStructConverter.cs b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStructConverter.cs new file mode 100644 index 0000000000..b9e67ce2de --- /dev/null +++ b/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 +{ + /// + /// Converts a string N/A to string.Empty. + /// + /// The resulting type. + public class JsonOmdbNotAvailableStructConverter : JsonConverter + where T : struct + { + /// + 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(ref reader, options); + } + + /// + public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(value, options); + } + } +} diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 3c21629990..1ebd1b13a3 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -8,10 +8,12 @@ using System.Linq; using System.Net.Http; using System.Text; using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; using MediaBrowser.Common.Json; +using MediaBrowser.Common.Json.Converters; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -467,74 +469,104 @@ namespace MediaBrowser.Providers.Plugins.Omdb return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase); } - internal class SeasonRootObject + public class SeasonRootObject { + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Title { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string seriesID { get; set; } - public int Season { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + public int? Season { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public int? totalSeasons { get; set; } public RootObject[] Episodes { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Response { get; set; } } - internal class RootObject + public class RootObject { + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Title { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Year { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Rated { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Released { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Runtime { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Genre { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Director { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Writer { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Actors { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Plot { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Language { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Country { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Awards { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Poster { get; set; } public List Ratings { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Metascore { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string imdbRating { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string imdbVotes { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string imdbID { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Type { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string DVD { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string BoxOffice { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Production { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Website { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] public string Response { get; set; } - public int Episode { get; set; } + [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + public int? Episode { get; set; } public float? GetRottenTomatoScore() { diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index af4684f562..91b34546f3 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -29,6 +29,7 @@ + diff --git a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs new file mode 100644 index 0000000000..90537dc1ff --- /dev/null +++ b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs @@ -0,0 +1,21 @@ +using System.Text.Json; +using MediaBrowser.Common.Json; +using MediaBrowser.Providers.Plugins.Omdb; +using Xunit; + +namespace Jellyfin.Common.Tests.Json +{ + public static class JsonOmdbConverterTests + { + [Fact] + public static 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(Input, JsonDefaults.GetOptions()); + Assert.NotNull(seasonRootObject); + Assert.Null(seasonRootObject?.Awards); + Assert.Null(seasonRootObject?.Episode); + Assert.Null(seasonRootObject?.Metascore); + } + } +} From fc212e5e5f698482abbf19912787cd2691bae981 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 24 Dec 2020 08:16:50 -0700 Subject: [PATCH 26/52] Remove JsonOmdbNotAvailableConverterFactory --- .../JsonOmdbNotAvailableConverterFactory.cs | 36 ----------- .../Plugins/Omdb/OmdbProvider.cs | 60 +++++++++---------- 2 files changed, 30 insertions(+), 66 deletions(-) delete mode 100644 MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableConverterFactory.cs diff --git a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableConverterFactory.cs deleted file mode 100644 index 5994ce9226..0000000000 --- a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableConverterFactory.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Json Omdb converter factory. - /// - /// - /// Remove when Omdb is moved to plugin. - /// - public class JsonOmdbNotAvailableConverterFactory : JsonConverterFactory - { - /// - public override bool CanConvert(Type typeToConvert) - { - return (typeToConvert.IsGenericType - && typeToConvert.GetGenericTypeDefinition() == typeof(Nullable<>) - && typeToConvert.GenericTypeArguments[0].IsValueType) - || typeToConvert == typeof(string); - } - - /// - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) - { - if (typeToConvert == typeof(string)) - { - return (JsonConverter)Activator.CreateInstance(typeof(JsonOmdbNotAvailableStringConverter)); - } - - var structType = typeToConvert.GenericTypeArguments[0]; - return (JsonConverter)Activator.CreateInstance(typeof(JsonOmdbNotAvailableStructConverter<>).MakeGenericType(structType)); - } - } -} diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 1ebd1b13a3..e700e6969b 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -471,101 +471,101 @@ namespace MediaBrowser.Providers.Plugins.Omdb public class SeasonRootObject { - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Title { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string seriesID { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStructConverter))] public int? Season { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStructConverter))] public int? totalSeasons { get; set; } public RootObject[] Episodes { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Response { get; set; } } public class RootObject { - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Title { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Year { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Rated { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Released { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Runtime { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Genre { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Director { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Writer { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Actors { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Plot { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Language { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Country { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Awards { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Poster { get; set; } public List Ratings { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Metascore { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string imdbRating { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string imdbVotes { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string imdbID { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Type { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string DVD { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string BoxOffice { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Production { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Website { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Response { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableConverterFactory))] + [JsonConverter(typeof(JsonOmdbNotAvailableStructConverter))] public int? Episode { get; set; } public float? GetRottenTomatoScore() From e09dd5aa87a61c38e3995d351efd8419555939f6 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 24 Dec 2020 08:26:26 -0700 Subject: [PATCH 27/52] Add targeted tests --- .../Json/JsonOmdbConverterTests.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs index 90537dc1ff..aae95e7314 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs +++ b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs @@ -1,5 +1,7 @@ using System.Text.Json; +using System.Text.Json.Serialization; using MediaBrowser.Common.Json; +using MediaBrowser.Common.Json.Converters; using MediaBrowser.Providers.Plugins.Omdb; using Xunit; @@ -17,5 +19,39 @@ namespace Jellyfin.Common.Tests.Json Assert.Null(seasonRootObject?.Episode); Assert.Null(seasonRootObject?.Metascore); } + + [Fact] + public static void Deserialize_Not_Available_Int_Success() + { + const string Input = "\"N/A\""; + var options = new JsonSerializerOptions + { + NumberHandling = JsonNumberHandling.AllowReadingFromString, + Converters = + { + new JsonOmdbNotAvailableStructConverter() + } + }; + + var result = JsonSerializer.Deserialize(Input, options); + Assert.Null(result); + } + + [Fact] + public static void Deserialize_Not_Available_String_Success() + { + const string Input = "\"N/A\""; + var options = new JsonSerializerOptions + { + Converters = + { + new JsonOmdbNotAvailableStringConverter() + } + }; + + options.Converters.Add(new JsonOmdbNotAvailableStringConverter()); + var result = JsonSerializer.Deserialize(Input, options); + Assert.Null(result); + } } } From af62ab490c1afc4702db88e5f004aa2fb5930b33 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 25 Dec 2020 15:21:57 +0100 Subject: [PATCH 28/52] Update tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj Co-authored-by: Bond-009 --- tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index 91b34546f3..19c5612c06 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -29,7 +29,7 @@ - + From 4839c2006bfa01e55e447e474234501a7e2496a2 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 26 Dec 2020 01:24:25 +0800 Subject: [PATCH 29/52] fix boxes in library name backdrop for CJK character --- Jellyfin.Drawing.Skia/StripCollageBuilder.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs index 0e94f87f6a..e9f9aad57d 100644 --- a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs +++ b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text.RegularExpressions; using SkiaSharp; namespace Jellyfin.Drawing.Skia @@ -118,6 +119,16 @@ namespace Jellyfin.Drawing.Skia }; canvas.DrawRect(0, 0, width, height, paintColor); + var typeFace = SKTypeface.FromFamilyName("sans-serif", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright); + + // use the system fallback to find a typeface for the given CJK character + var nonCjkPattern = @"[^\p{IsCJKUnifiedIdeographs}\p{IsCJKUnifiedIdeographsExtensionA}\p{IsKatakana}\p{IsHiragana}\p{IsHangulSyllables}\p{IsHangulJamo}]"; + var filteredName = Regex.Replace(libraryName ?? string.Empty, nonCjkPattern, string.Empty); + if (!string.IsNullOrEmpty(filteredName)) + { + typeFace = SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, filteredName[0]); + } + // draw library name var textPaint = new SKPaint { @@ -125,7 +136,7 @@ namespace Jellyfin.Drawing.Skia Style = SKPaintStyle.Fill, TextSize = 112, TextAlign = SKTextAlign.Center, - Typeface = SKTypeface.FromFamilyName("sans-serif", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright), + Typeface = typeFace, IsAntialias = true }; From aacda01ca5a1197a1b6e637488639dae6455d6d8 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 25 Dec 2020 19:21:58 +0100 Subject: [PATCH 30/52] Add more tests --- .../JsonOmdbNotAvailableStringConverter.cs | 2 + .../Json/JsonOmdbConverterTests.cs | 86 ++++++++++++++----- 2 files changed, 66 insertions(+), 22 deletions(-) diff --git a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs index 2b343a5059..4fec2ea3fd 100644 --- a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs @@ -19,6 +19,8 @@ namespace MediaBrowser.Common.Json.Converters { return null; } + + return str; } return JsonSerializer.Deserialize(ref reader, options); diff --git a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs index aae95e7314..e1436a648e 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs +++ b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs @@ -7,10 +7,20 @@ using Xunit; namespace Jellyfin.Common.Tests.Json { - public static class JsonOmdbConverterTests + public class JsonOmdbConverterTests { + private readonly JsonSerializerOptions _options; + + public JsonOmdbConverterTests() + { + _options = new JsonSerializerOptions(); + _options.Converters.Add(new JsonOmdbNotAvailableStringConverter()); + _options.Converters.Add(new JsonOmdbNotAvailableStructConverter()); + _options.NumberHandling = JsonNumberHandling.AllowReadingFromString; + } + [Fact] - public static void Deserialize_Omdb_Response_Not_Available_Success() + 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(Input, JsonDefaults.GetOptions()); @@ -21,36 +31,68 @@ namespace Jellyfin.Common.Tests.Json } [Fact] - public static void Deserialize_Not_Available_Int_Success() + public void Deserialize_Not_Available_Int_Success() { const string Input = "\"N/A\""; - var options = new JsonSerializerOptions - { - NumberHandling = JsonNumberHandling.AllowReadingFromString, - Converters = - { - new JsonOmdbNotAvailableStructConverter() - } - }; - var result = JsonSerializer.Deserialize(Input, options); + var result = JsonSerializer.Deserialize(Input, _options); Assert.Null(result); } [Fact] - public static void Deserialize_Not_Available_String_Success() + public void Deserialize_Not_Available_String_Success() { const string Input = "\"N/A\""; - var options = new JsonSerializerOptions - { - Converters = - { - new JsonOmdbNotAvailableStringConverter() - } - }; - options.Converters.Add(new JsonOmdbNotAvailableStringConverter()); - var result = JsonSerializer.Deserialize(Input, options); + var result = JsonSerializer.Deserialize(Input, _options); + Assert.Null(result); + } + + [Fact] + public void Deserialize_Normal_String_Success() + { + const string Expected = "Jellyfin"; + const string Input = "\"Jellyfin\""; + + var result = JsonSerializer.Deserialize(Input, _options); + Assert.Equal(Expected, result); + } + + [Fact] + public void Deserialize_Null_Success() + { + const string Input = "null"; + + var result = JsonSerializer.Deserialize(Input, _options); + Assert.Null(result); + } + + [Fact] + public void Deserialize_Number_Success() + { + const int Number = 8; + const string Input = "8"; + + var result = JsonSerializer.Deserialize(Input, _options); + Assert.Equal(Number, result); + } + + [Fact] + public void Deserialize_Quoted_Number_Success() + { + const int Number = 8; + const string Input = "\"8\""; + + var result = JsonSerializer.Deserialize(Input, _options); + Assert.Equal(Number, result); + } + + [Fact] + public void Deserialize_NA_Number_Success() + { + const string Input = "\"N/A\""; + + var result = JsonSerializer.Deserialize(Input, _options); Assert.Null(result); } } From 470f40442cd6ecbcfe81888804bf4e6a04846aec Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sat, 26 Dec 2020 02:28:38 +0800 Subject: [PATCH 31/52] not apply to progressive playback --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 29978a56c6..efab87a38e 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1725,6 +1725,7 @@ namespace MediaBrowser.Controller.MediaEncoding // Avoid transcoding to audio channels other than 1ch, 2ch, 6ch (5.1 layout) and 8ch (7.1 layout). // https://developer.apple.com/documentation/http_live_streaming/hls_authoring_specification_for_apple_devices if (isTranscodingAudio + && state.TranscodingType != TranscodingJobType.Progressive && resultChannels.HasValue && (resultChannels.Value > 2 && resultChannels.Value < 6 || resultChannels.Value == 7)) { From e0499f87690bca88c878b2115ba9e2dbb00f0ad4 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 25 Dec 2020 19:37:38 +0100 Subject: [PATCH 32/52] Remove attributes --- .../Plugins/Omdb/OmdbItemProvider.cs | 7 +++- .../Plugins/Omdb/OmdbProvider.cs | 38 +++---------------- .../Json/JsonOmdbConverterTests.cs | 2 +- 3 files changed, 13 insertions(+), 34 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs index 3ef404b535..71d5510632 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; using MediaBrowser.Common.Json; +using MediaBrowser.Common.Json.Converters; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -32,7 +33,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; private readonly IApplicationHost _appHost; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions; public OmdbItemProvider( IApplicationHost appHost, @@ -46,6 +47,10 @@ namespace MediaBrowser.Providers.Plugins.Omdb _fileSystem = fileSystem; _configurationManager = configurationManager; _appHost = appHost; + + _jsonOptions = new JsonSerializerOptions(JsonDefaults.GetOptions()); + _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStringConverter()); + _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStructConverter()); } public string Name => "The Open Movie Database"; diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index e700e6969b..a177ae30a9 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -30,7 +30,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb private readonly IHttpClientFactory _httpClientFactory; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IApplicationHost _appHost; - private static readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions; public OmdbProvider(IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager) { @@ -38,6 +38,10 @@ namespace MediaBrowser.Providers.Plugins.Omdb _fileSystem = fileSystem; _configurationManager = configurationManager; _appHost = appHost; + + _jsonOptions = new JsonSerializerOptions(JsonDefaults.GetOptions()); + _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStringConverter()); + _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStructConverter()); } public async Task Fetch(MetadataResult itemResult, string imdbId, string language, string country, CancellationToken cancellationToken) @@ -344,7 +348,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb return path; } - public static async Task GetDeserializedOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken) + public async Task GetDeserializedOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken) { using var response = await GetOmdbResponse(httpClient, url, cancellationToken).ConfigureAwait(false); var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); @@ -471,101 +475,71 @@ namespace MediaBrowser.Providers.Plugins.Omdb public class SeasonRootObject { - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Title { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string seriesID { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStructConverter))] public int? Season { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStructConverter))] public int? totalSeasons { get; set; } public RootObject[] Episodes { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Response { get; set; } } public class RootObject { - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Title { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Year { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Rated { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Released { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Runtime { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Genre { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Director { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Writer { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Actors { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Plot { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Language { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Country { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Awards { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Poster { get; set; } public List Ratings { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Metascore { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string imdbRating { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string imdbVotes { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string imdbID { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Type { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string DVD { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string BoxOffice { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Production { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Website { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStringConverter))] public string Response { get; set; } - [JsonConverter(typeof(JsonOmdbNotAvailableStructConverter))] public int? Episode { get; set; } public float? GetRottenTomatoScore() diff --git a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs index e1436a648e..06a4855bfd 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs +++ b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs @@ -23,7 +23,7 @@ namespace Jellyfin.Common.Tests.Json 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(Input, JsonDefaults.GetOptions()); + var seasonRootObject = JsonSerializer.Deserialize(Input, _options); Assert.NotNull(seasonRootObject); Assert.Null(seasonRootObject?.Awards); Assert.Null(seasonRootObject?.Episode); From 41324300ec67c16e19d98b9d568debdaeb72a138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Milinkovi=C4=87?= Date: Fri, 25 Dec 2020 13:47:11 +0000 Subject: [PATCH 33/52] Translated using Weblate (Croatian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hr/ --- Emby.Server.Implementations/Localization/Core/hr.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index 9be91b7240..9eb80b83ba 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -115,5 +115,8 @@ "TasksChannelsCategory": "Internet kanali", "TasksLibraryCategory": "Biblioteka", "TaskCleanActivityLogDescription": "Briše zapise dnevnika aktivnosti starije od navedenog vremena.", - "TaskCleanActivityLog": "Očisti dnevnik aktivnosti" + "TaskCleanActivityLog": "Očisti dnevnik aktivnosti", + "Undefined": "Nedefinirano", + "Forced": "Forsirani", + "Default": "Zadano" } From 108d2da0aad9978620c0ee1b1e97d15221240091 Mon Sep 17 00:00:00 2001 From: mahi160 Date: Fri, 25 Dec 2020 09:24:18 +0000 Subject: [PATCH 34/52] Translated using Weblate (Bengali) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/bn/ --- Emby.Server.Implementations/Localization/Core/bn.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json index 5667bf337e..84c8a99f7a 100644 --- a/Emby.Server.Implementations/Localization/Core/bn.json +++ b/Emby.Server.Implementations/Localization/Core/bn.json @@ -112,5 +112,7 @@ "TaskRefreshPeople": "পিপল রিফ্রেশ করুন", "TaskCleanLogsDescription": "{0} দিনের বেশী পুরানো লগ ফাইলগুলি মুছে ফেলুন।", "TaskCleanLogs": "লগ ডিরেক্টরি ক্লিন করুন", - "TaskRefreshLibraryDescription": "নতুন ফাইলের জন্য মিডিয়া লাইব্রেরি স্ক্যান এবং মেটাডাটা রিফ্রেশ করুন।" + "TaskRefreshLibraryDescription": "নতুন ফাইলের জন্য মিডিয়া লাইব্রেরি স্ক্যান এবং মেটাডাটা রিফ্রেশ করুন।", + "Undefined": "অসঙ্গায়িত", + "Forced": "জোরপূর্বক" } From f73bb92ce3a86a024cd72a59bbc461707687a4c7 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 26 Dec 2020 20:00:54 +0100 Subject: [PATCH 35/52] Remove manual N/A removal and write directly to stream --- .../LiveTv/EmbyTV/EncodedRecorder.cs | 8 +++----- MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs | 2 -- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index a70a72b74f..2545ffdd2e 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _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; Directory.CreateDirectory(Path.GetDirectoryName(targetFile)); @@ -93,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. _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); - var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(mediaSource, _jsonOptions) + 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); _process = new Process { @@ -113,8 +113,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _ = StartStreamingLog(_process.StandardError.BaseStream, _logFileStream); _logger.LogInformation("ffmpeg recording process started for {0}", _targetPath); - - return _taskCompletionSource.Task; } private string GetCommandLineArgs(MediaSourceInfo mediaSource, string inputTempFile, string targetFile, TimeSpan duration) diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index a177ae30a9..9f3fdd60b1 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -353,8 +353,6 @@ namespace MediaBrowser.Providers.Plugins.Omdb using var response = await GetOmdbResponse(httpClient, url, cancellationToken).ConfigureAwait(false); var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); - // OMDb is sending "N/A" for no empty number fields - content = content.Replace("\"N/A\"", "\"\"", StringComparison.InvariantCulture); return JsonSerializer.Deserialize(content, _jsonOptions); } From 21fd124bcaabcc1c01d58757ed54b5fe09273c36 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 27 Dec 2020 11:15:46 +0100 Subject: [PATCH 36/52] Code revie --- .../LiveTv/EmbyTV/EncodedRecorder.cs | 2 +- .../ScheduledTasks/ScheduledTaskWorker.cs | 6 +++--- MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 2545ffdd2e..78a82118ed 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); 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); + await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false); _process = new Process { diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 3dca05bf98..0bb627192f 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -151,7 +151,7 @@ namespace Emby.Server.Implementations.ScheduledTasks } else { - _logger.LogDebug("Scheduled Task history file {path} is empty. Skipping deserialization.", path); + _logger.LogDebug("Scheduled Task history file {Path} is empty. Skipping deserialization.", path); } } catch (Exception ex) @@ -577,8 +577,8 @@ namespace Emby.Server.Implementations.ScheduledTasks Directory.CreateDirectory(Path.GetDirectoryName(path)); - using FileStream stream = File.OpenWrite(path); - JsonSerializer.SerializeAsync(stream, triggers, _jsonOptions); + var json = JsonSerializer.Serialize(triggers, _jsonOptions); + File.WriteAllText(path, json); } /// diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 9f3fdd60b1..25371e27ea 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -351,9 +351,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb public async Task GetDeserializedOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken) { using var response = await GetOmdbResponse(httpClient, url, cancellationToken).ConfigureAwait(false); - var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + await using Stream content = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - return JsonSerializer.Deserialize(content, _jsonOptions); + return await JsonSerializer.DeserializeAsync(content, _jsonOptions, cancellationToken).ConfigureAwait(false); } public static Task GetOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken) From d96c8d7efdeced56b8d8ed716cd38689d4c86f38 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 27 Dec 2020 15:57:27 +0000 Subject: [PATCH 37/52] Remove todo --- Jellyfin.Networking/Manager/NetworkManager.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 43f2f7add6..2585355412 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -913,15 +913,6 @@ namespace Jellyfin.Networking.Manager { string[] lanAddresses = config.LocalNetworkAddresses; - // TODO: remove when bug fixed: https://github.com/jellyfin/jellyfin-web/issues/1334 - - if (lanAddresses.Length == 1 && lanAddresses[0].IndexOf(',', StringComparison.OrdinalIgnoreCase) != -1) - { - lanAddresses = lanAddresses[0].Split(','); - } - - // TODO: end fix: https://github.com/jellyfin/jellyfin-web/issues/1334 - // Add virtual machine interface names to the list of bind exclusions, so that they are auto-excluded. if (config.IgnoreVirtualInterfaces) { From 926c70f9f7181ba86a367ca078302486501ff995 Mon Sep 17 00:00:00 2001 From: WWWesten Date: Sun, 27 Dec 2020 11:57:08 +0000 Subject: [PATCH 38/52] Translated using Weblate (Kazakh) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/kk/ --- .../Localization/Core/kk.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json index 47c0879be4..7ce9822b6d 100644 --- a/Emby.Server.Implementations/Localization/Core/kk.json +++ b/Emby.Server.Implementations/Localization/Core/kk.json @@ -108,5 +108,15 @@ "TasksLibraryCategory": "Tasyǵyshhana", "TasksMaintenanceCategory": "Qyzmet kórsetý", "Undefined": "Anyqtalmady", - "Forced": "Májbúrli" + "Forced": "Májbúrli", + "TaskDownloadMissingSubtitlesDescription": "Metaderekter teńshelimi negіzіnde joq sýbtıtrlerdі Internetten іzdeıdі.", + "TaskRefreshChannelsDescription": "Internet-arnalar málimetterin jańartady.", + "TaskCleanTranscodeDescription": "Bіr kúnnen asqan qaıta kodtaý faıldaryn joıady.", + "TaskUpdatePluginsDescription": "Avtomatty túrde jańartýǵa teńshelgen plagınder úshin jańartýlardy júktep alady jáne ornatady.", + "TaskRefreshPeopleDescription": "Tasyǵyshhanadaǵy aktórler men rejısórler metaderekterіn jańartady.", + "TaskCleanLogsDescription": "{0} kúnnen asqan jurnal faıldaryn joıady.", + "TaskRefreshLibraryDescription": "Tasyǵyshhanadaǵy jańa faıldardy skanerleıdі jáne metaderekterdі jańartady.", + "TaskRefreshChapterImagesDescription": "Sahnalarǵa bólіngen beıneler úshіn nobaılar jasaıdy.", + "TaskCleanCacheDescription": "Júıede qajet emes keshtelgen faıldardy joıady.", + "TaskCleanActivityLogDescription": "Áreketter jurnalyndaǵy teńshelgen jasynan asqan jazbalaly joıady." } From 48d8536d2f920e768ea71c8005cd934a87805f05 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Mon, 28 Dec 2020 09:19:08 +0100 Subject: [PATCH 39/52] Enable TMDB and OMDB by default --- MediaBrowser.Model/Configuration/ServerConfiguration.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 0dbd51bdc1..7013cb300e 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -49,8 +49,6 @@ namespace MediaBrowser.Model.Configuration new MetadataOptions { ItemType = "Series", - DisabledMetadataFetchers = new[] { "TheMovieDb" }, - DisabledImageFetchers = new[] { "TheMovieDb" } }, new MetadataOptions { @@ -69,13 +67,10 @@ namespace MediaBrowser.Model.Configuration new MetadataOptions { ItemType = "Season", - DisabledMetadataFetchers = new[] { "TheMovieDb" }, }, new MetadataOptions { ItemType = "Episode", - DisabledMetadataFetchers = new[] { "The Open Movie Database", "TheMovieDb" }, - DisabledImageFetchers = new[] { "The Open Movie Database", "TheMovieDb" } } }; } From 9e0f4257838ca79b0babe7bbb62ae2a79797fc26 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 28 Dec 2020 23:41:46 +0100 Subject: [PATCH 40/52] Make RootObject and SeasonRootObject internal again --- MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs | 4 ++-- MediaBrowser.Providers/Properties/AssemblyInfo.cs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 25371e27ea..2372e31834 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -471,7 +471,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase); } - public class SeasonRootObject + internal class SeasonRootObject { public string Title { get; set; } @@ -486,7 +486,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb public string Response { get; set; } } - public class RootObject + internal class RootObject { public string Title { get; set; } diff --git a/MediaBrowser.Providers/Properties/AssemblyInfo.cs b/MediaBrowser.Providers/Properties/AssemblyInfo.cs index f1c46899ce..fe4749c799 100644 --- a/MediaBrowser.Providers/Properties/AssemblyInfo.cs +++ b/MediaBrowser.Providers/Properties/AssemblyInfo.cs @@ -1,5 +1,6 @@ using System.Reflection; using System.Resources; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -14,6 +15,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] +[assembly: InternalsVisibleTo("Jellyfin.Common.Tests")] // 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 From 8ac1ed16ca6d539cf9061b3c9de139dcf148f401 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 29 Dec 2020 00:15:36 +0100 Subject: [PATCH 41/52] Use Theory instead of Fact for unit tests --- .../Json/JsonOmdbConverterTests.cs | 71 ++++++------------- 1 file changed, 20 insertions(+), 51 deletions(-) diff --git a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs index 06a4855bfd..6f85fe0929 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs +++ b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs @@ -1,6 +1,5 @@ using System.Text.Json; using System.Text.Json.Serialization; -using MediaBrowser.Common.Json; using MediaBrowser.Common.Json.Converters; using MediaBrowser.Providers.Plugins.Omdb; using Xunit; @@ -30,70 +29,40 @@ namespace Jellyfin.Common.Tests.Json Assert.Null(seasonRootObject?.Metascore); } - [Fact] - public void Deserialize_Not_Available_Int_Success() + [Theory] + [InlineData("\"N/A\"")] + [InlineData("null")] + public void Deserialization_To_Nullable_Int_Shoud_Be_Null(string input) { - const string Input = "\"N/A\""; - - var result = JsonSerializer.Deserialize(Input, _options); + var result = JsonSerializer.Deserialize(input, _options); Assert.Null(result); } - [Fact] - public void Deserialize_Not_Available_String_Success() + [Theory] + [InlineData("\"N/A\"")] + [InlineData("null")] + public void Deserialization_To_Nullable_String_Shoud_Be_Null(string input) { - const string Input = "\"N/A\""; - - var result = JsonSerializer.Deserialize(Input, _options); + var result = JsonSerializer.Deserialize(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(input, _options); + Assert.Equal(result, expected); + } + [Fact] public void Deserialize_Normal_String_Success() { - const string Expected = "Jellyfin"; const string Input = "\"Jellyfin\""; - + const string Expected = "Jellyfin"; var result = JsonSerializer.Deserialize(Input, _options); Assert.Equal(Expected, result); } - - [Fact] - public void Deserialize_Null_Success() - { - const string Input = "null"; - - var result = JsonSerializer.Deserialize(Input, _options); - Assert.Null(result); - } - - [Fact] - public void Deserialize_Number_Success() - { - const int Number = 8; - const string Input = "8"; - - var result = JsonSerializer.Deserialize(Input, _options); - Assert.Equal(Number, result); - } - - [Fact] - public void Deserialize_Quoted_Number_Success() - { - const int Number = 8; - const string Input = "\"8\""; - - var result = JsonSerializer.Deserialize(Input, _options); - Assert.Equal(Number, result); - } - - [Fact] - public void Deserialize_NA_Number_Success() - { - const string Input = "\"N/A\""; - - var result = JsonSerializer.Deserialize(Input, _options); - Assert.Null(result); - } } } From 3dec1fd6b23fa4f7f9bc3f66edca8ec0f285ae0f Mon Sep 17 00:00:00 2001 From: David Date: Tue, 29 Dec 2020 00:35:59 +0100 Subject: [PATCH 42/52] Use UTF8 encoding and async correctly --- Emby.Server.Implementations/ApplicationHost.cs | 3 ++- Emby.Server.Implementations/Channels/ChannelManager.cs | 7 ++++--- .../LiveTv/EmbyTV/ItemDataProvider.cs | 8 +++++--- .../ScheduledTasks/ScheduledTaskWorker.cs | 7 ++++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 115d94b316..8fa712914a 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -10,6 +10,7 @@ using System.Net; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -1047,7 +1048,7 @@ namespace Emby.Server.Implementations var metafile = Path.Combine(dir, "meta.json"); if (File.Exists(metafile)) { - var jsonString = File.ReadAllText(metafile); + var jsonString = File.ReadAllText(metafile, Encoding.UTF8); var manifest = JsonSerializer.Deserialize(jsonString, _jsonOptions); if (!Version.TryParse(manifest.TargetAbi, out var targetAbi)) diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index cf6a87ecfd..2d5b19fa69 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -341,7 +342,7 @@ namespace Emby.Server.Implementations.Channels try { - var jsonString = File.ReadAllText(path); + var jsonString = File.ReadAllText(path, Encoding.UTF8); return JsonSerializer.Deserialize>(jsonString, _jsonOptions) ?? new List(); } @@ -1180,11 +1181,11 @@ namespace Emby.Server.Implementations.Channels { if (enableMediaProbe && !info.IsLiveStream && item.HasPathProtocol) { - await SaveMediaSources(item, new List()); + await SaveMediaSources(item, new List()).ConfigureAwait(false); } else { - await SaveMediaSources(item, info.MediaSources); + await SaveMediaSources(item, info.MediaSources).ConfigureAwait(false); } } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index f16d96a592..c80ecd6b3c 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Text.Json; +using System.Threading.Tasks; using MediaBrowser.Common.Json; using Microsoft.Extensions.Logging; @@ -45,7 +47,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV try { - var jsonString = File.ReadAllText(_dataPath); + var jsonString = File.ReadAllText(_dataPath, Encoding.UTF8); _items = JsonSerializer.Deserialize(jsonString, _jsonOptions); return; } @@ -61,8 +63,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private void SaveList() { Directory.CreateDirectory(Path.GetDirectoryName(_dataPath)); - using FileStream stream = File.OpenWrite(_dataPath); - JsonSerializer.SerializeAsync(stream, _items, _jsonOptions); + var jsonString = JsonSerializer.Serialize(_items, _jsonOptions); + File.WriteAllText(_dataPath, jsonString); } public IReadOnlyList GetAll() diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 0bb627192f..29440b64a0 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -4,6 +4,7 @@ using System; using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -144,7 +145,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { try { - var jsonString = File.ReadAllText(path); + var jsonString = File.ReadAllText(path, Encoding.UTF8); if (!string.IsNullOrWhiteSpace(jsonString)) { _lastExecutionResult = JsonSerializer.Deserialize(jsonString, _jsonOptions); @@ -540,7 +541,7 @@ namespace Emby.Server.Implementations.ScheduledTasks TaskTriggerInfo[] list = null; if (File.Exists(path)) { - var jsonString = File.ReadAllText(path); + var jsonString = File.ReadAllText(path, Encoding.UTF8); list = JsonSerializer.Deserialize(jsonString, _jsonOptions); } @@ -578,7 +579,7 @@ namespace Emby.Server.Implementations.ScheduledTasks Directory.CreateDirectory(Path.GetDirectoryName(path)); var json = JsonSerializer.Serialize(triggers, _jsonOptions); - File.WriteAllText(path, json); + File.WriteAllText(path, json, Encoding.UTF8); } /// From 782c23338989edb17fe8739e7dcad315c983fff2 Mon Sep 17 00:00:00 2001 From: Shaunak Basu Date: Tue, 29 Dec 2020 07:39:12 +0000 Subject: [PATCH 43/52] Translated using Weblate (Bengali) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/bn/ --- Emby.Server.Implementations/Localization/Core/bn.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json index 84c8a99f7a..a23037af8e 100644 --- a/Emby.Server.Implementations/Localization/Core/bn.json +++ b/Emby.Server.Implementations/Localization/Core/bn.json @@ -14,8 +14,8 @@ "HeaderFavoriteArtists": "প্রিয় শিল্পীরা", "HeaderFavoriteAlbums": "প্রিয় এলবামগুলো", "HeaderContinueWatching": "দেখতে থাকুন", - "HeaderAlbumArtists": "এলবাম শিল্পী", - "Genres": "জেনার", + "HeaderAlbumArtists": "এলবাম শিল্পীবৃন্দ", + "Genres": "শৈলী", "Folders": "ফোল্ডারগুলো", "Favorites": "পছন্দসমূহ", "FailedLoginAttemptWithUserName": "{0} লগিন করতে ব্যর্থ হয়েছে", @@ -114,5 +114,8 @@ "TaskCleanLogs": "লগ ডিরেক্টরি ক্লিন করুন", "TaskRefreshLibraryDescription": "নতুন ফাইলের জন্য মিডিয়া লাইব্রেরি স্ক্যান এবং মেটাডাটা রিফ্রেশ করুন।", "Undefined": "অসঙ্গায়িত", - "Forced": "জোরপূর্বক" + "Forced": "জোরকরে", + "TaskCleanActivityLogDescription": "নির্ধারিত সময়ের আগের কাজের হিসাব মুছে দিন খালি করুন", + "TaskCleanActivityLog": "কাজের ফাইল খালি করুন", + "Default": "প্রাথমিক" } From 8dfe89ea5203fb53e111cf5dc2b846fede9097a1 Mon Sep 17 00:00:00 2001 From: Shaunak Basu Date: Tue, 29 Dec 2020 07:45:32 +0000 Subject: [PATCH 44/52] Translated using Weblate (Hindi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hi/ --- .../Localization/Core/hi.json | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json index df68d3bbd4..4cc2b378b5 100644 --- a/Emby.Server.Implementations/Localization/Core/hi.json +++ b/Emby.Server.Implementations/Localization/Core/hi.json @@ -1,3 +1,30 @@ { - "Albums": "आल्बुम्" + "Albums": "संग्रह", + "HeaderRecordingGroups": "रिकॉर्डिंग समूह", + "HeaderNextUp": "इसके बाद", + "HeaderLiveTV": "लाइव टीवी", + "HeaderFavoriteSongs": "पसंदीदा गीत", + "HeaderFavoriteShows": "पसंदीदा शोज", + "HeaderFavoriteEpisodes": "पसंदीदा एपिसोड्स", + "HeaderFavoriteArtists": "पसंदीदा कलाकारसमूह", + "HeaderFavoriteAlbums": "पसंदीदा एलबम्स", + "HeaderContinueWatching": "देखते रहिए", + "HeaderAlbumArtists": "एल्बम कलकरसमुह", + "Genres": "शैली", + "Forced": "बलपूर्वक", + "Folders": "फोल्डेरें", + "Favorites": "पसंदीदा", + "FailedLoginAttemptWithUserName": "{0} से लॉगिन असफल हुआ है", + "DeviceOnlineWithName": "{0} से संयोग हो गया है", + "DeviceOfflineWithName": "{0} से संयोग विच्छिन्न हो गया है", + "Default": "प्राथमिक", + "Collections": "संग्रह", + "ChapterNameValue": "अध्याय", + "Channels": "चैनल", + "CameraImageUploadedFrom": "कैमरा से एक नया चित्र अपलोड किया गया है", + "Books": "किताब", + "AuthenticationSucceededWithUserName": "सफलता से प्रमाणीकृत", + "Artists": "कलाकारों", + "Application": "एप्लिकेशन", + "AppDeviceValues": "एप: {0}, मशीन: {1}" } From cae38f3a7ed80231f1e34cb69f4af9328ff8839f Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 29 Dec 2020 16:08:16 -0700 Subject: [PATCH 45/52] Add JsonConverter for Nullable Guids --- .../Converters/JsonNullableGuidConverter.cs | 33 +++++++++ MediaBrowser.Common/Json/JsonDefaults.cs | 1 + .../Json/JsonNullableGuidConverterTests.cs | 69 +++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 MediaBrowser.Common/Json/Converters/JsonNullableGuidConverter.cs create mode 100644 tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableGuidConverter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableGuidConverter.cs new file mode 100644 index 0000000000..6d96d5496f --- /dev/null +++ b/MediaBrowser.Common/Json/Converters/JsonNullableGuidConverter.cs @@ -0,0 +1,33 @@ +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Common.Json.Converters +{ + /// + /// Converts a GUID object or value to/from JSON. + /// + public class JsonNullableGuidConverter : JsonConverter + { + /// + public override Guid? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var guidStr = reader.GetString(); + return guidStr == null ? null : new Guid(guidStr); + } + + /// + public override void Write(Utf8JsonWriter writer, Guid? value, JsonSerializerOptions options) + { + if (value == null || value == Guid.Empty) + { + writer.WriteNullValue(); + } + else + { + writer.WriteStringValue(value.Value.ToString("N", CultureInfo.InvariantCulture)); + } + } + } +} diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index 1ec2a87c53..2ef24a884a 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -34,6 +34,7 @@ namespace MediaBrowser.Common.Json Converters = { new JsonGuidConverter(), + new JsonNullableGuidConverter(), new JsonVersionConverter(), new JsonStringEnumConverter(), new JsonNullableStructConverterFactory(), diff --git a/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs new file mode 100644 index 0000000000..efc0c4af9a --- /dev/null +++ b/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs @@ -0,0 +1,69 @@ +using System; +using System.Globalization; +using System.Text.Json; +using MediaBrowser.Common.Json.Converters; +using Xunit; + +namespace Jellyfin.Common.Tests.Json +{ + public class JsonNullableGuidConverterTests + { + private readonly JsonSerializerOptions _options; + + public JsonNullableGuidConverterTests() + { + _options = new JsonSerializerOptions(); + _options.Converters.Add(new JsonNullableGuidConverter()); + } + + [Fact] + public void Deserialize_Valid_Success() + { + Guid? value = JsonSerializer.Deserialize(@"""a852a27afe324084ae66db579ee3ee18""", _options); + Assert.Equal(new Guid("a852a27afe324084ae66db579ee3ee18"), value); + } + + [Fact] + public void Deserialize_ValidDashed_Success() + { + Guid? value = JsonSerializer.Deserialize(@"""e9b2dcaa-529c-426e-9433-5e9981f27f2e""", _options); + Assert.Equal(new Guid("e9b2dcaa-529c-426e-9433-5e9981f27f2e"), value); + } + + [Fact] + public void Roundtrip_Valid_Success() + { + Guid guid = new Guid("a852a27afe324084ae66db579ee3ee18"); + string value = JsonSerializer.Serialize(guid, _options); + Assert.Equal(guid, JsonSerializer.Deserialize(value, _options)); + } + + [Fact] + public void Deserialize_Null_EmptyGuid() + { + Assert.Null(JsonSerializer.Deserialize("null", _options)); + } + + [Fact] + public void Serialize_EmptyGuid_EmptyGuid() + { + Assert.Equal("null", JsonSerializer.Serialize((Guid?)Guid.Empty, _options)); + } + + [Fact] + public void Serialize_Valid_NoDash_Success() + { + var guid = (Guid?)new Guid("531797E9-9457-40E0-88BC-B1D6D38752FA"); + var str = JsonSerializer.Serialize(guid, _options); + Assert.Equal($"\"{guid:N}\"", str); + } + + [Fact] + public void Serialize_Nullable_Success() + { + Guid? guid = new Guid("531797E9-9457-40E0-88BC-B1D6D38752FA"); + var str = JsonSerializer.Serialize(guid, _options); + Assert.Equal($"\"{guid:N}\"", str); + } + } +} From 2bb84c0675b95d1b2d42912248467fa52ecebe5c Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Wed, 30 Dec 2020 11:16:09 +0100 Subject: [PATCH 46/52] Fix limit parameter error for search hints endpoint --- Emby.Server.Implementations/Library/SearchEngine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index 1d9529dff4..94602582b7 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.Library if (query.Limit.HasValue) { - results = results.GetRange(0, query.Limit.Value); + results = results.GetRange(0, Math.Min(query.Limit.Value, results.Count)); } return new QueryResult From 99adbf04975dcb233b49ea9f3eead358dd4d39fa Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 30 Dec 2020 08:48:33 -0500 Subject: [PATCH 47/52] Split resume function for Audiobooks --- .../Library/UserDataManager.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index f9e5e6bbcd..df8c6adfac 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -14,6 +14,7 @@ using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using Book = MediaBrowser.Controller.Entities.Book; +using AudioBook = MediaBrowser.Controller.Entities.AudioBook; namespace Emby.Server.Implementations.Library { @@ -219,7 +220,7 @@ namespace Emby.Server.Implementations.Library var hasRuntime = runtimeTicks > 0; // If a position has been reported, and if we know the duration - if (positionTicks > 0 && hasRuntime) + if (positionTicks > 0 && hasRuntime && !(item is AudioBook)) { var pctIn = decimal.Divide(positionTicks, runtimeTicks) * 100; @@ -245,6 +246,24 @@ namespace Emby.Server.Implementations.Library } } } + else if (positionTicks > 0 && hasRuntime && (item is AudioBook)) + { + var minIn = TimeSpan.FromTicks(positionTicks).TotalSeconds / 60; + // 10,000 ticks per millisecond * 60,000 milliseconds per minute = 600,000,000 ticks per minute + var minOut = (runtimeTicks - positionTicks) / 600000000; + + if (minIn > _config.Configuration.MinAudiobookResume) + { + // ignore progress during the beginning + positionTicks = 0; + } + else if (minOut < _config.Configuration.MaxAudiobookResume || positionTicks >= runtimeTicks) + { + // mark as completed close to the end + positionTicks = 0; + data.Played = playedToCompletion = true; + } + } else if (!hasRuntime) { // If we don't know the runtime we'll just have to assume it was fully played From cd979e6b6291d426f3e180e36b5a8ba68c208cbd Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 30 Dec 2020 08:52:11 -0500 Subject: [PATCH 48/52] Add default of 5 minutes --- .../Configuration/ServerConfiguration.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 7013cb300e..9fb978e9be 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -298,6 +298,18 @@ namespace MediaBrowser.Model.Configuration /// The min resume duration seconds. public int MinResumeDurationSeconds { get; set; } = 300; + /// + /// Gets or sets the minimum minutes of a book that must be played in order for playstate to be updated. + /// + /// The min resume in minutes. + public int MinAudiobookResume { get; set; } = 5; + + /// + /// Gets or sets the remaining minutes of a book that can be played while still saving playstate. If this percentage is crossed playstate will be reset to the beginning and the item will be marked watched. + /// + /// The remaining time in minutes. + public int MaxAudiobookResume { get; set; } = 5; + /// /// Gets or sets the delay in seconds that we will wait after a file system change to try and discover what has been added/removed /// Some delay is necessary with some items because their creation is not atomic. It involves the creation of several From 77b478c726df8012e8eb27ee59f3bd937848a661 Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 30 Dec 2020 09:12:13 -0500 Subject: [PATCH 49/52] Update Emby.Server.Implementations/Library/UserDataManager.cs Co-authored-by: Bond-009 --- Emby.Server.Implementations/Library/UserDataManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index df8c6adfac..1b38f5e0e0 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -248,7 +248,7 @@ namespace Emby.Server.Implementations.Library } else if (positionTicks > 0 && hasRuntime && (item is AudioBook)) { - var minIn = TimeSpan.FromTicks(positionTicks).TotalSeconds / 60; + var minIn = TimeSpan.FromTicks(positionTicks).TotalMinutes; // 10,000 ticks per millisecond * 60,000 milliseconds per minute = 600,000,000 ticks per minute var minOut = (runtimeTicks - positionTicks) / 600000000; From c7cb177260e80859d18e40615673dc2b7afeda9c Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 30 Dec 2020 09:12:36 -0500 Subject: [PATCH 50/52] Update Emby.Server.Implementations/Library/UserDataManager.cs Co-authored-by: Bond-009 --- Emby.Server.Implementations/Library/UserDataManager.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index 1b38f5e0e0..415e207102 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -249,8 +249,7 @@ namespace Emby.Server.Implementations.Library else if (positionTicks > 0 && hasRuntime && (item is AudioBook)) { var minIn = TimeSpan.FromTicks(positionTicks).TotalMinutes; - // 10,000 ticks per millisecond * 60,000 milliseconds per minute = 600,000,000 ticks per minute - var minOut = (runtimeTicks - positionTicks) / 600000000; + var minOut = TimeSpan.FromTicks(runtimeTicks - positionTicks).TotalMinutes; if (minIn > _config.Configuration.MinAudiobookResume) { From f411353c8ca27eb17db4258d400a38721041c9a2 Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 30 Dec 2020 09:30:02 -0500 Subject: [PATCH 51/52] Update Emby.Server.Implementations/Library/UserDataManager.cs Co-authored-by: Bond-009 --- Emby.Server.Implementations/Library/UserDataManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index 415e207102..d16275b192 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -246,7 +246,7 @@ namespace Emby.Server.Implementations.Library } } } - else if (positionTicks > 0 && hasRuntime && (item is AudioBook)) + else if (positionTicks > 0 && hasRuntime && item is AudioBook) { var minIn = TimeSpan.FromTicks(positionTicks).TotalMinutes; var minOut = TimeSpan.FromTicks(runtimeTicks - positionTicks).TotalMinutes; From 4c291da45c57f1999868182490b55fb7872c26ba Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 30 Dec 2020 17:31:26 +0000 Subject: [PATCH 52/52] Encoding fix for System Logs. (#4564) --- Emby.Dlna/Service/BaseControlHandler.cs | 2 +- Jellyfin.Api/Controllers/SystemController.cs | 2 +- Jellyfin.Server/Program.cs | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 198852ec17..8d2486fee6 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -49,7 +49,7 @@ namespace Emby.Dlna.Service { ControlRequestInfo requestInfo = null; - using (var streamReader = new StreamReader(request.InputXml)) + using (var streamReader = new StreamReader(request.InputXml, Encoding.UTF8)) { var readerSettings = new XmlReaderSettings() { diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs index d79bea9855..e67a27ae30 100644 --- a/Jellyfin.Api/Controllers/SystemController.cs +++ b/Jellyfin.Api/Controllers/SystemController.cs @@ -203,7 +203,7 @@ namespace Jellyfin.Api.Controllers // For older files, assume fully static var fileShare = file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1) ? FileShare.Read : FileShare.ReadWrite; FileStream stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, fileShare); - return File(stream, "text/plain"); + return File(stream, "text/plain; charset=utf-8"); } /// diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index a1a7a30534..f05cdfe9bd 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -598,7 +598,8 @@ namespace Jellyfin.Server .WriteTo.Async(x => x.File( Path.Combine(appPaths.LogDirectoryPath, "log_.log"), rollingInterval: RollingInterval.Day, - outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}")) + outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}", + encoding: Encoding.UTF8)) .Enrich.FromLogContext() .Enrich.WithThreadId() .CreateLogger();