From 83741d798086d34db22639ab5ecab14891e113be Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sun, 14 Dec 2025 10:53:13 +0100 Subject: [PATCH] track progress --- .../Services/LmsDeviceDiscoveryService.cs | 68 ++++++++++++++++++ .../Services/LmsSessionController.cs | 72 ++++++++++++++++--- 2 files changed, 130 insertions(+), 10 deletions(-) diff --git a/Jellyfin.Plugin.JellyLMS/Services/LmsDeviceDiscoveryService.cs b/Jellyfin.Plugin.JellyLMS/Services/LmsDeviceDiscoveryService.cs index cdc3c29..9ea4131 100644 --- a/Jellyfin.Plugin.JellyLMS/Services/LmsDeviceDiscoveryService.cs +++ b/Jellyfin.Plugin.JellyLMS/Services/LmsDeviceDiscoveryService.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Concurrent; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Enums; using Jellyfin.Plugin.JellyLMS.Models; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Session; using Microsoft.Extensions.DependencyInjection; @@ -219,6 +221,72 @@ public class LmsDeviceDiscoveryService : IHostedService, IDisposable player.MacAddress, session.Id); } + + // Report playback progress if the controller has an active item + if (controller is LmsSessionController lmsController) + { + await ReportPlaybackProgressAsync(sessionManager, session, lmsController).ConfigureAwait(false); + } + } + + private async Task ReportPlaybackProgressAsync( + ISessionManager sessionManager, + SessionInfo session, + LmsSessionController controller) + { + if (!controller.IsPlaying || !controller.CurrentItemId.HasValue) + { + return; + } + + try + { + // Get current playback status from LMS + var status = await _lmsClient.GetPlayerStatusAsync(controller.PlayerMac).ConfigureAwait(false); + if (status == null) + { + return; + } + + // Get the item from Jellyfin library + var libraryManager = _serviceProvider.GetService(); + if (libraryManager == null) + { + return; + } + + var item = libraryManager.GetItemById(controller.CurrentItemId.Value); + if (item == null) + { + return; + } + + // Calculate position in ticks + var positionTicks = (long)(status.Time * TimeSpan.TicksPerSecond); + + // Determine if paused + var isPaused = status.Mode == "pause"; + + // Create playback progress info + var progressInfo = new PlaybackProgressInfo + { + ItemId = controller.CurrentItemId.Value, + SessionId = session.Id, + IsPaused = isPaused, + PositionTicks = positionTicks, + PlayMethod = PlayMethod.DirectStream, + CanSeek = true, + IsMuted = status.Volume == 0, + VolumeLevel = status.Volume + }; + + // Report progress to session manager + await sessionManager.OnPlaybackProgress(progressInfo).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.LogDebug(ex, "Error reporting playback progress for player {Name}", controller.PlayerMac); + } } /// diff --git a/Jellyfin.Plugin.JellyLMS/Services/LmsSessionController.cs b/Jellyfin.Plugin.JellyLMS/Services/LmsSessionController.cs index 62bd65f..21fa6e3 100644 --- a/Jellyfin.Plugin.JellyLMS/Services/LmsSessionController.cs +++ b/Jellyfin.Plugin.JellyLMS/Services/LmsSessionController.cs @@ -39,8 +39,23 @@ public class LmsSessionController : ISessionController _session = session; } + /// + /// Gets or sets the currently playing item ID. + /// + public Guid? CurrentItemId { get; set; } + + /// + /// Gets or sets a value indicating whether playback is currently active. + /// + public bool IsPlaying { get; set; } + + /// + /// Gets or sets a value indicating whether playback is paused. + /// + public bool IsPaused { get; set; } + /// - public bool IsSessionActive => _player.IsConnected && _player.IsPoweredOn; + public bool IsSessionActive => _player.IsConnected; /// public bool SupportsMediaControl => true; @@ -57,10 +72,11 @@ public class LmsSessionController : ISessionController T data, CancellationToken cancellationToken) { - _logger.LogDebug( - "LMS Session Controller received message {MessageType} for player {PlayerName}", + _logger.LogInformation( + "LMS Session Controller received message {MessageType} for player {PlayerName} ({Mac})", name, - _player.Name); + _player.Name, + _player.MacAddress); try { @@ -118,6 +134,11 @@ public class LmsSessionController : ISessionController _logger.LogInformation("Playing stream URL: {Url}", streamUrl); await _lmsClient.PlayUrlAsync(_player.MacAddress, streamUrl).ConfigureAwait(false); + // Track current playback state + CurrentItemId = itemId; + IsPlaying = true; + IsPaused = false; + // Seek to start position if specified if (playRequest.StartPositionTicks.HasValue && playRequest.StartPositionTicks.Value > 0) { @@ -135,29 +156,60 @@ public class LmsSessionController : ISessionController return; } - _logger.LogDebug( - "Playstate command {Command} for player {PlayerName}", + _logger.LogInformation( + "Playstate command {Command} for player {PlayerName} ({Mac})", playstateRequest.Command, - _player.Name); + _player.Name, + _player.MacAddress); switch (playstateRequest.Command) { case PlaystateCommand.Stop: - await _lmsClient.StopAsync(_player.MacAddress).ConfigureAwait(false); + var stopResult = await _lmsClient.StopAsync(_player.MacAddress).ConfigureAwait(false); + _logger.LogInformation("Stop command result: {Result}", stopResult); + IsPlaying = false; + IsPaused = false; + CurrentItemId = null; break; case PlaystateCommand.Pause: - await _lmsClient.PauseAsync(_player.MacAddress).ConfigureAwait(false); + var pauseResult = await _lmsClient.PauseAsync(_player.MacAddress).ConfigureAwait(false); + _logger.LogInformation("Pause command result: {Result}", pauseResult); + IsPaused = true; break; case PlaystateCommand.Unpause: - await _lmsClient.PlayAsync(_player.MacAddress).ConfigureAwait(false); + var playResult = await _lmsClient.PlayAsync(_player.MacAddress).ConfigureAwait(false); + _logger.LogInformation("Unpause/Play command result: {Result}", playResult); + IsPaused = false; + break; + + case PlaystateCommand.PlayPause: + // Toggle play/pause - check current state first + var currentState = await _lmsClient.GetPlayerStatusAsync(_player.MacAddress).ConfigureAwait(false); + if (currentState?.Mode == "play") + { + var togglePauseResult = await _lmsClient.PauseAsync(_player.MacAddress).ConfigureAwait(false); + _logger.LogInformation("PlayPause toggle (pause) result: {Result}", togglePauseResult); + IsPaused = true; + } + else + { + var togglePlayResult = await _lmsClient.PlayAsync(_player.MacAddress).ConfigureAwait(false); + _logger.LogInformation("PlayPause toggle (play) result: {Result}", togglePlayResult); + IsPaused = false; + } + break; case PlaystateCommand.Seek: if (playstateRequest.SeekPositionTicks.HasValue) { var positionSeconds = playstateRequest.SeekPositionTicks.Value / TimeSpan.TicksPerSecond; + _logger.LogInformation( + "Seeking player {PlayerName} to {Seconds} seconds", + _player.Name, + positionSeconds); await _lmsClient.SeekAsync(_player.MacAddress, positionSeconds).ConfigureAwait(false); }