Browse Source

Used a UUID in HDHomeRun config to allow multiple instances on a same network (#1810)

* Used a UUID in HDHomeRun config to allow multiple instances on a same network

* tweak some async calls

* try to fix line endings

---------

Co-authored-by: Jason Dove <1695733+jasongdove@users.noreply.github.com>
pull/1820/head
Sylvain 11 months ago committed by GitHub
parent
commit
8488fe5d3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 8
      .gitattributes
  2. 3
      ErsatzTV.Application/HDHR/Queries/GetHDHRUUID.cs
  3. 24
      ErsatzTV.Application/HDHR/Queries/GetHDHRUUIDHandler.cs
  4. 3
      ErsatzTV.Core/Domain/ConfigElementKey.cs
  5. 6
      ErsatzTV.Core/Hdhr/DeviceXml.cs
  6. 10
      ErsatzTV.Core/Hdhr/Discover.cs
  7. 7
      ErsatzTV.Infrastructure/Data/Repositories/ConfigElementRepository.cs
  8. 24
      ErsatzTV/Controllers/HdhrController.cs
  9. 73
      ErsatzTV/Pages/Settings.razor

8
.gitattributes vendored

@ -0,0 +1,8 @@
# Auto detect text files and perform LF normalization
* text=auto
*.cs text diff=csharp
*.cshtml text diff=html
*.csx text diff=csharp
*.sln text eol=crlf
*.csproj text eol=crlf

3
ErsatzTV.Application/HDHR/Queries/GetHDHRUUID.cs

@ -0,0 +1,3 @@
namespace ErsatzTV.Application.HDHR;
public record GetHDHRUUID : IRequest<Guid>;

24
ErsatzTV.Application/HDHR/Queries/GetHDHRUUIDHandler.cs

@ -0,0 +1,24 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
namespace ErsatzTV.Application.HDHR;
public class GetHDHRUUIDHandler : IRequestHandler<GetHDHRUUID, Guid>
{
private readonly IConfigElementRepository _configElementRepository;
public GetHDHRUUIDHandler(IConfigElementRepository configElementRepository) =>
_configElementRepository = configElementRepository;
public async Task<Guid> Handle(GetHDHRUUID request, CancellationToken cancellationToken)
{
Option<Guid> maybeGuid = await _configElementRepository.GetValue<Guid>(ConfigElementKey.HDHRUUID);
return await maybeGuid.IfNoneAsync(
async () =>
{
Guid guid = Guid.NewGuid();
await _configElementRepository.Upsert(ConfigElementKey.HDHRUUID, guid);
return guid;
});
}
}

3
ErsatzTV.Core/Domain/ConfigElementKey.cs

@ -1,4 +1,4 @@
namespace ErsatzTV.Core.Domain; namespace ErsatzTV.Core.Domain;
public class ConfigElementKey public class ConfigElementKey
{ {
@ -27,6 +27,7 @@ public class ConfigElementKey
public static ConfigElementKey FFmpegHlsDirectOutputFormat => new("ffmpeg.hls_direct.output_format"); public static ConfigElementKey FFmpegHlsDirectOutputFormat => new("ffmpeg.hls_direct.output_format");
public static ConfigElementKey SearchIndexVersion => new("search_index.version"); public static ConfigElementKey SearchIndexVersion => new("search_index.version");
public static ConfigElementKey HDHRTunerCount => new("hdhr.tuner_count"); public static ConfigElementKey HDHRTunerCount => new("hdhr.tuner_count");
public static ConfigElementKey HDHRUUID => new("hdhr.uuid");
public static ConfigElementKey ChannelsPageSize => new("pages.channels.page_size"); public static ConfigElementKey ChannelsPageSize => new("pages.channels.page_size");
public static ConfigElementKey CollectionsPageSize => new("pages.collections.page_size"); public static ConfigElementKey CollectionsPageSize => new("pages.collections.page_size");
public static ConfigElementKey MultiCollectionsPageSize => new("pages.multi_collections.page_size"); public static ConfigElementKey MultiCollectionsPageSize => new("pages.multi_collections.page_size");

6
ErsatzTV.Core/Hdhr/DeviceXml.cs

@ -1,6 +1,6 @@
namespace ErsatzTV.Core.Hdhr; namespace ErsatzTV.Core.Hdhr;
public record DeviceXml(string Scheme, string Host) public record DeviceXml(string Scheme, string Host, Guid uuid)
{ {
public string ToXml() => public string ToXml() =>
@$"<root xmlns=""urn:schemas-upnp-org:device-1-0""> @$"<root xmlns=""urn:schemas-upnp-org:device-1-0"">
@ -15,8 +15,8 @@ public record DeviceXml(string Scheme, string Host)
<manufacturer>Silicondust</manufacturer> <manufacturer>Silicondust</manufacturer>
<modelName>HDTC-2US</modelName> <modelName>HDTC-2US</modelName>
<modelNumber>HDTC-2US</modelNumber> <modelNumber>HDTC-2US</modelNumber>
<serialNumber/> <serialNumber>{uuid}</serialNumber>
<UDN>uuid:2020-03-S3LA-BG3LIA:2</UDN> <UDN>uuid:{uuid}</UDN>
</device> </device>
</root>"; </root>";
} }

10
ErsatzTV.Core/Hdhr/Discover.cs

@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace ErsatzTV.Core.Hdhr; namespace ErsatzTV.Core.Hdhr;
@ -8,21 +8,23 @@ public class Discover
{ {
private readonly string _host; private readonly string _host;
private readonly string _scheme; private readonly string _scheme;
private readonly Guid _UUID;
public Discover(string scheme, string host, int tunerCount) public Discover(string scheme, string host, int tunerCount, Guid uuid)
{ {
_scheme = scheme; _scheme = scheme;
_host = host; _host = host;
TunerCount = tunerCount; TunerCount = tunerCount;
_UUID = uuid;
} }
public string DeviceAuth => ""; public string DeviceAuth => "";
public string DeviceID => "ErsatzTV"; public string DeviceID => _UUID.ToString();
public string FirmwareName => "hdhomeruntc_atsc"; public string FirmwareName => "hdhomeruntc_atsc";
public string FirmwareVersion => "20190621"; public string FirmwareVersion => "20190621";
public string FriendlyName => "ErsatzTV"; public string FriendlyName => "ErsatzTV";
public string LineupURL => $"{_scheme}://{_host}/lineup.json"; public string LineupURL => $"{_scheme}://{_host}/lineup.json";
public string Manufacturer => "ErsatzTV - Silicondust"; public string Manufacturer => "ErsatzTV";
public string ManufacturerURL => "https://github.com/ErsatzTV/ErsatzTV"; public string ManufacturerURL => "https://github.com/ErsatzTV/ErsatzTV";
public string ModelNumber => "HDTC-2US"; public string ModelNumber => "HDTC-2US";
public int TunerCount { get; } public int TunerCount { get; }

7
ErsatzTV.Infrastructure/Data/Repositories/ConfigElementRepository.cs

@ -1,4 +1,4 @@
using System.Globalization; using System.Globalization;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Infrastructure.Extensions; using ErsatzTV.Infrastructure.Extensions;
@ -54,6 +54,11 @@ public class ConfigElementRepository : IConfigElementRepository
GetConfigElement(key).MapT( GetConfigElement(key).MapT(
ce => ce =>
{ {
if (typeof(T).Name == "Guid")
{
return (T)Convert.ChangeType(Guid.Parse(ce.Value), typeof(T), CultureInfo.InvariantCulture);
}
if (typeof(T).IsEnum) if (typeof(T).IsEnum)
{ {
return (T)Enum.Parse(typeof(T), ce.Value); return (T)Enum.Parse(typeof(T), ce.Value);

24
ErsatzTV/Controllers/HdhrController.cs

@ -9,21 +9,23 @@ namespace ErsatzTV.Controllers;
[ApiController] [ApiController]
[ApiExplorerSettings(IgnoreApi = true)] [ApiExplorerSettings(IgnoreApi = true)]
public class HdhrController : ControllerBase public class HdhrController(IMediator mediator) : ControllerBase
{ {
private readonly IMediator _mediator;
public HdhrController(IMediator mediator) => _mediator = mediator;
[HttpGet("device.xml")] [HttpGet("device.xml")]
public IActionResult DeviceXml() => public async Task<IActionResult> DeviceXml()
new OkObjectResult(new DeviceXml(Request.Scheme, Request.Host.ToString())); {
Guid uuid = await mediator.Send(new GetHDHRUUID());
return new OkObjectResult(new DeviceXml(Request.Scheme, Request.Host.ToString(), uuid));
}
[HttpGet("discover.json")] [HttpGet("discover.json")]
[ResponseCache(NoStore = true)] [ResponseCache(NoStore = true)]
public Task<IActionResult> Discover() => public async Task<IActionResult> Discover()
_mediator.Send(new GetHDHRTunerCount()).Map<int, IActionResult>( {
tunerCount => new OkObjectResult(new Discover(Request.Scheme, Request.Host.ToString(), tunerCount))); Guid uuid = await mediator.Send(new GetHDHRUUID());
int tunerCount = await mediator.Send(new GetHDHRTunerCount());
return new OkObjectResult(new Discover(Request.Scheme, Request.Host.ToString(), tunerCount, uuid));
}
[HttpGet("lineup_status.json")] [HttpGet("lineup_status.json")]
public IActionResult LineupStatus() => public IActionResult LineupStatus() =>
@ -31,5 +33,5 @@ public class HdhrController : ControllerBase
[HttpGet("lineup.json")] [HttpGet("lineup.json")]
public Task<IActionResult> Lineup() => public Task<IActionResult> Lineup() =>
_mediator.Send(new GetChannelLineup(Request.Scheme, Request.Host.ToString())).ToActionResult(); mediator.Send(new GetChannelLineup(Request.Scheme, Request.Host.ToString())).ToActionResult();
} }

73
ErsatzTV/Pages/Settings.razor

@ -1,4 +1,4 @@
@page "/settings" @page "/settings"
@using ErsatzTV.Application.Configuration @using ErsatzTV.Application.Configuration
@using ErsatzTV.Application.FFmpegProfiles @using ErsatzTV.Application.FFmpegProfiles
@using ErsatzTV.Application.Filler @using ErsatzTV.Application.Filler
@ -191,7 +191,10 @@
<CardHeaderContent> <CardHeaderContent>
<MudText Typo="Typo.h6">HDHomeRun Settings</MudText> <MudText Typo="Typo.h6">HDHomeRun Settings</MudText>
</CardHeaderContent> </CardHeaderContent>
</MudCardHeader> </MudCardHeader>
<MudCardContent>
<MudTextField T="Guid" Label="UUID" @bind-Value="_uuid" ReadOnly="true" Disabled="true" />
</MudCardContent>
<MudCardContent> <MudCardContent>
<MudForm @bind-IsValid="@_hdhrSuccess"> <MudForm @bind-IsValid="@_hdhrSuccess">
<MudTextField T="int" Label="Tuner Count" @bind-Value="_tunerCount" Validation="@(new Func<int, string>(ValidateTunerCount))" Required="true" RequiredError="Tuner count is required!"/> <MudTextField T="int" Label="Tuner Count" @bind-Value="_tunerCount" Validation="@(new Func<int, string>(ValidateTunerCount))" Required="true" RequiredError="Tuner count is required!"/>
@ -325,42 +328,44 @@
</MudContainer> </MudContainer>
@code { @code {
private readonly CancellationTokenSource _cts = new(); private readonly CancellationTokenSource _cts = new();
private bool _success; private bool _success;
private bool _hdhrSuccess; private bool _hdhrSuccess;
private bool _scannerSuccess; private bool _scannerSuccess;
private bool _playoutSuccess; private bool _playoutSuccess;
private List<FFmpegProfileViewModel> _ffmpegProfiles = new(); private List<FFmpegProfileViewModel> _ffmpegProfiles = new();
private FFmpegSettingsViewModel _ffmpegSettings = new(); private FFmpegSettingsViewModel _ffmpegSettings = new();
private List<LanguageCodeViewModel> _availableCultures = new(); private List<LanguageCodeViewModel> _availableCultures = new();
private List<WatermarkViewModel> _watermarks = new(); private List<WatermarkViewModel> _watermarks = new();
private List<FillerPresetViewModel> _fillerPresets = new(); private List<FillerPresetViewModel> _fillerPresets = new();
private List<ResolutionViewModel> _customResolutions = new(); private List<ResolutionViewModel> _customResolutions = new();
private int _tunerCount; private int _tunerCount;
private int _libraryRefreshInterval; private int _libraryRefreshInterval;
private PlayoutSettingsViewModel _playoutSettings = new(); private PlayoutSettingsViewModel _playoutSettings = new();
private GeneralSettingsViewModel _generalSettings = new(); private GeneralSettingsViewModel _generalSettings = new();
private XmltvSettingsViewModel _xmltvSettings = new(); private XmltvSettingsViewModel _xmltvSettings = new();
private Guid _uuid;
public void Dispose() public void Dispose()
{ {
_cts.Cancel(); _cts.Cancel();
_cts.Dispose(); _cts.Dispose();
} }
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
await LoadFFmpegProfilesAsync(); await LoadFFmpegProfilesAsync();
_ffmpegSettings = await Mediator.Send(new GetFFmpegSettings(), _cts.Token); _ffmpegSettings = await Mediator.Send(new GetFFmpegSettings(), _cts.Token);
_success = File.Exists(_ffmpegSettings.FFmpegPath) && File.Exists(_ffmpegSettings.FFprobePath); _success = File.Exists(_ffmpegSettings.FFmpegPath) && File.Exists(_ffmpegSettings.FFprobePath);
_availableCultures = await Mediator.Send(new GetAllLanguageCodes(), _cts.Token); _availableCultures = await Mediator.Send(new GetAllLanguageCodes(), _cts.Token);
_watermarks = await Mediator.Send(new GetAllWatermarks(), _cts.Token); _watermarks = await Mediator.Send(new GetAllWatermarks(), _cts.Token);
_fillerPresets = await Mediator.Send(new GetAllFillerPresets(), _cts.Token) _fillerPresets = await Mediator.Send(new GetAllFillerPresets(), _cts.Token)
.Map(list => list.Filter(fp => fp.FillerKind == FillerKind.Fallback).ToList()); .Map(list => list.Filter(fp => fp.FillerKind == FillerKind.Fallback).ToList());
_tunerCount = await Mediator.Send(new GetHDHRTunerCount(), _cts.Token); _tunerCount = await Mediator.Send(new GetHDHRTunerCount(), _cts.Token);
_hdhrSuccess = string.IsNullOrWhiteSpace(ValidateTunerCount(_tunerCount)); _uuid = await Mediator.Send(new GetHDHRUUID(), _cts.Token);
_hdhrSuccess = string.IsNullOrWhiteSpace(ValidateTunerCount(_tunerCount));
_libraryRefreshInterval = await Mediator.Send(new GetLibraryRefreshInterval(), _cts.Token); _libraryRefreshInterval = await Mediator.Send(new GetLibraryRefreshInterval(), _cts.Token);
_scannerSuccess = _libraryRefreshInterval is >= 0 and < 1_000_000; _scannerSuccess = _libraryRefreshInterval is >= 0 and < 1_000_000;
_playoutSettings = await Mediator.Send(new GetPlayoutSettings(), _cts.Token); _playoutSettings = await Mediator.Send(new GetPlayoutSettings(), _cts.Token);

Loading…
Cancel
Save