aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrobibero <cody@robibe.ro>2020-09-04 16:42:40 -0600
committercrobibero <cody@robibe.ro>2020-09-04 16:42:40 -0600
commitd5eb246557f9c943d8e3c5ddcc357ef351425deb (patch)
tree99b257fa694de3c263767aa44c8932ca21348dc2
parent4fc611bf29c9d19c333d60c23325ae2b20900de1 (diff)
parentd8c6d291829f6a80ad1c13e039293f04083470f0 (diff)
Merge remote-tracking branch 'upstream/master' into api-stream-return
-rw-r--r--Dockerfile2
-rw-r--r--Dockerfile.arm2
-rw-r--r--Dockerfile.arm642
-rw-r--r--Emby.Dlna/ConnectionManager/ConnectionManagerService.cs6
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectoryService.cs4
-rw-r--r--Emby.Dlna/Emby.Dlna.csproj1
-rw-r--r--Emby.Dlna/Eventing/DlnaEventManager.cs30
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs15
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs6
-rw-r--r--Emby.Dlna/PlayTo/Device.cs41
-rw-r--r--Emby.Dlna/PlayTo/PlayToManager.cs9
-rw-r--r--Emby.Dlna/PlayTo/SsdpHttpClient.cs116
-rw-r--r--Emby.Dlna/Service/BaseService.cs10
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs35
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs335
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs50
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs9
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs378
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs31
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs98
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs13
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs16
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs47
-rw-r--r--Emby.Server.Implementations/Localization/Core/ta.json32
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs88
-rw-r--r--Jellyfin.Api/BaseJellyfinApiController.cs6
-rw-r--r--Jellyfin.Api/Controllers/DashboardController.cs101
-rw-r--r--Jellyfin.Api/Controllers/LiveTvController.cs3
-rw-r--r--Jellyfin.Api/Controllers/RemoteImageController.cs3
-rw-r--r--Jellyfin.Api/Controllers/VideosController.cs3
-rw-r--r--Jellyfin.Api/Helpers/AudioHelper.cs3
-rw-r--r--Jellyfin.Api/MvcRoutePrefix.cs56
-rw-r--r--Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs13
-rw-r--r--Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs11
-rw-r--r--Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs2
-rw-r--r--Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs5
-rw-r--r--Jellyfin.Server/Formatters/XmlOutputFormatter.cs3
-rw-r--r--Jellyfin.Server/HealthChecks/JellyfinDbHealthCheck.cs36
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj1
-rw-r--r--Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs6
-rw-r--r--Jellyfin.Server/Program.cs2
-rw-r--r--Jellyfin.Server/Startup.cs98
-rw-r--r--MediaBrowser.Common/Configuration/IApplicationPaths.cs5
-rw-r--r--MediaBrowser.Common/Json/JsonDefaults.cs10
-rw-r--r--MediaBrowser.Common/Net/CacheMode.cs11
-rw-r--r--MediaBrowser.Common/Net/CompressionMethods.cs15
-rw-r--r--MediaBrowser.Common/Net/HttpRequestOptions.cs105
-rw-r--r--MediaBrowser.Common/Net/HttpResponseInfo.cs80
-rw-r--r--MediaBrowser.Common/Net/IHttpClient.cs53
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs59
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs179
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj1
-rw-r--r--MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs14
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs10
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs27
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs29
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs6
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs18
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs2
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs5
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs5
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs5
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs3
-rw-r--r--MediaBrowser.Providers/Studios/StudiosImageProvider.cs5
-rwxr-xr-xdebian/rules2
-rw-r--r--deployment/Dockerfile.docker.amd642
-rw-r--r--deployment/Dockerfile.docker.arm642
-rw-r--r--deployment/Dockerfile.docker.armhf2
-rwxr-xr-xdeployment/build.linux.amd642
-rwxr-xr-xdeployment/build.macos2
-rwxr-xr-xdeployment/build.portable2
-rwxr-xr-xdeployment/build.windows.amd642
-rw-r--r--fedora/jellyfin.spec2
96 files changed, 839 insertions, 1622 deletions
diff --git a/Dockerfile b/Dockerfile
index 4fdffc740..69af9b77b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -14,7 +14,7 @@ COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# because of changes in docker and systemd we need to not build in parallel at the moment
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
-RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none"
+RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:DebugSymbols=false;DebugType=none"
FROM debian:buster-slim
diff --git a/Dockerfile.arm b/Dockerfile.arm
index 751ab8611..efeed25df 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -21,7 +21,7 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# Discard objs - may cause failures if exists
RUN find . -type d -name obj | xargs -r rm -r
# Build
-RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none"
+RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:DebugSymbols=false;DebugType=none"
FROM multiarch/qemu-user-static:x86_64-arm as qemu
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 0d2e91d91..1f2c2ec36 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -21,7 +21,7 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# Discard objs - may cause failures if exists
RUN find . -type d -name obj | xargs -r rm -r
# Build
-RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none"
+RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:DebugSymbols=false;DebugType=none"
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
FROM arm64v8/debian:buster-slim
diff --git a/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs
index 12338e2b4..f5a7eca72 100644
--- a/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs
+++ b/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs
@@ -1,8 +1,8 @@
#pragma warning disable CS1591
+using System.Net.Http;
using System.Threading.Tasks;
using Emby.Dlna.Service;
-using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using Microsoft.Extensions.Logging;
@@ -18,8 +18,8 @@ namespace Emby.Dlna.ConnectionManager
IDlnaManager dlna,
IServerConfigurationManager config,
ILogger<ConnectionManagerService> logger,
- IHttpClient httpClient)
- : base(logger, httpClient)
+ IHttpClientFactory httpClientFactory)
+ : base(logger, httpClientFactory)
{
_dlna = dlna;
_config = config;
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs
index 72732823a..5760f260c 100644
--- a/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs
@@ -2,11 +2,11 @@
using System;
using System.Linq;
+using System.Net.Http;
using System.Threading.Tasks;
using Emby.Dlna.Service;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
-using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
@@ -41,7 +41,7 @@ namespace Emby.Dlna.ContentDirectory
IServerConfigurationManager config,
IUserManager userManager,
ILogger<ContentDirectoryService> logger,
- IHttpClient httpClient,
+ IHttpClientFactory httpClient,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
IUserViewManager userViewManager,
diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj
index 8538f580c..6ed49944c 100644
--- a/Emby.Dlna/Emby.Dlna.csproj
+++ b/Emby.Dlna/Emby.Dlna.csproj
@@ -80,6 +80,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
+ <PackageReference Include="Microsoft.Extensions.Http" Version="3.1.6" />
</ItemGroup>
</Project>
diff --git a/Emby.Dlna/Eventing/DlnaEventManager.cs b/Emby.Dlna/Eventing/DlnaEventManager.cs
index b66e966df..7d8da86ef 100644
--- a/Emby.Dlna/Eventing/DlnaEventManager.cs
+++ b/Emby.Dlna/Eventing/DlnaEventManager.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
+using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
@@ -20,13 +21,13 @@ namespace Emby.Dlna.Eventing
new ConcurrentDictionary<string, EventSubscription>(StringComparer.OrdinalIgnoreCase);
private readonly ILogger _logger;
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- public DlnaEventManager(ILogger logger, IHttpClient httpClient)
+ public DlnaEventManager(ILogger logger, IHttpClientFactory httpClientFactory)
{
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_logger = logger;
}
@@ -167,24 +168,17 @@ namespace Emby.Dlna.Eventing
builder.Append("</e:propertyset>");
- var options = new HttpRequestOptions
- {
- RequestContent = builder.ToString(),
- RequestContentType = "text/xml",
- Url = subscription.CallbackUrl,
- BufferContent = false
- };
-
- options.RequestHeaders.Add("NT", subscription.NotificationType);
- options.RequestHeaders.Add("NTS", "upnp:propchange");
- options.RequestHeaders.Add("SID", subscription.Id);
- options.RequestHeaders.Add("SEQ", subscription.TriggerCount.ToString(_usCulture));
+ using var options = new HttpRequestMessage(new HttpMethod("NOTIFY"), subscription.CallbackUrl);
+ options.Content = new StringContent(builder.ToString(), Encoding.UTF8, MediaTypeNames.Text.Xml);
+ options.Headers.TryAddWithoutValidation("NT", subscription.NotificationType);
+ options.Headers.TryAddWithoutValidation("NTS", "upnp:propchange");
+ options.Headers.TryAddWithoutValidation("SID", subscription.Id);
+ options.Headers.TryAddWithoutValidation("SEQ", subscription.TriggerCount.ToString(_usCulture));
try
{
- using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false))
- {
- }
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .SendAsync(options, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index 0ad82022d..40c2cc0e0 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -2,6 +2,7 @@
using System;
using System.Globalization;
+using System.Net.Http;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
@@ -36,7 +37,7 @@ namespace Emby.Dlna.Main
private readonly ILogger<DlnaEntryPoint> _logger;
private readonly IServerApplicationHost _appHost;
private readonly ISessionManager _sessionManager;
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IDlnaManager _dlnaManager;
@@ -61,7 +62,7 @@ namespace Emby.Dlna.Main
ILoggerFactory loggerFactory,
IServerApplicationHost appHost,
ISessionManager sessionManager,
- IHttpClient httpClient,
+ IHttpClientFactory httpClientFactory,
ILibraryManager libraryManager,
IUserManager userManager,
IDlnaManager dlnaManager,
@@ -79,7 +80,7 @@ namespace Emby.Dlna.Main
_config = config;
_appHost = appHost;
_sessionManager = sessionManager;
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_libraryManager = libraryManager;
_userManager = userManager;
_dlnaManager = dlnaManager;
@@ -101,7 +102,7 @@ namespace Emby.Dlna.Main
config,
userManager,
loggerFactory.CreateLogger<ContentDirectory.ContentDirectoryService>(),
- httpClient,
+ httpClientFactory,
localizationManager,
mediaSourceManager,
userViewManager,
@@ -112,11 +113,11 @@ namespace Emby.Dlna.Main
dlnaManager,
config,
loggerFactory.CreateLogger<ConnectionManager.ConnectionManagerService>(),
- httpClient);
+ httpClientFactory);
MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrarService(
loggerFactory.CreateLogger<MediaReceiverRegistrar.MediaReceiverRegistrarService>(),
- httpClient,
+ httpClientFactory,
config);
Current = this;
}
@@ -364,7 +365,7 @@ namespace Emby.Dlna.Main
_appHost,
_imageProcessor,
_deviceDiscovery,
- _httpClient,
+ _httpClientFactory,
_config,
_userDataManager,
_localization,
diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs
index 28de2fef5..e6d845e1e 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs
@@ -1,8 +1,8 @@
#pragma warning disable CS1591
+using System.Net.Http;
using System.Threading.Tasks;
using Emby.Dlna.Service;
-using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using Microsoft.Extensions.Logging;
@@ -14,9 +14,9 @@ namespace Emby.Dlna.MediaReceiverRegistrar
public MediaReceiverRegistrarService(
ILogger<MediaReceiverRegistrarService> logger,
- IHttpClient httpClient,
+ IHttpClientFactory httpClientFactory,
IServerConfigurationManager config)
- : base(logger, httpClient)
+ : base(logger, httpClientFactory)
{
_config = config;
}
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index 5462e7abc..c97acdb02 100644
--- a/Emby.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Net.Http;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
@@ -21,7 +22,7 @@ namespace Emby.Dlna.PlayTo
{
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger _logger;
@@ -34,10 +35,10 @@ namespace Emby.Dlna.PlayTo
private int _connectFailureCount;
private bool _disposed;
- public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger)
+ public Device(DeviceInfo deviceProperties, IHttpClientFactory httpClientFactory, ILogger logger)
{
Properties = deviceProperties;
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_logger = logger;
}
@@ -236,7 +237,7 @@ namespace Emby.Dlna.PlayTo
_logger.LogDebug("Setting mute");
var value = mute ? 1 : 0;
- await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
+ await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
.ConfigureAwait(false);
IsMuted = mute;
@@ -271,7 +272,7 @@ namespace Emby.Dlna.PlayTo
// Remote control will perform better
Volume = value;
- await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
+ await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
.ConfigureAwait(false);
}
@@ -292,7 +293,7 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
- await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
+ await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
.ConfigureAwait(false);
RestartTimer(true);
@@ -326,7 +327,7 @@ namespace Emby.Dlna.PlayTo
}
var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
- await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header)
+ await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header)
.ConfigureAwait(false);
await Task.Delay(50).ConfigureAwait(false);
@@ -368,7 +369,7 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
- return new SsdpHttpClient(_httpClient).SendCommandAsync(
+ return new SsdpHttpClient(_httpClientFactory).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
@@ -397,7 +398,7 @@ namespace Emby.Dlna.PlayTo
var service = GetAvTransportService();
- await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
+ await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false);
RestartTimer(true);
@@ -415,7 +416,7 @@ namespace Emby.Dlna.PlayTo
var service = GetAvTransportService();
- await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
+ await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false);
TransportState = TransportState.Paused;
@@ -542,7 +543,7 @@ namespace Emby.Dlna.PlayTo
return;
}
- var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
+ var result = await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
@@ -592,7 +593,7 @@ namespace Emby.Dlna.PlayTo
return;
}
- var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
+ var result = await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
@@ -625,7 +626,7 @@ namespace Emby.Dlna.PlayTo
return null;
}
- var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
+ var result = await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
@@ -667,7 +668,7 @@ namespace Emby.Dlna.PlayTo
var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
- var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
+ var result = await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
@@ -734,7 +735,7 @@ namespace Emby.Dlna.PlayTo
var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
- var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
+ var result = await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
@@ -912,7 +913,7 @@ namespace Emby.Dlna.PlayTo
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
- var httpClient = new SsdpHttpClient(_httpClient);
+ var httpClient = new SsdpHttpClient(_httpClientFactory);
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
@@ -940,7 +941,7 @@ namespace Emby.Dlna.PlayTo
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
- var httpClient = new SsdpHttpClient(_httpClient);
+ var httpClient = new SsdpHttpClient(_httpClientFactory);
_logger.LogDebug("Dlna Device.GetRenderingProtocolAsync");
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
@@ -969,9 +970,9 @@ namespace Emby.Dlna.PlayTo
return baseUrl + url;
}
- public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, ILogger logger, CancellationToken cancellationToken)
+ public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClientFactory httpClientFactory, ILogger logger, CancellationToken cancellationToken)
{
- var ssdpHttpClient = new SsdpHttpClient(httpClient);
+ var ssdpHttpClient = new SsdpHttpClient(httpClientFactory);
var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false);
@@ -1079,7 +1080,7 @@ namespace Emby.Dlna.PlayTo
}
}
- return new Device(deviceProperties, httpClient, logger);
+ return new Device(deviceProperties, httpClientFactory, logger);
}
private static DeviceIcon CreateIcon(XElement element)
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index ff801f263..3d1dd3e73 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -4,6 +4,7 @@ using System;
using System.Globalization;
using System.Linq;
using System.Net;
+using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Events;
@@ -33,7 +34,7 @@ namespace Emby.Dlna.PlayTo
private readonly IDlnaManager _dlnaManager;
private readonly IServerApplicationHost _appHost;
private readonly IImageProcessor _imageProcessor;
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly IServerConfigurationManager _config;
private readonly IUserDataManager _userDataManager;
private readonly ILocalizationManager _localization;
@@ -46,7 +47,7 @@ namespace Emby.Dlna.PlayTo
private SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
- public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder)
+ public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClientFactory httpClientFactory, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder)
{
_logger = logger;
_sessionManager = sessionManager;
@@ -56,7 +57,7 @@ namespace Emby.Dlna.PlayTo
_appHost = appHost;
_imageProcessor = imageProcessor;
_deviceDiscovery = deviceDiscovery;
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_config = config;
_userDataManager = userDataManager;
_localization = localization;
@@ -174,7 +175,7 @@ namespace Emby.Dlna.PlayTo
if (controller == null)
{
- var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _logger, cancellationToken).ConfigureAwait(false);
+ var device = await Device.CreateuPnpDeviceAsync(uri, _httpClientFactory, _logger, cancellationToken).ConfigureAwait(false);
string deviceName = device.Properties.Name;
diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
index ab262bebf..8683c8997 100644
--- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs
+++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
@@ -4,6 +4,8 @@ using System;
using System.Globalization;
using System.IO;
using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Net.Mime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -20,11 +22,11 @@ namespace Emby.Dlna.PlayTo
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
- public SsdpHttpClient(IHttpClient httpClient)
+ public SsdpHttpClient(IHttpClientFactory httpClientFactory)
{
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
}
public async Task<XDocument> SendCommandAsync(
@@ -36,20 +38,18 @@ namespace Emby.Dlna.PlayTo
CancellationToken cancellationToken = default)
{
var url = NormalizeServiceUrl(baseUrl, service.ControlUrl);
- using (var response = await PostSoapDataAsync(
- url,
- $"\"{service.ServiceType}#{command}\"",
- postData,
- header,
- cancellationToken)
- .ConfigureAwait(false))
- using (var stream = response.Content)
- using (var reader = new StreamReader(stream, Encoding.UTF8))
- {
- return XDocument.Parse(
- await reader.ReadToEndAsync().ConfigureAwait(false),
- LoadOptions.PreserveWhitespace);
- }
+ using var response = await PostSoapDataAsync(
+ url,
+ $"\"{service.ServiceType}#{command}\"",
+ postData,
+ header,
+ cancellationToken)
+ .ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ using var reader = new StreamReader(stream, Encoding.UTF8);
+ return XDocument.Parse(
+ await reader.ReadToEndAsync().ConfigureAwait(false),
+ LoadOptions.PreserveWhitespace);
}
private static string NormalizeServiceUrl(string baseUrl, string serviceUrl)
@@ -76,49 +76,32 @@ namespace Emby.Dlna.PlayTo
int eventport,
int timeOut = 3600)
{
- var options = new HttpRequestOptions
- {
- Url = url,
- UserAgent = USERAGENT,
- LogErrorResponseBody = true,
- BufferContent = false,
- };
-
- options.RequestHeaders["HOST"] = ip + ":" + port.ToString(_usCulture);
- options.RequestHeaders["CALLBACK"] = "<" + localIp + ":" + eventport.ToString(_usCulture) + ">";
- options.RequestHeaders["NT"] = "upnp:event";
- options.RequestHeaders["TIMEOUT"] = "Second-" + timeOut.ToString(_usCulture);
-
- using (await _httpClient.SendAsync(options, new HttpMethod("SUBSCRIBE")).ConfigureAwait(false))
- {
- }
+ using var options = new HttpRequestMessage(new HttpMethod("SUBSCRIBE"), url);
+ options.Headers.UserAgent.ParseAdd(USERAGENT);
+ options.Headers.TryAddWithoutValidation("HOST", ip + ":" + port.ToString(_usCulture));
+ options.Headers.TryAddWithoutValidation("CALLBACK", "<" + localIp + ":" + eventport.ToString(_usCulture) + ">");
+ options.Headers.TryAddWithoutValidation("NT", "upnp:event");
+ options.Headers.TryAddWithoutValidation("TIMEOUT", "Second-" + timeOut.ToString(_usCulture));
+
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .SendAsync(options, HttpCompletionOption.ResponseHeadersRead)
+ .ConfigureAwait(false);
}
public async Task<XDocument> GetDataAsync(string url, CancellationToken cancellationToken)
{
- var options = new HttpRequestOptions
- {
- Url = url,
- UserAgent = USERAGENT,
- LogErrorResponseBody = true,
- BufferContent = false,
-
- CancellationToken = cancellationToken
- };
-
- options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
-
- using (var response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false))
- using (var stream = response.Content)
- using (var reader = new StreamReader(stream, Encoding.UTF8))
- {
- return XDocument.Parse(
- await reader.ReadToEndAsync().ConfigureAwait(false),
- LoadOptions.PreserveWhitespace);
- }
+ using var options = new HttpRequestMessage(HttpMethod.Get, url);
+ options.Headers.UserAgent.ParseAdd(USERAGENT);
+ options.Headers.TryAddWithoutValidation("FriendlyName.DLNA.ORG", FriendlyName);
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ using var reader = new StreamReader(stream, Encoding.UTF8);
+ return XDocument.Parse(
+ await reader.ReadToEndAsync().ConfigureAwait(false),
+ LoadOptions.PreserveWhitespace);
}
- private Task<HttpResponseInfo> PostSoapDataAsync(
+ private Task<HttpResponseMessage> PostSoapDataAsync(
string url,
string soapAction,
string postData,
@@ -130,29 +113,20 @@ namespace Emby.Dlna.PlayTo
soapAction = $"\"{soapAction}\"";
}
- var options = new HttpRequestOptions
- {
- Url = url,
- UserAgent = USERAGENT,
- LogErrorResponseBody = true,
- BufferContent = false,
-
- CancellationToken = cancellationToken
- };
-
- options.RequestHeaders["SOAPAction"] = soapAction;
- options.RequestHeaders["Pragma"] = "no-cache";
- options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
+ using var options = new HttpRequestMessage(HttpMethod.Post, url);
+ options.Headers.UserAgent.ParseAdd(USERAGENT);
+ options.Headers.TryAddWithoutValidation("SOAPACTION", soapAction);
+ options.Headers.TryAddWithoutValidation("Pragma", "no-cache");
+ options.Headers.TryAddWithoutValidation("FriendlyName.DLNA.ORG", FriendlyName);
if (!string.IsNullOrEmpty(header))
{
- options.RequestHeaders["contentFeatures.dlna.org"] = header;
+ options.Headers.TryAddWithoutValidation("contentFeatures.dlna.org", header);
}
- options.RequestContentType = "text/xml";
- options.RequestContent = postData;
+ options.Content = new StringContent(postData, Encoding.UTF8, MediaTypeNames.Text.Xml);
- return _httpClient.Post(options);
+ return _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
}
}
}
diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs
index 40d069e7c..a97c4d63a 100644
--- a/Emby.Dlna/Service/BaseService.cs
+++ b/Emby.Dlna/Service/BaseService.cs
@@ -1,25 +1,21 @@
#pragma warning disable CS1591
+using System.Net.Http;
using Emby.Dlna.Eventing;
-using MediaBrowser.Common.Net;
using Microsoft.Extensions.Logging;
namespace Emby.Dlna.Service
{
public class BaseService : IDlnaEventManager
{
- protected BaseService(ILogger<BaseService> logger, IHttpClient httpClient)
+ protected BaseService(ILogger<BaseService> logger, IHttpClientFactory httpClientFactory)
{
Logger = logger;
- HttpClient = httpClient;
-
- EventManager = new DlnaEventManager(logger, HttpClient);
+ EventManager = new DlnaEventManager(logger, httpClientFactory);
}
protected IDlnaEventManager EventManager { get; }
- protected IHttpClient HttpClient { get; }
-
protected ILogger Logger { get; }
public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 155bdcf65..c37e87d96 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -49,6 +49,7 @@ using Jellyfin.Api.Helpers;
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;
@@ -122,8 +123,8 @@ namespace Emby.Server.Implementations
private IMediaEncoder _mediaEncoder;
private ISessionManager _sessionManager;
+ private IHttpClientFactory _httpClientFactory;
private IWebSocketManager _webSocketManager;
- private IHttpClient _httpClient;
private string[] _urlPrefixes;
@@ -526,8 +527,6 @@ namespace Emby.Server.Implementations
ServiceCollection.AddSingleton(_fileSystemManager);
ServiceCollection.AddSingleton<TvdbClientManager>();
- ServiceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>();
-
ServiceCollection.AddSingleton(_networkManager);
ServiceCollection.AddSingleton<IIsoManager, IsoManager>();
@@ -654,8 +653,8 @@ namespace Emby.Server.Implementations
_mediaEncoder = Resolve<IMediaEncoder>();
_sessionManager = Resolve<ISessionManager>();
+ _httpClientFactory = Resolve<IHttpClientFactory>();
_webSocketManager = Resolve<IWebSocketManager>();
- _httpClient = Resolve<IHttpClient>();
((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize();
@@ -1300,25 +1299,17 @@ namespace Emby.Server.Implementations
try
{
- using (var response = await _httpClient.SendAsync(
- new HttpRequestOptions
- {
- Url = apiUrl,
- LogErrorResponseBody = false,
- BufferContent = false,
- CancellationToken = cancellationToken
- }, HttpMethod.Post).ConfigureAwait(false))
- {
- using (var reader = new StreamReader(response.Content))
- {
- var result = await reader.ReadToEndAsync().ConfigureAwait(false);
- var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase);
+ using var request = new HttpRequestMessage(HttpMethod.Post, apiUrl);
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
- _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid);
- Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, valid);
- return valid;
- }
- }
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ var result = await System.Text.Json.JsonSerializer.DeserializeAsync<string>(stream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false);
+ var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase);
+
+ _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid);
+ Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, valid);
+ return valid;
}
catch (OperationCanceledException)
{
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
deleted file mode 100644
index 25adc5812..000000000
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
+++ /dev/null
@@ -1,335 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Threading.Tasks;
-using MediaBrowser.Common;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Net;
-using Microsoft.Extensions.Logging;
-using Microsoft.Net.Http.Headers;
-
-namespace Emby.Server.Implementations.HttpClientManager
-{
- /// <summary>
- /// Class HttpClientManager.
- /// </summary>
- public class HttpClientManager : IHttpClient
- {
- private readonly ILogger<HttpClientManager> _logger;
- private readonly IApplicationPaths _appPaths;
- private readonly IFileSystem _fileSystem;
- private readonly IApplicationHost _appHost;
-
- /// <summary>
- /// Holds a dictionary of http clients by host. Use GetHttpClient(host) to retrieve or create a client for web requests.
- /// DON'T dispose it after use.
- /// </summary>
- /// <value>The HTTP clients.</value>
- private readonly ConcurrentDictionary<string, HttpClient> _httpClients = new ConcurrentDictionary<string, HttpClient>();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="HttpClientManager" /> class.
- /// </summary>
- public HttpClientManager(
- IApplicationPaths appPaths,
- ILogger<HttpClientManager> logger,
- IFileSystem fileSystem,
- IApplicationHost appHost)
- {
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- _fileSystem = fileSystem;
- _appPaths = appPaths ?? throw new ArgumentNullException(nameof(appPaths));
- _appHost = appHost;
- }
-
- /// <summary>
- /// Gets the correct http client for the given url.
- /// </summary>
- /// <param name="url">The url.</param>
- /// <returns>HttpClient.</returns>
- private HttpClient GetHttpClient(string url)
- {
- var key = GetHostFromUrl(url);
-
- if (!_httpClients.TryGetValue(key, out var client))
- {
- client = new HttpClient()
- {
- BaseAddress = new Uri(url)
- };
-
- _httpClients.TryAdd(key, client);
- }
-
- return client;
- }
-
- private HttpRequestMessage GetRequestMessage(HttpRequestOptions options, HttpMethod method)
- {
- string url = options.Url;
- var uriAddress = new Uri(url);
- string userInfo = uriAddress.UserInfo;
- if (!string.IsNullOrWhiteSpace(userInfo))
- {
- _logger.LogWarning("Found userInfo in url: {0} ... url: {1}", userInfo, url);
- url = url.Replace(userInfo + '@', string.Empty, StringComparison.Ordinal);
- }
-
- var request = new HttpRequestMessage(method, url);
-
- foreach (var header in options.RequestHeaders)
- {
- request.Headers.TryAddWithoutValidation(header.Key, header.Value);
- }
-
- if (options.EnableDefaultUserAgent
- && !request.Headers.TryGetValues(HeaderNames.UserAgent, out _))
- {
- request.Headers.Add(HeaderNames.UserAgent, _appHost.ApplicationUserAgent);
- }
-
- switch (options.DecompressionMethod)
- {
- case CompressionMethods.Deflate | CompressionMethods.Gzip:
- request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" });
- break;
- case CompressionMethods.Deflate:
- request.Headers.Add(HeaderNames.AcceptEncoding, "deflate");
- break;
- case CompressionMethods.Gzip:
- request.Headers.Add(HeaderNames.AcceptEncoding, "gzip");
- break;
- default:
- break;
- }
-
- if (options.EnableKeepAlive)
- {
- request.Headers.Add(HeaderNames.Connection, "Keep-Alive");
- }
-
- // request.Headers.Add(HeaderNames.CacheControl, "no-cache");
-
- /*
- if (!string.IsNullOrWhiteSpace(userInfo))
- {
- var parts = userInfo.Split(':');
- if (parts.Length == 2)
- {
- request.Headers.Add(HeaderNames., GetCredential(url, parts[0], parts[1]);
- }
- }
- */
-
- return request;
- }
-
- /// <summary>
- /// Gets the response internal.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- public Task<HttpResponseInfo> GetResponse(HttpRequestOptions options)
- => SendAsync(options, HttpMethod.Get);
-
- /// <summary>
- /// Performs a GET request and returns the resulting stream.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{Stream}.</returns>
- public async Task<Stream> Get(HttpRequestOptions options)
- {
- var response = await GetResponse(options).ConfigureAwait(false);
- return response.Content;
- }
-
- /// <summary>
- /// send as an asynchronous operation.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="httpMethod">The HTTP method.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- public Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
- => SendAsync(options, new HttpMethod(httpMethod));
-
- /// <summary>
- /// send as an asynchronous operation.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="httpMethod">The HTTP method.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, HttpMethod httpMethod)
- {
- if (options.CacheMode == CacheMode.None)
- {
- return await SendAsyncInternal(options, httpMethod).ConfigureAwait(false);
- }
-
- var url = options.Url;
- var urlHash = url.ToUpperInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
-
- var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
-
- var response = GetCachedResponse(responseCachePath, options.CacheLength, url);
- if (response != null)
- {
- return response;
- }
-
- response = await SendAsyncInternal(options, httpMethod).ConfigureAwait(false);
-
- if (response.StatusCode == HttpStatusCode.OK)
- {
- await CacheResponse(response, responseCachePath).ConfigureAwait(false);
- }
-
- return response;
- }
-
- private HttpResponseInfo GetCachedResponse(string responseCachePath, TimeSpan cacheLength, string url)
- {
- if (File.Exists(responseCachePath)
- && _fileSystem.GetLastWriteTimeUtc(responseCachePath).Add(cacheLength) > DateTime.UtcNow)
- {
- var stream = new FileStream(responseCachePath, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, true);
-
- return new HttpResponseInfo
- {
- ResponseUrl = url,
- Content = stream,
- StatusCode = HttpStatusCode.OK,
- ContentLength = stream.Length
- };
- }
-
- return null;
- }
-
- private async Task CacheResponse(HttpResponseInfo response, string responseCachePath)
- {
- Directory.CreateDirectory(Path.GetDirectoryName(responseCachePath));
-
- using (var fileStream = new FileStream(
- responseCachePath,
- FileMode.Create,
- FileAccess.Write,
- FileShare.None,
- IODefaults.FileStreamBufferSize,
- true))
- {
- await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
-
- response.Content.Position = 0;
- }
- }
-
- private async Task<HttpResponseInfo> SendAsyncInternal(HttpRequestOptions options, HttpMethod httpMethod)
- {
- ValidateParams(options);
-
- options.CancellationToken.ThrowIfCancellationRequested();
-
- var client = GetHttpClient(options.Url);
-
- var httpWebRequest = GetRequestMessage(options, httpMethod);
-
- if (!string.IsNullOrEmpty(options.RequestContent)
- || httpMethod == HttpMethod.Post)
- {
- if (options.RequestContent != null)
- {
- httpWebRequest.Content = new StringContent(
- options.RequestContent,
- null,
- options.RequestContentType);
- }
- else
- {
- httpWebRequest.Content = new ByteArrayContent(Array.Empty<byte>());
- }
- }
-
- options.CancellationToken.ThrowIfCancellationRequested();
-
- var response = await client.SendAsync(
- httpWebRequest,
- options.BufferContent || options.CacheMode == CacheMode.Unconditional ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead,
- options.CancellationToken).ConfigureAwait(false);
-
- await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
-
- options.CancellationToken.ThrowIfCancellationRequested();
-
- var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
- return new HttpResponseInfo(response.Headers, response.Content.Headers)
- {
- Content = stream,
- StatusCode = response.StatusCode,
- ContentType = response.Content.Headers.ContentType?.MediaType,
- ContentLength = response.Content.Headers.ContentLength,
- ResponseUrl = response.Content.Headers.ContentLocation?.ToString()
- };
- }
-
- /// <inheritdoc />
- public Task<HttpResponseInfo> Post(HttpRequestOptions options)
- => SendAsync(options, HttpMethod.Post);
-
- private void ValidateParams(HttpRequestOptions options)
- {
- if (string.IsNullOrEmpty(options.Url))
- {
- throw new ArgumentNullException(nameof(options));
- }
- }
-
- /// <summary>
- /// Gets the host from URL.
- /// </summary>
- /// <param name="url">The URL.</param>
- /// <returns>System.String.</returns>
- private static string GetHostFromUrl(string url)
- {
- var index = url.IndexOf("://", StringComparison.OrdinalIgnoreCase);
-
- if (index != -1)
- {
- url = url.Substring(index + 3);
- var host = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
-
- if (!string.IsNullOrWhiteSpace(host))
- {
- return host;
- }
- }
-
- return url;
- }
-
- private async Task EnsureSuccessStatusCode(HttpResponseMessage response, HttpRequestOptions options)
- {
- if (response.IsSuccessStatusCode)
- {
- return;
- }
-
- if (options.LogErrorResponseBody)
- {
- string msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
- _logger.LogError("HTTP request failed with message: {Message}", msg);
- }
-
- throw new HttpException(response.ReasonPhrase)
- {
- StatusCode = response.StatusCode
- };
- }
- }
-}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
index 0edd98031..44560d1e2 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
@@ -16,13 +16,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public class DirectRecorder : IRecorder
{
private readonly ILogger _logger;
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly IStreamHelper _streamHelper;
- public DirectRecorder(ILogger logger, IHttpClient httpClient, IStreamHelper streamHelper)
+ public DirectRecorder(ILogger logger, IHttpClientFactory httpClientFactory, IStreamHelper streamHelper)
{
_logger = logger;
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_streamHelper = streamHelper;
}
@@ -63,42 +63,28 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private async Task RecordFromMediaSource(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
- var httpRequestOptions = new HttpRequestOptions
- {
- Url = mediaSource.Path,
- BufferContent = false,
-
- // Some remote urls will expect a user agent to be supplied
- UserAgent = "Emby/3.0",
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .GetAsync(mediaSource.Path, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
- // Shouldn't matter but may cause issues
- DecompressionMethod = CompressionMethods.None,
- CancellationToken = cancellationToken
- };
+ _logger.LogInformation("Opened recording stream from tuner provider");
- using (var response = await _httpClient.SendAsync(httpRequestOptions, HttpMethod.Get).ConfigureAwait(false))
- {
- _logger.LogInformation("Opened recording stream from tuner provider");
+ Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
- Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
+ await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.Read);
- using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.Read))
- {
- onStarted();
+ onStarted();
- _logger.LogInformation("Copying recording stream to file {0}", targetFile);
+ _logger.LogInformation("Copying recording stream to file {0}", targetFile);
- // The media source if infinite so we need to handle stopping ourselves
- using var durationToken = new CancellationTokenSource(duration);
- using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token);
+ // The media source if infinite so we need to handle stopping ourselves
+ var durationToken = new CancellationTokenSource(duration);
+ cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
- await _streamHelper.CopyUntilCancelled(
- response.Content,
- output,
- IODefaults.CopyToBufferSize,
- cancellationTokenSource.Token).ConfigureAwait(false);
- }
- }
+ await _streamHelper.CopyUntilCancelled(
+ await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
+ output,
+ IODefaults.CopyToBufferSize,
+ cancellationToken).ConfigureAwait(false);
_logger.LogInformation("Recording completed to file {0}", targetFile);
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 5cf09b8e5..ca60c3366 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -48,7 +49,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly IServerApplicationHost _appHost;
private readonly ILogger<EmbyTV> _logger;
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly IServerConfigurationManager _config;
private readonly IJsonSerializer _jsonSerializer;
@@ -81,7 +82,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
IMediaSourceManager mediaSourceManager,
ILogger<EmbyTV> logger,
IJsonSerializer jsonSerializer,
- IHttpClient httpClient,
+ IHttpClientFactory httpClientFactory,
IServerConfigurationManager config,
ILiveTvManager liveTvManager,
IFileSystem fileSystem,
@@ -94,7 +95,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_appHost = appHost;
_logger = logger;
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_config = config;
_fileSystem = fileSystem;
_libraryManager = libraryManager;
@@ -1637,7 +1638,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer);
}
- return new DirectRecorder(_logger, _httpClient, _streamHelper);
+ return new DirectRecorder(_logger, _httpClientFactory, _streamHelper);
}
private void OnSuccessfulRecording(TimerInfo timer, string path)
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 33331adaf..f9ae55af8 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -8,6 +8,8 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
+using System.Net.Mime;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
@@ -28,19 +30,19 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private readonly ILogger<SchedulesDirect> _logger;
private readonly IJsonSerializer _jsonSerializer;
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1);
private readonly IApplicationHost _appHost;
public SchedulesDirect(
ILogger<SchedulesDirect> logger,
IJsonSerializer jsonSerializer,
- IHttpClient httpClient,
+ IHttpClientFactory httpClientFactory,
IApplicationHost appHost)
{
_logger = logger;
_jsonSerializer = jsonSerializer;
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_appHost = appHost;
}
@@ -102,95 +104,78 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var requestString = _jsonSerializer.SerializeToString(requestList);
_logger.LogDebug("Request string for schedules is: {RequestString}", requestString);
- var httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/schedules",
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true,
- RequestContent = requestString
- };
+ using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/schedules");
+ options.Content = new StringContent(requestString, Encoding.UTF8, MediaTypeNames.Application.Json);
+ options.Headers.TryAddWithoutValidation("token", token);
+ using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
+ await using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ var dailySchedules = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.Day>>(responseStream).ConfigureAwait(false);
+ _logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
- httpOptions.RequestHeaders["token"] = token;
+ using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs");
+ programRequestOptions.Headers.TryAddWithoutValidation("token", token);
- using (var response = await Post(httpOptions, true, info).ConfigureAwait(false))
- {
- var dailySchedules = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.Day>>(response.Content).ConfigureAwait(false);
- _logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
+ var programsID = dailySchedules.SelectMany(d => d.programs.Select(s => s.programID)).Distinct();
+ programRequestOptions.Content = new StringContent("[\"" + string.Join("\", \"", programsID) + "\"]", Encoding.UTF8, MediaTypeNames.Application.Json);
- httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/programs",
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true
- };
-
- httpOptions.RequestHeaders["token"] = token;
+ using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
+ await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ var programDetails = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ProgramDetails>>(innerResponseStream).ConfigureAwait(false);
+ var programDict = programDetails.ToDictionary(p => p.programID, y => y);
- var programsID = dailySchedules.SelectMany(d => d.programs.Select(s => s.programID)).Distinct();
- httpOptions.RequestContent = "[\"" + string.Join("\", \"", programsID) + "\"]";
+ var programIdsWithImages =
+ programDetails.Where(p => p.hasImageArtwork).Select(p => p.programID)
+ .ToList();
- using (var innerResponse = await Post(httpOptions, true, info).ConfigureAwait(false))
- {
- var programDetails = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ProgramDetails>>(innerResponse.Content).ConfigureAwait(false);
- var programDict = programDetails.ToDictionary(p => p.programID, y => y);
+ var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken).ConfigureAwait(false);
- var programIdsWithImages =
- programDetails.Where(p => p.hasImageArtwork).Select(p => p.programID)
- .ToList();
-
- var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken).ConfigureAwait(false);
+ var programsInfo = new List<ProgramInfo>();
+ foreach (ScheduleDirect.Program schedule in dailySchedules.SelectMany(d => d.programs))
+ {
+ // _logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
+ // " which corresponds to channel " + channelNumber + " and program id " +
+ // schedule.programID + " which says it has images? " +
+ // programDict[schedule.programID].hasImageArtwork);
- var programsInfo = new List<ProgramInfo>();
- foreach (ScheduleDirect.Program schedule in dailySchedules.SelectMany(d => d.programs))
+ if (images != null)
+ {
+ var imageIndex = images.FindIndex(i => i.programID == schedule.programID.Substring(0, 10));
+ if (imageIndex > -1)
{
- // _logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
- // " which corresponds to channel " + channelNumber + " and program id " +
- // schedule.programID + " which says it has images? " +
- // programDict[schedule.programID].hasImageArtwork);
-
- if (images != null)
- {
- var imageIndex = images.FindIndex(i => i.programID == schedule.programID.Substring(0, 10));
- if (imageIndex > -1)
- {
- var programEntry = programDict[schedule.programID];
+ var programEntry = programDict[schedule.programID];
- var allImages = images[imageIndex].data ?? new List<ScheduleDirect.ImageData>();
- var imagesWithText = allImages.Where(i => string.Equals(i.text, "yes", StringComparison.OrdinalIgnoreCase));
- var imagesWithoutText = allImages.Where(i => string.Equals(i.text, "no", StringComparison.OrdinalIgnoreCase));
+ var allImages = images[imageIndex].data ?? new List<ScheduleDirect.ImageData>();
+ var imagesWithText = allImages.Where(i => string.Equals(i.text, "yes", StringComparison.OrdinalIgnoreCase));
+ var imagesWithoutText = allImages.Where(i => string.Equals(i.text, "no", StringComparison.OrdinalIgnoreCase));
- const double DesiredAspect = 2.0 / 3;
+ const double DesiredAspect = 2.0 / 3;
- programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
- GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
+ programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
+ GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
- const double WideAspect = 16.0 / 9;
+ const double WideAspect = 16.0 / 9;
- programEntry.thumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
+ programEntry.thumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
- // Don't supply the same image twice
- if (string.Equals(programEntry.primaryImage, programEntry.thumbImage, StringComparison.Ordinal))
- {
- programEntry.thumbImage = null;
- }
-
- programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
-
- // programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
- // GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
- // GetProgramImage(ApiUrl, data, "Banner-LO", false) ??
- // GetProgramImage(ApiUrl, data, "Banner-LOT", false);
- }
+ // Don't supply the same image twice
+ if (string.Equals(programEntry.primaryImage, programEntry.thumbImage, StringComparison.Ordinal))
+ {
+ programEntry.thumbImage = null;
}
- programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.programID]));
- }
+ programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
- return programsInfo;
+ // programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
+ // GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
+ // GetProgramImage(ApiUrl, data, "Banner-LO", false) ??
+ // GetProgramImage(ApiUrl, data, "Banner-LOT", false);
+ }
}
+
+ programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.programID]));
}
+
+ return programsInfo;
}
private static int GetSizeOrder(ScheduleDirect.ImageData image)
@@ -483,22 +468,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
imageIdString = imageIdString.TrimEnd(',') + "]";
- var httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/metadata/programs",
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- RequestContent = imageIdString,
- LogErrorResponseBody = true,
- };
+ using var message = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/metadata/programs");
+ message.Content = new StringContent(imageIdString, Encoding.UTF8, MediaTypeNames.Application.Json);
try
{
- using (var innerResponse2 = await Post(httpOptions, true, info).ConfigureAwait(false))
- {
- return await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ShowImages>>(
- innerResponse2.Content).ConfigureAwait(false);
- }
+ using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false);
+ await using var response = await innerResponse2.Content.ReadAsStreamAsync();
+ return await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ShowImages>>(
+ response).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -519,41 +497,33 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return lineups;
}
- var options = new HttpRequestOptions()
- {
- Url = ApiUrl + "/headends?country=" + country + "&postalcode=" + location,
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true
- };
-
- options.RequestHeaders["token"] = token;
+ using var options = new HttpRequestMessage(HttpMethod.Get, ApiUrl + "/headends?country=" + country + "&postalcode=" + location);
+ options.Headers.TryAddWithoutValidation("token", token);
try
{
- using (var httpResponse = await Get(options, false, info).ConfigureAwait(false))
- using (Stream responce = httpResponse.Content)
- {
- var root = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.Headends>>(responce).ConfigureAwait(false);
+ using var httpResponse = await Send(options, false, info, cancellationToken).ConfigureAwait(false);
+ await using var response = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
+
+ var root = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.Headends>>(response).ConfigureAwait(false);
- if (root != null)
+ if (root != null)
+ {
+ foreach (ScheduleDirect.Headends headend in root)
{
- foreach (ScheduleDirect.Headends headend in root)
+ foreach (ScheduleDirect.Lineup lineup in headend.lineups)
{
- foreach (ScheduleDirect.Lineup lineup in headend.lineups)
+ lineups.Add(new NameIdPair
{
- lineups.Add(new NameIdPair
- {
- Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
- Id = lineup.uri.Substring(18)
- });
- }
+ Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
+ Id = lineup.uri.Substring(18)
+ });
}
}
- else
- {
- _logger.LogInformation("No lineups available");
- }
+ }
+ else
+ {
+ _logger.LogInformation("No lineups available");
}
}
catch (Exception ex)
@@ -634,17 +604,16 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- private async Task<HttpResponseInfo> Post(
- HttpRequestOptions options,
+ private async Task<HttpResponseMessage> Send(
+ HttpRequestMessage options,
bool enableRetry,
- ListingsProviderInfo providerInfo)
+ ListingsProviderInfo providerInfo,
+ CancellationToken cancellationToken,
+ HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead)
{
- // Schedules direct requires that the client support compression and will return a 400 response without it
- options.DecompressionMethod = CompressionMethods.Deflate;
-
try
{
- return await _httpClient.Post(options).ConfigureAwait(false);
+ return await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, completionOption, cancellationToken).ConfigureAwait(false);
}
catch (HttpException ex)
{
@@ -661,39 +630,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- options.RequestHeaders["token"] = await GetToken(providerInfo, options.CancellationToken).ConfigureAwait(false);
- return await Post(options, false, providerInfo).ConfigureAwait(false);
- }
-
- private async Task<HttpResponseInfo> Get(
- HttpRequestOptions options,
- bool enableRetry,
- ListingsProviderInfo providerInfo)
- {
- // Schedules direct requires that the client support compression and will return a 400 response without it
- options.DecompressionMethod = CompressionMethods.Deflate;
-
- try
- {
- return await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false);
- }
- catch (HttpException ex)
- {
- _tokens.Clear();
-
- if (!ex.StatusCode.HasValue || (int)ex.StatusCode.Value >= 500)
- {
- enableRetry = false;
- }
-
- if (!enableRetry)
- {
- throw;
- }
- }
-
- options.RequestHeaders["token"] = await GetToken(providerInfo, options.CancellationToken).ConfigureAwait(false);
- return await Get(options, false, providerInfo).ConfigureAwait(false);
+ options.Headers.TryAddWithoutValidation("token", await GetToken(providerInfo, cancellationToken).ConfigureAwait(false));
+ return await Send(options, false, providerInfo, cancellationToken).ConfigureAwait(false);
}
private async Task<string> GetTokenInternal(
@@ -701,28 +639,19 @@ namespace Emby.Server.Implementations.LiveTv.Listings
string password,
CancellationToken cancellationToken)
{
- var httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/token",
- UserAgent = UserAgent,
- RequestContent = "{\"username\":\"" + username + "\",\"password\":\"" + password + "\"}",
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true
- };
- // _logger.LogInformation("Obtaining token from Schedules Direct from addres: " + httpOptions.Url + " with body " +
- // httpOptions.RequestContent);
+ using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/token");
+ options.Content = new StringContent("{\"username\":\"" + username + "\",\"password\":\"" + password + "\"}", Encoding.UTF8, MediaTypeNames.Application.Json);
- using (var response = await Post(httpOptions, false, null).ConfigureAwait(false))
+ using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Token>(stream).ConfigureAwait(false);
+ if (root.message == "OK")
{
- var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Token>(response.Content).ConfigureAwait(false);
- if (root.message == "OK")
- {
- _logger.LogInformation("Authenticated with Schedules Direct token: " + root.token);
- return root.token;
- }
-
- throw new Exception("Could not authenticate with Schedules Direct Error: " + root.message);
+ _logger.LogInformation("Authenticated with Schedules Direct token: " + root.token);
+ return root.token;
}
+
+ throw new Exception("Could not authenticate with Schedules Direct Error: " + root.message);
}
private async Task AddLineupToAccount(ListingsProviderInfo info, CancellationToken cancellationToken)
@@ -741,20 +670,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
_logger.LogInformation("Adding new LineUp ");
- var httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/lineups/" + info.ListingsId,
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true,
- BufferContent = false
- };
-
- httpOptions.RequestHeaders["token"] = token;
-
- using (await _httpClient.SendAsync(httpOptions, HttpMethod.Put).ConfigureAwait(false))
- {
- }
+ using var options = new HttpRequestMessage(HttpMethod.Put, ApiUrl + "/lineups/" + info.ListingsId);
+ options.Headers.TryAddWithoutValidation("token", token);
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
}
private async Task<bool> HasLineup(ListingsProviderInfo info, CancellationToken cancellationToken)
@@ -773,25 +691,17 @@ namespace Emby.Server.Implementations.LiveTv.Listings
_logger.LogInformation("Headends on account ");
- var options = new HttpRequestOptions()
- {
- Url = ApiUrl + "/lineups",
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true
- };
-
- options.RequestHeaders["token"] = token;
+ using var options = new HttpRequestMessage(HttpMethod.Get, ApiUrl + "/lineups");
+ options.Headers.TryAddWithoutValidation("token", token);
try
{
- using (var httpResponse = await Get(options, false, null).ConfigureAwait(false))
- using (var response = httpResponse.Content)
- {
- var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Lineups>(response).ConfigureAwait(false);
+ using var httpResponse = await Send(options, false, null, cancellationToken).ConfigureAwait(false);
+ await using var stream = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ using var response = httpResponse.Content;
+ var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Lineups>(stream).ConfigureAwait(false);
- return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
- }
+ return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
}
catch (HttpException ex)
{
@@ -856,55 +766,43 @@ namespace Emby.Server.Implementations.LiveTv.Listings
throw new Exception("token required");
}
- var httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/lineups/" + listingsId,
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true,
- };
-
- httpOptions.RequestHeaders["token"] = token;
+ using var options = new HttpRequestMessage(HttpMethod.Get, ApiUrl + "/lineups/" + listingsId);
+ options.Headers.TryAddWithoutValidation("token", token);
var list = new List<ChannelInfo>();
- using (var httpResponse = await Get(httpOptions, true, info).ConfigureAwait(false))
- using (var response = httpResponse.Content)
- {
- var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Channel>(response).ConfigureAwait(false);
- _logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count);
- _logger.LogInformation("Mapping Stations to Channel");
-
- var allStations = root.stations ?? Enumerable.Empty<ScheduleDirect.Station>();
+ using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
+ await using var stream = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Channel>(stream).ConfigureAwait(false);
+ _logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count);
+ _logger.LogInformation("Mapping Stations to Channel");
- foreach (ScheduleDirect.Map map in root.map)
- {
- var channelNumber = GetChannelNumber(map);
+ var allStations = root.stations ?? Enumerable.Empty<ScheduleDirect.Station>();
- var station = allStations.FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase));
- if (station == null)
- {
- station = new ScheduleDirect.Station
- {
- stationID = map.stationID
- };
- }
+ foreach (ScheduleDirect.Map map in root.map)
+ {
+ var channelNumber = GetChannelNumber(map);
- var channelInfo = new ChannelInfo
- {
- Id = station.stationID,
- CallSign = station.callsign,
- Number = channelNumber,
- Name = string.IsNullOrWhiteSpace(station.name) ? channelNumber : station.name
- };
+ var station = allStations.FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase));
+ if (station == null)
+ {
+ station = new ScheduleDirect.Station { stationID = map.stationID };
+ }
- if (station.logo != null)
- {
- channelInfo.ImageUrl = station.logo.URL;
- }
+ var channelInfo = new ChannelInfo
+ {
+ Id = station.stationID,
+ CallSign = station.callsign,
+ Number = channelNumber,
+ Name = string.IsNullOrWhiteSpace(station.name) ? channelNumber : station.name
+ };
- list.Add(channelInfo);
+ if (station.logo != null)
+ {
+ channelInfo.ImageUrl = station.logo.URL;
}
+
+ list.Add(channelInfo);
}
return list;
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index f33d07174..2d6f453bd 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -25,20 +25,20 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public class XmlTvListingsProvider : IListingsProvider
{
private readonly IServerConfigurationManager _config;
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<XmlTvListingsProvider> _logger;
private readonly IFileSystem _fileSystem;
private readonly IZipClient _zipClient;
public XmlTvListingsProvider(
IServerConfigurationManager config,
- IHttpClient httpClient,
+ IHttpClientFactory httpClientFactory,
ILogger<XmlTvListingsProvider> logger,
IFileSystem fileSystem,
IZipClient zipClient)
{
_config = config;
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_logger = logger;
_fileSystem = fileSystem;
_zipClient = zipClient;
@@ -78,28 +78,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
Directory.CreateDirectory(Path.GetDirectoryName(cacheFile));
- using (var res = await _httpClient.SendAsync(
- new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Url = path,
- DecompressionMethod = CompressionMethods.Gzip,
- },
- HttpMethod.Get).ConfigureAwait(false))
- using (var stream = res.Content)
- using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew))
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(path, cancellationToken).ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ await using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew))
{
- if (res.ContentHeaders.ContentEncoding.Contains("gzip"))
- {
- using (var gzStream = new GZipStream(stream, CompressionMode.Decompress))
- {
- await gzStream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
- }
- }
- else
- {
- await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
- }
+ await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
}
return UnzipIfNeeded(path, cacheFile);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 2b5f69d41..28e30fac8 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -31,7 +31,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
public class HdHomerunHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
{
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly IServerApplicationHost _appHost;
private readonly ISocketFactory _socketFactory;
private readonly INetworkManager _networkManager;
@@ -43,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
IServerConfigurationManager config,
ILogger<HdHomerunHost> logger,
IFileSystem fileSystem,
- IHttpClient httpClient,
+ IHttpClientFactory httpClientFactory,
IServerApplicationHost appHost,
ISocketFactory socketFactory,
INetworkManager networkManager,
@@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
IMemoryCache memoryCache)
: base(config, logger, fileSystem, memoryCache)
{
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_appHost = appHost;
_socketFactory = socketFactory;
_networkManager = networkManager;
@@ -71,15 +71,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
- var options = new HttpRequestOptions
- {
- Url = model.LineupURL,
- CancellationToken = cancellationToken,
- BufferContent = false
- };
-
- using var response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false);
- await using var stream = response.Content;
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(model.LineupURL, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var lineup = await JsonSerializer.DeserializeAsync<List<Channels>>(stream, cancellationToken: cancellationToken)
.ConfigureAwait(false) ?? new List<Channels>();
@@ -133,14 +126,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
try
{
- using var response = await _httpClient.SendAsync(
- new HttpRequestOptions
- {
- Url = string.Format(CultureInfo.InvariantCulture, "{0}/discover.json", GetApiUrl(info)),
- CancellationToken = cancellationToken,
- BufferContent = false
- }, HttpMethod.Get).ConfigureAwait(false);
- await using var stream = response.Content;
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .GetAsync(string.Format(CultureInfo.InvariantCulture, "{0}/discover.json", GetApiUrl(info)), HttpCompletionOption.ResponseHeadersRead, cancellationToken)
+ .ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var discoverResponse = await JsonSerializer.DeserializeAsync<DiscoverResponse>(stream, cancellationToken: cancellationToken)
.ConfigureAwait(false);
@@ -183,48 +172,41 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
- using (var response = await _httpClient.SendAsync(
- new HttpRequestOptions()
- {
- Url = string.Format(CultureInfo.InvariantCulture, "{0}/tuners.html", GetApiUrl(info)),
- CancellationToken = cancellationToken,
- BufferContent = false
- },
- HttpMethod.Get).ConfigureAwait(false))
- using (var stream = response.Content)
- using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8))
- {
- var tuners = new List<LiveTvTunerInfo>();
- while (!sr.EndOfStream)
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .GetAsync(string.Format(CultureInfo.InvariantCulture, "{0}/tuners.html", GetApiUrl(info)), HttpCompletionOption.ResponseHeadersRead, cancellationToken)
+ .ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ using var sr = new StreamReader(stream, System.Text.Encoding.UTF8);
+ var tuners = new List<LiveTvTunerInfo>();
+ while (!sr.EndOfStream)
+ {
+ string line = StripXML(sr.ReadLine());
+ if (line.Contains("Channel", StringComparison.Ordinal))
{
- string line = StripXML(sr.ReadLine());
- if (line.Contains("Channel", StringComparison.Ordinal))
+ LiveTvTunerStatus status;
+ var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
+ var name = line.Substring(0, index - 1);
+ var currentChannel = line.Substring(index + 7);
+ if (currentChannel != "none")
{
- LiveTvTunerStatus status;
- var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
- var name = line.Substring(0, index - 1);
- var currentChannel = line.Substring(index + 7);
- if (currentChannel != "none")
- {
- status = LiveTvTunerStatus.LiveTv;
- }
- else
- {
- status = LiveTvTunerStatus.Available;
- }
-
- tuners.Add(new LiveTvTunerInfo
- {
- Name = name,
- SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber,
- ProgramName = currentChannel,
- Status = status
- });
+ status = LiveTvTunerStatus.LiveTv;
+ }
+ else
+ {
+ status = LiveTvTunerStatus.Available;
}
- }
- return tuners;
+ tuners.Add(new LiveTvTunerInfo
+ {
+ Name = name,
+ SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber,
+ ProgramName = currentChannel,
+ Status = status
+ });
+ }
}
+
+ return tuners;
}
private static string StripXML(string source)
@@ -634,7 +616,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
info,
streamId,
FileSystem,
- _httpClient,
+ _httpClientFactory,
Logger,
Config,
_appHost,
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 8fc29fb4a..8107bc427 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
@@ -26,7 +27,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
public class M3UTunerHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
{
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly IServerApplicationHost _appHost;
private readonly INetworkManager _networkManager;
private readonly IMediaSourceManager _mediaSourceManager;
@@ -37,14 +38,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
IMediaSourceManager mediaSourceManager,
ILogger<M3UTunerHost> logger,
IFileSystem fileSystem,
- IHttpClient httpClient,
+ IHttpClientFactory httpClientFactory,
IServerApplicationHost appHost,
INetworkManager networkManager,
IStreamHelper streamHelper,
IMemoryCache memoryCache)
: base(config, logger, fileSystem, memoryCache)
{
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_appHost = appHost;
_networkManager = networkManager;
_mediaSourceManager = mediaSourceManager;
@@ -64,7 +65,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var channelIdPrefix = GetFullChannelIdPrefix(info);
- return await new M3uParser(Logger, _httpClient, _appHost).Parse(info.Url, channelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
+ return await new M3uParser(Logger, _httpClientFactory, _appHost).Parse(info.Url, channelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
}
public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
@@ -116,7 +117,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
if (!_disallowedSharedStreamExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
- return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config, _appHost, _streamHelper);
+ return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClientFactory, Logger, Config, _appHost, _streamHelper);
}
}
@@ -125,7 +126,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public async Task Validate(TunerHostInfo info)
{
- using (var stream = await new M3uParser(Logger, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
+ using (var stream = await new M3uParser(Logger, _httpClientFactory, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
{
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 875977219..f066a749e 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@@ -19,13 +20,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public class M3uParser
{
private readonly ILogger _logger;
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly IServerApplicationHost _appHost;
- public M3uParser(ILogger logger, IHttpClient httpClient, IServerApplicationHost appHost)
+ public M3uParser(ILogger logger, IHttpClientFactory httpClientFactory, IServerApplicationHost appHost)
{
_logger = logger;
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_appHost = appHost;
}
@@ -51,13 +52,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
- return _httpClient.Get(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = cancellationToken,
- // Some data providers will require a user agent
- UserAgent = _appHost.ApplicationUserAgent
- });
+ return _httpClientFactory.CreateClient(NamedClient.Default)
+ .GetStreamAsync(url);
}
return Task.FromResult((Stream)File.OpenRead(url));
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index bc4dcd894..6c10fca8c 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
public class SharedHttpStream : LiveStream, IDirectStreamProvider
{
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly IServerApplicationHost _appHost;
public SharedHttpStream(
@@ -29,14 +29,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
TunerHostInfo tunerHostInfo,
string originalStreamId,
IFileSystem fileSystem,
- IHttpClient httpClient,
+ IHttpClientFactory httpClientFactory,
ILogger logger,
IConfigurationManager configurationManager,
IServerApplicationHost appHost,
IStreamHelper streamHelper)
: base(mediaSource, tunerHostInfo, fileSystem, logger, configurationManager, streamHelper)
{
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_appHost = appHost;
OriginalStreamId = originalStreamId;
EnableStreamSharing = true;
@@ -55,25 +55,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var typeName = GetType().Name;
Logger.LogInformation("Opening " + typeName + " Live stream from {0}", url);
- var httpRequestOptions = new HttpRequestOptions
- {
- Url = url,
- CancellationToken = CancellationToken.None,
- BufferContent = false,
- DecompressionMethod = CompressionMethods.None
- };
-
- foreach (var header in mediaSource.RequiredHttpHeaders)
- {
- httpRequestOptions.RequestHeaders[header.Key] = header.Value;
- }
-
- var response = await _httpClient.SendAsync(httpRequestOptions, HttpMethod.Get).ConfigureAwait(false);
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .GetAsync(url, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None)
+ .ConfigureAwait(false);
var extension = "ts";
var requiresRemux = false;
- var contentType = response.ContentType ?? string.Empty;
+ var contentType = response.Content.Headers.ContentType.ToString();
if (contentType.IndexOf("matroska", StringComparison.OrdinalIgnoreCase) != -1)
{
requiresRemux = true;
@@ -132,24 +121,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
}
- private Task StartStreaming(HttpResponseInfo response, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
+ private Task StartStreaming(HttpResponseMessage response, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{
return Task.Run(async () =>
{
try
{
Logger.LogInformation("Beginning {0} stream to {1}", GetType().Name, TempFilePath);
- using (response)
- using (var stream = response.Content)
- using (var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
- {
- await StreamHelper.CopyToAsync(
- stream,
- fileStream,
- IODefaults.CopyToBufferSize,
- () => Resolve(openTaskCompletionSource),
- cancellationToken).ConfigureAwait(false);
- }
+ using var message = response;
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ await using var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read);
+ await StreamHelper.CopyToAsync(
+ stream,
+ fileStream,
+ IODefaults.CopyToBufferSize,
+ () => Resolve(openTaskCompletionSource),
+ cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException ex)
{
diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json
index d6be86da3..ed6877f7d 100644
--- a/Emby.Server.Implementations/Localization/Core/ta.json
+++ b/Emby.Server.Implementations/Localization/Core/ta.json
@@ -18,7 +18,7 @@
"MessageServerConfigurationUpdated": "சேவையக அமைப்புகள் புதுப்பிக்கப்பட்டன",
"MessageApplicationUpdatedTo": "ஜெல்லிஃபின் சேவையகம் {0} இற்கு புதுப்பிக்கப்பட்டது",
"MessageApplicationUpdated": "ஜெல்லிஃபின் சேவையகம் புதுப்பிக்கப்பட்டது",
- "Inherit": "மரபரிமையாகப் பெறு",
+ "Inherit": "மரபுரிமையாகப் பெறு",
"HeaderRecordingGroups": "பதிவு குழுக்கள்",
"HeaderCameraUploads": "புகைப்பட பதிவேற்றங்கள்",
"Folders": "கோப்புறைகள்",
@@ -31,7 +31,7 @@
"TaskDownloadMissingSubtitles": "விடுபட்டுபோன வசன வரிகளைப் பதிவிறக்கு",
"TaskRefreshChannels": "சேனல்களை புதுப்பி",
"TaskUpdatePlugins": "உட்செருகிகளை புதுப்பி",
- "TaskRefreshLibrary": "மீடியா நூலகத்தை ஆராய்",
+ "TaskRefreshLibrary": "ஊடக நூலகத்தை ஆராய்",
"TasksChannelsCategory": "இணைய சேனல்கள்",
"TasksApplicationCategory": "செயலி",
"TasksLibraryCategory": "நூலகம்",
@@ -46,7 +46,7 @@
"Sync": "ஒத்திசைவு",
"StartupEmbyServerIsLoading": "ஜெல்லிஃபின் சேவையகம் துவங்குகிறது. சிறிது நேரம் கழித்து முயற்சிக்கவும்.",
"Songs": "பாடல்கள்",
- "Shows": "தொடர்கள்",
+ "Shows": "நிகழ்ச்சிகள்",
"ServerNameNeedsToBeRestarted": "{0} மறுதொடக்கம் செய்யப்பட வேண்டும்",
"ScheduledTaskStartedWithName": "{0} துவங்கியது",
"ScheduledTaskFailedWithName": "{0} தோல்வியடைந்தது",
@@ -67,20 +67,20 @@
"NotificationOptionAudioPlayback": "ஒலி இசைக்கத் துவங்கியுள்ளது",
"NotificationOptionApplicationUpdateInstalled": "செயலி புதுப்பிக்கப்பட்டது",
"NotificationOptionApplicationUpdateAvailable": "செயலியினை புதுப்பிக்கலாம்",
- "NameSeasonUnknown": "பருவம் அறியப்படாதவை",
+ "NameSeasonUnknown": "அறியப்படாத பருவம்",
"NameSeasonNumber": "பருவம் {0}",
"NameInstallFailed": "{0} நிறுவல் தோல்வியடைந்தது",
"MusicVideos": "இசைப்படங்கள்",
"Music": "இசை",
"Movies": "திரைப்படங்கள்",
- "Latest": "புதியன",
+ "Latest": "புதியவை",
"LabelRunningTimeValue": "ஓடும் நேரம்: {0}",
"LabelIpAddressValue": "ஐபி முகவரி: {0}",
"ItemRemovedWithName": "{0} நூலகத்திலிருந்து அகற்றப்பட்டது",
"ItemAddedWithName": "{0} நூலகத்தில் சேர்க்கப்பட்டது",
- "HeaderNextUp": "அடுத்ததாக",
+ "HeaderNextUp": "அடுத்தது",
"HeaderLiveTV": "நேரடித் தொலைக்காட்சி",
- "HeaderFavoriteSongs": "பிடித்த பாட்டுகள்",
+ "HeaderFavoriteSongs": "பிடித்த பாடல்கள்",
"HeaderFavoriteShows": "பிடித்த தொடர்கள்",
"HeaderFavoriteEpisodes": "பிடித்த அத்தியாயங்கள்",
"HeaderFavoriteArtists": "பிடித்த கலைஞர்கள்",
@@ -93,25 +93,25 @@
"Channels": "சேனல்கள்",
"Books": "புத்தகங்கள்",
"AuthenticationSucceededWithUserName": "{0} வெற்றிகரமாக அங்கீகரிக்கப்பட்டது",
- "Artists": "கலைஞர்",
+ "Artists": "கலைஞர்கள்",
"Application": "செயலி",
"Albums": "ஆல்பங்கள்",
"NewVersionIsAvailable": "ஜெல்லிஃபின் சேவையகத்தின் புதிய பதிப்பு பதிவிறக்கத்திற்கு கிடைக்கிறது.",
- "MessageNamedServerConfigurationUpdatedWithValue": "சேவையக உள்ளமைவு பிரிவு {0 புதுப்பிக்கப்பட்டது",
+ "MessageNamedServerConfigurationUpdatedWithValue": "சேவையக உள்ளமைவு பிரிவு {0} புதுப்பிக்கப்பட்டது",
"TaskCleanCacheDescription": "கணினிக்கு இனி தேவைப்படாத தற்காலிக கோப்புகளை நீக்கு.",
"UserOfflineFromDevice": "{0} இலிருந்து {1} துண்டிக்கப்பட்டுள்ளது",
- "SubtitleDownloadFailureFromForItem": "வசன வரிகள் {0 } இலிருந்து {1} க்கு பதிவிறக்கத் தவறிவிட்டன",
- "TaskDownloadMissingSubtitlesDescription": "மெட்டாடேட்டா உள்ளமைவின் அடிப்படையில் வசன வரிகள் காணாமல் போனதற்கு இணையத்தைத் தேடுகிறது.",
+ "SubtitleDownloadFailureFromForItem": "வசன வரிகள் {0} இலிருந்து {1} க்கு பதிவிறக்கத் தவறிவிட்டன",
+ "TaskDownloadMissingSubtitlesDescription": "மீத்தரவு உள்ளமைவின் அடிப்படையில் வசன வரிகள் காணாமல் போனதற்கு இணையத்தைத் தேடுகிறது.",
"TaskCleanTranscodeDescription": "டிரான்ஸ்கோட் கோப்புகளை ஒரு நாளுக்கு மேல் பழையதாக நீக்குகிறது.",
- "TaskUpdatePluginsDescription": "தானாகவே புதுப்பிக்க கட்டமைக்கப்பட்ட செருகுநிரல்களுக்கான புதுப்பிப்புகளை பதிவிறக்குகிறது மற்றும் நிறுவுகிறது.",
- "TaskRefreshPeopleDescription": "உங்கள் மீடியா நூலகத்தில் உள்ள நடிகர்கள் மற்றும் இயக்குனர்களுக்கான மெட்டாடேட்டாவை புதுப்பிக்கும்.",
+ "TaskUpdatePluginsDescription": "தானாகவே புதுப்பிக்க கட்டமைக்கப்பட்ட உட்செருகிகளுக்கான புதுப்பிப்புகளை பதிவிறக்குகிறது மற்றும் நிறுவுகிறது.",
+ "TaskRefreshPeopleDescription": "உங்கள் ஊடக நூலகத்தில் உள்ள நடிகர்கள் மற்றும் இயக்குனர்களுக்கான மீத்தரவை புதுப்பிக்கும்.",
"TaskCleanLogsDescription": "{0} நாட்களுக்கு மேல் இருக்கும் பதிவு கோப்புகளை நீக்கும்.",
- "TaskCleanLogs": "பதிவு அடைவு சுத்தம் செய்யுங்கள்",
- "TaskRefreshLibraryDescription": "புதிய கோப்புகளுக்காக உங்கள் மீடியா நூலகத்தை ஸ்கேன் செய்து மீத்தரவை புதுப்பிக்கும்.",
+ "TaskCleanLogs": "பதிவு அடைவை சுத்தம் செய்யுங்கள்",
+ "TaskRefreshLibraryDescription": "புதிய கோப்புகளுக்காக உங்கள் ஊடக நூலகத்தை ஆராய்ந்து மீத்தரவை புதுப்பிக்கும்.",
"TaskRefreshChapterImagesDescription": "அத்தியாயங்களைக் கொண்ட வீடியோக்களுக்கான சிறு உருவங்களை உருவாக்குகிறது.",
"ValueHasBeenAddedToLibrary": "உங்கள் மீடியா நூலகத்தில் {0} சேர்க்கப்பட்டது",
"UserOnlineFromDevice": "{1} இருந்து {0} ஆன்லைன்",
"HomeVideos": "முகப்பு வீடியோக்கள்",
- "UserStoppedPlayingItemWithValues": "{2} இல் {1} முடித்துவிட்டது",
+ "UserStoppedPlayingItemWithValues": "{0} {2} இல் {1} முடித்துவிட்டது",
"UserStartedPlayingItemWithValues": "{0} {2}இல் {1} ஐ இயக்குகிறது"
}
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 4f54c06dd..f121a3493 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -34,7 +34,7 @@ namespace Emby.Server.Implementations.Updates
/// </summary>
private readonly ILogger<InstallationManager> _logger;
private readonly IApplicationPaths _appPaths;
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly IJsonSerializer _jsonSerializer;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
@@ -63,7 +63,7 @@ namespace Emby.Server.Implementations.Updates
ILogger<InstallationManager> logger,
IApplicationHost appHost,
IApplicationPaths appPaths,
- IHttpClient httpClient,
+ IHttpClientFactory httpClientFactory,
IJsonSerializer jsonSerializer,
IServerConfigurationManager config,
IFileSystem fileSystem,
@@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.Updates
_logger = logger;
_applicationHost = appHost;
_appPaths = appPaths;
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_jsonSerializer = jsonSerializer;
_config = config;
_fileSystem = fileSystem;
@@ -116,26 +116,18 @@ namespace Emby.Server.Implementations.Updates
{
try
{
- using (var response = await _httpClient.SendAsync(
- new HttpRequestOptions
- {
- Url = manifest,
- CancellationToken = cancellationToken,
- CacheMode = CacheMode.Unconditional,
- CacheLength = TimeSpan.FromMinutes(3)
- },
- HttpMethod.Get).ConfigureAwait(false))
- using (Stream stream = response.Content)
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .GetAsync(manifest, cancellationToken).ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+
+ try
{
- try
- {
- return await _jsonSerializer.DeserializeFromStreamAsync<IReadOnlyList<PackageInfo>>(stream).ConfigureAwait(false);
- }
- catch (SerializationException ex)
- {
- _logger.LogError(ex, "Failed to deserialize the plugin manifest retrieved from {Manifest}", manifest);
- return Array.Empty<PackageInfo>();
- }
+ return await _jsonSerializer.DeserializeFromStreamAsync<IReadOnlyList<PackageInfo>>(stream).ConfigureAwait(false);
+ }
+ catch (SerializationException ex)
+ {
+ _logger.LogError(ex, "Failed to deserialize the plugin manifest retrieved from {Manifest}", manifest);
+ return Array.Empty<PackageInfo>();
}
}
catch (UriFormatException ex)
@@ -360,42 +352,34 @@ namespace Emby.Server.Implementations.Updates
// Always override the passed-in target (which is a file) and figure it out again
string targetDir = Path.Combine(_appPaths.PluginsPath, package.Name);
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .GetAsync(package.SourceUrl, cancellationToken).ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+
// CA5351: Do Not Use Broken Cryptographic Algorithms
#pragma warning disable CA5351
- using (var res = await _httpClient.SendAsync(
- new HttpRequestOptions
- {
- Url = package.SourceUrl,
- CancellationToken = cancellationToken,
- // We need it to be buffered for setting the position
- BufferContent = true
- },
- HttpMethod.Get).ConfigureAwait(false))
- using (var stream = res.Content)
- using (var md5 = MD5.Create())
- {
- cancellationToken.ThrowIfCancellationRequested();
+ using var md5 = MD5.Create();
+ cancellationToken.ThrowIfCancellationRequested();
- var hash = Hex.Encode(md5.ComputeHash(stream));
- if (!string.Equals(package.Checksum, hash, StringComparison.OrdinalIgnoreCase))
- {
- _logger.LogError(
- "The checksums didn't match while installing {Package}, expected: {Expected}, got: {Received}",
- package.Name,
- package.Checksum,
- hash);
- throw new InvalidDataException("The checksum of the received data doesn't match.");
- }
-
- if (Directory.Exists(targetDir))
- {
- Directory.Delete(targetDir, true);
- }
+ var hash = Hex.Encode(md5.ComputeHash(stream));
+ if (!string.Equals(package.Checksum, hash, StringComparison.OrdinalIgnoreCase))
+ {
+ _logger.LogError(
+ "The checksums didn't match while installing {Package}, expected: {Expected}, got: {Received}",
+ package.Name,
+ package.Checksum,
+ hash);
+ throw new InvalidDataException("The checksum of the received data doesn't match.");
+ }
- stream.Position = 0;
- _zipClient.ExtractAllFromZip(stream, targetDir, true);
+ if (Directory.Exists(targetDir))
+ {
+ Directory.Delete(targetDir, true);
}
+ stream.Position = 0;
+ _zipClient.ExtractAllFromZip(stream, targetDir, true);
+
#pragma warning restore CA5351
}
diff --git a/Jellyfin.Api/BaseJellyfinApiController.cs b/Jellyfin.Api/BaseJellyfinApiController.cs
index a34f9eb62..1c1fc71d7 100644
--- a/Jellyfin.Api/BaseJellyfinApiController.cs
+++ b/Jellyfin.Api/BaseJellyfinApiController.cs
@@ -1,4 +1,5 @@
using System.Net.Mime;
+using MediaBrowser.Common.Json;
using Microsoft.AspNetCore.Mvc;
namespace Jellyfin.Api
@@ -8,7 +9,10 @@ namespace Jellyfin.Api
/// </summary>
[ApiController]
[Route("[controller]")]
- [Produces(MediaTypeNames.Application.Json)]
+ [Produces(
+ MediaTypeNames.Application.Json,
+ JsonDefaults.CamelCaseMediaType,
+ JsonDefaults.PascalCaseMediaType)]
public class BaseJellyfinApiController : ControllerBase
{
}
diff --git a/Jellyfin.Api/Controllers/DashboardController.cs b/Jellyfin.Api/Controllers/DashboardController.cs
index 7624fd79f..a859ac114 100644
--- a/Jellyfin.Api/Controllers/DashboardController.cs
+++ b/Jellyfin.Api/Controllers/DashboardController.cs
@@ -17,6 +17,7 @@ using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
+using Microsoft.Net.Http.Headers;
namespace Jellyfin.Api.Controllers
{
@@ -28,39 +29,21 @@ namespace Jellyfin.Api.Controllers
{
private readonly ILogger<DashboardController> _logger;
private readonly IServerApplicationHost _appHost;
- private readonly IConfiguration _appConfig;
- private readonly IServerConfigurationManager _serverConfigurationManager;
- private readonly IResourceFileManager _resourceFileManager;
/// <summary>
/// Initializes a new instance of the <see cref="DashboardController"/> class.
/// </summary>
/// <param name="logger">Instance of <see cref="ILogger{DashboardController}"/> interface.</param>
/// <param name="appHost">Instance of <see cref="IServerApplicationHost"/> interface.</param>
- /// <param name="appConfig">Instance of <see cref="IConfiguration"/> interface.</param>
- /// <param name="resourceFileManager">Instance of <see cref="IResourceFileManager"/> interface.</param>
- /// <param name="serverConfigurationManager">Instance of <see cref="IServerConfigurationManager"/> interface.</param>
public DashboardController(
ILogger<DashboardController> logger,
- IServerApplicationHost appHost,
- IConfiguration appConfig,
- IResourceFileManager resourceFileManager,
- IServerConfigurationManager serverConfigurationManager)
+ IServerApplicationHost appHost)
{
_logger = logger;
_appHost = appHost;
- _appConfig = appConfig;
- _resourceFileManager = resourceFileManager;
- _serverConfigurationManager = serverConfigurationManager;
}
/// <summary>
- /// Gets the path of the directory containing the static web interface content, or null if the server is not
- /// hosting the web client.
- /// </summary>
- private string? WebClientUiPath => GetWebClientUiPath(_appConfig, _serverConfigurationManager);
-
- /// <summary>
/// Gets the configuration pages.
/// </summary>
/// <param name="enableInMainMenu">Whether to enable in the main menu.</param>
@@ -172,86 +155,6 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
- /// <summary>
- /// Gets the robots.txt.
- /// </summary>
- /// <response code="200">Robots.txt returned.</response>
- /// <returns>The robots.txt.</returns>
- [HttpGet("robots.txt")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ApiExplorerSettings(IgnoreApi = true)]
- public ActionResult GetRobotsTxt()
- {
- return GetWebClientResource("robots.txt");
- }
-
- /// <summary>
- /// Gets a resource from the web client.
- /// </summary>
- /// <param name="resourceName">The resource name.</param>
- /// <response code="200">Web client returned.</response>
- /// <response code="404">Server does not host a web client.</response>
- /// <returns>The resource.</returns>
- [HttpGet("web/{*resourceName}")]
- [ApiExplorerSettings(IgnoreApi = true)]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult GetWebClientResource([FromRoute] string resourceName)
- {
- if (!_appConfig.HostWebClient() || WebClientUiPath == null)
- {
- return NotFound("Server does not host a web client.");
- }
-
- var path = resourceName;
- var basePath = WebClientUiPath;
-
- var requestPathAndQuery = Request.GetEncodedPathAndQuery();
- // Bounce them to the startup wizard if it hasn't been completed yet
- if (!_serverConfigurationManager.Configuration.IsStartupWizardCompleted
- && !requestPathAndQuery.Contains("wizard", StringComparison.OrdinalIgnoreCase)
- && requestPathAndQuery.Contains("index", StringComparison.OrdinalIgnoreCase))
- {
- return Redirect("index.html?start=wizard#!/wizardstart.html");
- }
-
- return PhysicalFile(_resourceFileManager.GetResourcePath(basePath, path), MimeTypes.GetMimeType(path));
- }
-
- /// <summary>
- /// Gets the favicon.
- /// </summary>
- /// <response code="200">Favicon.ico returned.</response>
- /// <returns>The favicon.</returns>
- [HttpGet("favicon.ico")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ApiExplorerSettings(IgnoreApi = true)]
- public ActionResult GetFavIcon()
- {
- return GetWebClientResource("favicon.ico");
- }
-
- /// <summary>
- /// Gets the path of the directory containing the static web interface content.
- /// </summary>
- /// <param name="appConfig">The app configuration.</param>
- /// <param name="serverConfigManager">The server configuration manager.</param>
- /// <returns>The directory path, or null if the server is not hosting the web client.</returns>
- public static string? GetWebClientUiPath(IConfiguration appConfig, IServerConfigurationManager serverConfigManager)
- {
- if (!appConfig.HostWebClient())
- {
- return null;
- }
-
- if (!string.IsNullOrEmpty(serverConfigManager.Configuration.DashboardSourcePath))
- {
- return serverConfigManager.Configuration.DashboardSourcePath;
- }
-
- return serverConfigManager.ApplicationPaths.WebPath;
- }
-
private IEnumerable<ConfigurationPageInfo> GetConfigPages(IPlugin plugin)
{
return GetPluginPages(plugin).Select(i => new ConfigurationPageInfo(plugin, i.Item1));
diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs
index 07a176edd..7d720a5c5 100644
--- a/Jellyfin.Api/Controllers/LiveTvController.cs
+++ b/Jellyfin.Api/Controllers/LiveTvController.cs
@@ -17,6 +17,7 @@ using Jellyfin.Api.Models.LiveTvDtos;
using Jellyfin.Data.Enums;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
@@ -1071,7 +1072,7 @@ namespace Jellyfin.Api.Controllers
[ProducesFile(MediaTypeNames.Application.Json)]
public async Task<ActionResult> GetSchedulesDirectCountries()
{
- var client = _httpClientFactory.CreateClient();
+ var client = _httpClientFactory.CreateClient(NamedClient.Default);
// https://json.schedulesdirect.org/20141201/available/countries
// Can't dispose the response as it's required up the call chain.
var response = await client.GetAsync("https://json.schedulesdirect.org/20141201/available/countries")
diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs
index e8c5ba7a9..597596422 100644
--- a/Jellyfin.Api/Controllers/RemoteImageController.cs
+++ b/Jellyfin.Api/Controllers/RemoteImageController.cs
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@@ -246,7 +247,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>Task.</returns>
private async Task DownloadImage(string url, Guid urlHash, string pointerCachePath)
{
- var httpClient = _httpClientFactory.CreateClient();
+ var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
using var response = await httpClient.GetAsync(url).ConfigureAwait(false);
var ext = response.Content.Headers.ContentType.MediaType.Split('/').Last();
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs
index c45ac5fdc..2bf5b4856 100644
--- a/Jellyfin.Api/Controllers/VideosController.cs
+++ b/Jellyfin.Api/Controllers/VideosController.cs
@@ -12,6 +12,7 @@ using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.Models.StreamingDtos;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
@@ -475,7 +476,7 @@ namespace Jellyfin.Api.Controllers
{
StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager);
- var httpClient = _httpClientFactory.CreateClient();
+ var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, httpClient, HttpContext).ConfigureAwait(false);
}
diff --git a/Jellyfin.Api/Helpers/AudioHelper.cs b/Jellyfin.Api/Helpers/AudioHelper.cs
index eae2be05b..a3f2d88ce 100644
--- a/Jellyfin.Api/Helpers/AudioHelper.cs
+++ b/Jellyfin.Api/Helpers/AudioHelper.cs
@@ -3,6 +3,7 @@ using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Models.StreamingDtos;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
@@ -138,7 +139,7 @@ namespace Jellyfin.Api.Helpers
{
StreamingHelpers.AddDlnaHeaders(state, _httpContextAccessor.HttpContext.Response.Headers, true, streamingRequest.StartTimeTicks, _httpContextAccessor.HttpContext.Request, _dlnaManager);
- var httpClient = _httpClientFactory.CreateClient();
+ var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, httpClient, _httpContextAccessor.HttpContext).ConfigureAwait(false);
}
diff --git a/Jellyfin.Api/MvcRoutePrefix.cs b/Jellyfin.Api/MvcRoutePrefix.cs
deleted file mode 100644
index e00973094..000000000
--- a/Jellyfin.Api/MvcRoutePrefix.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.ApplicationModels;
-
-namespace Jellyfin.Api
-{
- /// <summary>
- /// Route prefixing for ASP.NET MVC.
- /// </summary>
- public static class MvcRoutePrefix
- {
- /// <summary>
- /// Adds route prefixes to the MVC conventions.
- /// </summary>
- /// <param name="opts">The MVC options.</param>
- /// <param name="prefixes">The list of prefixes.</param>
- public static void UseGeneralRoutePrefix(this MvcOptions opts, params string[] prefixes)
- {
- opts.Conventions.Insert(0, new RoutePrefixConvention(prefixes));
- }
-
- private class RoutePrefixConvention : IApplicationModelConvention
- {
- private readonly AttributeRouteModel[] _routePrefixes;
-
- public RoutePrefixConvention(IEnumerable<string> prefixes)
- {
- _routePrefixes = prefixes.Select(p => new AttributeRouteModel(new RouteAttribute(p))).ToArray();
- }
-
- public void Apply(ApplicationModel application)
- {
- foreach (var controller in application.Controllers)
- {
- if (controller.Selectors == null)
- {
- continue;
- }
-
- var newSelectors = new List<SelectorModel>();
- foreach (var selector in controller.Selectors)
- {
- newSelectors.AddRange(_routePrefixes.Select(routePrefix => new SelectorModel(selector)
- {
- AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(routePrefix, selector.AttributeRouteModel)
- }));
- }
-
- controller.Selectors.Clear();
- newSelectors.ForEach(selector => controller.Selectors.Add(selector));
- }
- }
- }
- }
-}
diff --git a/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs b/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs
index 2b002c6f3..c7fbfa4d0 100644
--- a/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs
@@ -1,6 +1,8 @@
+using System.Collections.Generic;
using Jellyfin.Server.Middleware;
using MediaBrowser.Controller.Configuration;
using Microsoft.AspNetCore.Builder;
+using Microsoft.OpenApi.Models;
namespace Jellyfin.Server.Extensions
{
@@ -23,6 +25,7 @@ namespace Jellyfin.Server.Extensions
// specifying the Swagger JSON endpoint.
var baseUrl = serverConfigurationManager.Configuration.BaseUrl.Trim('/');
+ var apiDocBaseUrl = serverConfigurationManager.Configuration.BaseUrl;
if (!string.IsNullOrEmpty(baseUrl))
{
baseUrl += '/';
@@ -32,21 +35,25 @@ namespace Jellyfin.Server.Extensions
.UseSwagger(c =>
{
// Custom path requires {documentName}, SwaggerDoc documentName is 'api-docs'
- c.RouteTemplate = $"/{baseUrl}{{documentName}}/openapi.json";
+ c.RouteTemplate = "{documentName}/openapi.json";
+ c.PreSerializeFilters.Add((swagger, httpReq) =>
+ {
+ swagger.Servers = new List<OpenApiServer> { new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}{apiDocBaseUrl}" } };
+ });
})
.UseSwaggerUI(c =>
{
c.DocumentTitle = "Jellyfin API";
c.SwaggerEndpoint($"/{baseUrl}api-docs/openapi.json", "Jellyfin API");
- c.RoutePrefix = $"{baseUrl}api-docs/swagger";
c.InjectStylesheet($"/{baseUrl}api-docs/swagger/custom.css");
+ c.RoutePrefix = "api-docs/swagger";
})
.UseReDoc(c =>
{
c.DocumentTitle = "Jellyfin API";
c.SpecUrl($"/{baseUrl}api-docs/openapi.json");
- c.RoutePrefix = $"{baseUrl}api-docs/redoc";
c.InjectStylesheet($"/{baseUrl}api-docs/redoc/custom.css");
+ c.RoutePrefix = "api-docs/redoc";
});
}
diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index 04b611123..31454265a 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
-using Jellyfin.Api;
using Jellyfin.Api.Auth;
using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
using Jellyfin.Api.Auth.DownloadPolicy;
@@ -19,7 +18,6 @@ using Jellyfin.Api.Controllers;
using Jellyfin.Server.Filters;
using Jellyfin.Server.Formatters;
using Jellyfin.Server.Models;
-using MediaBrowser.Common;
using MediaBrowser.Common.Json;
using MediaBrowser.Model.Entities;
using Microsoft.AspNetCore.Authentication;
@@ -136,10 +134,9 @@ namespace Jellyfin.Server.Extensions
/// Extension method for adding the jellyfin API to the service collection.
/// </summary>
/// <param name="serviceCollection">The service collection.</param>
- /// <param name="baseUrl">The base url for the API.</param>
- /// <param name="pluginAssemblies">An IEnumberable containing all plugin assemblies with API controllers.</param>
+ /// <param name="pluginAssemblies">An IEnumerable containing all plugin assemblies with API controllers.</param>
/// <returns>The MVC builder.</returns>
- public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, string baseUrl, IEnumerable<Assembly> pluginAssemblies)
+ public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, IEnumerable<Assembly> pluginAssemblies)
{
IMvcBuilder mvcBuilder = serviceCollection
.AddCors(options =>
@@ -152,7 +149,9 @@ namespace Jellyfin.Server.Extensions
})
.AddMvc(opts =>
{
- opts.UseGeneralRoutePrefix(baseUrl);
+ // Allow requester to change between camelCase and PascalCase
+ opts.RespectBrowserAcceptHeader = true;
+
opts.OutputFormatters.Insert(0, new CamelCaseJsonProfileFormatter());
opts.OutputFormatters.Insert(0, new PascalCaseJsonProfileFormatter());
diff --git a/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs b/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs
index 9b347ae2c..8043989b1 100644
--- a/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs
+++ b/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs
@@ -15,7 +15,7 @@ namespace Jellyfin.Server.Formatters
public CamelCaseJsonProfileFormatter() : base(JsonDefaults.GetCamelCaseOptions())
{
SupportedMediaTypes.Clear();
- SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json;profile=\"CamelCase\""));
+ SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(JsonDefaults.CamelCaseMediaType));
}
}
}
diff --git a/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs b/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs
index 0024708ba..d0110b125 100644
--- a/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs
+++ b/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs
@@ -1,3 +1,4 @@
+using System.Net.Mime;
using MediaBrowser.Common.Json;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Net.Http.Headers;
@@ -16,8 +17,8 @@ namespace Jellyfin.Server.Formatters
{
SupportedMediaTypes.Clear();
// Add application/json for default formatter
- SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
- SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json;profile=\"PascalCase\""));
+ SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json));
+ SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(JsonDefaults.PascalCaseMediaType));
}
}
}
diff --git a/Jellyfin.Server/Formatters/XmlOutputFormatter.cs b/Jellyfin.Server/Formatters/XmlOutputFormatter.cs
index 58319657d..01d99d7c8 100644
--- a/Jellyfin.Server/Formatters/XmlOutputFormatter.cs
+++ b/Jellyfin.Server/Formatters/XmlOutputFormatter.cs
@@ -16,8 +16,9 @@ namespace Jellyfin.Server.Formatters
/// </summary>
public XmlOutputFormatter()
{
+ SupportedMediaTypes.Clear();
SupportedMediaTypes.Add(MediaTypeNames.Text.Xml);
- SupportedMediaTypes.Add("text/xml;charset=UTF-8");
+
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
diff --git a/Jellyfin.Server/HealthChecks/JellyfinDbHealthCheck.cs b/Jellyfin.Server/HealthChecks/JellyfinDbHealthCheck.cs
deleted file mode 100644
index aea684479..000000000
--- a/Jellyfin.Server/HealthChecks/JellyfinDbHealthCheck.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Threading;
-using System.Threading.Tasks;
-using Jellyfin.Server.Implementations;
-using Microsoft.Extensions.Diagnostics.HealthChecks;
-
-namespace Jellyfin.Server.HealthChecks
-{
- /// <summary>
- /// Checks connectivity to the database.
- /// </summary>
- public class JellyfinDbHealthCheck : IHealthCheck
- {
- private readonly JellyfinDbProvider _dbProvider;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="JellyfinDbHealthCheck"/> class.
- /// </summary>
- /// <param name="dbProvider">The jellyfin db provider.</param>
- public JellyfinDbHealthCheck(JellyfinDbProvider dbProvider)
- {
- _dbProvider = dbProvider;
- }
-
- /// <inheritdoc />
- public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
- {
- await using var jellyfinDb = _dbProvider.CreateContext();
- if (await jellyfinDb.Database.CanConnectAsync(cancellationToken).ConfigureAwait(false))
- {
- return HealthCheckResult.Healthy("Database connection successful.");
- }
-
- return HealthCheckResult.Unhealthy("Unable to connect to the database.");
- }
- }
-}
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 24ba8369a..85d5f2a3f 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -44,6 +44,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.7" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.1.7" />
+ <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="3.1.7" />
<PackageReference Include="prometheus-net" Version="3.6.0" />
<PackageReference Include="prometheus-net.AspNetCore" Version="3.6.0" />
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
diff --git a/Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs b/Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs
index 9316737bd..ae3a3a1c5 100644
--- a/Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs
+++ b/Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs
@@ -44,11 +44,7 @@ namespace Jellyfin.Server.Middleware
var localPath = httpContext.Request.Path.ToString();
var baseUrlPrefix = serverConfigurationManager.Configuration.BaseUrl;
- if (string.Equals(localPath, baseUrlPrefix + "/", StringComparison.OrdinalIgnoreCase)
- || string.Equals(localPath, baseUrlPrefix, StringComparison.OrdinalIgnoreCase)
- || string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)
- || string.IsNullOrEmpty(localPath)
- || !localPath.StartsWith(baseUrlPrefix, StringComparison.OrdinalIgnoreCase))
+ if (!localPath.StartsWith(baseUrlPrefix, StringComparison.OrdinalIgnoreCase))
{
// Always redirect back to the default path if the base prefix is invalid or missing
_logger.LogDebug("Normalizing an URL at {LocalPath}", localPath);
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index b9a90f9db..45959aec2 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -169,7 +169,7 @@ namespace Jellyfin.Server
// If hosting the web client, validate the client content path
if (startupConfig.HostWebClient())
{
- string? webContentPath = DashboardController.GetWebClientUiPath(startupConfig, appHost.ServerConfigurationManager);
+ string? webContentPath = appHost.ServerConfigurationManager.ApplicationPaths.WebPath;
if (!Directory.Exists(webContentPath) || Directory.GetFiles(webContentPath).Length == 0)
{
throw new InvalidOperationException(
diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs
index 1a34de269..597323b86 100644
--- a/Jellyfin.Server/Startup.cs
+++ b/Jellyfin.Server/Startup.cs
@@ -3,15 +3,18 @@ using System.ComponentModel;
using System.Net.Http.Headers;
using Jellyfin.Api.TypeConverters;
using Jellyfin.Server.Extensions;
-using Jellyfin.Server.HealthChecks;
+using Jellyfin.Server.Implementations;
using Jellyfin.Server.Middleware;
using Jellyfin.Server.Models;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Extensions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Prometheus;
@@ -50,9 +53,7 @@ namespace Jellyfin.Server
{
options.HttpsPort = _serverApplicationHost.HttpsPort;
});
- services.AddJellyfinApi(
- _serverConfigurationManager.Configuration.BaseUrl.TrimStart('/'),
- _serverApplicationHost.GetApiPluginAssemblies());
+ services.AddJellyfinApi(_serverApplicationHost.GetApiPluginAssemblies());
services.AddJellyfinApiSwagger();
@@ -79,7 +80,7 @@ namespace Jellyfin.Server
.ConfigurePrimaryHttpMessageHandler(x => new DefaultHttpClientHandler());
services.AddHealthChecks()
- .AddCheck<JellyfinDbHealthCheck>("JellyfinDb");
+ .AddDbContextCheck<JellyfinDb>();
}
/// <summary>
@@ -87,57 +88,78 @@ namespace Jellyfin.Server
/// </summary>
/// <param name="app">The application builder.</param>
/// <param name="env">The webhost environment.</param>
+ /// <param name="appConfig">The application config.</param>
public void Configure(
IApplicationBuilder app,
- IWebHostEnvironment env)
+ IWebHostEnvironment env,
+ IConfiguration appConfig)
{
- if (env.IsDevelopment())
+ // Only add base url redirection if a base url is set.
+ if (!string.IsNullOrEmpty(_serverConfigurationManager.Configuration.BaseUrl))
{
- app.UseDeveloperExceptionPage();
+ app.UseBaseUrlRedirection();
}
- app.UseMiddleware<ExceptionMiddleware>();
+ // Wrap rest of configuration so everything only listens on BaseUrl.
+ app.Map(_serverConfigurationManager.Configuration.BaseUrl, mainApp =>
+ {
+ if (env.IsDevelopment())
+ {
+ mainApp.UseDeveloperExceptionPage();
+ }
- app.UseMiddleware<ResponseTimeMiddleware>();
+ mainApp.UseMiddleware<ExceptionMiddleware>();
- app.UseWebSockets();
+ mainApp.UseMiddleware<ResponseTimeMiddleware>();
- app.UseResponseCompression();
+ mainApp.UseWebSockets();
- app.UseCors(ServerCorsPolicy.DefaultPolicyName);
+ mainApp.UseResponseCompression();
- if (_serverConfigurationManager.Configuration.RequireHttps
- && _serverApplicationHost.ListenWithHttps)
- {
- app.UseHttpsRedirection();
- }
+ mainApp.UseCors(ServerCorsPolicy.DefaultPolicyName);
- app.UseStaticFiles();
- app.UseAuthentication();
- app.UseJellyfinApiSwagger(_serverConfigurationManager);
- app.UseRouting();
- app.UseAuthorization();
- if (_serverConfigurationManager.Configuration.EnableMetrics)
- {
- // Must be registered after any middleware that could chagne HTTP response codes or the data will be bad
- app.UseHttpMetrics();
- }
+ if (_serverConfigurationManager.Configuration.RequireHttps
+ && _serverApplicationHost.ListenWithHttps)
+ {
+ mainApp.UseHttpsRedirection();
+ }
- app.UseLanFiltering();
- app.UseIpBasedAccessValidation();
- app.UseBaseUrlRedirection();
- app.UseWebSocketHandler();
- app.UseServerStartupMessage();
+ mainApp.UseStaticFiles();
+ if (appConfig.HostWebClient())
+ {
+ mainApp.UseStaticFiles(new StaticFileOptions
+ {
+ FileProvider = new PhysicalFileProvider(_serverConfigurationManager.ApplicationPaths.WebPath),
+ RequestPath = "/web"
+ });
+ }
+
+ mainApp.UseAuthentication();
+ mainApp.UseJellyfinApiSwagger(_serverConfigurationManager);
+ mainApp.UseRouting();
+ mainApp.UseAuthorization();
+
+ mainApp.UseLanFiltering();
+ mainApp.UseIpBasedAccessValidation();
+ mainApp.UseWebSocketHandler();
+ mainApp.UseServerStartupMessage();
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllers();
if (_serverConfigurationManager.Configuration.EnableMetrics)
{
- endpoints.MapMetrics(_serverConfigurationManager.Configuration.BaseUrl.TrimStart('/') + "/metrics");
+ // Must be registered after any middleware that could change HTTP response codes or the data will be bad
+ mainApp.UseHttpMetrics();
}
- endpoints.MapHealthChecks(_serverConfigurationManager.Configuration.BaseUrl.TrimStart('/') + "/health");
+ mainApp.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllers();
+ if (_serverConfigurationManager.Configuration.EnableMetrics)
+ {
+ endpoints.MapMetrics("/metrics");
+ }
+
+ endpoints.MapHealthChecks("/health");
+ });
});
// Add type descriptor for legacy datetime parsing.
diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
index 4cea61682..57c654667 100644
--- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs
+++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
@@ -1,5 +1,3 @@
-using MediaBrowser.Model.Configuration;
-
namespace MediaBrowser.Common.Configuration
{
/// <summary>
@@ -17,8 +15,7 @@ namespace MediaBrowser.Common.Configuration
/// Gets the path to the web UI resources folder.
/// </summary>
/// <remarks>
- /// This value is not relevant if the server is configured to not host any static web content. Additionally,
- /// the value for <see cref="ServerConfiguration.DashboardSourcePath"/> takes precedence over this one.
+ /// This value is not relevant if the server is configured to not host any static web content.
/// </remarks>
string WebPath { get; }
diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs
index 5867cd4a0..67f7e8f14 100644
--- a/MediaBrowser.Common/Json/JsonDefaults.cs
+++ b/MediaBrowser.Common/Json/JsonDefaults.cs
@@ -10,6 +10,16 @@ namespace MediaBrowser.Common.Json
public static class JsonDefaults
{
/// <summary>
+ /// Pascal case json profile media type.
+ /// </summary>
+ public const string PascalCaseMediaType = "application/json; profile=\"PascalCase\"";
+
+ /// <summary>
+ /// Camel case json profile media type.
+ /// </summary>
+ public const string CamelCaseMediaType = "application/json; profile=\"CamelCase\"";
+
+ /// <summary>
/// Gets the default <see cref="JsonSerializerOptions" /> options.
/// </summary>
/// <remarks>
diff --git a/MediaBrowser.Common/Net/CacheMode.cs b/MediaBrowser.Common/Net/CacheMode.cs
deleted file mode 100644
index 78fa3bf9b..000000000
--- a/MediaBrowser.Common/Net/CacheMode.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1602
-
-namespace MediaBrowser.Common.Net
-{
- public enum CacheMode
- {
- None = 0,
- Unconditional = 1
- }
-}
diff --git a/MediaBrowser.Common/Net/CompressionMethods.cs b/MediaBrowser.Common/Net/CompressionMethods.cs
deleted file mode 100644
index 39b72609f..000000000
--- a/MediaBrowser.Common/Net/CompressionMethods.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1602
-
-using System;
-
-namespace MediaBrowser.Common.Net
-{
- [Flags]
- public enum CompressionMethods
- {
- None = 0b00000001,
- Deflate = 0b00000010,
- Gzip = 0b00000100
- }
-}
diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs
deleted file mode 100644
index 347fc9833..000000000
--- a/MediaBrowser.Common/Net/HttpRequestOptions.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using Microsoft.Net.Http.Headers;
-
-namespace MediaBrowser.Common.Net
-{
- /// <summary>
- /// Class HttpRequestOptions.
- /// </summary>
- public class HttpRequestOptions
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="HttpRequestOptions"/> class.
- /// </summary>
- public HttpRequestOptions()
- {
- RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- CacheMode = CacheMode.None;
- DecompressionMethod = CompressionMethods.Deflate;
- }
-
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
-
- public CompressionMethods DecompressionMethod { get; set; }
-
- /// <summary>
- /// Gets or sets the accept header.
- /// </summary>
- /// <value>The accept header.</value>
- public string AcceptHeader
- {
- get => GetHeaderValue(HeaderNames.Accept);
- set => RequestHeaders[HeaderNames.Accept] = value;
- }
-
- /// <summary>
- /// Gets or sets the cancellation token.
- /// </summary>
- /// <value>The cancellation token.</value>
- public CancellationToken CancellationToken { get; set; }
-
- /// <summary>
- /// Gets or sets the user agent.
- /// </summary>
- /// <value>The user agent.</value>
- public string UserAgent
- {
- get => GetHeaderValue(HeaderNames.UserAgent);
- set => RequestHeaders[HeaderNames.UserAgent] = value;
- }
-
- /// <summary>
- /// Gets or sets the referrer.
- /// </summary>
- /// <value>The referrer.</value>
- public string Referer
- {
- get => GetHeaderValue(HeaderNames.Referer);
- set => RequestHeaders[HeaderNames.Referer] = value;
- }
-
- /// <summary>
- /// Gets or sets the host.
- /// </summary>
- /// <value>The host.</value>
- public string Host
- {
- get => GetHeaderValue(HeaderNames.Host);
- set => RequestHeaders[HeaderNames.Host] = value;
- }
-
- public Dictionary<string, string> RequestHeaders { get; private set; }
-
- public string RequestContentType { get; set; }
-
- public string RequestContent { get; set; }
-
- public bool BufferContent { get; set; }
-
- public bool LogErrorResponseBody { get; set; }
-
- public bool EnableKeepAlive { get; set; }
-
- public CacheMode CacheMode { get; set; }
-
- public TimeSpan CacheLength { get; set; }
-
- public bool EnableDefaultUserAgent { get; set; }
-
- private string GetHeaderValue(string name)
- {
- RequestHeaders.TryGetValue(name, out var value);
-
- return value;
- }
- }
-}
diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs
deleted file mode 100644
index d4fee6c78..000000000
--- a/MediaBrowser.Common/Net/HttpResponseInfo.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using System;
-using System.IO;
-using System.Net;
-using System.Net.Http.Headers;
-
-namespace MediaBrowser.Common.Net
-{
- /// <summary>
- /// Class HttpResponseInfo.
- /// </summary>
- public sealed class HttpResponseInfo : IDisposable
- {
-#pragma warning disable CS1591
- public HttpResponseInfo()
- {
- }
-
- public HttpResponseInfo(HttpResponseHeaders headers, HttpContentHeaders contentHeader)
- {
- Headers = headers;
- ContentHeaders = contentHeader;
- }
-
-#pragma warning restore CS1591
-
- /// <summary>
- /// Gets or sets the type of the content.
- /// </summary>
- /// <value>The type of the content.</value>
- public string ContentType { get; set; }
-
- /// <summary>
- /// Gets or sets the response URL.
- /// </summary>
- /// <value>The response URL.</value>
- public string ResponseUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the content.
- /// </summary>
- /// <value>The content.</value>
- public Stream Content { get; set; }
-
- /// <summary>
- /// Gets or sets the status code.
- /// </summary>
- /// <value>The status code.</value>
- public HttpStatusCode StatusCode { get; set; }
-
- /// <summary>
- /// Gets or sets the temp file path.
- /// </summary>
- /// <value>The temp file path.</value>
- public string TempFilePath { get; set; }
-
- /// <summary>
- /// Gets or sets the length of the content.
- /// </summary>
- /// <value>The length of the content.</value>
- public long? ContentLength { get; set; }
-
- /// <summary>
- /// Gets or sets the headers.
- /// </summary>
- /// <value>The headers.</value>
- public HttpResponseHeaders Headers { get; set; }
-
- /// <summary>
- /// Gets or sets the content headers.
- /// </summary>
- /// <value>The content headers.</value>
- public HttpContentHeaders ContentHeaders { get; set; }
-
- /// <inheritdoc />
- public void Dispose()
- {
- // backwards compatibility
- }
- }
-}
diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs
deleted file mode 100644
index 534e22edd..000000000
--- a/MediaBrowser.Common/Net/IHttpClient.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System;
-using System.IO;
-using System.Net.Http;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common.Net
-{
- /// <summary>
- /// Interface IHttpClient.
- /// </summary>
- public interface IHttpClient
- {
- /// <summary>
- /// Gets the response.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> GetResponse(HttpRequestOptions options);
-
- /// <summary>
- /// Gets the specified options.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{Stream}.</returns>
- Task<Stream> Get(HttpRequestOptions options);
-
- /// <summary>
- /// Warning: Deprecated function,
- /// use 'Task{HttpResponseInfo} SendAsync(HttpRequestOptions options, HttpMethod httpMethod);' instead
- /// Sends the asynchronous.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="httpMethod">The HTTP method.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- [Obsolete("Use 'Task{HttpResponseInfo} SendAsync(HttpRequestOptions options, HttpMethod httpMethod);' instead")]
- Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod);
-
- /// <summary>
- /// Sends the asynchronous.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="httpMethod">The HTTP method.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, HttpMethod httpMethod);
-
- /// <summary>
- /// Posts the specified options.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> Post(HttpRequestOptions options);
- }
-}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index a5c22e50f..68126bd8a 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -60,8 +60,6 @@ namespace MediaBrowser.Controller.Entities
protected BaseItem()
{
- ThemeSongIds = Array.Empty<Guid>();
- ThemeVideoIds = Array.Empty<Guid>();
Tags = Array.Empty<string>();
Genres = Array.Empty<string>();
Studios = Array.Empty<string>();
@@ -100,12 +98,52 @@ namespace MediaBrowser.Controller.Entities
};
[JsonIgnore]
- public Guid[] ThemeSongIds { get; set; }
+ public Guid[] ThemeSongIds
+ {
+ get
+ {
+ if (_themeSongIds == null)
+ {
+ _themeSongIds = GetExtras()
+ .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeSong)
+ .Select(song => song.Id)
+ .ToArray();
+ }
+
+ return _themeSongIds;
+ }
+
+ private set
+ {
+ _themeSongIds = value;
+ }
+ }
+
[JsonIgnore]
- public Guid[] ThemeVideoIds { get; set; }
+ public Guid[] ThemeVideoIds
+ {
+ get
+ {
+ if (_themeVideoIds == null)
+ {
+ _themeVideoIds = GetExtras()
+ .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeVideo)
+ .Select(song => song.Id)
+ .ToArray();
+ }
+
+ return _themeVideoIds;
+ }
+
+ private set
+ {
+ _themeVideoIds = value;
+ }
+ }
[JsonIgnore]
public string PreferredMetadataCountryCode { get; set; }
+
[JsonIgnore]
public string PreferredMetadataLanguage { get; set; }
@@ -635,6 +673,9 @@ namespace MediaBrowser.Controller.Entities
}
private string _sortName;
+ private Guid[] _themeSongIds;
+ private Guid[] _themeVideoIds;
+
/// <summary>
/// Gets the name of the sort.
/// </summary>
@@ -1582,7 +1623,8 @@ namespace MediaBrowser.Controller.Entities
await Task.WhenAll(tasks).ConfigureAwait(false);
- item.ThemeVideoIds = newThemeVideoIds;
+ // They are expected to be sorted by SortName
+ item.ThemeVideoIds = newThemeVideos.OrderBy(i => i.SortName).Select(i => i.Id).ToArray();
return themeVideosChanged;
}
@@ -1619,7 +1661,8 @@ namespace MediaBrowser.Controller.Entities
await Task.WhenAll(tasks).ConfigureAwait(false);
- item.ThemeSongIds = newThemeSongIds;
+ // They are expected to be sorted by SortName
+ item.ThemeSongIds = newThemeSongs.OrderBy(i => i.SortName).Select(i => i.Id).ToArray();
return themeSongsChanged;
}
@@ -2910,12 +2953,12 @@ namespace MediaBrowser.Controller.Entities
public IEnumerable<BaseItem> GetThemeSongs()
{
- return ThemeVideoIds.Select(LibraryManager.GetItemById).Where(i => i.ExtraType.Equals(Model.Entities.ExtraType.ThemeSong)).OrderBy(i => i.SortName);
+ return ThemeSongIds.Select(LibraryManager.GetItemById);
}
public IEnumerable<BaseItem> GetThemeVideos()
{
- return ThemeVideoIds.Select(LibraryManager.GetItemById).Where(i => i.ExtraType.Equals(Model.Entities.ExtraType.ThemeVideo)).OrderBy(i => i.SortName);
+ return ThemeVideoIds.Select(LibraryManager.GetItemById);
}
/// <summary>
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 9f6b2be8f..2c30ca458 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -456,6 +456,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isQsvDecoder = videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
+ var isNvencHevcDecoder = videoDecoder.IndexOf("hevc_cuvid", StringComparison.OrdinalIgnoreCase) != -1;
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
var isMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
@@ -516,6 +517,24 @@ namespace MediaBrowser.Controller.MediaEncoding
}
if (state.IsVideoRequest
+ && string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
+ {
+ var isColorDepth10 = IsColorDepth10(state);
+
+ if (isNvencHevcDecoder && isColorDepth10
+ && _mediaEncoder.SupportsHwaccel("opencl")
+ && encodingOptions.EnableTonemapping
+ && !string.IsNullOrEmpty(state.VideoStream.VideoRange)
+ && state.VideoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase))
+ {
+ arg.Append("-init_hw_device opencl=ocl:")
+ .Append(encodingOptions.OpenclDevice)
+ .Append(' ')
+ .Append("-filter_hw_device ocl ");
+ }
+ }
+
+ if (state.IsVideoRequest
&& string.Equals(encodingOptions.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
{
arg.Append("-hwaccel videotoolbox ");
@@ -1003,11 +1022,33 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
+ && !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
{
param = "-pix_fmt yuv420p " + param;
}
+ if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
+ {
+ var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty;
+ var videoStream = state.VideoStream;
+ var isColorDepth10 = IsColorDepth10(state);
+
+ if (videoDecoder.IndexOf("hevc_cuvid", StringComparison.OrdinalIgnoreCase) != -1
+ && isColorDepth10
+ && _mediaEncoder.SupportsHwaccel("opencl")
+ && encodingOptions.EnableTonemapping
+ && !string.IsNullOrEmpty(videoStream.VideoRange)
+ && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase))
+ {
+ param = "-pix_fmt nv12 " + param;
+ }
+ else
+ {
+ param = "-pix_fmt yuv420p " + param;
+ }
+ }
+
if (string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
{
param = "-pix_fmt nv21 " + param;
@@ -1611,64 +1652,45 @@ namespace MediaBrowser.Controller.MediaEncoding
var outputSizeParam = ReadOnlySpan<char>.Empty;
var request = state.BaseRequest;
- // Add resolution params, if specified
- if (request.Width.HasValue
- || request.Height.HasValue
- || request.MaxHeight.HasValue
- || request.MaxWidth.HasValue)
+ outputSizeParam = GetOutputSizeParam(state, options, outputVideoCodec).TrimEnd('"');
+
+ // All possible beginning of video filters
+ // Don't break the order
+ string[] beginOfOutputSizeParam = new[]
{
- outputSizeParam = GetOutputSizeParam(state, options, outputVideoCodec).TrimEnd('"');
+ // for tonemap_opencl
+ "hwupload,tonemap_opencl",
// hwupload=extra_hw_frames=64,vpp_qsv (for overlay_qsv on linux)
- var index = outputSizeParam.IndexOf("hwupload=extra_hw_frames", StringComparison.OrdinalIgnoreCase);
+ "hwupload=extra_hw_frames",
+
+ // vpp_qsv
+ "vpp",
+
+ // hwdownload,format=p010le (hardware decode + software encode for vaapi)
+ "hwdownload",
+
+ // format=nv12|vaapi,hwupload,scale_vaapi
+ "format",
+
+ // bwdif,scale=expr
+ "bwdif",
+
+ // yadif,scale=expr
+ "yadif",
+
+ // scale=expr
+ "scale"
+ };
+
+ var index = -1;
+ foreach (var param in beginOfOutputSizeParam)
+ {
+ index = outputSizeParam.IndexOf(param, StringComparison.OrdinalIgnoreCase);
if (index != -1)
{
outputSizeParam = outputSizeParam.Slice(index);
- }
- else
- {
- // vpp_qsv
- index = outputSizeParam.IndexOf("vpp", StringComparison.OrdinalIgnoreCase);
- if (index != -1)
- {
- outputSizeParam = outputSizeParam.Slice(index);
- }
- else
- {
- // hwdownload,format=p010le (hardware decode + software encode for vaapi)
- index = outputSizeParam.IndexOf("hwdownload", StringComparison.OrdinalIgnoreCase);
- if (index != -1)
- {
- outputSizeParam = outputSizeParam.Slice(index);
- }
- else
- {
- // format=nv12|vaapi,hwupload,scale_vaapi
- index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase);
- if (index != -1)
- {
- outputSizeParam = outputSizeParam.Slice(index);
- }
- else
- {
- // yadif,scale=expr
- index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase);
- if (index != -1)
- {
- outputSizeParam = outputSizeParam.Slice(index);
- }
- else
- {
- // scale=expr
- index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase);
- if (index != -1)
- {
- outputSizeParam = outputSizeParam.Slice(index);
- }
- }
- }
- }
- }
+ break;
}
}
@@ -1747,9 +1769,9 @@ namespace MediaBrowser.Controller.MediaEncoding
*/
if (isLinux)
{
- retStr = !outputSizeParam.IsEmpty ?
- " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\"" :
- " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\"";
+ retStr = !outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\"";
}
}
@@ -2084,8 +2106,10 @@ namespace MediaBrowser.Controller.MediaEncoding
var isVaapiH264Encoder = outputVideoCodec.IndexOf("h264_vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isQsvH264Encoder = outputVideoCodec.IndexOf("h264_qsv", StringComparison.OrdinalIgnoreCase) != -1;
var isNvdecH264Decoder = videoDecoder.IndexOf("h264_cuvid", StringComparison.OrdinalIgnoreCase) != -1;
+ var isNvdecHevcDecoder = videoDecoder.IndexOf("hevc_cuvid", StringComparison.OrdinalIgnoreCase) != -1;
var isLibX264Encoder = outputVideoCodec.IndexOf("libx264", StringComparison.OrdinalIgnoreCase) != -1;
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+ var isColorDepth10 = IsColorDepth10(state);
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
@@ -2093,6 +2117,50 @@ namespace MediaBrowser.Controller.MediaEncoding
// If double rate deinterlacing is enabled and the input framerate is 30fps or below, otherwise the output framerate will be too high for many devices
var doubleRateDeinterlace = options.DeinterlaceDoubleRate && (videoStream?.RealFrameRate ?? 60) <= 30;
+ // Currently only with the use of NVENC decoder can we get a decent performance.
+ // Currently only the HEVC/H265 format is supported.
+ // NVIDIA Pascal and Turing or higher are recommended.
+ if (isNvdecHevcDecoder && isColorDepth10
+ && _mediaEncoder.SupportsHwaccel("opencl")
+ && options.EnableTonemapping
+ && !string.IsNullOrEmpty(videoStream.VideoRange)
+ && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase))
+ {
+ var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}";
+
+ if (options.TonemappingParam != 0)
+ {
+ parameters += ":param={4}";
+ }
+
+ if (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase))
+ {
+ parameters += ":range={5}";
+ }
+
+ // Upload the HDR10 or HLG data to the OpenCL device,
+ // use tonemap_opencl filter for tone mapping,
+ // and then download the SDR data to memory.
+ filters.Add("hwupload");
+ filters.Add(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ parameters,
+ options.TonemappingAlgorithm,
+ options.TonemappingDesat,
+ options.TonemappingThreshold,
+ options.TonemappingPeak,
+ options.TonemappingParam,
+ options.TonemappingRange));
+ filters.Add("hwdownload");
+
+ if (hasGraphicalSubs || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)
+ || string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
+ {
+ filters.Add("format=nv12");
+ }
+ }
+
// When the input may or may not be hardware VAAPI decodable
if (isVaapiH264Encoder)
{
@@ -2110,7 +2178,6 @@ namespace MediaBrowser.Controller.MediaEncoding
else if (IsVaapiSupported(state) && isVaapiDecoder && isLibX264Encoder)
{
var codec = videoStream.Codec.ToLowerInvariant();
- var isColorDepth10 = IsColorDepth10(state);
// Assert 10-bit hardware VAAPI decodable
if (isColorDepth10 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index 814edd732..6ead93e09 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -24,6 +24,7 @@
<ItemGroup>
<PackageReference Include="BDInfo" Version="0.7.6.1" />
+ <PackageReference Include="Microsoft.Extensions.Http" Version="3.1.6" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.7.1" />
<PackageReference Include="UTF.Unknown" Version="2.3.0" />
</ItemGroup>
diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
index b7b23deff..8a7c032c5 100644
--- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
+++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
@@ -283,6 +283,20 @@ namespace MediaBrowser.MediaEncoding.Probing
public IReadOnlyDictionary<string, int> Disposition { get; set; }
/// <summary>
+ /// Gets or sets the color range.
+ /// </summary>
+ /// <value>The color range.</value>
+ [JsonPropertyName("color_range")]
+ public string ColorRange { get; set; }
+
+ /// <summary>
+ /// Gets or sets the color space.
+ /// </summary>
+ /// <value>The color space.</value>
+ [JsonPropertyName("color_space")]
+ public string ColorSpace { get; set; }
+
+ /// <summary>
/// Gets or sets the color transfer.
/// </summary>
/// <value>The color transfer.</value>
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 40a3b43e1..22537a4d9 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -714,6 +714,16 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.RefFrames = streamInfo.Refs;
}
+ if (!string.IsNullOrEmpty(streamInfo.ColorRange))
+ {
+ stream.ColorRange = streamInfo.ColorRange;
+ }
+
+ if (!string.IsNullOrEmpty(streamInfo.ColorSpace))
+ {
+ stream.ColorSpace = streamInfo.ColorSpace;
+ }
+
if (!string.IsNullOrEmpty(streamInfo.ColorTransfer))
{
stream.ColorTransfer = streamInfo.ColorTransfer;
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 6ac5ac2ff..0a9958b9e 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -6,6 +6,7 @@ using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -31,7 +32,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly IMediaEncoder _mediaEncoder;
- private readonly IHttpClient _httpClient;
+ private readonly IHttpClientFactory _httpClientFactory;
private readonly IMediaSourceManager _mediaSourceManager;
/// <summary>
@@ -46,7 +47,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
IApplicationPaths appPaths,
IFileSystem fileSystem,
IMediaEncoder mediaEncoder,
- IHttpClient httpClient,
+ IHttpClientFactory httpClientFactory,
IMediaSourceManager mediaSourceManager)
{
_libraryManager = libraryManager;
@@ -54,7 +55,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
_appPaths = appPaths;
_fileSystem = fileSystem;
_mediaEncoder = mediaEncoder;
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_mediaSourceManager = mediaSourceManager;
}
@@ -750,24 +751,20 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
}
- private Task<Stream> GetStream(string path, MediaProtocol protocol, CancellationToken cancellationToken)
+ private async Task<Stream> GetStream(string path, MediaProtocol protocol, CancellationToken cancellationToken)
{
switch (protocol)
{
case MediaProtocol.Http:
- var opts = new HttpRequestOptions()
- {
- Url = path,
- CancellationToken = cancellationToken,
-
- // Needed for seeking
- BufferContent = true
- };
-
- return _httpClient.Get(opts);
+ {
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .GetAsync(path, cancellationToken)
+ .ConfigureAwait(false);
+ return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ }
case MediaProtocol.File:
- return Task.FromResult<Stream>(File.OpenRead(path));
+ return File.OpenRead(path);
default:
throw new ArgumentOutOfRangeException(nameof(protocol));
}
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index 41ff517a0..2cd637c5b 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -31,6 +31,22 @@ namespace MediaBrowser.Model.Configuration
public string VaapiDevice { get; set; }
+ public string OpenclDevice { get; set; }
+
+ public bool EnableTonemapping { get; set; }
+
+ public string TonemappingAlgorithm { get; set; }
+
+ public string TonemappingRange { get; set; }
+
+ public double TonemappingDesat { get; set; }
+
+ public double TonemappingThreshold { get; set; }
+
+ public double TonemappingPeak { get; set; }
+
+ public double TonemappingParam { get; set; }
+
public int H264Crf { get; set; }
public int H265Crf { get; set; }
@@ -58,8 +74,19 @@ namespace MediaBrowser.Model.Configuration
EnableThrottling = false;
ThrottleDelaySeconds = 180;
EncodingThreadCount = -1;
- // This is a DRM device that is almost guaranteed to be there on every intel platform, plus it's the default one in ffmpeg if you don't specify anything
+ // This is a DRM device that is almost guaranteed to be there on every intel platform,
+ // plus it's the default one in ffmpeg if you don't specify anything
VaapiDevice = "/dev/dri/renderD128";
+ // This is the OpenCL device that is used for tonemapping.
+ // The left side of the dot is the platform number, and the right side is the device number on the platform.
+ OpenclDevice = "0.0";
+ EnableTonemapping = false;
+ TonemappingAlgorithm = "reinhard";
+ TonemappingRange = "auto";
+ TonemappingDesat = 0;
+ TonemappingThreshold = 0.8;
+ TonemappingPeak = 0;
+ TonemappingParam = 0;
H264Crf = 23;
H265Crf = 28;
DeinterlaceDoubleRate = false;
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 33975bc1e..97748bd0c 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -167,12 +167,6 @@ namespace MediaBrowser.Model.Configuration
public bool EnableDashboardResponseCaching { get; set; }
/// <summary>
- /// Gets or sets a custom path to serve the dashboard from.
- /// </summary>
- /// <value>The dashboard source path, or null if the default path should be used.</value>
- public string DashboardSourcePath { get; set; }
-
- /// <summary>
/// Gets or sets the image saving convention.
/// </summary>
/// <value>The image saving convention.</value>
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index f9ec0d238..2d37618c2 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -36,6 +36,18 @@ namespace MediaBrowser.Model.Entities
public string Language { get; set; }
/// <summary>
+ /// Gets or sets the color range.
+ /// </summary>
+ /// <value>The color range.</value>
+ public string ColorRange { get; set; }
+
+ /// <summary>
+ /// Gets or sets the color space.
+ /// </summary>
+ /// <value>The color space.</value>
+ public string ColorSpace { get; set; }
+
+ /// <summary>
/// Gets or sets the color transfer.
/// </summary>
/// <value>The color transfer.</value>
@@ -48,12 +60,6 @@ namespace MediaBrowser.Model.Entities
public string ColorPrimaries { get; set; }
/// <summary>
- /// Gets or sets the color space.
- /// </summary>
- /// <value>The color space.</value>
- public string ColorSpace { get; set; }
-
- /// <summary>
/// Gets or sets the comment.
/// </summary>
/// <value>The comment.</value>
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
index 771ca84f7..afe7351d3 100644
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ b/MediaBrowser.Model/Net/MimeTypes.cs
@@ -125,7 +125,7 @@ namespace MediaBrowser.Model.Net
{ ".wmv", "video/x-ms-wmv" },
// Type audio
- { ".aac", "audio/mp4" },
+ { ".aac", "audio/aac" },
{ ".ac3", "audio/ac3" },
{ ".ape", "audio/x-ape" },
{ ".dsf", "audio/dsf" },
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index 155e8cb8a..171b824ca 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -156,7 +156,7 @@ namespace MediaBrowser.Providers.Manager
/// <inheritdoc/>
public async Task SaveImage(BaseItem item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken)
{
- var httpClient = _httpClientFactory.CreateClient();
+ var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
using var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false);
var contentType = response.Content.Headers.ContentType.MediaType;
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
index c9dac9ecd..e3a1decb8 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -96,7 +97,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
/// <inheritdoc />
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- var httpClient = _httpClientFactory.CreateClient();
+ var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
return httpClient.GetAsync(url, cancellationToken);
}
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
index 321144edf..e6d89e688 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
@@ -10,6 +10,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
@@ -173,7 +174,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
Directory.CreateDirectory(Path.GetDirectoryName(path));
- using var response = await _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
index 651266868..54851c4d0 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -137,7 +138,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- var httpClient = _httpClientFactory.CreateClient();
+ var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
return httpClient.GetAsync(url, cancellationToken);
}
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
index 708426500..670c0cd05 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
@@ -9,6 +9,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
@@ -154,7 +155,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId);
- using var response = await _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
Directory.CreateDirectory(Path.GetDirectoryName(path));
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs
index 7f10e6922..8414c9328 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs
@@ -14,6 +14,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Common;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
@@ -765,7 +766,7 @@ namespace MediaBrowser.Providers.Music
_logger.LogDebug("GetMusicBrainzResponse: Time since previous request: {0} ms", _stopWatchMusicBrainz.ElapsedMilliseconds);
_stopWatchMusicBrainz.Restart();
- response = await _httpClientFactory.CreateClient().SendAsync(options).ConfigureAwait(false);
+ response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options).ConfigureAwait(false);
// We retry a finite number of times, and only whilst MB is indicating 503 (throttling)
}
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
index c18725e0a..8b8fea09e 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
@@ -6,6 +6,7 @@ using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
@@ -82,7 +83,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
public string Name => "The Open Movie Database";
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
index 102ad82e1..d53eba7e9 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
@@ -9,6 +9,7 @@ using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
@@ -129,7 +130,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
var url = OmdbProvider.GetOmdbUrl(urlQuery);
- using var response = await OmdbProvider.GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false);
+ using var response = await OmdbProvider.GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var resultList = new List<SearchResult>();
@@ -274,7 +275,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
class SearchResult
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
index c45149c3a..32dab60a6 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
@@ -10,6 +10,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
@@ -296,7 +297,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
"i={0}&plot=short&tomatoes=true&r=json",
imdbParam));
- using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false);
+ using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(stream).ConfigureAwait(false);
Directory.CreateDirectory(Path.GetDirectoryName(path));
@@ -334,7 +335,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
imdbParam,
seasonId));
- using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false);
+ using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<SeasonRootObject>(stream).ConfigureAwait(false);
Directory.CreateDirectory(Path.GetDirectoryName(path));
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs
index 4e7c0e5a6..de2f6875f 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs
@@ -6,6 +6,7 @@ using System.Net.Http;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
@@ -116,7 +117,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs
index 90436c7c9..07167bda6 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
@@ -244,7 +245,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
public int Order => 0;
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs
index 388a4e3e7..dc3c60dee 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
@@ -106,7 +107,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
/// <inheritdoc />
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs
index ff8c3455f..49576d488 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
@@ -148,7 +149,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs
index d287828eb..d96840e51 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
@@ -146,7 +147,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs
index c6dd8a5f3..ca9b1d738 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs
@@ -8,6 +8,7 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@@ -410,7 +411,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
index f43444028..f6592afe4 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
@@ -155,7 +156,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
index 4da2c042f..e627550f1 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
@@ -10,6 +10,7 @@ using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
@@ -271,7 +272,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs
index 60d7a599e..a975fb8f6 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
@@ -204,7 +205,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
index d8918bb6b..f8bc19395 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
@@ -11,6 +11,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
@@ -383,7 +384,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
internal Task<HttpResponseMessage> GetMovieDbResponse(HttpRequestMessage message)
{
message.Headers.UserAgent.ParseAdd(_appHost.ApplicationUserAgent);
- return _httpClientFactory.CreateClient().SendAsync(message);
+ return _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(message);
}
/// <inheritdoc />
@@ -392,7 +393,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
/// <inheritdoc />
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
index 291b36027..f2d2c8120 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
@@ -131,7 +132,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
index a4b1387d3..6bf04b81a 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
@@ -12,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
@@ -267,7 +268,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs
index 5705885b4..30b7674e3 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs
@@ -7,6 +7,7 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
@@ -138,7 +139,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
protected Task<HttpResponseMessage> GetResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs
index 787514d05..e7e2fd05b 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
@@ -38,7 +39,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
index e59504cc6..73ed13267 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
@@ -9,6 +9,7 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
@@ -124,7 +125,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
private async Task<SeasonResult> GetSeasonInfo(string seriesTmdbId, int season, string preferredMetadataLanguage,
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
index f11eeb15b..125560175 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
@@ -182,7 +183,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
index 0eded3233..aaba6ffc0 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
@@ -10,6 +10,7 @@ using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
@@ -551,7 +552,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs
index 10374bde9..25296387b 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Providers;
@@ -36,7 +37,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Trailers
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- return _httpClientFactory.CreateClient().GetAsync(url, cancellationToken);
+ return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
index 321153c6b..76dc7df7f 100644
--- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
+++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
@@ -8,6 +8,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
@@ -122,7 +123,7 @@ namespace MediaBrowser.Providers.Studios
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
- var httpClient = _httpClientFactory.CreateClient();
+ var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
return httpClient.GetAsync(url, cancellationToken);
}
@@ -140,7 +141,7 @@ namespace MediaBrowser.Providers.Studios
if (!fileInfo.Exists || (DateTime.UtcNow - fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays > 1)
{
- var httpClient = _httpClientFactory.CreateClient();
+ var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
Directory.CreateDirectory(Path.GetDirectoryName(file));
await using var response = await httpClient.GetStreamAsync(url).ConfigureAwait(false);
diff --git a/debian/rules b/debian/rules
index 6c7fbb779..96541f41b 100755
--- a/debian/rules
+++ b/debian/rules
@@ -40,7 +40,7 @@ override_dh_clistrip:
override_dh_auto_build:
dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) \
- "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none" Jellyfin.Server
+ "-p:DebugSymbols=false;DebugType=none" Jellyfin.Server
override_dh_auto_clean:
dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true
diff --git a/deployment/Dockerfile.docker.amd64 b/deployment/Dockerfile.docker.amd64
index cf0901404..e04442606 100644
--- a/deployment/Dockerfile.docker.amd64
+++ b/deployment/Dockerfile.docker.amd64
@@ -12,4 +12,4 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# because of changes in docker and systemd we need to not build in parallel at the moment
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
-RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none"
+RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-x64 "-p:DebugSymbols=false;DebugType=none"
diff --git a/deployment/Dockerfile.docker.arm64 b/deployment/Dockerfile.docker.arm64
index 5454ef024..a7ac40492 100644
--- a/deployment/Dockerfile.docker.arm64
+++ b/deployment/Dockerfile.docker.arm64
@@ -12,4 +12,4 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# because of changes in docker and systemd we need to not build in parallel at the moment
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
-RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none"
+RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm64 "-p:DebugSymbols=false;DebugType=none"
diff --git a/deployment/Dockerfile.docker.armhf b/deployment/Dockerfile.docker.armhf
index d99fa2a85..b5a42f55f 100644
--- a/deployment/Dockerfile.docker.armhf
+++ b/deployment/Dockerfile.docker.armhf
@@ -12,4 +12,4 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# because of changes in docker and systemd we need to not build in parallel at the moment
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
-RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none"
+RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm "-p:DebugSymbols=false;DebugType=none"
diff --git a/deployment/build.linux.amd64 b/deployment/build.linux.amd64
index d0a5c1747..a1e7f661a 100755
--- a/deployment/build.linux.amd64
+++ b/deployment/build.linux.amd64
@@ -16,7 +16,7 @@ else
fi
# Build archives
-dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none;UseAppHost=true"
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true"
tar -czf jellyfin-server_${version}_linux-amd64.tar.gz -C dist jellyfin-server_${version}
rm -rf dist/jellyfin-server_${version}
diff --git a/deployment/build.macos b/deployment/build.macos
index 8bb8b2ebd..6255c80cb 100755
--- a/deployment/build.macos
+++ b/deployment/build.macos
@@ -16,7 +16,7 @@ else
fi
# Build archives
-dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none;UseAppHost=true"
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true"
tar -czf jellyfin-server_${version}_macos-amd64.tar.gz -C dist jellyfin-server_${version}
rm -rf dist/jellyfin-server_${version}
diff --git a/deployment/build.portable b/deployment/build.portable
index 4c81d5672..ea40ade5d 100755
--- a/deployment/build.portable
+++ b/deployment/build.portable
@@ -16,7 +16,7 @@ else
fi
# Build archives
-dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none;UseAppHost=true"
+dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true"
tar -czf jellyfin-server_${version}_portable.tar.gz -C dist jellyfin-server_${version}
rm -rf dist/jellyfin-server_${version}
diff --git a/deployment/build.windows.amd64 b/deployment/build.windows.amd64
index 47cfa2cd6..afe490576 100755
--- a/deployment/build.windows.amd64
+++ b/deployment/build.windows.amd64
@@ -23,7 +23,7 @@ fi
output_dir="dist/jellyfin-server_${version}"
# Build binary
-dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output ${output_dir}/ "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none;UseAppHost=true"
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output ${output_dir}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true"
# Prepare addins
addin_build_dir="$( mktemp -d )"
diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec
index 291017d94..37b573e50 100644
--- a/fedora/jellyfin.spec
+++ b/fedora/jellyfin.spec
@@ -54,7 +54,7 @@ The Jellyfin media server backend.
export DOTNET_CLI_TELEMETRY_OPTOUT=1
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
dotnet publish --configuration Release --output='%{buildroot}%{_libdir}/jellyfin' --self-contained --runtime %{dotnet_runtime} \
- "-p:GenerateDocumentationFile=true;DebugSymbols=false;DebugType=none" Jellyfin.Server
+ "-p:DebugSymbols=false;DebugType=none" Jellyfin.Server
%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/jellyfin/LICENSE
%{__install} -D -m 0644 %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf
%{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/jellyfin/logging.json