@ -1,4 +1,5 @@
@@ -1,4 +1,5 @@
@page "/system/troubleshooting/playback"
@using ErsatzTV.Application.Channels
@using ErsatzTV.Application.FFmpegProfiles
@using ErsatzTV.Application.Graphics
@using ErsatzTV.Application.MediaItems
@ -7,6 +8,7 @@
@@ -7,6 +8,7 @@
@using ErsatzTV.Application.Watermarks
@using ErsatzTV.Core.Notifications
@using MediatR.Courier
@using Microsoft.AspNetCore.WebUtilities
@implements IDisposable
@inject IMediator Mediator
@inject NavigationManager NavigationManager
@ -55,6 +57,17 @@
@@ -55,6 +57,17 @@
}
</MudSelect>
</MudStack>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Stream Selector</MudText>
</div>
<MudSelect @bind-Value="_streamSelector" For="@(() => _streamSelector)" Clearable="true">
@foreach (string selector in _streamSelectors)
{
<MudSelectItem T="string" Value="@selector">@selector</MudSelectItem>
}
</MudSelect>
</MudStack>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Streaming Mode</MudText>
@ -68,7 +81,7 @@
@@ -68,7 +81,7 @@
<div class="d-flex">
<MudText>Subtitle</MudText>
</div>
<MudSelect @bind-Value="_subtitleId" For="@(() => _subtitleId)" Clearable="true">
<MudSelect @bind-Value="_subtitleId" For="@(() => _subtitleId)" Clearable="true" Disabled="@(!string.IsNullOrWhiteSpace(_streamSelector))" >
<MudSelectItem T="int?" Value="@((int?)null)">(none)</MudSelectItem>
@foreach (SubtitleViewModel subtitleStream in _subtitleStreams)
{
@ -149,12 +162,14 @@
@@ -149,12 +162,14 @@
private CancellationTokenSource _cts;
private readonly List<FFmpegProfileViewModel> _ffmpegProfiles = [];
private readonly List<string> _streamSelectors = [];
private readonly List<WatermarkViewModel> _watermarks = [];
private readonly List<SubtitleViewModel> _subtitleStreams = [];
private readonly List<GraphicsElementViewModel> _graphicsElements = [];
private MediaItemInfo _info;
private StreamingMode _streamingMode = StreamingMode.HttpLiveStreamingSegmenter;
private int _ffmpegProfileId;
private string _streamSelector;
private IEnumerable<string> _watermarkNames = new System.Collections.Generic.HashSet<string>();
private IEnumerable<string> _graphicsElementNames = new System.Collections.Generic.HashSet<string>();
private bool _startFromBeginning;
@ -193,6 +208,10 @@
@@ -193,6 +208,10 @@
_ffmpegProfileId = _ffmpegProfiles.Map(f => f.Id).Head();
}
_streamSelectors.Clear();
_streamSelectors.AddRange(await Mediator.Send(new GetChannelStreamSelectors(), token));
_streamSelector = null;
_watermarks.Clear();
_watermarks.AddRange(await Mediator.Send(new GetAllWatermarks(), token));
@ -221,14 +240,21 @@
@@ -221,14 +240,21 @@
private async Task PreviewChannel()
{
var uri = new UriBuilder(NavigationManager.ToAbsoluteUri(NavigationManager.Uri));
uri.Path = uri.Path.Replace("/system/troubleshooting/playback", "/api/troubleshoot/playback.m3u8");
uri.Query = $"?mediaItem={MediaItemId}&ffmpegProfile={_ffmpegProfileId}&streamingMode={(int)_streamingMode}&seekSeconds={_seekSeconds}";
var baseUri = NavigationManager.ToAbsoluteUri(NavigationManager.Uri).ToString();
string apiUri = baseUri.Replace("/system/troubleshooting/playback", "/api/troubleshoot/playback.m3u8");
var queryString = new Dictionary<string, string>
{
["mediaItem"] = (MediaItemId ?? 0).ToString(),
["ffmpegProfile"] = _ffmpegProfileId.ToString(),
["streamingMode"] = ((int)_streamingMode).ToString(),
["seekSeconds"] = _seekSeconds.ToString()
};
foreach (string watermarkName in _watermarkNames)
{
foreach (WatermarkViewModel watermark in _watermarks.Where(wm => wm.Name == watermarkName))
{
uri.Query += $"&watermark={watermark.Id}";
queryString.Add("watermark", watermark.Id.ToString()) ;
}
}
@ -236,16 +262,21 @@
@@ -236,16 +262,21 @@
{
foreach (GraphicsElementViewModel graphicsElement in _graphicsElements.Where(ge => ge.Name == graphicsElementName))
{
uri.Query += $"&graphicsElement={graphicsElement.Id}" ;
queryString.Add("graphicsElement", graphicsElement.Id.ToString()) ;
}
}
if (_subtitleId is not null )
if (!string.IsNullOrWhiteSpace(_streamSelector) )
{
uri.Query += $"&subtitleId={_subtitleId.Value}";
queryString.Add("streamSelector", _streamSelector);
}
else if (_subtitleId is not null)
{
queryString.Add("subtitleId", _subtitleId.Value.ToString());
}
await JsRuntime.InvokeVoidAsync("previewChannel", uri.ToString());
string uriWithQuery = QueryHelpers.AddQueryString(apiUri, queryString);
await JsRuntime.InvokeVoidAsync("previewChannel", uriWithQuery);
await Task.Delay(TimeSpan.FromSeconds(1));
@ -281,13 +312,20 @@
@@ -281,13 +312,20 @@
private async Task DownloadResults()
{
var uri = $"api/troubleshoot/playback/archive?mediaItem={MediaItemId ?? 0}&ffmpegProfile={_ffmpegProfileId}&streamingMode={(int)_streamingMode}&seekSeconds={_seekSeconds}";
string apiUri = "api/troubleshoot/playback/archive";
var queryString = new Dictionary<string, string>
{
["mediaItem"] = (MediaItemId ?? 0).ToString(),
["ffmpegProfile"] = _ffmpegProfileId.ToString(),
["streamingMode"] = ((int)_streamingMode).ToString(),
["seekSeconds"] = _seekSeconds.ToString()
};
foreach (string watermarkName in _watermarkNames)
{
foreach (WatermarkViewModel watermark in _watermarks.Where(wm => wm.Name == watermarkName))
{
uri += $"&watermark={watermark.Id}";
queryString.Add("watermark", watermark.Id.ToString()) ;
}
}
@ -295,11 +333,12 @@
@@ -295,11 +333,12 @@
{
foreach (GraphicsElementViewModel graphicsElement in _graphicsElements.Where(ge => ge.Name == graphicsElementName))
{
uri += $"&graphicsElement={graphicsElement.Id}" ;
queryString.Add("graphicsElement", graphicsElement.Id.ToString()) ;
}
}
await JsRuntime.InvokeVoidAsync("window.open", uri);
string uriWithQuery = QueryHelpers.AddQueryString(apiUri, queryString);
await JsRuntime.InvokeVoidAsync("window.open", uriWithQuery);
}
private void HandleTroubleshootingCompleted(PlaybackTroubleshootingCompletedNotification result)