using System; using System.Threading; using System.Threading.Tasks; using Jellyfin.Plugin.SRFPlay.Services.Interfaces; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Session; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Jellyfin.Plugin.SRFPlay.Services; /// /// Watches for livestream playback ending and immediately drops the resume point Jellyfin /// just saved for it. Without this, every livestream a user stops watching sticks in /// "Continue Watching" until the nightly cleanup task runs. /// public class PlaybackResumeGuard : IHostedService { private readonly ILogger _logger; private readonly ISessionManager _sessionManager; private readonly IResumeCleanupService _resumeCleanupService; /// /// Initializes a new instance of the class. /// /// The logger. /// The session manager raising playback events. /// The resume cleanup service. public PlaybackResumeGuard( ILogger logger, ISessionManager sessionManager, IResumeCleanupService resumeCleanupService) { _logger = logger; _sessionManager = sessionManager; _resumeCleanupService = resumeCleanupService; } /// public Task StartAsync(CancellationToken cancellationToken) { _sessionManager.PlaybackStopped += OnPlaybackStopped; _logger.LogDebug("PlaybackResumeGuard attached to session manager"); return Task.CompletedTask; } /// public Task StopAsync(CancellationToken cancellationToken) { _sessionManager.PlaybackStopped -= OnPlaybackStopped; return Task.CompletedTask; } private void OnPlaybackStopped(object? sender, PlaybackStopEventArgs e) { // Jellyfin saves the resume point before raising this event, so clearing it here wins. if (e?.Item == null || e.Users == null || e.Users.Count == 0) { return; } try { var cleared = _resumeCleanupService.ClearLiveStreamResumePoint(e.Item, e.Users); if (cleared > 0) { _logger.LogDebug( "Dropped {Count} resume point(s) for finished livestream '{Name}'", cleared, e.Item.Name); } } catch (Exception ex) { // Never let a cleanup failure escape into Jellyfin's playback event pipeline. _logger.LogError(ex, "Error clearing livestream resume point for '{Name}'", e.Item.Name); } } }