Add ITunerHostManager service

pull/10858/head
Patrick Barron 4 months ago
parent 449365182c
commit 9c2c066e6f

@ -695,7 +695,7 @@ namespace Emby.Server.Implementations
GetExports<IMetadataSaver>(),
GetExports<IExternalId>());
Resolve<ILiveTvManager>().AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>());
Resolve<ILiveTvManager>().AddParts(GetExports<ILiveTvService>(), GetExports<IListingsProvider>());
Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>());
}

@ -10,7 +10,6 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
@ -43,6 +42,7 @@ namespace Jellyfin.Api.Controllers;
public class LiveTvController : BaseJellyfinApiController
{
private readonly ILiveTvManager _liveTvManager;
private readonly ITunerHostManager _tunerHostManager;
private readonly IUserManager _userManager;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILibraryManager _libraryManager;
@ -55,6 +55,7 @@ public class LiveTvController : BaseJellyfinApiController
/// Initializes a new instance of the <see cref="LiveTvController"/> class.
/// </summary>
/// <param name="liveTvManager">Instance of the <see cref="ILiveTvManager"/> interface.</param>
/// <param name="tunerHostManager">Instance of the <see cref="ITunerHostManager"/> interface.</param>
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
@ -64,6 +65,7 @@ public class LiveTvController : BaseJellyfinApiController
/// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
public LiveTvController(
ILiveTvManager liveTvManager,
ITunerHostManager tunerHostManager,
IUserManager userManager,
IHttpClientFactory httpClientFactory,
ILibraryManager libraryManager,
@ -73,6 +75,7 @@ public class LiveTvController : BaseJellyfinApiController
ITranscodeManager transcodeManager)
{
_liveTvManager = liveTvManager;
_tunerHostManager = tunerHostManager;
_userManager = userManager;
_httpClientFactory = httpClientFactory;
_libraryManager = libraryManager;
@ -951,9 +954,7 @@ public class LiveTvController : BaseJellyfinApiController
[Authorize(Policy = Policies.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<TunerHostInfo>> AddTunerHost([FromBody] TunerHostInfo tunerHostInfo)
{
return await _liveTvManager.SaveTunerHost(tunerHostInfo).ConfigureAwait(false);
}
=> await _tunerHostManager.SaveTunerHost(tunerHostInfo).ConfigureAwait(false);
/// <summary>
/// Deletes a tuner host.
@ -1130,10 +1131,8 @@ public class LiveTvController : BaseJellyfinApiController
[HttpGet("TunerHosts/Types")]
[Authorize(Policy = Policies.LiveTvAccess)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<NameIdPair>> GetTunerHostTypes()
{
return _liveTvManager.GetTunerHostTypes();
}
public IEnumerable<NameIdPair> GetTunerHostTypes()
=> _tunerHostManager.GetTunerHostTypes();
/// <summary>
/// Discover tuners.
@ -1146,9 +1145,7 @@ public class LiveTvController : BaseJellyfinApiController
[Authorize(Policy = Policies.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<IEnumerable<TunerHostInfo>>> DiscoverTuners([FromQuery] bool newDevicesOnly = false)
{
return await _liveTvManager.DiscoverTuners(newDevicesOnly, CancellationToken.None).ConfigureAwait(false);
}
=> await _tunerHostManager.DiscoverTuners(newDevicesOnly, CancellationToken.None).ConfigureAwait(false);
/// <summary>
/// Gets a live tv recording stream.

@ -71,9 +71,8 @@ namespace MediaBrowser.Controller.LiveTv
/// Adds the parts.
/// </summary>
/// <param name="services">The services.</param>
/// <param name="tunerHosts">The tuner hosts.</param>
/// <param name="listingProviders">The listing providers.</param>
void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders);
void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<IListingsProvider> listingProviders);
/// <summary>
/// Gets the timer.
@ -253,14 +252,6 @@ namespace MediaBrowser.Controller.LiveTv
/// <returns>Task.</returns>
Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem Item, BaseItemDto ItemDto)> programs, IReadOnlyList<ItemFields> fields, User user = null);
/// <summary>
/// Saves the tuner host.
/// </summary>
/// <param name="info">Turner host to save.</param>
/// <param name="dataSourceChanged">Option to specify that data source has changed.</param>
/// <returns>Tuner host information wrapped in a task.</returns>
Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true);
/// <summary>
/// Saves the listing provider.
/// </summary>
@ -298,10 +289,6 @@ namespace MediaBrowser.Controller.LiveTv
Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken);
List<NameIdPair> GetTunerHostTypes();
Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken);
string GetEmbyTvActiveRecordingPath(string id);
ActiveRecordingInfo GetActiveRecordingInfo(string path);

@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
namespace MediaBrowser.Controller.LiveTv;
/// <summary>
/// Service responsible for managing the <see cref="ITunerHost"/>s.
/// </summary>
public interface ITunerHostManager
{
/// <summary>
/// Gets the available <see cref="ITunerHost"/>s.
/// </summary>
IReadOnlyList<ITunerHost> TunerHosts { get; }
/// <summary>
/// Gets the <see cref="NameIdPair"/>s for the available <see cref="ITunerHost"/>s.
/// </summary>
/// <returns>The <see cref="NameIdPair"/>s.</returns>
IEnumerable<NameIdPair> GetTunerHostTypes();
/// <summary>
/// Saves the tuner host.
/// </summary>
/// <param name="info">Turner host to save.</param>
/// <param name="dataSourceChanged">Option to specify that data source has changed.</param>
/// <returns>Tuner host information wrapped in a task.</returns>
Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true);
/// <summary>
/// Discovers the available tuners.
/// </summary>
/// <param name="newDevicesOnly">A value indicating whether to only return new devices.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
/// <returns>The <see cref="TunerHostInfo"/>s.</returns>
Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken);
/// <summary>
/// Scans for tuner devices that have changed URLs.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
/// <returns>A task that represents the scanning operation.</returns>
Task ScanForTunerDeviceChanges(CancellationToken cancellationToken);
}

@ -44,8 +44,6 @@ namespace Jellyfin.LiveTv.EmbyTV
{
public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
private const int TunerDiscoveryDurationMs = 3000;
private readonly ILogger<EmbyTV> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IServerConfigurationManager _config;
@ -54,6 +52,7 @@ namespace Jellyfin.LiveTv.EmbyTV
private readonly TimerManager _timerProvider;
private readonly LiveTvManager _liveTvManager;
private readonly ITunerHostManager _tunerHostManager;
private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _libraryMonitor;
@ -80,6 +79,7 @@ namespace Jellyfin.LiveTv.EmbyTV
IHttpClientFactory httpClientFactory,
IServerConfigurationManager config,
ILiveTvManager liveTvManager,
ITunerHostManager tunerHostManager,
IFileSystem fileSystem,
ILibraryManager libraryManager,
ILibraryMonitor libraryMonitor,
@ -97,6 +97,7 @@ namespace Jellyfin.LiveTv.EmbyTV
_providerManager = providerManager;
_mediaEncoder = mediaEncoder;
_liveTvManager = (LiveTvManager)liveTvManager;
_tunerHostManager = tunerHostManager;
_mediaSourceManager = mediaSourceManager;
_streamHelper = streamHelper;
@ -310,7 +311,7 @@ namespace Jellyfin.LiveTv.EmbyTV
{
var list = new List<ChannelInfo>();
foreach (var hostInstance in _liveTvManager.TunerHosts)
foreach (var hostInstance in _tunerHostManager.TunerHosts)
{
try
{
@ -510,7 +511,7 @@ namespace Jellyfin.LiveTv.EmbyTV
{
var list = new List<ChannelInfo>();
foreach (var hostInstance in _liveTvManager.TunerHosts)
foreach (var hostInstance in _tunerHostManager.TunerHosts)
{
try
{
@ -966,7 +967,7 @@ namespace Jellyfin.LiveTv.EmbyTV
return result;
}
foreach (var hostInstance in _liveTvManager.TunerHosts)
foreach (var hostInstance in _tunerHostManager.TunerHosts)
{
try
{
@ -998,7 +999,7 @@ namespace Jellyfin.LiveTv.EmbyTV
throw new ArgumentNullException(nameof(channelId));
}
foreach (var hostInstance in _liveTvManager.TunerHosts)
foreach (var hostInstance in _tunerHostManager.TunerHosts)
{
try
{
@ -2537,81 +2538,5 @@ namespace Jellyfin.LiveTv.EmbyTV
};
}
}
public async Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)
{
var list = new List<TunerHostInfo>();
var configuredDeviceIds = _config.GetLiveTvConfiguration().TunerHosts
.Where(i => !string.IsNullOrWhiteSpace(i.DeviceId))
.Select(i => i.DeviceId)
.ToList();
foreach (var host in _liveTvManager.TunerHosts)
{
var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false);
if (newDevicesOnly)
{
discoveredDevices = discoveredDevices.Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparison.OrdinalIgnoreCase))
.ToList();
}
list.AddRange(discoveredDevices);
}
return list;
}
public async Task ScanForTunerDeviceChanges(CancellationToken cancellationToken)
{
foreach (var host in _liveTvManager.TunerHosts)
{
await ScanForTunerDeviceChanges(host, cancellationToken).ConfigureAwait(false);
}
}
private async Task ScanForTunerDeviceChanges(ITunerHost host, CancellationToken cancellationToken)
{
var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false);
var configuredDevices = _config.GetLiveTvConfiguration().TunerHosts
.Where(i => string.Equals(i.Type, host.Type, StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var device in discoveredDevices)
{
var configuredDevice = configuredDevices.FirstOrDefault(i => string.Equals(i.DeviceId, device.DeviceId, StringComparison.OrdinalIgnoreCase));
if (configuredDevice is not null && !string.Equals(device.Url, configuredDevice.Url, StringComparison.OrdinalIgnoreCase))
{
_logger.LogInformation("Tuner url has changed from {PreviousUrl} to {NewUrl}", configuredDevice.Url, device.Url);
configuredDevice.Url = device.Url;
await _liveTvManager.SaveTunerHost(configuredDevice).ConfigureAwait(false);
}
}
}
private async Task<List<TunerHostInfo>> DiscoverDevices(ITunerHost host, int discoveryDurationMs, CancellationToken cancellationToken)
{
try
{
var discoveredDevices = await host.DiscoverDevices(discoveryDurationMs, cancellationToken).ConfigureAwait(false);
foreach (var device in discoveredDevices)
{
_logger.LogInformation("Discovered tuner device {0} at {1}", host.Name, device.Url);
}
return discoveredDevices;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error discovering tuner devices");
return new List<TunerHostInfo>();
}
}
}
}

@ -1,4 +1,6 @@
using Jellyfin.LiveTv.Channels;
using Jellyfin.LiveTv.TunerHosts;
using Jellyfin.LiveTv.TunerHosts.HdHomerun;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.IO;
@ -21,5 +23,9 @@ public static class LiveTvServiceCollectionExtensions
services.AddSingleton<ILiveTvManager, LiveTvManager>();
services.AddSingleton<IChannelManager, ChannelManager>();
services.AddSingleton<IStreamHelper, StreamHelper>();
services.AddSingleton<ITunerHostManager, TunerHostManager>();
services.AddSingleton<ITunerHost, HdHomerunHost>();
services.AddSingleton<ITunerHost, M3UTunerHost>();
}
}

@ -57,9 +57,9 @@ namespace Jellyfin.LiveTv
private readonly IFileSystem _fileSystem;
private readonly IChannelManager _channelManager;
private readonly LiveTvDtoService _tvDtoService;
private readonly ITunerHostManager _tunerHostManager;
private ILiveTvService[] _services = Array.Empty<ILiveTvService>();
private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>();
private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
public LiveTvManager(
@ -74,7 +74,8 @@ namespace Jellyfin.LiveTv
ILocalizationManager localization,
IFileSystem fileSystem,
IChannelManager channelManager,
LiveTvDtoService liveTvDtoService)
LiveTvDtoService liveTvDtoService,
ITunerHostManager tunerHostManager)
{
_config = config;
_logger = logger;
@ -88,6 +89,7 @@ namespace Jellyfin.LiveTv
_userDataManager = userDataManager;
_channelManager = channelManager;
_tvDtoService = liveTvDtoService;
_tunerHostManager = tunerHostManager;
}
public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
@ -104,8 +106,6 @@ namespace Jellyfin.LiveTv
/// <value>The services.</value>
public IReadOnlyList<ILiveTvService> Services => _services;
public IReadOnlyList<ITunerHost> TunerHosts => _tunerHosts;
public IReadOnlyList<IListingsProvider> ListingProviders => _listingProviders;
public string GetEmbyTvActiveRecordingPath(string id)
@ -113,16 +113,10 @@ namespace Jellyfin.LiveTv
return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
}
/// <summary>
/// Adds the parts.
/// </summary>
/// <param name="services">The services.</param>
/// <param name="tunerHosts">The tuner hosts.</param>
/// <param name="listingProviders">The listing providers.</param>
public void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders)
/// <inheritdoc />
public void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<IListingsProvider> listingProviders)
{
_services = services.ToArray();
_tunerHosts = tunerHosts.Where(i => i.IsSupported).ToArray();
_listingProviders = listingProviders.ToArray();
@ -154,20 +148,6 @@ namespace Jellyfin.LiveTv
}));
}
public List<NameIdPair> GetTunerHostTypes()
{
return _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair
{
Name = i.Name,
Id = i.Type
}).ToList();
}
public Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)
{
return EmbyTV.EmbyTV.Current.DiscoverTuners(newDevicesOnly, cancellationToken);
}
public QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
{
var user = query.UserId.Equals(default)
@ -1029,7 +1009,7 @@ namespace Jellyfin.LiveTv
{
await EmbyTV.EmbyTV.Current.CreateRecordingFolders().ConfigureAwait(false);
await EmbyTV.EmbyTV.Current.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false);
await _tunerHostManager.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false);
var numComplete = 0;
double progressPerService = _services.Length == 0
@ -2166,48 +2146,6 @@ namespace Jellyfin.LiveTv
return _libraryManager.GetNamedView(name, CollectionType.livetv, name);
}
public async Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
{
info = JsonSerializer.Deserialize<TunerHostInfo>(JsonSerializer.SerializeToUtf8Bytes(info));
var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
if (provider is null)
{
throw new ResourceNotFoundException();
}
if (provider is IConfigurableTunerHost configurable)
{
await configurable.Validate(info).ConfigureAwait(false);
}
var config = _config.GetLiveTvConfiguration();
var list = config.TunerHosts.ToList();
var index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
{
info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
list.Add(info);
config.TunerHosts = list.ToArray();
}
else
{
config.TunerHosts[index] = info;
}
_config.SaveConfiguration("livetv", config);
if (dataSourceChanged)
{
_taskManager.CancelIfRunningAndQueue<RefreshGuideScheduledTask>();
}
return info;
}
public async Task<ListingsProviderInfo> SaveListingProvider(ListingsProviderInfo info, bool validateLogin, bool validateListings)
{
// Hack to make the object a pure ListingsProviderInfo instead of an AddListingProvider

@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Extensions;
using Jellyfin.LiveTv.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Jellyfin.LiveTv.TunerHosts;
/// <inheritdoc />
public class TunerHostManager : ITunerHostManager
{
private const int TunerDiscoveryDurationMs = 3000;
private readonly ILogger<TunerHostManager> _logger;
private readonly IConfigurationManager _config;
private readonly ITaskManager _taskManager;
private readonly ITunerHost[] _tunerHosts;
/// <summary>
/// Initializes a new instance of the <see cref="TunerHostManager"/> class.
/// </summary>
/// <param name="logger">The <see cref="ILogger{T}"/>.</param>
/// <param name="config">The <see cref="IConfigurationManager"/>.</param>
/// <param name="taskManager">The <see cref="ITaskManager"/>.</param>
/// <param name="tunerHosts">The <see cref="IEnumerable{T}"/>.</param>
public TunerHostManager(
ILogger<TunerHostManager> logger,
IConfigurationManager config,
ITaskManager taskManager,
IEnumerable<ITunerHost> tunerHosts)
{
_logger = logger;
_config = config;
_taskManager = taskManager;
_tunerHosts = tunerHosts.Where(t => t.IsSupported).ToArray();
}
/// <inheritdoc />
public IReadOnlyList<ITunerHost> TunerHosts => _tunerHosts;
/// <inheritdoc />
public IEnumerable<NameIdPair> GetTunerHostTypes()
=> _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair
{
Name = i.Name,
Id = i.Type
});
/// <inheritdoc />
public async Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
{
info = JsonSerializer.Deserialize<TunerHostInfo>(JsonSerializer.SerializeToUtf8Bytes(info))!;
var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
if (provider is null)
{
throw new ResourceNotFoundException();
}
if (provider is IConfigurableTunerHost configurable)
{
await configurable.Validate(info).ConfigureAwait(false);
}
var config = _config.GetLiveTvConfiguration();
var list = config.TunerHosts.ToList();
var index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
{
info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
list.Add(info);
config.TunerHosts = list.ToArray();
}
else
{
config.TunerHosts[index] = info;
}
_config.SaveConfiguration("livetv", config);
if (dataSourceChanged)
{
_taskManager.CancelIfRunningAndQueue<RefreshGuideScheduledTask>();
}
return info;
}
/// <inheritdoc />
public async Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)
{
var list = new List<TunerHostInfo>();
var configuredDeviceIds = _config.GetLiveTvConfiguration().TunerHosts
.Where(i => !string.IsNullOrWhiteSpace(i.DeviceId))
.Select(i => i.DeviceId)
.ToList();
foreach (var host in _tunerHosts)
{
var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false);
if (newDevicesOnly)
{
discoveredDevices = discoveredDevices
.Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparison.OrdinalIgnoreCase))
.ToList();
}
list.AddRange(discoveredDevices);
}
return list;
}
/// <inheritdoc />
public async Task ScanForTunerDeviceChanges(CancellationToken cancellationToken)
{
foreach (var host in _tunerHosts)
{
await ScanForTunerDeviceChanges(host, cancellationToken).ConfigureAwait(false);
}
}
private async Task ScanForTunerDeviceChanges(ITunerHost host, CancellationToken cancellationToken)
{
var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false);
var configuredDevices = _config.GetLiveTvConfiguration().TunerHosts
.Where(i => string.Equals(i.Type, host.Type, StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var device in discoveredDevices)
{
var configuredDevice = configuredDevices.FirstOrDefault(i => string.Equals(i.DeviceId, device.DeviceId, StringComparison.OrdinalIgnoreCase));
if (configuredDevice is not null && !string.Equals(device.Url, configuredDevice.Url, StringComparison.OrdinalIgnoreCase))
{
_logger.LogInformation("Tuner url has changed from {PreviousUrl} to {NewUrl}", configuredDevice.Url, device.Url);
configuredDevice.Url = device.Url;
await SaveTunerHost(configuredDevice).ConfigureAwait(false);
}
}
}
private async Task<List<TunerHostInfo>> DiscoverDevices(ITunerHost host, int discoveryDurationMs, CancellationToken cancellationToken)
{
try
{
var discoveredDevices = await host.DiscoverDevices(discoveryDurationMs, cancellationToken).ConfigureAwait(false);
foreach (var device in discoveredDevices)
{
_logger.LogInformation("Discovered tuner device {0} at {1}", host.Name, device.Url);
}
return discoveredDevices;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error discovering tuner devices");
return new List<TunerHostInfo>();
}
}
}
Loading…
Cancel
Save