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>
This commit is contained in:
8
.gitattributes
vendored
Normal file
8
.gitattributes
vendored
Normal file
@@ -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
Normal file
3
ErsatzTV.Application/HDHR/Queries/GetHDHRUUID.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace ErsatzTV.Application.HDHR;
|
||||
|
||||
public record GetHDHRUUID : IRequest<Guid>;
|
||||
24
ErsatzTV.Application/HDHR/Queries/GetHDHRUUIDHandler.cs
Normal file
24
ErsatzTV.Application/HDHR/Queries/GetHDHRUUIDHandler.cs
Normal file
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace ErsatzTV.Core.Domain;
|
||||
namespace ErsatzTV.Core.Domain;
|
||||
|
||||
public class ConfigElementKey
|
||||
{
|
||||
@@ -27,6 +27,7 @@ public class ConfigElementKey
|
||||
public static ConfigElementKey FFmpegHlsDirectOutputFormat => new("ffmpeg.hls_direct.output_format");
|
||||
public static ConfigElementKey SearchIndexVersion => new("search_index.version");
|
||||
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 CollectionsPageSize => new("pages.collections.page_size");
|
||||
public static ConfigElementKey MultiCollectionsPageSize => new("pages.multi_collections.page_size");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace ErsatzTV.Core.Hdhr;
|
||||
|
||||
public record DeviceXml(string Scheme, string Host)
|
||||
public record DeviceXml(string Scheme, string Host, Guid uuid)
|
||||
{
|
||||
public string ToXml() =>
|
||||
@$"<root xmlns=""urn:schemas-upnp-org:device-1-0"">
|
||||
@@ -15,8 +15,8 @@ public record DeviceXml(string Scheme, string Host)
|
||||
<manufacturer>Silicondust</manufacturer>
|
||||
<modelName>HDTC-2US</modelName>
|
||||
<modelNumber>HDTC-2US</modelNumber>
|
||||
<serialNumber/>
|
||||
<UDN>uuid:2020-03-S3LA-BG3LIA:2</UDN>
|
||||
<serialNumber>{uuid}</serialNumber>
|
||||
<UDN>uuid:{uuid}</UDN>
|
||||
</device>
|
||||
</root>";
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ErsatzTV.Core.Hdhr;
|
||||
|
||||
@@ -8,21 +8,23 @@ public class Discover
|
||||
{
|
||||
private readonly string _host;
|
||||
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;
|
||||
_host = host;
|
||||
TunerCount = tunerCount;
|
||||
_UUID = uuid;
|
||||
}
|
||||
|
||||
public string DeviceAuth => "";
|
||||
public string DeviceID => "ErsatzTV";
|
||||
public string DeviceID => _UUID.ToString();
|
||||
public string FirmwareName => "hdhomeruntc_atsc";
|
||||
public string FirmwareVersion => "20190621";
|
||||
public string FriendlyName => "ErsatzTV";
|
||||
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 ModelNumber => "HDTC-2US";
|
||||
public int TunerCount { get; }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Globalization;
|
||||
using System.Globalization;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Infrastructure.Extensions;
|
||||
@@ -54,6 +54,11 @@ public class ConfigElementRepository : IConfigElementRepository
|
||||
GetConfigElement(key).MapT(
|
||||
ce =>
|
||||
{
|
||||
if (typeof(T).Name == "Guid")
|
||||
{
|
||||
return (T)Convert.ChangeType(Guid.Parse(ce.Value), typeof(T), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (typeof(T).IsEnum)
|
||||
{
|
||||
return (T)Enum.Parse(typeof(T), ce.Value);
|
||||
|
||||
@@ -9,21 +9,23 @@ namespace ErsatzTV.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[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")]
|
||||
public IActionResult DeviceXml() =>
|
||||
new OkObjectResult(new DeviceXml(Request.Scheme, Request.Host.ToString()));
|
||||
public async Task<IActionResult> DeviceXml()
|
||||
{
|
||||
Guid uuid = await mediator.Send(new GetHDHRUUID());
|
||||
return new OkObjectResult(new DeviceXml(Request.Scheme, Request.Host.ToString(), uuid));
|
||||
}
|
||||
|
||||
[HttpGet("discover.json")]
|
||||
[ResponseCache(NoStore = true)]
|
||||
public Task<IActionResult> Discover() =>
|
||||
_mediator.Send(new GetHDHRTunerCount()).Map<int, IActionResult>(
|
||||
tunerCount => new OkObjectResult(new Discover(Request.Scheme, Request.Host.ToString(), tunerCount)));
|
||||
public async Task<IActionResult> Discover()
|
||||
{
|
||||
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")]
|
||||
public IActionResult LineupStatus() =>
|
||||
@@ -31,5 +33,5 @@ public class HdhrController : ControllerBase
|
||||
|
||||
[HttpGet("lineup.json")]
|
||||
public Task<IActionResult> Lineup() =>
|
||||
_mediator.Send(new GetChannelLineup(Request.Scheme, Request.Host.ToString())).ToActionResult();
|
||||
mediator.Send(new GetChannelLineup(Request.Scheme, Request.Host.ToString())).ToActionResult();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@page "/settings"
|
||||
@page "/settings"
|
||||
@using ErsatzTV.Application.Configuration
|
||||
@using ErsatzTV.Application.FFmpegProfiles
|
||||
@using ErsatzTV.Application.Filler
|
||||
@@ -191,7 +191,10 @@
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">HDHomeRun Settings</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
<MudTextField T="Guid" Label="UUID" @bind-Value="_uuid" ReadOnly="true" Disabled="true" />
|
||||
</MudCardContent>
|
||||
<MudCardContent>
|
||||
<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!"/>
|
||||
@@ -325,42 +328,44 @@
|
||||
</MudContainer>
|
||||
|
||||
@code {
|
||||
private readonly CancellationTokenSource _cts = new();
|
||||
private readonly CancellationTokenSource _cts = new();
|
||||
|
||||
private bool _success;
|
||||
private bool _hdhrSuccess;
|
||||
private bool _scannerSuccess;
|
||||
private bool _playoutSuccess;
|
||||
private List<FFmpegProfileViewModel> _ffmpegProfiles = new();
|
||||
private FFmpegSettingsViewModel _ffmpegSettings = new();
|
||||
private List<LanguageCodeViewModel> _availableCultures = new();
|
||||
private List<WatermarkViewModel> _watermarks = new();
|
||||
private List<FillerPresetViewModel> _fillerPresets = new();
|
||||
private List<ResolutionViewModel> _customResolutions = new();
|
||||
private int _tunerCount;
|
||||
private int _libraryRefreshInterval;
|
||||
private PlayoutSettingsViewModel _playoutSettings = new();
|
||||
private GeneralSettingsViewModel _generalSettings = new();
|
||||
private XmltvSettingsViewModel _xmltvSettings = new();
|
||||
private bool _success;
|
||||
private bool _hdhrSuccess;
|
||||
private bool _scannerSuccess;
|
||||
private bool _playoutSuccess;
|
||||
private List<FFmpegProfileViewModel> _ffmpegProfiles = new();
|
||||
private FFmpegSettingsViewModel _ffmpegSettings = new();
|
||||
private List<LanguageCodeViewModel> _availableCultures = new();
|
||||
private List<WatermarkViewModel> _watermarks = new();
|
||||
private List<FillerPresetViewModel> _fillerPresets = new();
|
||||
private List<ResolutionViewModel> _customResolutions = new();
|
||||
private int _tunerCount;
|
||||
private int _libraryRefreshInterval;
|
||||
private PlayoutSettingsViewModel _playoutSettings = new();
|
||||
private GeneralSettingsViewModel _generalSettings = new();
|
||||
private XmltvSettingsViewModel _xmltvSettings = new();
|
||||
private Guid _uuid;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cts.Cancel();
|
||||
_cts.Dispose();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
_cts.Cancel();
|
||||
_cts.Dispose();
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await LoadFFmpegProfilesAsync();
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await LoadFFmpegProfilesAsync();
|
||||
|
||||
_ffmpegSettings = await Mediator.Send(new GetFFmpegSettings(), _cts.Token);
|
||||
_success = File.Exists(_ffmpegSettings.FFmpegPath) && File.Exists(_ffmpegSettings.FFprobePath);
|
||||
_availableCultures = await Mediator.Send(new GetAllLanguageCodes(), _cts.Token);
|
||||
_watermarks = await Mediator.Send(new GetAllWatermarks(), _cts.Token);
|
||||
_fillerPresets = await Mediator.Send(new GetAllFillerPresets(), _cts.Token)
|
||||
.Map(list => list.Filter(fp => fp.FillerKind == FillerKind.Fallback).ToList());
|
||||
_tunerCount = await Mediator.Send(new GetHDHRTunerCount(), _cts.Token);
|
||||
_hdhrSuccess = string.IsNullOrWhiteSpace(ValidateTunerCount(_tunerCount));
|
||||
_ffmpegSettings = await Mediator.Send(new GetFFmpegSettings(), _cts.Token);
|
||||
_success = File.Exists(_ffmpegSettings.FFmpegPath) && File.Exists(_ffmpegSettings.FFprobePath);
|
||||
_availableCultures = await Mediator.Send(new GetAllLanguageCodes(), _cts.Token);
|
||||
_watermarks = await Mediator.Send(new GetAllWatermarks(), _cts.Token);
|
||||
_fillerPresets = await Mediator.Send(new GetAllFillerPresets(), _cts.Token)
|
||||
.Map(list => list.Filter(fp => fp.FillerKind == FillerKind.Fallback).ToList());
|
||||
_tunerCount = await Mediator.Send(new GetHDHRTunerCount(), _cts.Token);
|
||||
_uuid = await Mediator.Send(new GetHDHRUUID(), _cts.Token);
|
||||
_hdhrSuccess = string.IsNullOrWhiteSpace(ValidateTunerCount(_tunerCount));
|
||||
_libraryRefreshInterval = await Mediator.Send(new GetLibraryRefreshInterval(), _cts.Token);
|
||||
_scannerSuccess = _libraryRefreshInterval is >= 0 and < 1_000_000;
|
||||
_playoutSettings = await Mediator.Send(new GetPlayoutSettings(), _cts.Token);
|
||||
|
||||
Reference in New Issue
Block a user