From cf33af4572a68bfbe6e4f300a90e69547b163354 Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 8 Jul 2026 23:46:25 +0200 Subject: [PATCH] feat(troubleshoot): add troubleshooting playback status store Add a singleton ITroubleshootingPlaybackStatusStore (Core, alongside TroubleshootingNotifier) that records the exit code + speed of the most recent troubleshooting playback session. A new MediatR notification handler writes to it on PlaybackTroubleshootingCompletedNotification, and PrepareTroubleshootingPlaybackHandler resets it when a new session starts (both the channel and media-item lock paths). Co-Authored-By: Claude Fable 5 --- .../PrepareTroubleshootingPlaybackHandler.cs | 4 +++ ...ordTroubleshootingPlaybackStatusHandler.cs | 14 ++++++++ .../ITroubleshootingPlaybackStatusStore.cs | 16 +++++++++ .../TroubleshootingPlaybackResult.cs | 3 ++ .../TroubleshootingPlaybackStatusStore.cs | 36 +++++++++++++++++++ ErsatzTV/Startup.cs | 1 + 6 files changed, 74 insertions(+) create mode 100644 ErsatzTV.Application/Troubleshooting/RecordTroubleshootingPlaybackStatusHandler.cs create mode 100644 ErsatzTV.Core/Interfaces/Troubleshooting/ITroubleshootingPlaybackStatusStore.cs create mode 100644 ErsatzTV.Core/Troubleshooting/TroubleshootingPlaybackResult.cs create mode 100644 ErsatzTV.Core/Troubleshooting/TroubleshootingPlaybackStatusStore.cs diff --git a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs index 09c470884..94daedf0d 100644 --- a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs +++ b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs @@ -11,6 +11,7 @@ using ErsatzTV.Core.Interfaces.Jellyfin; using ErsatzTV.Core.Interfaces.Locking; using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Plex; +using ErsatzTV.Core.Interfaces.Troubleshooting; using ErsatzTV.Core.Notifications; using ErsatzTV.FFmpeg; using ErsatzTV.FFmpeg.State; @@ -34,6 +35,7 @@ public class PrepareTroubleshootingPlaybackHandler( ISongVideoGenerator songVideoGenerator, IWatermarkSelector watermarkSelector, IEntityLocker entityLocker, + ITroubleshootingPlaybackStatusStore statusStore, IMediator mediator, LoggingLevelSwitches loggingLevelSwitches, ILogger logger) @@ -68,6 +70,7 @@ public class PrepareTroubleshootingPlaybackHandler( } entityLocker.LockTroubleshootingPlayback(); + statusStore.Reset(); localFileSystem.EnsureFolderExists(FileSystemLayout.TranscodeTroubleshootingFolder); localFileSystem.EmptyFolder(FileSystemLayout.TranscodeTroubleshootingFolder); @@ -166,6 +169,7 @@ public class PrepareTroubleshootingPlaybackHandler( } entityLocker.LockTroubleshootingPlayback(); + statusStore.Reset(); localFileSystem.EnsureFolderExists(FileSystemLayout.TranscodeTroubleshootingFolder); localFileSystem.EmptyFolder(FileSystemLayout.TranscodeTroubleshootingFolder); diff --git a/ErsatzTV.Application/Troubleshooting/RecordTroubleshootingPlaybackStatusHandler.cs b/ErsatzTV.Application/Troubleshooting/RecordTroubleshootingPlaybackStatusHandler.cs new file mode 100644 index 000000000..47438511e --- /dev/null +++ b/ErsatzTV.Application/Troubleshooting/RecordTroubleshootingPlaybackStatusHandler.cs @@ -0,0 +1,14 @@ +using ErsatzTV.Core.Interfaces.Troubleshooting; +using ErsatzTV.Core.Notifications; + +namespace ErsatzTV.Application.Troubleshooting; + +public class RecordTroubleshootingPlaybackStatusHandler(ITroubleshootingPlaybackStatusStore statusStore) + : INotificationHandler +{ + public Task Handle(PlaybackTroubleshootingCompletedNotification notification, CancellationToken cancellationToken) + { + statusStore.RecordCompletion(notification.ExitCode, notification.MaybeSpeed); + return Task.CompletedTask; + } +} diff --git a/ErsatzTV.Core/Interfaces/Troubleshooting/ITroubleshootingPlaybackStatusStore.cs b/ErsatzTV.Core/Interfaces/Troubleshooting/ITroubleshootingPlaybackStatusStore.cs new file mode 100644 index 000000000..0bb6d1a8b --- /dev/null +++ b/ErsatzTV.Core/Interfaces/Troubleshooting/ITroubleshootingPlaybackStatusStore.cs @@ -0,0 +1,16 @@ +using ErsatzTV.Core.Troubleshooting; + +namespace ErsatzTV.Core.Interfaces.Troubleshooting; + +// Records the outcome of the most recent troubleshooting playback session so the SPA can poll +// for completion (the legacy Blazor page instead subscribed to an in-process notification). The +// store is reset when a new session starts (see PrepareTroubleshootingPlaybackHandler) and written +// by the PlaybackTroubleshootingCompletedNotification handler. +public interface ITroubleshootingPlaybackStatusStore +{ + Option CurrentResult { get; } + + void Reset(); + + void RecordCompletion(int exitCode, Option speed); +} diff --git a/ErsatzTV.Core/Troubleshooting/TroubleshootingPlaybackResult.cs b/ErsatzTV.Core/Troubleshooting/TroubleshootingPlaybackResult.cs new file mode 100644 index 000000000..0b280099c --- /dev/null +++ b/ErsatzTV.Core/Troubleshooting/TroubleshootingPlaybackResult.cs @@ -0,0 +1,3 @@ +namespace ErsatzTV.Core.Troubleshooting; + +public record TroubleshootingPlaybackResult(int ExitCode, Option Speed); diff --git a/ErsatzTV.Core/Troubleshooting/TroubleshootingPlaybackStatusStore.cs b/ErsatzTV.Core/Troubleshooting/TroubleshootingPlaybackStatusStore.cs new file mode 100644 index 000000000..da7fa5400 --- /dev/null +++ b/ErsatzTV.Core/Troubleshooting/TroubleshootingPlaybackStatusStore.cs @@ -0,0 +1,36 @@ +using ErsatzTV.Core.Interfaces.Troubleshooting; + +namespace ErsatzTV.Core.Troubleshooting; + +public class TroubleshootingPlaybackStatusStore : ITroubleshootingPlaybackStatusStore +{ + private readonly object _sync = new(); + private Option _result = Option.None; + + public Option CurrentResult + { + get + { + lock (_sync) + { + return _result; + } + } + } + + public void Reset() + { + lock (_sync) + { + _result = Option.None; + } + } + + public void RecordCompletion(int exitCode, Option speed) + { + lock (_sync) + { + _result = new TroubleshootingPlaybackResult(exitCode, speed); + } + } +} diff --git a/ErsatzTV/Startup.cs b/ErsatzTV/Startup.cs index 7b5ed4fe4..8edf58221 100644 --- a/ErsatzTV/Startup.cs +++ b/ErsatzTV/Startup.cs @@ -832,6 +832,7 @@ public class Startup services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(Program.InMemoryLogService);