< Summary - Jellyfin

Information
Class: Jellyfin.LiveTv.TunerHosts.TunerHostManager
Assembly: Jellyfin.LiveTv
File(s): /srv/git/jellyfin/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs
Line coverage
45%
Covered lines: 5
Uncovered lines: 6
Coverable lines: 11
Total lines: 174
Line coverage: 45.4%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_TunerHosts()100%210%
GetTunerHostTypes()100%210%

File(s)

/srv/git/jellyfin/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Globalization;
 4using System.Linq;
 5using System.Text.Json;
 6using System.Threading;
 7using System.Threading.Tasks;
 8using Jellyfin.LiveTv.Configuration;
 9using Jellyfin.LiveTv.Guide;
 10using MediaBrowser.Common.Configuration;
 11using MediaBrowser.Common.Extensions;
 12using MediaBrowser.Controller.LiveTv;
 13using MediaBrowser.Model.Dto;
 14using MediaBrowser.Model.LiveTv;
 15using MediaBrowser.Model.Tasks;
 16using Microsoft.Extensions.Logging;
 17
 18namespace Jellyfin.LiveTv.TunerHosts;
 19
 20/// <inheritdoc />
 21public class TunerHostManager : ITunerHostManager
 22{
 23    private const int TunerDiscoveryDurationMs = 3000;
 24
 25    private readonly ILogger<TunerHostManager> _logger;
 26    private readonly IConfigurationManager _config;
 27    private readonly ITaskManager _taskManager;
 28    private readonly ITunerHost[] _tunerHosts;
 29
 30    /// <summary>
 31    /// Initializes a new instance of the <see cref="TunerHostManager"/> class.
 32    /// </summary>
 33    /// <param name="logger">The <see cref="ILogger{T}"/>.</param>
 34    /// <param name="config">The <see cref="IConfigurationManager"/>.</param>
 35    /// <param name="taskManager">The <see cref="ITaskManager"/>.</param>
 36    /// <param name="tunerHosts">The <see cref="IEnumerable{T}"/>.</param>
 37    public TunerHostManager(
 38        ILogger<TunerHostManager> logger,
 39        IConfigurationManager config,
 40        ITaskManager taskManager,
 41        IEnumerable<ITunerHost> tunerHosts)
 42    {
 2243        _logger = logger;
 2244        _config = config;
 2245        _taskManager = taskManager;
 2246        _tunerHosts = tunerHosts.Where(t => t.IsSupported).ToArray();
 2247    }
 48
 49    /// <inheritdoc />
 050    public IReadOnlyList<ITunerHost> TunerHosts => _tunerHosts;
 51
 52    /// <inheritdoc />
 53    public IEnumerable<NameIdPair> GetTunerHostTypes()
 054        => _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair
 055        {
 056            Name = i.Name,
 057            Id = i.Type
 058        });
 59
 60    /// <inheritdoc />
 61    public async Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
 62    {
 63        info = JsonSerializer.Deserialize<TunerHostInfo>(JsonSerializer.SerializeToUtf8Bytes(info))!;
 64
 65        var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCa
 66
 67        if (provider is null)
 68        {
 69            throw new ResourceNotFoundException();
 70        }
 71
 72        if (provider is IConfigurableTunerHost configurable)
 73        {
 74            await configurable.Validate(info).ConfigureAwait(false);
 75        }
 76
 77        var config = _config.GetLiveTvConfiguration();
 78
 79        var list = config.TunerHosts;
 80        var index = Array.FindIndex(list, i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
 81
 82        if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
 83        {
 84            info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
 85            config.TunerHosts = [..list, info];
 86        }
 87        else
 88        {
 89            config.TunerHosts[index] = info;
 90        }
 91
 92        _config.SaveConfiguration("livetv", config);
 93
 94        if (dataSourceChanged)
 95        {
 96            _taskManager.CancelIfRunningAndQueue<RefreshGuideScheduledTask>();
 97        }
 98
 99        return info;
 100    }
 101
 102    /// <inheritdoc />
 103    public async IAsyncEnumerable<TunerHostInfo> DiscoverTuners(bool newDevicesOnly)
 104    {
 105        var configuredDeviceIds = _config.GetLiveTvConfiguration().TunerHosts
 106            .Where(i => !string.IsNullOrWhiteSpace(i.DeviceId))
 107            .Select(i => i.DeviceId)
 108            .ToList();
 109
 110        foreach (var host in _tunerHosts)
 111        {
 112            var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, CancellationToken.None).Config
 113            foreach (var tuner in discoveredDevices)
 114            {
 115                if (!newDevicesOnly || !configuredDeviceIds.Contains(tuner.DeviceId, StringComparer.OrdinalIgnoreCase))
 116                {
 117                    yield return tuner;
 118                }
 119            }
 120        }
 121    }
 122
 123    /// <inheritdoc />
 124    public async Task ScanForTunerDeviceChanges(CancellationToken cancellationToken)
 125    {
 126        foreach (var host in _tunerHosts)
 127        {
 128            await ScanForTunerDeviceChanges(host, cancellationToken).ConfigureAwait(false);
 129        }
 130    }
 131
 132    private async Task ScanForTunerDeviceChanges(ITunerHost host, CancellationToken cancellationToken)
 133    {
 134        var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(
 135
 136        var configuredDevices = _config.GetLiveTvConfiguration().TunerHosts
 137            .Where(i => string.Equals(i.Type, host.Type, StringComparison.OrdinalIgnoreCase))
 138            .ToList();
 139
 140        foreach (var device in discoveredDevices)
 141        {
 142            var configuredDevice = configuredDevices.FirstOrDefault(i => string.Equals(i.DeviceId, device.DeviceId, Stri
 143
 144            if (configuredDevice is not null && !string.Equals(device.Url, configuredDevice.Url, StringComparison.Ordina
 145            {
 146                _logger.LogInformation("Tuner url has changed from {PreviousUrl} to {NewUrl}", configuredDevice.Url, dev
 147
 148                configuredDevice.Url = device.Url;
 149                await SaveTunerHost(configuredDevice).ConfigureAwait(false);
 150            }
 151        }
 152    }
 153
 154    private async Task<IList<TunerHostInfo>> DiscoverDevices(ITunerHost host, int discoveryDurationMs, CancellationToken
 155    {
 156        try
 157        {
 158            var discoveredDevices = await host.DiscoverDevices(discoveryDurationMs, cancellationToken).ConfigureAwait(fa
 159
 160            foreach (var device in discoveredDevices)
 161            {
 162                _logger.LogInformation("Discovered tuner device {0} at {1}", host.Name, device.Url);
 163            }
 164
 165            return discoveredDevices;
 166        }
 167        catch (Exception ex)
 168        {
 169            _logger.LogError(ex, "Error discovering tuner devices");
 170
 171            return Array.Empty<TunerHostInfo>();
 172        }
 173    }
 174}