@@ -70,55 +71,67 @@
}
-
@@ -126,7 +139,7 @@
Play
@@ -155,7 +168,7 @@
}
-
+
@@ -168,14 +181,17 @@
@code {
private CancellationTokenSource _cts;
+ private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentUICulture.DateTimeFormat;
private readonly List _ffmpegProfiles = [];
private readonly List _streamSelectors = [];
private readonly List _watermarks = [];
private readonly List _subtitleStreams = [];
private readonly List _graphicsElements = [];
+ private string _title;
private MediaItemInfo _info;
private readonly StreamingMode _streamingMode = StreamingMode.HttpLiveStreamingSegmenter;
private int _ffmpegProfileId;
+ private bool _channelMode;
private string _streamSelector;
private IEnumerable _watermarkNames = new System.Collections.Generic.HashSet();
private IEnumerable _graphicsElementNames = new System.Collections.Generic.HashSet();
@@ -184,11 +200,16 @@
private int _seekSeconds;
private bool _hasPlayed;
private double? _lastSpeed;
- private string _logs;
+ private MudTextField _logsField;
+ private DateTimeOffset? _start;
+ private string _startString;
[SupplyParameterFromQuery(Name = "mediaItem")]
public int? MediaItemId { get; set; }
+ [SupplyParameterFromQuery(Name = "channel")]
+ public int? ChannelId { get; set; }
+
public void Dispose()
{
_cts?.Cancel();
@@ -230,7 +251,17 @@
if (MediaItemId is not null)
{
- await OnMediaItemIdChanged(MediaItemId, token);
+ _channelMode = false;
+ await LoadMediaItem(MediaItemId.Value, token);
+ }
+ else if (ChannelId is not null)
+ {
+ _channelMode = true;
+ await LoadChannel(ChannelId.Value, token);
+ }
+ else
+ {
+ NavigationManager.NavigateTo("", new NavigationOptions { ReplaceHistoryEntry = true });
}
}
catch (OperationCanceledException)
@@ -249,19 +280,39 @@
private async Task PreviewChannel()
{
- _logs = null;
+ await _logsField.SetText(string.Empty);
_lastSpeed = null;
var baseUri = NavigationManager.ToAbsoluteUri(NavigationManager.Uri).ToString();
string apiUri = baseUri.Replace("/system/troubleshooting/playback", "/api/troubleshoot/playback.m3u8");
var queryString = new List>
{
- new("mediaItem", (MediaItemId ?? 0).ToString()),
new("ffmpegProfile", _ffmpegProfileId.ToString()),
new("streamingMode", ((int)_streamingMode).ToString()),
- new("seekSeconds", _seekSeconds.ToString())
};
+ if (_channelMode)
+ {
+ if (!_start.HasValue)
+ {
+ return;
+ }
+
+ queryString.AddRange(
+ [
+ new KeyValuePair("channel", (ChannelId ?? 0).ToString()),
+ new KeyValuePair("start", _start!.Value.ToString("o"))
+ ]);
+ }
+ else
+ {
+ queryString.AddRange(
+ [
+ new KeyValuePair("mediaItem", (MediaItemId ?? 0).ToString()),
+ new KeyValuePair("seekSeconds", _seekSeconds.ToString())
+ ]);
+ }
+
foreach (string watermarkName in _watermarkNames)
{
foreach (WatermarkViewModel watermark in _watermarks.Where(wm => wm.Name == watermarkName))
@@ -295,66 +346,65 @@
_hasPlayed = true;
}
- private async Task OnMediaItemIdChanged(int? mediaItemId, CancellationToken cancellationToken)
+ private async Task LoadMediaItem(int mediaItemId, CancellationToken cancellationToken)
{
- MediaItemId = mediaItemId;
_hasPlayed = false;
+ _info = null;
- foreach (int id in Optional(mediaItemId))
+ Either maybeInfo = await Mediator.Send(new GetMediaItemInfo(mediaItemId), cancellationToken);
+ foreach (MediaItemInfo info in maybeInfo.RightToSeq())
{
- Either maybeInfo = await Mediator.Send(new GetMediaItemInfo(id), cancellationToken);
- foreach (MediaItemInfo info in maybeInfo.RightToSeq())
- {
- _info = info;
- OnStartFromBeginningChanged(string.Equals(info.Kind, "RemoteStream", StringComparison.OrdinalIgnoreCase));
+ IEnumerable kindString = info.Kind.SelectMany((c, i) => i != 0 && char.IsUpper(c) && !char.IsUpper(info.Kind[i - 1]) ? new[] { ' ', c } : new[] { c });
+ _info = info with { Kind = new string(kindString.ToArray()) };
+ _title = info.Title;
- _subtitleId = null;
- _subtitleStreams.Clear();
- _subtitleStreams.AddRange(await Mediator.Send(new GetTroubleshootingSubtitles(id), cancellationToken));
- }
+ OnStartFromBeginningChanged(string.Equals(info.Kind, "RemoteStream", StringComparison.OrdinalIgnoreCase));
- if (maybeInfo.IsLeft)
- {
- MediaItemId = null;
- }
+ _subtitleId = null;
+ _subtitleStreams.Clear();
+ _subtitleStreams.AddRange(await Mediator.Send(new GetTroubleshootingSubtitles(mediaItemId), cancellationToken));
+ }
+
+ if (maybeInfo.IsLeft)
+ {
+ NavigationManager.NavigateTo("", new NavigationOptions { ReplaceHistoryEntry = true });
}
StateHasChanged();
}
- private async Task DownloadResults()
+ private async Task LoadChannel(int channelId, CancellationToken cancellationToken)
{
- var queryString = new List>
- {
- new("mediaItem", (MediaItemId ?? 0).ToString()),
- new("ffmpegProfile", _ffmpegProfileId.ToString()),
- new("streamingMode", ((int)_streamingMode).ToString()),
- new("seekSeconds", _seekSeconds.ToString())
- };
+ _hasPlayed = false;
+ _info = null;
- foreach (string watermarkName in _watermarkNames)
+ Option maybeChannel = await Mediator.Send(new GetChannelById(channelId), cancellationToken);
+ foreach (ChannelViewModel channel in maybeChannel)
{
- foreach (WatermarkViewModel watermark in _watermarks.Where(wm => wm.Name == watermarkName))
+ _title = channel.Name;
+ _ffmpegProfileId = channel.FFmpegProfileId;
+ if (channel.StreamSelectorMode is ChannelStreamSelectorMode.Custom)
{
- queryString.Add(new KeyValuePair("watermark", watermark.Id.ToString()));
+ _streamSelector = channel.StreamSelector;
}
}
- foreach (string graphicsElementName in _graphicsElementNames)
+ if (maybeChannel.IsNone)
{
- foreach (GraphicsElementViewModel graphicsElement in _graphicsElements.Where(ge => ge.Name == graphicsElementName))
- {
- queryString.Add(new KeyValuePair("graphicsElement", graphicsElement.Id.ToString()));
- }
+ NavigationManager.NavigateTo("", new NavigationOptions { ReplaceHistoryEntry = true });
}
- string uriWithQuery = QueryHelpers.AddQueryString("api/troubleshoot/playback/archive", queryString);
- await JsRuntime.InvokeVoidAsync("window.open", uriWithQuery);
+ StateHasChanged();
+ }
+
+ private async Task DownloadResults()
+ {
+ await JsRuntime.InvokeVoidAsync("window.open", "api/troubleshoot/playback/archive");
}
- private void HandleTroubleshootingCompleted(PlaybackTroubleshootingCompletedNotification result)
+ private async Task HandleTroubleshootingCompleted(PlaybackTroubleshootingCompletedNotification result)
{
- _logs = null;
+ await InvokeAsync(async () => { await _logsField.SetText(string.Empty); });
_lastSpeed = null;
foreach (double speed in result.MaybeSpeed)
@@ -374,15 +424,15 @@
string logFileName = Path.Combine(FileSystemLayout.TranscodeTroubleshootingFolder, "logs.txt");
if (LocalFileSystem.FileExists(logFileName))
{
- _logs = File.ReadAllText(logFileName);
- InvokeAsync(StateHasChanged);
+ string text = await File.ReadAllTextAsync(logFileName);
+ await InvokeAsync(async () => { await _logsField.SetText(text); });
}
else
{
foreach (var exception in result.MaybeException)
{
- _logs = exception.Message + Environment.NewLine + Environment.NewLine + exception;
- InvokeAsync(StateHasChanged);
+ string text = exception.Message + Environment.NewLine + Environment.NewLine + exception;
+ await InvokeAsync(async () => { await _logsField.SetText(text); });
}
}
}
@@ -402,4 +452,26 @@
return "mud-warning-text";
}
+ private async Task OnStartStringBlur(FocusEventArgs e)
+ {
+ await TryNormalizeStartString();
+ }
+
+ private async Task TryNormalizeStartString()
+ {
+ if (string.IsNullOrWhiteSpace(_startString))
+ {
+ _start = null;
+ return;
+ }
+
+ var parser = new Chronic.Core.Parser();
+ var parsedResult = parser.Parse(_startString);
+ if (DateTimeOffset.TryParse(parsedResult?.Start?.ToString() ?? _startString, out DateTimeOffset dateTimeOffset))
+ {
+ _start = dateTimeOffset;
+ await InvokeAsync(() => { _startString = dateTimeOffset.ToString("G", _dtf); });
+ }
+ }
+
}
diff --git a/ErsatzTV/Pages/Troubleshooting/Troubleshooting.razor b/ErsatzTV/Pages/Troubleshooting/Troubleshooting.razor
index b0cf038a0..b73924336 100644
--- a/ErsatzTV/Pages/Troubleshooting/Troubleshooting.razor
+++ b/ErsatzTV/Pages/Troubleshooting/Troubleshooting.razor
@@ -17,14 +17,17 @@
+