From 91c4e8f57508e1f4c40e4909ba1cd4d7ee32e135 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Mon, 11 Aug 2025 18:10:36 +0000 Subject: [PATCH] add seek seconds to playback troubleshooting (#2300) --- CHANGELOG.md | 1 + .../Commands/ArchiveTroubleshootingResults.cs | 2 +- .../PrepareTroubleshootingPlayback.cs | 2 +- .../PrepareTroubleshootingPlaybackHandler.cs | 19 ++++++++++---- .../Controllers/Api/TroubleshootController.cs | 12 ++++++--- ErsatzTV/Pages/PlaybackTroubleshooting.razor | 26 +++++++++++++++---- 6 files changed, 46 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90b673eb..56290f2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - `graphics_on` requires the name of a graphics element template, e.g. `text/cool_element.yml` - The `variables` property can be used to dynamically replace text from the template - `graphics_off` will turn off a specific element, or all elements if none are specified +- Add `Seek Seconds` to playback troubleshooting to support capturing timing-related issues ### Fix - Fix database operations that were slowing down playout builds diff --git a/ErsatzTV.Application/Troubleshooting/Commands/ArchiveTroubleshootingResults.cs b/ErsatzTV.Application/Troubleshooting/Commands/ArchiveTroubleshootingResults.cs index 6815991a..3f8b1416 100644 --- a/ErsatzTV.Application/Troubleshooting/Commands/ArchiveTroubleshootingResults.cs +++ b/ErsatzTV.Application/Troubleshooting/Commands/ArchiveTroubleshootingResults.cs @@ -5,5 +5,5 @@ public record ArchiveTroubleshootingResults( int FFmpegProfileId, List WatermarkIds, List GraphicsElementIds, - bool StartFromBeginning) + Option SeekSeconds) : IRequest>; diff --git a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlayback.cs b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlayback.cs index b7f0515a..089f21fc 100644 --- a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlayback.cs +++ b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlayback.cs @@ -9,5 +9,5 @@ public record PrepareTroubleshootingPlayback( List WatermarkIds, List GraphicsElementIds, int? SubtitleId, - bool StartFromBeginning) + Option SeekSeconds) : IRequest>; diff --git a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs index de7daf54..7896e57d 100644 --- a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs +++ b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs @@ -103,14 +103,23 @@ public class PrepareTroubleshootingPlaybackHandler( TimeSpan inPoint = TimeSpan.Zero; TimeSpan outPoint = duration; - if (!hlsRealtime && !request.StartFromBeginning) + if (!hlsRealtime) { - inPoint = TimeSpan.FromSeconds(version.Duration.TotalSeconds / 2.0); - if (inPoint.TotalSeconds < 30) + foreach (var seekSeconds in request.SeekSeconds) { - duration = inPoint; + inPoint = TimeSpan.FromSeconds(seekSeconds); + if (inPoint > version.Duration) + { + inPoint = version.Duration - duration; + } + + if (inPoint + duration > version.Duration) + { + duration = version.Duration - inPoint; + } + + outPoint = inPoint + duration; } - outPoint = inPoint + duration; } var graphicsElements = await dbContext.GraphicsElements diff --git a/ErsatzTV/Controllers/Api/TroubleshootController.cs b/ErsatzTV/Controllers/Api/TroubleshootController.cs index fff0113e..305cd9e2 100644 --- a/ErsatzTV/Controllers/Api/TroubleshootController.cs +++ b/ErsatzTV/Controllers/Api/TroubleshootController.cs @@ -33,13 +33,15 @@ public class TroubleshootController( [FromQuery] int? subtitleId, [FromQuery] - bool startFromBeginning, + int seekSeconds, CancellationToken cancellationToken) { try { + Option ss = seekSeconds > 0 ? seekSeconds : Option.None; + Either result = await mediator.Send( - new PrepareTroubleshootingPlayback(mediaItem, ffmpegProfile, watermark, graphicsElement, subtitleId, startFromBeginning), + new PrepareTroubleshootingPlayback(mediaItem, ffmpegProfile, watermark, graphicsElement, subtitleId, ss), cancellationToken); if (result.IsLeft) @@ -119,11 +121,13 @@ public class TroubleshootController( [FromQuery] List graphicsElement, [FromQuery] - bool startFromBeginning, + int seekSeconds, CancellationToken cancellationToken) { + Option ss = seekSeconds > 0 ? seekSeconds : Option.None; + Option maybeArchivePath = await mediator.Send( - new ArchiveTroubleshootingResults(mediaItem, ffmpegProfile, watermark, graphicsElement, startFromBeginning), + new ArchiveTroubleshootingResults(mediaItem, ffmpegProfile, watermark, graphicsElement, ss), cancellationToken); foreach (string archivePath in maybeArchivePath) diff --git a/ErsatzTV/Pages/PlaybackTroubleshooting.razor b/ErsatzTV/Pages/PlaybackTroubleshooting.razor index ed76b2bb..2df72070 100644 --- a/ErsatzTV/Pages/PlaybackTroubleshooting.razor +++ b/ErsatzTV/Pages/PlaybackTroubleshooting.razor @@ -93,7 +93,16 @@
Start From Beginning
- + + + +
+ Seek Seconds +
+
Preview @@ -138,8 +147,9 @@ private int _ffmpegProfileId; private IEnumerable _watermarkNames = new System.Collections.Generic.HashSet(); private IEnumerable _graphicsElementNames = new System.Collections.Generic.HashSet(); - private int? _subtitleId; private bool _startFromBeginning; + private int? _subtitleId; + private int _seekSeconds; private bool _hasPlayed; [SupplyParameterFromQuery(Name = "mediaItem")] @@ -178,13 +188,19 @@ } } + private void OnStartFromBeginningChanged(bool value) + { + _startFromBeginning = value; + _seekSeconds = value ? 0 : (int)Math.Round((_info?.Duration.TotalSeconds ?? 0) / 2.0); + } + private void LockChanged(object sender, EventArgs e) => InvokeAsync(StateHasChanged); 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}&startFromBeginning={_startFromBeginning}"; + uri.Query = $"?mediaItem={MediaItemId}&ffmpegProfile={_ffmpegProfileId}&seekSeconds={_seekSeconds}"; foreach (var watermarkName in _watermarkNames) { foreach (var watermark in _watermarks.Where(wm => wm.Name == watermarkName)) @@ -221,7 +237,7 @@ foreach (MediaItemInfo info in maybeInfo.RightToSeq()) { _info = info; - _startFromBeginning = string.Equals(info.Kind, "RemoteStream", StringComparison.OrdinalIgnoreCase); + OnStartFromBeginningChanged(string.Equals(info.Kind, "RemoteStream", StringComparison.OrdinalIgnoreCase)); _subtitleId = null; _subtitleStreams.Clear(); @@ -239,7 +255,7 @@ private async Task DownloadResults() { - var uri = $"api/troubleshoot/playback/archive?mediaItem={MediaItemId ?? 0}&ffmpegProfile={_ffmpegProfileId}&startFromBeginning={_startFromBeginning}"; + var uri = $"api/troubleshoot/playback/archive?mediaItem={MediaItemId ?? 0}&ffmpegProfile={_ffmpegProfileId}&seekSeconds={_seekSeconds}"; foreach (var watermarkName in _watermarkNames) {