aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/LiveTv
diff options
context:
space:
mode:
authornyanmisaka <nst799610810@gmail.com>2020-11-08 19:11:54 +0800
committernyanmisaka <nst799610810@gmail.com>2020-11-08 19:11:54 +0800
commit737cb727f9f943f8bb55fca8a5c5023a98aca2d7 (patch)
tree8858a423545a03d0f7dfe5c1d1b0ae1157d62c5e /Emby.Server.Implementations/LiveTv
parent05e78ee78c56364971956507f6239ded61f0af87 (diff)
parent96dcd9c87e2eb4b14004368856949e9fde2db261 (diff)
Merge remote-tracking branch 'upstream/master' into fonts
Diffstat (limited to 'Emby.Server.Implementations/LiveTv')
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs51
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs47
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs43
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs401
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs36
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs8
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs147
-rw-r--r--Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs116
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs15
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs36
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs53
15 files changed, 402 insertions, 562 deletions
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
index 2e13a3bb3..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;
}
@@ -52,10 +52,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.LogInformation("Copying recording stream to file {0}", targetFile);
// The media source is infinite so we need to handle stopping ourselves
- var durationToken = new CancellationTokenSource(duration);
- cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
+ using var durationToken = new CancellationTokenSource(duration);
+ using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token);
- await directStreamProvider.CopyToAsync(output, cancellationToken).ConfigureAwait(false);
+ await directStreamProvider.CopyToAsync(output, cancellationTokenSource.Token).ConfigureAwait(false);
}
_logger.LogInformation("Recording completed to file {0}", targetFile);
@@ -63,37 +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
- };
+ _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
- var durationToken = new CancellationTokenSource(duration);
- cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).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, 81920, cancellationToken).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 80e09f0a3..fcc2d1eeb 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -7,12 +7,14 @@ 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;
using System.Xml;
using Emby.Server.Implementations.Library;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
@@ -29,7 +31,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo;
@@ -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;
@@ -604,11 +605,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return Task.CompletedTask;
}
- public Task DeleteRecordingAsync(string recordingId, CancellationToken cancellationToken)
- {
- return Task.CompletedTask;
- }
-
public Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
{
throw new NotImplementedException();
@@ -808,11 +804,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return null;
}
- public IEnumerable<ActiveRecordingInfo> GetAllActiveRecordings()
- {
- return _activeRecordings.Values.Where(i => i.Timer.Status == RecordingStatus.InProgress && !i.CancellationTokenSource.IsCancellationRequested);
- }
-
public ActiveRecordingInfo GetActiveRecordingInfo(string path)
{
if (string.IsNullOrWhiteSpace(path))
@@ -1015,16 +1006,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
throw new Exception("Tuner not found.");
}
- private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, bool enableStreamSharing)
- {
- var json = _jsonSerializer.SerializeToString(mediaSource);
- mediaSource = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
-
- mediaSource.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture) + "_" + mediaSource.Id;
-
- return mediaSource;
- }
-
public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(channelId))
@@ -1654,10 +1635,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
if (mediaSource.RequiresLooping || !(mediaSource.Container ?? string.Empty).EndsWith("ts", StringComparison.OrdinalIgnoreCase) || (mediaSource.Protocol != MediaProtocol.File && mediaSource.Protocol != MediaProtocol.Http))
{
- return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _config);
+ return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer);
}
- return new DirectRecorder(_logger, _httpClient, _streamHelper);
+ return new DirectRecorder(_logger, _httpClientFactory, _streamHelper);
}
private void OnSuccessfulRecording(TimerInfo timer, string path)
@@ -1809,7 +1790,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var program = string.IsNullOrWhiteSpace(timer.ProgramId) ? null : _libraryManager.GetItemList(new InternalItemsQuery
{
- IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
+ IncludeItemTypes = new[] { nameof(LiveTvProgram) },
Limit = 1,
ExternalId = timer.ProgramId,
DtoOptions = new DtoOptions(true)
@@ -2170,7 +2151,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var query = new InternalItemsQuery
{
- IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
+ IncludeItemTypes = new string[] { nameof(LiveTvProgram) },
Limit = 1,
DtoOptions = new DtoOptions(true)
{
@@ -2389,7 +2370,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var query = new InternalItemsQuery
{
- IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
+ IncludeItemTypes = new string[] { nameof(LiveTvProgram) },
ExternalSeriesId = seriesTimer.SeriesId,
DtoOptions = new DtoOptions(true)
{
@@ -2424,7 +2405,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
channel = _libraryManager.GetItemList(
new InternalItemsQuery
{
- IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name },
+ IncludeItemTypes = new string[] { nameof(LiveTvChannel) },
ItemIds = new[] { parent.ChannelId },
DtoOptions = new DtoOptions()
}).FirstOrDefault() as LiveTvChannel;
@@ -2483,7 +2464,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
channel = _libraryManager.GetItemList(
new InternalItemsQuery
{
- IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name },
+ IncludeItemTypes = new string[] { nameof(LiveTvChannel) },
ItemIds = new[] { programInfo.ChannelId },
DtoOptions = new DtoOptions()
}).FirstOrDefault() as LiveTvChannel;
@@ -2548,7 +2529,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var seriesIds = _libraryManager.GetItemIds(
new InternalItemsQuery
{
- IncludeItemTypes = new[] { typeof(Series).Name },
+ IncludeItemTypes = new[] { nameof(Series) },
Name = program.Name
}).ToArray();
@@ -2561,7 +2542,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var result = _libraryManager.GetItemIds(new InternalItemsQuery
{
- IncludeItemTypes = new[] { typeof(Episode).Name },
+ IncludeItemTypes = new[] { nameof(Episode) },
ParentIndexNumber = program.SeasonNumber.Value,
IndexNumber = program.EpisodeNumber.Value,
AncestorIds = seriesIds,
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index d8ec107ec..3e5457dbd 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -8,12 +8,9 @@ using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
@@ -26,26 +23,24 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly ILogger _logger;
private readonly IMediaEncoder _mediaEncoder;
private readonly IServerApplicationPaths _appPaths;
+ private readonly IJsonSerializer _json;
+ private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
+
private bool _hasExited;
private Stream _logFileStream;
private string _targetPath;
private Process _process;
- private readonly IJsonSerializer _json;
- private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
- private readonly IServerConfigurationManager _config;
public EncodedRecorder(
ILogger logger,
IMediaEncoder mediaEncoder,
IServerApplicationPaths appPaths,
- IJsonSerializer json,
- IServerConfigurationManager config)
+ IJsonSerializer json)
{
_logger = logger;
_mediaEncoder = mediaEncoder;
_appPaths = appPaths;
_json = json;
- _config = config;
}
private static bool CopySubtitles => false;
@@ -58,19 +53,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public async Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
// The media source is infinite so we need to handle stopping ourselves
- var durationToken = new CancellationTokenSource(duration);
- cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
+ using var durationToken = new CancellationTokenSource(duration);
+ using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token);
- await RecordFromFile(mediaSource, mediaSource.Path, targetFile, duration, onStarted, cancellationToken).ConfigureAwait(false);
+ await RecordFromFile(mediaSource, mediaSource.Path, targetFile, duration, onStarted, cancellationTokenSource.Token).ConfigureAwait(false);
_logger.LogInformation("Recording completed to file {0}", targetFile);
}
- private EncodingOptions GetEncodingOptions()
- {
- return _config.GetConfiguration<EncodingOptions>("encoding");
- }
-
private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
_targetPath = targetFile;
@@ -108,7 +98,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
StartInfo = processStartInfo,
EnableRaisingEvents = true
};
- _process.Exited += (sender, args) => OnFfMpegProcessExited(_process, inputFile);
+ _process.Exited += (sender, args) => OnFfMpegProcessExited(_process);
_process.Start();
@@ -221,20 +211,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
protected string GetOutputSizeParam()
- {
- var filters = new List<string>();
-
- filters.Add("yadif=0:-1:0");
-
- var output = string.Empty;
-
- if (filters.Count > 0)
- {
- output += string.Format(" -vf \"{0}\"", string.Join(",", filters.ToArray()));
- }
-
- return output;
- }
+ => "-vf \"yadif=0:-1:0\"";
private void Stop()
{
@@ -291,7 +268,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
/// <summary>
/// Processes the exited.
/// </summary>
- private void OnFfMpegProcessExited(Process process, string inputFile)
+ private void OnFfMpegProcessExited(Process process)
{
using (process)
{
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
index 69a9cb78a..a2ec2df37 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Controller.Plugins;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
- public class EntryPoint : IServerEntryPoint
+ public sealed class EntryPoint : IServerEntryPoint
{
/// <inheritdoc />
public Task RunAsync()
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 285a59a24..dd479b7d1 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -5,8 +5,8 @@ using System.Collections.Concurrent;
using System.Globalization;
using System.Linq;
using System.Threading;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 77a7069eb..28aabc159 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;
@@ -24,23 +26,23 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
public class SchedulesDirect : IListingsProvider
{
+ private const string ApiUrl = "https://json.schedulesdirect.org/20141201";
+
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;
- private const string ApiUrl = "https://json.schedulesdirect.org/20141201";
-
public SchedulesDirect(
ILogger<SchedulesDirect> logger,
IJsonSerializer jsonSerializer,
- IHttpClient httpClient,
+ IHttpClientFactory httpClientFactory,
IApplicationHost appHost)
{
_logger = logger;
_jsonSerializer = jsonSerializer;
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_appHost = appHost;
}
@@ -61,7 +63,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
while (start <= end)
{
- dates.Add(start.ToString("yyyy-MM-dd"));
+ dates.Add(start.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
start = start.AddDays(1);
}
@@ -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
- };
-
- httpOptions.RequestHeaders["token"] = token;
+ 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);
- 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);
+ using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs");
+ programRequestOptions.Headers.TryAddWithoutValidation("token", token);
- httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/programs",
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true
- };
+ 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.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);
+ var programEntry = programDict[schedule.programID];
- if (images != null)
- {
- var imageIndex = images.FindIndex(i => i.programID == schedule.programID.Substring(0, 10));
- if (imageIndex > -1)
- {
- 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));
-
- const double DesiredAspect = 2.0 / 3;
+ 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));
- programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
- GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
+ const double DesiredAspect = 2.0 / 3;
- const double WideAspect = 16.0 / 9;
+ programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
+ GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
- programEntry.thumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
+ const double WideAspect = 16.0 / 9;
- // Don't supply the same image twice
- if (string.Equals(programEntry.primaryImage, programEntry.thumbImage, StringComparison.Ordinal))
- {
- programEntry.thumbImage = null;
- }
+ programEntry.thumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
- 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)
@@ -367,13 +352,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
if (!string.IsNullOrWhiteSpace(details.originalAirDate))
{
- info.OriginalAirDate = DateTime.Parse(details.originalAirDate);
+ info.OriginalAirDate = DateTime.Parse(details.originalAirDate, CultureInfo.InvariantCulture);
info.ProductionYear = info.OriginalAirDate.Value.Year;
}
if (details.movie != null)
{
- if (!string.IsNullOrEmpty(details.movie.year) && int.TryParse(details.movie.year, out int year))
+ if (!string.IsNullOrEmpty(details.movie.year)
+ && int.TryParse(details.movie.year, out int year))
{
info.ProductionYear = year;
}
@@ -482,22 +468,17 @@ namespace Emby.Server.Implementations.LiveTv.Listings
imageIdString = imageIdString.TrimEnd(',') + "]";
- var httpOptions = new HttpRequestOptions()
+ using var message = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/metadata/programs")
{
- Url = ApiUrl + "/metadata/programs",
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- RequestContent = imageIdString,
- LogErrorResponseBody = true,
+ 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().ConfigureAwait(false);
+ return await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ShowImages>>(
+ response).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -518,41 +499,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);
- if (root != null)
+ var root = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.Headends>>(response).ConfigureAwait(false);
+
+ 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)
@@ -587,7 +560,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return null;
}
- NameValuePair savedToken = null;
+ NameValuePair savedToken;
if (!_tokens.TryGetValue(username, out savedToken))
{
savedToken = new NameValuePair();
@@ -633,16 +606,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)
{
@@ -659,65 +632,28 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- options.RequestHeaders["token"] = await GetToken(providerInfo, options.CancellationToken).ConfigureAwait(false);
- return await Post(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<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);
- }
-
- private async Task<string> GetTokenInternal(string username, string password,
+ private async Task<string> GetTokenInternal(
+ string username,
+ 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)
@@ -736,20 +672,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)
@@ -768,25 +693,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)
{
@@ -851,55 +768,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");
+ 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");
- var allStations = root.stations ?? Enumerable.Empty<ScheduleDirect.Station>();
+ var allStations = root.stations ?? Enumerable.Empty<ScheduleDirect.Station>();
- foreach (ScheduleDirect.Map map in root.map)
- {
- var channelNumber = GetChannelNumber(map);
-
- 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;
@@ -929,7 +834,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private static string NormalizeName(string value)
{
- return value.Replace(" ", string.Empty).Replace("-", string.Empty);
+ return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal);
}
public class ScheduleDirect
@@ -969,7 +874,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public List<Lineup> lineups { get; set; }
}
-
public class Headends
{
public string headend { get; set; }
@@ -981,8 +885,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public List<Lineup> lineups { get; set; }
}
-
-
public class Map
{
public string stationID { get; set; }
@@ -1066,9 +968,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public List<string> date { get; set; }
}
-
-
-
public class Rating
{
public string body { get; set; }
@@ -1112,8 +1011,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public string isPremiereOrFinale { get; set; }
}
-
-
public class MetadataSchedule
{
public string modified { get; set; }
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index 0a93c4674..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);
@@ -237,7 +220,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
&& !programInfo.IsRepeat
&& (programInfo.EpisodeNumber ?? 0) == 0)
{
- programInfo.ShowId = programInfo.ShowId + programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture);
+ programInfo.ShowId += programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture);
}
}
else
@@ -246,7 +229,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
// Construct an id from the channel and start date
- programInfo.Id = string.Format("{0}_{1:O}", program.ChannelId, program.StartDate);
+ programInfo.Id = string.Format(CultureInfo.InvariantCulture, "{0}_{1:O}", program.ChannelId, program.StartDate);
if (programInfo.IsMovie)
{
@@ -296,7 +279,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
Name = c.DisplayName,
ImageUrl = c.Icon != null && !string.IsNullOrEmpty(c.Icon.Source) ? c.Icon.Source : null,
Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number
-
}).ToList();
}
}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index 49ad73af3..6af49dd45 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -159,7 +159,7 @@ namespace Emby.Server.Implementations.LiveTv
{
var librarySeries = _libraryManager.GetItemList(new InternalItemsQuery
{
- IncludeItemTypes = new string[] { typeof(Series).Name },
+ IncludeItemTypes = new string[] { nameof(Series) },
Name = seriesName,
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Thumb },
@@ -253,7 +253,7 @@ namespace Emby.Server.Implementations.LiveTv
{
var librarySeries = _libraryManager.GetItemList(new InternalItemsQuery
{
- IncludeItemTypes = new string[] { typeof(Series).Name },
+ IncludeItemTypes = new string[] { nameof(Series) },
Name = seriesName,
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Thumb },
@@ -296,7 +296,7 @@ namespace Emby.Server.Implementations.LiveTv
var program = _libraryManager.GetItemList(new InternalItemsQuery
{
- IncludeItemTypes = new string[] { typeof(Series).Name },
+ IncludeItemTypes = new string[] { nameof(Series) },
Name = seriesName,
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Primary },
@@ -307,7 +307,7 @@ namespace Emby.Server.Implementations.LiveTv
{
program = _libraryManager.GetItemList(new InternalItemsQuery
{
- IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
+ IncludeItemTypes = new string[] { nameof(LiveTvProgram) },
ExternalSeriesId = programSeriesId,
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Primary },
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 1b075d86a..9c7d624ee 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Emby.Server.Implementations.Library;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
@@ -24,7 +25,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
@@ -41,6 +41,7 @@ namespace Emby.Server.Implementations.LiveTv
/// </summary>
public class LiveTvManager : ILiveTvManager, IDisposable
{
+ private const int MaxGuideDays = 14;
private const string ExternalServiceTag = "ExternalServiceId";
private const string EtagKey = "ProgramEtag";
@@ -186,7 +187,7 @@ namespace Emby.Server.Implementations.LiveTv
IsKids = query.IsKids,
IsSports = query.IsSports,
IsSeries = query.IsSeries,
- IncludeItemTypes = new[] { typeof(LiveTvChannel).Name },
+ IncludeItemTypes = new[] { nameof(LiveTvChannel) },
TopParentIds = new[] { topFolder.Id },
IsFavorite = query.IsFavorite,
IsLiked = query.IsLiked,
@@ -421,7 +422,7 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
+ private async Task<LiveTvChannel> GetChannelAsync(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
{
var parentFolderId = parentFolder.Id;
var isNew = false;
@@ -511,7 +512,7 @@ namespace Emby.Server.Implementations.LiveTv
}
else if (forceUpdate)
{
- _libraryManager.UpdateItem(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken);
+ await _libraryManager.UpdateItemAsync(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
}
return item;
@@ -560,7 +561,7 @@ namespace Emby.Server.Implementations.LiveTv
item.Audio = info.Audio;
item.ChannelId = channel.Id;
- item.CommunityRating = item.CommunityRating ?? info.CommunityRating;
+ item.CommunityRating ??= info.CommunityRating;
if ((item.CommunityRating ?? 0).Equals(0))
{
item.CommunityRating = null;
@@ -645,8 +646,8 @@ namespace Emby.Server.Implementations.LiveTv
item.IsSeries = isSeries;
item.Name = info.Name;
- item.OfficialRating = item.OfficialRating ?? info.OfficialRating;
- item.Overview = item.Overview ?? info.Overview;
+ item.OfficialRating ??= info.OfficialRating;
+ item.Overview ??= info.Overview;
item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
item.ProviderIds = info.ProviderIds;
@@ -683,19 +684,23 @@ namespace Emby.Server.Implementations.LiveTv
{
if (!string.IsNullOrWhiteSpace(info.ImagePath))
{
- item.SetImage(new ItemImageInfo
- {
- Path = info.ImagePath,
- Type = ImageType.Primary
- }, 0);
+ item.SetImage(
+ new ItemImageInfo
+ {
+ Path = info.ImagePath,
+ Type = ImageType.Primary
+ },
+ 0);
}
else if (!string.IsNullOrWhiteSpace(info.ImageUrl))
{
- item.SetImage(new ItemImageInfo
- {
- Path = info.ImageUrl,
- Type = ImageType.Primary
- }, 0);
+ item.SetImage(
+ new ItemImageInfo
+ {
+ Path = info.ImageUrl,
+ Type = ImageType.Primary
+ },
+ 0);
}
}
@@ -703,11 +708,13 @@ namespace Emby.Server.Implementations.LiveTv
{
if (!string.IsNullOrWhiteSpace(info.ThumbImageUrl))
{
- item.SetImage(new ItemImageInfo
- {
- Path = info.ThumbImageUrl,
- Type = ImageType.Thumb
- }, 0);
+ item.SetImage(
+ new ItemImageInfo
+ {
+ Path = info.ThumbImageUrl,
+ Type = ImageType.Thumb
+ },
+ 0);
}
}
@@ -715,11 +722,13 @@ namespace Emby.Server.Implementations.LiveTv
{
if (!string.IsNullOrWhiteSpace(info.LogoImageUrl))
{
- item.SetImage(new ItemImageInfo
- {
- Path = info.LogoImageUrl,
- Type = ImageType.Logo
- }, 0);
+ item.SetImage(
+ new ItemImageInfo
+ {
+ Path = info.LogoImageUrl,
+ Type = ImageType.Logo
+ },
+ 0);
}
}
@@ -727,11 +736,13 @@ namespace Emby.Server.Implementations.LiveTv
{
if (!string.IsNullOrWhiteSpace(info.BackdropImageUrl))
{
- item.SetImage(new ItemImageInfo
- {
- Path = info.BackdropImageUrl,
- Type = ImageType.Backdrop
- }, 0);
+ item.SetImage(
+ new ItemImageInfo
+ {
+ Path = info.BackdropImageUrl,
+ Type = ImageType.Backdrop
+ },
+ 0);
}
}
@@ -786,7 +797,6 @@ namespace Emby.Server.Implementations.LiveTv
if (query.OrderBy.Count == 0)
{
-
// Unless something else was specified, order by start date to take advantage of a specialized index
query.OrderBy = new[]
{
@@ -798,7 +808,7 @@ namespace Emby.Server.Implementations.LiveTv
var internalQuery = new InternalItemsQuery(user)
{
- IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
+ IncludeItemTypes = new[] { nameof(LiveTvProgram) },
MinEndDate = query.MinEndDate,
MinStartDate = query.MinStartDate,
MaxEndDate = query.MaxEndDate,
@@ -824,7 +834,7 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
{
- var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false);
+ var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false);
var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N", CultureInfo.InvariantCulture), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
if (seriesTimer != null)
{
@@ -847,13 +857,11 @@ namespace Emby.Server.Implementations.LiveTv
var returnArray = _dtoService.GetBaseItemDtos(queryResult.Items, options, user);
- var result = new QueryResult<BaseItemDto>
+ return new QueryResult<BaseItemDto>
{
Items = returnArray,
TotalRecordCount = queryResult.TotalRecordCount
};
-
- return result;
}
public QueryResult<BaseItem> GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
@@ -864,7 +872,7 @@ namespace Emby.Server.Implementations.LiveTv
var internalQuery = new InternalItemsQuery(user)
{
- IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
+ IncludeItemTypes = new[] { nameof(LiveTvProgram) },
IsAiring = query.IsAiring,
HasAired = query.HasAired,
IsNews = query.IsNews,
@@ -1081,8 +1089,8 @@ namespace Emby.Server.Implementations.LiveTv
if (cleanDatabase)
{
- CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken);
- CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken);
+ CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { nameof(LiveTvChannel) }, progress, cancellationToken);
+ CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { nameof(LiveTvProgram) }, progress, cancellationToken);
}
var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault();
@@ -1121,7 +1129,7 @@ namespace Emby.Server.Implementations.LiveTv
try
{
- var item = GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken);
+ var item = await GetChannelAsync(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken).ConfigureAwait(false);
list.Add(item);
}
@@ -1138,7 +1146,7 @@ namespace Emby.Server.Implementations.LiveTv
double percent = numComplete;
percent /= allChannelsList.Count;
- progress.Report(5 * percent + 10);
+ progress.Report((5 * percent) + 10);
}
progress.Report(15);
@@ -1173,8 +1181,7 @@ namespace Emby.Server.Implementations.LiveTv
var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
{
-
- IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
+ IncludeItemTypes = new string[] { nameof(LiveTvProgram) },
ChannelIds = new Guid[] { currentChannel.Id },
DtoOptions = new DtoOptions(true)
}).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
@@ -1214,7 +1221,11 @@ namespace Emby.Server.Implementations.LiveTv
if (updatedPrograms.Count > 0)
{
- _libraryManager.UpdateItems(updatedPrograms, currentChannel, ItemUpdateType.MetadataImport, cancellationToken);
+ await _libraryManager.UpdateItemsAsync(
+ updatedPrograms,
+ currentChannel,
+ ItemUpdateType.MetadataImport,
+ cancellationToken).ConfigureAwait(false);
}
currentChannel.IsMovie = isMovie;
@@ -1227,7 +1238,7 @@ namespace Emby.Server.Implementations.LiveTv
currentChannel.AddTag("Kids");
}
- currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
+ await currentChannel.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
await currentChannel.RefreshMetadata(
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
@@ -1298,8 +1309,6 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- private const int MaxGuideDays = 14;
-
private double GetGuideDays()
{
var config = GetConfiguration();
@@ -1337,11 +1346,11 @@ namespace Emby.Server.Implementations.LiveTv
{
if (query.IsMovie.Value)
{
- includeItemTypes.Add(typeof(Movie).Name);
+ includeItemTypes.Add(nameof(Movie));
}
else
{
- excludeItemTypes.Add(typeof(Movie).Name);
+ excludeItemTypes.Add(nameof(Movie));
}
}
@@ -1349,11 +1358,11 @@ namespace Emby.Server.Implementations.LiveTv
{
if (query.IsSeries.Value)
{
- includeItemTypes.Add(typeof(Episode).Name);
+ includeItemTypes.Add(nameof(Episode));
}
else
{
- excludeItemTypes.Add(typeof(Episode).Name);
+ excludeItemTypes.Add(nameof(Episode));
}
}
@@ -1712,7 +1721,7 @@ namespace Emby.Server.Implementations.LiveTv
if (timer == null)
{
- throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
+ throw new ResourceNotFoundException(string.Format(CultureInfo.InvariantCulture, "Timer with Id {0} not found", id));
}
var service = GetService(timer.ServiceName);
@@ -1731,7 +1740,7 @@ namespace Emby.Server.Implementations.LiveTv
if (timer == null)
{
- throw new ResourceNotFoundException(string.Format("SeriesTimer with Id {0} not found", id));
+ throw new ResourceNotFoundException(string.Format(CultureInfo.InvariantCulture, "SeriesTimer with Id {0} not found", id));
}
var service = GetService(timer.ServiceName);
@@ -1743,10 +1752,12 @@ namespace Emby.Server.Implementations.LiveTv
public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
{
- var results = await GetTimers(new TimerQuery
- {
- Id = id
- }, cancellationToken).ConfigureAwait(false);
+ var results = await GetTimers(
+ new TimerQuery
+ {
+ Id = id
+ },
+ cancellationToken).ConfigureAwait(false);
return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
}
@@ -1794,10 +1805,7 @@ namespace Emby.Server.Implementations.LiveTv
}
var returnArray = timers
- .Select(i =>
- {
- return i.Item1;
- })
+ .Select(i => i.Item1)
.ToArray();
return new QueryResult<SeriesTimerInfo>
@@ -1875,7 +1883,7 @@ namespace Emby.Server.Implementations.LiveTv
var programs = options.AddCurrentProgram ? _libraryManager.GetItemList(new InternalItemsQuery(user)
{
- IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
+ IncludeItemTypes = new[] { nameof(LiveTvProgram) },
ChannelIds = channelIds,
MaxStartDate = now,
MinEndDate = now,
@@ -1968,7 +1976,7 @@ namespace Emby.Server.Implementations.LiveTv
if (service == null)
{
- service = _services.First();
+ service = _services[0];
}
var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
@@ -1994,9 +2002,7 @@ namespace Emby.Server.Implementations.LiveTv
{
var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false);
- var obj = _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
-
- return obj;
+ return _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
}
public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken)
@@ -2125,9 +2131,11 @@ namespace Emby.Server.Implementations.LiveTv
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
private bool _disposed = false;
+
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
@@ -2447,8 +2455,7 @@ namespace Emby.Server.Implementations.LiveTv
.SelectMany(i => i.Locations)
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(i => _libraryManager.FindByPath(i, true))
- .Where(i => i != null)
- .Where(i => i.IsVisibleStandalone(user))
+ .Where(i => i != null && i.IsVisibleStandalone(user))
.SelectMany(i => _libraryManager.GetCollectionFolders(i))
.GroupBy(x => x.Id)
.Select(x => x.First())
diff --git a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
index f1b61f7c7..582b64923 100644
--- a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
@@ -43,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv
return new[]
{
// Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks }
};
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index c61189c0a..2f4c60117 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -31,17 +31,19 @@ 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;
private readonly IStreamHelper _streamHelper;
+ private readonly Dictionary<string, DiscoverResponse> _modelCache = new Dictionary<string, DiscoverResponse>();
+
public HdHomerunHost(
IServerConfigurationManager config,
ILogger<HdHomerunHost> logger,
IFileSystem fileSystem,
- IHttpClient httpClient,
+ IHttpClientFactory httpClientFactory,
IServerApplicationHost appHost,
ISocketFactory socketFactory,
INetworkManager networkManager,
@@ -49,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;
@@ -69,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>();
@@ -114,7 +109,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}).Cast<ChannelInfo>().ToList();
}
- private readonly Dictionary<string, DiscoverResponse> _modelCache = new Dictionary<string, DiscoverResponse>();
private async Task<DiscoverResponse> GetModelInfo(TunerHostInfo info, bool throwAllExceptions, CancellationToken cancellationToken)
{
var cacheKey = info.Id;
@@ -132,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);
@@ -157,10 +147,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
{
- var defaultValue = "HDHR";
+ const string DefaultValue = "HDHR";
var response = new DiscoverResponse
{
- ModelNumber = defaultValue
+ ModelNumber = DefaultValue
};
if (!string.IsNullOrEmpty(cacheKey))
{
@@ -182,46 +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("{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))
+ 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)
{
- 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)
@@ -578,6 +563,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, ChannelInfo channelInfo, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
+ var tunerCount = info.TunerCount;
+
+ if (tunerCount > 0)
+ {
+ var tunerHostId = info.Id;
+ var liveStreams = currentLiveStreams.Where(i => string.Equals(i.TunerHostId, tunerHostId, StringComparison.OrdinalIgnoreCase));
+
+ if (liveStreams.Count() >= tunerCount)
+ {
+ throw new LiveTvConflictException("HDHomeRun simultaneous stream limit has been reached.");
+ }
+ }
+
var profile = streamId.Split('_')[0];
Logger.LogInformation("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channelInfo.Id, streamId, profile);
@@ -631,7 +629,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
info,
streamId,
FileSystem,
- _httpClient,
+ _httpClientFactory,
Logger,
Config,
_appHost,
@@ -730,7 +728,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
// Need a way to set the Receive timeout on the socket otherwise this might never timeout?
try
{
- await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken);
+ await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken).ConfigureAwait(false);
var receiveBuffer = new byte[8192];
while (!cancellationToken.IsCancellationRequested)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index 6730751d5..858c10030 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -131,6 +131,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
await taskCompletionSource.Task.ConfigureAwait(false);
}
+ public string GetFilePath()
+ {
+ return TempFilePath;
+ }
+
private Task StartStreaming(UdpClient udpClient, HdHomerunManager hdHomerunManager, IPAddress remoteAddress, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{
return Task.Run(async () =>
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 8fc29fb4a..4b170b2e4 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,9 @@ 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, channelIdPrefix, cancellationToken)
+ .ConfigureAwait(false);
}
public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
@@ -116,7 +119,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 +128,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, CancellationToken.None).ConfigureAwait(false))
{
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 875977219..c064e2fe6 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;
@@ -12,6 +13,7 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.LiveTv;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.TunerHosts
@@ -19,22 +21,22 @@ 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;
}
- public async Task<List<ChannelInfo>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken)
+ public async Task<List<ChannelInfo>> Parse(TunerHostInfo info, string channelIdPrefix, CancellationToken cancellationToken)
{
// Read the file and display it line by line.
- using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false)))
+ using (var reader = new StreamReader(await GetListingsStream(info, cancellationToken).ConfigureAwait(false)))
{
- return GetChannels(reader, channelIdPrefix, tunerHostId);
+ return GetChannels(reader, channelIdPrefix, info.Id);
}
}
@@ -47,20 +49,24 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
}
- public Task<Stream> GetListingsStream(string url, CancellationToken cancellationToken)
+ public async Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken)
{
- if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ if (info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
- return _httpClient.Get(new HttpRequestOptions
+ using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
+ if (!string.IsNullOrEmpty(info.UserAgent))
{
- Url = url,
- CancellationToken = cancellationToken,
- // Some data providers will require a user agent
- UserAgent = _appHost.ApplicationUserAgent
- });
+ requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
+ }
+
+ var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .SendAsync(requestMessage, cancellationToken)
+ .ConfigureAwait(false);
+ response.EnsureSuccessStatusCode();
+ return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
}
- return Task.FromResult((Stream)File.OpenRead(url));
+ return File.OpenRead(info.Url);
}
private const string ExtInfPrefix = "#EXTINF:";
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index bc4dcd894..2e1b89509 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,15 @@ 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);
+ // Response stream is disposed manually.
+ 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 +122,27 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
}
- private Task StartStreaming(HttpResponseInfo response, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
+ public string GetFilePath()
+ {
+ return TempFilePath;
+ }
+
+ 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)
{