From 8bb44b85d7008ee167f2260934a1f325abb06e2d Mon Sep 17 00:00:00 2001 From: herby2212 <12448284+herby2212@users.noreply.github.com> Date: Mon, 1 May 2023 16:24:15 +0200 Subject: [PATCH 1/4] close inactive sessions after 10 minutes --- .../Session/SessionManager.cs | 76 +++++++++++++++++-- .../Session/SessionInfo.cs | 6 ++ 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 5f6dc93fb3..32eff350b1 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -63,6 +63,9 @@ namespace Emby.Server.Implementations.Session private readonly ConcurrentDictionary _activeConnections = new(StringComparer.OrdinalIgnoreCase); private Timer _idleTimer; + private Timer _inactiveTimer; + + private int inactiveMinutesThreshold = 10; private DtoOptions _itemInfoDtoOptions; private bool _disposed = false; @@ -171,9 +174,11 @@ namespace Emby.Server.Implementations.Session if (disposing) { _idleTimer?.Dispose(); + _inactiveTimer?.Dispose(); } _idleTimer = null; + _inactiveTimer = null; _deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated; @@ -397,6 +402,15 @@ namespace Emby.Server.Implementations.Session session.LastPlaybackCheckIn = DateTime.UtcNow; } + if (info.IsPaused && session.LastPausedDate.HasValue == false) + { + session.LastPausedDate = DateTime.UtcNow; + } + else if (!info.IsPaused) + { + session.LastPausedDate = null; + } + session.PlayState.IsPaused = info.IsPaused; session.PlayState.PositionTicks = info.PositionTicks; session.PlayState.MediaSourceId = info.MediaSourceId; @@ -564,9 +578,10 @@ namespace Emby.Server.Implementations.Session return users; } - private void StartIdleCheckTimer() + private void StartCheckTimers() { _idleTimer ??= new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)); + _inactiveTimer ??= new Timer(CheckForInactiveSteams, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); } private void StopIdleCheckTimer() @@ -578,6 +593,15 @@ namespace Emby.Server.Implementations.Session } } + private void StopInactiveCheckTimer() + { + if (_inactiveTimer is not null) + { + _inactiveTimer.Dispose(); + _inactiveTimer = null; + } + } + private async void CheckForIdlePlayback(object state) { var playingSessions = Sessions.Where(i => i.NowPlayingItem is not null) @@ -613,13 +637,55 @@ namespace Emby.Server.Implementations.Session playingSessions = Sessions.Where(i => i.NowPlayingItem is not null) .ToList(); } - - if (playingSessions.Count == 0) + else { StopIdleCheckTimer(); } } + private async void CheckForInactiveSteams(object state) + { + var pausedSessions = Sessions.Where(i => + (i.NowPlayingItem is not null) && + (i.PlayState.IsPaused == true) && + (i.LastPausedDate is not null)).ToList(); + + if (pausedSessions.Count > 0) + { + var inactiveSessions = Sessions.Where(i => (DateTime.UtcNow - i.LastPausedDate).Value.TotalMinutes > inactiveMinutesThreshold).ToList(); + + foreach (var session in inactiveSessions) + { + _logger.LogDebug("Session {0} has been inactive for {1} minutes. Stopping it.", session.Id, inactiveMinutesThreshold); + + try + { + await SendPlaystateCommand( + session.Id, + session.Id, + new PlaystateRequest() + { + Command = PlaystateCommand.Stop, + ControllingUserId = session.UserId.ToString(), + SeekPositionTicks = session.PlayState?.PositionTicks + }, + CancellationToken.None).ConfigureAwait(true); + } + catch (Exception ex) + { + _logger.LogDebug(ex, "Error calling SendPlaystateCommand for stopping inactive session {0}.", session.Id); + } + } + } + + var playingSessions = Sessions.Where(i => i.NowPlayingItem is not null) + .ToList(); + if (playingSessions.Count == 0) + { + StopInactiveCheckTimer(); + } + } + private BaseItem GetNowPlayingItem(SessionInfo session, Guid itemId) { var item = session.FullNowPlayingItem; @@ -696,7 +762,7 @@ namespace Emby.Server.Implementations.Session eventArgs, _logger); - StartIdleCheckTimer(); + StartCheckTimers(); } /// @@ -790,7 +856,7 @@ namespace Emby.Server.Implementations.Session session.StartAutomaticProgress(info); } - StartIdleCheckTimer(); + StartCheckTimers(); } private void OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info) diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index 25bf23d61b..172d79a599 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -109,6 +109,12 @@ namespace MediaBrowser.Controller.Session /// The last playback check in. public DateTime LastPlaybackCheckIn { get; set; } + /// + /// Gets or sets the last paused date. + /// + /// The last paused date. + public DateTime? LastPausedDate { get; set; } + /// /// Gets or sets the name of the device. /// From e1190d15d6ca0b7cad2b4991e524b35ecca35949 Mon Sep 17 00:00:00 2001 From: herby2212 <12448284+herby2212@users.noreply.github.com> Date: Mon, 1 May 2023 20:11:22 +0200 Subject: [PATCH 2/4] option to disable and configure inactive session threshold --- .../Session/SessionManager.cs | 20 ++++++++++++++----- .../Configuration/ServerConfiguration.cs | 6 ++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 32eff350b1..056c3e4b61 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -19,6 +19,7 @@ using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; using MediaBrowser.Controller.Authentication; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; @@ -46,6 +47,7 @@ namespace Emby.Server.Implementations.Session public class SessionManager : ISessionManager, IDisposable { private readonly IUserDataManager _userDataManager; + private readonly IServerConfigurationManager _config; private readonly ILogger _logger; private readonly IEventManager _eventManager; private readonly ILibraryManager _libraryManager; @@ -65,8 +67,6 @@ namespace Emby.Server.Implementations.Session private Timer _idleTimer; private Timer _inactiveTimer; - private int inactiveMinutesThreshold = 10; - private DtoOptions _itemInfoDtoOptions; private bool _disposed = false; @@ -74,6 +74,7 @@ namespace Emby.Server.Implementations.Session ILogger logger, IEventManager eventManager, IUserDataManager userDataManager, + IServerConfigurationManager config, ILibraryManager libraryManager, IUserManager userManager, IMusicManager musicManager, @@ -86,6 +87,7 @@ namespace Emby.Server.Implementations.Session _logger = logger; _eventManager = eventManager; _userDataManager = userDataManager; + _config = config; _libraryManager = libraryManager; _userManager = userManager; _musicManager = musicManager; @@ -581,7 +583,15 @@ namespace Emby.Server.Implementations.Session private void StartCheckTimers() { _idleTimer ??= new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)); - _inactiveTimer ??= new Timer(CheckForInactiveSteams, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); + + if (_config.Configuration.InactiveSessionThreshold > 0) + { + _inactiveTimer ??= new Timer(CheckForInactiveSteams, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); + } + else + { + StopInactiveCheckTimer(); + } } private void StopIdleCheckTimer() @@ -652,11 +662,11 @@ namespace Emby.Server.Implementations.Session if (pausedSessions.Count > 0) { - var inactiveSessions = Sessions.Where(i => (DateTime.UtcNow - i.LastPausedDate).Value.TotalMinutes > inactiveMinutesThreshold).ToList(); + var inactiveSessions = pausedSessions.Where(i => (DateTime.UtcNow - i.LastPausedDate).Value.TotalMinutes > _config.Configuration.InactiveSessionThreshold).ToList(); foreach (var session in inactiveSessions) { - _logger.LogDebug("Session {0} has been inactive for {1} minutes. Stopping it.", session.Id, inactiveMinutesThreshold); + _logger.LogDebug("Session {0} has been inactive for {1} minutes. Stopping it.", session.Id, _config.Configuration.InactiveSessionThreshold); try { diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 07f02d1879..f619f384c6 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -157,6 +157,12 @@ namespace MediaBrowser.Model.Configuration /// The remaining time in minutes. public int MaxAudiobookResume { get; set; } = 5; + /// + /// Gets or sets the threshold in minutes after a inactive session gets closed automatically. + /// + /// The close inactive session threshold in minutes. + public int InactiveSessionThreshold { get; set; } = 10; + /// /// Gets or sets the delay in seconds that we will wait after a file system change to try and discover what has been added/removed /// Some delay is necessary with some items because their creation is not atomic. It involves the creation of several From ace89e45976a65a365a3d9d7a2ed737a61d584d4 Mon Sep 17 00:00:00 2001 From: herby2212 <12448284+herby2212@users.noreply.github.com> Date: Sun, 14 May 2023 15:05:03 +0200 Subject: [PATCH 3/4] fix formatting and update summary --- Emby.Server.Implementations/Session/SessionManager.cs | 9 +++++---- MediaBrowser.Model/Configuration/ServerConfiguration.cs | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 056c3e4b61..14a62626c5 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -404,7 +404,7 @@ namespace Emby.Server.Implementations.Session session.LastPlaybackCheckIn = DateTime.UtcNow; } - if (info.IsPaused && session.LastPausedDate.HasValue == false) + if (info.IsPaused && session.LastPausedDate is null) { session.LastPausedDate = DateTime.UtcNow; } @@ -656,9 +656,10 @@ namespace Emby.Server.Implementations.Session private async void CheckForInactiveSteams(object state) { var pausedSessions = Sessions.Where(i => - (i.NowPlayingItem is not null) && - (i.PlayState.IsPaused == true) && - (i.LastPausedDate is not null)).ToList(); + i.NowPlayingItem is not null + && i.PlayState.IsPaused + && i.LastPausedDate is not null) + .ToList(); if (pausedSessions.Count > 0) { diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index f619f384c6..40dcf50b75 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -159,8 +159,9 @@ namespace MediaBrowser.Model.Configuration /// /// Gets or sets the threshold in minutes after a inactive session gets closed automatically. + /// If set to 0 the check for inactive sessions gets disabled. /// - /// The close inactive session threshold in minutes. + /// The close inactive session threshold in minutes. 0 to disable. public int InactiveSessionThreshold { get; set; } = 10; /// From 56aa37a314df17ba7608d7d9d00925f9e4816a89 Mon Sep 17 00:00:00 2001 From: herby2212 <12448284+herby2212@users.noreply.github.com> Date: Sat, 7 Oct 2023 20:52:16 +0200 Subject: [PATCH 4/4] Switch to named placeholders --- Emby.Server.Implementations/Session/SessionManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 14a62626c5..a6155aa621 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -667,7 +667,7 @@ namespace Emby.Server.Implementations.Session foreach (var session in inactiveSessions) { - _logger.LogDebug("Session {0} has been inactive for {1} minutes. Stopping it.", session.Id, _config.Configuration.InactiveSessionThreshold); + _logger.LogDebug("Session {Session} has been inactive for {InactiveTime} minutes. Stopping it.", session.Id, _config.Configuration.InactiveSessionThreshold); try { @@ -684,7 +684,7 @@ namespace Emby.Server.Implementations.Session } catch (Exception ex) { - _logger.LogDebug(ex, "Error calling SendPlaystateCommand for stopping inactive session {0}.", session.Id); + _logger.LogDebug(ex, "Error calling SendPlaystateCommand for stopping inactive session {Session}.", session.Id); } } }