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/7] close inactive sessions after 10 minutes --- .../Session/SessionManager.cs | 74 ++++++++++++++++++- .../Session/SessionInfo.cs | 6 ++ 2 files changed, 76 insertions(+), 4 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,10 +637,52 @@ namespace Emby.Server.Implementations.Session playingSessions = Sessions.Where(i => i.NowPlayingItem is not null) .ToList(); } + 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) { - StopIdleCheckTimer(); + StopInactiveCheckTimer(); } } @@ -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/7] 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/7] 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/7] 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); } } } From 994619afb2287b0ee80eab2511393a7803a3ddb6 Mon Sep 17 00:00:00 2001 From: herby2212 <12448284+herby2212@users.noreply.github.com> Date: Sun, 8 Oct 2023 13:49:35 +0200 Subject: [PATCH 5/7] fix formatting for build process --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index b5d8f6c3b7..329138feb8 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1873,7 +1873,7 @@ namespace Emby.Server.Implementations.Session _idleTimer = null; } - if(_inactiveTimer is not null) + if (_inactiveTimer is not null) { await _inactiveTimer.DisposeAsync().ConfigureAwait(false); _inactiveTimer = null; From e8a05ad996ab35909cd5928d49c4e8c46e9985d5 Mon Sep 17 00:00:00 2001 From: herby2212 <12448284+herby2212@users.noreply.github.com> Date: Mon, 9 Oct 2023 19:15:25 +0200 Subject: [PATCH 6/7] optimize checkForInactiveStreams logic --- .../Session/SessionManager.cs | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 329138feb8..dc59a45239 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -625,43 +625,37 @@ namespace Emby.Server.Implementations.Session private async void CheckForInactiveSteams(object state) { - var pausedSessions = Sessions.Where(i => + var inactiveSessions = Sessions.Where(i => i.NowPlayingItem is not null && i.PlayState.IsPaused - && i.LastPausedDate is not null) - .ToList(); + && (DateTime.UtcNow - i.LastPausedDate).Value.TotalMinutes > _config.Configuration.InactiveSessionThreshold); - if (pausedSessions.Count > 0) + foreach (var session in inactiveSessions) { - var inactiveSessions = pausedSessions.Where(i => (DateTime.UtcNow - i.LastPausedDate).Value.TotalMinutes > _config.Configuration.InactiveSessionThreshold).ToList(); + _logger.LogDebug("Session {Session} has been inactive for {InactiveTime} minutes. Stopping it.", session.Id, _config.Configuration.InactiveSessionThreshold); - foreach (var session in inactiveSessions) + try { - _logger.LogDebug("Session {Session} has been inactive for {InactiveTime} minutes. Stopping it.", session.Id, _config.Configuration.InactiveSessionThreshold); - - 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 {Session}.", session.Id); - } + 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 {Session}.", session.Id); } } - var playingSessions = Sessions.Where(i => i.NowPlayingItem is not null) - .ToList(); - if (playingSessions.Count == 0) + bool playingSessions = Sessions.Any(i => i.NowPlayingItem is not null); + + if (!playingSessions) { StopInactiveCheckTimer(); } From 6c5a4a93ccdb7c6ca8b26c36505d0954a7c79300 Mon Sep 17 00:00:00 2001 From: herby2212 <12448284+herby2212@users.noreply.github.com> Date: Sat, 21 Oct 2023 11:49:01 +0200 Subject: [PATCH 7/7] fix indentation after merge --- .../Configuration/ServerConfiguration.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 2fb5fbd0b0..fe92251e9b 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -158,20 +158,20 @@ public class ServerConfiguration : BaseApplicationConfiguration /// 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. - /// If set to 0 the check for inactive sessions gets disabled. - /// - /// The close inactive session threshold in minutes. 0 to disable. - 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 - /// different directories and files. - /// - /// The file watcher delay. - public int LibraryMonitorDelay { get; set; } = 60; + /// + /// 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. 0 to disable. + 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 + /// different directories and files. + /// + /// The file watcher delay. + public int LibraryMonitorDelay { get; set; } = 60; /// /// Gets or sets the duration in seconds that we will wait after a library updated event before executing the library changed notification.