Pārlūkot izejas kodu

Merge pull request #10357 from herby2212/kill-inactive-streams-v2

Add auto close of an inactive session after X minutes
Claus Vium 1 gadu atpakaļ
vecāks
revīzija
0973f1e0ba

+ 80 - 5
Emby.Server.Implementations/Session/SessionManager.cs

@@ -19,6 +19,7 @@ using MediaBrowser.Common.Events;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Authentication;
 using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Dto;
@@ -48,6 +49,7 @@ namespace Emby.Server.Implementations.Session
     public sealed class SessionManager : ISessionManager, IAsyncDisposable
     public sealed class SessionManager : ISessionManager, IAsyncDisposable
     {
     {
         private readonly IUserDataManager _userDataManager;
         private readonly IUserDataManager _userDataManager;
+        private readonly IServerConfigurationManager _config;
         private readonly ILogger<SessionManager> _logger;
         private readonly ILogger<SessionManager> _logger;
         private readonly IEventManager _eventManager;
         private readonly IEventManager _eventManager;
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
@@ -63,6 +65,7 @@ namespace Emby.Server.Implementations.Session
             = new(StringComparer.OrdinalIgnoreCase);
             = new(StringComparer.OrdinalIgnoreCase);
 
 
         private Timer _idleTimer;
         private Timer _idleTimer;
+        private Timer _inactiveTimer;
 
 
         private DtoOptions _itemInfoDtoOptions;
         private DtoOptions _itemInfoDtoOptions;
         private bool _disposed = false;
         private bool _disposed = false;
@@ -71,6 +74,7 @@ namespace Emby.Server.Implementations.Session
             ILogger<SessionManager> logger,
             ILogger<SessionManager> logger,
             IEventManager eventManager,
             IEventManager eventManager,
             IUserDataManager userDataManager,
             IUserDataManager userDataManager,
+            IServerConfigurationManager config,
             ILibraryManager libraryManager,
             ILibraryManager libraryManager,
             IUserManager userManager,
             IUserManager userManager,
             IMusicManager musicManager,
             IMusicManager musicManager,
@@ -84,6 +88,7 @@ namespace Emby.Server.Implementations.Session
             _logger = logger;
             _logger = logger;
             _eventManager = eventManager;
             _eventManager = eventManager;
             _userDataManager = userDataManager;
             _userDataManager = userDataManager;
+            _config = config;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _userManager = userManager;
             _userManager = userManager;
             _musicManager = musicManager;
             _musicManager = musicManager;
@@ -369,6 +374,15 @@ namespace Emby.Server.Implementations.Session
                 session.LastPlaybackCheckIn = DateTime.UtcNow;
                 session.LastPlaybackCheckIn = DateTime.UtcNow;
             }
             }
 
 
+            if (info.IsPaused && session.LastPausedDate is null)
+            {
+                session.LastPausedDate = DateTime.UtcNow;
+            }
+            else if (!info.IsPaused)
+            {
+                session.LastPausedDate = null;
+            }
+
             session.PlayState.IsPaused = info.IsPaused;
             session.PlayState.IsPaused = info.IsPaused;
             session.PlayState.PositionTicks = info.PositionTicks;
             session.PlayState.PositionTicks = info.PositionTicks;
             session.PlayState.MediaSourceId = info.MediaSourceId;
             session.PlayState.MediaSourceId = info.MediaSourceId;
@@ -536,9 +550,18 @@ namespace Emby.Server.Implementations.Session
             return users;
             return users;
         }
         }
 
 
-        private void StartIdleCheckTimer()
+        private void StartCheckTimers()
         {
         {
             _idleTimer ??= new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
             _idleTimer ??= new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
+
+            if (_config.Configuration.InactiveSessionThreshold > 0)
+            {
+                _inactiveTimer ??= new Timer(CheckForInactiveSteams, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
+            }
+            else
+            {
+                StopInactiveCheckTimer();
+            }
         }
         }
 
 
         private void StopIdleCheckTimer()
         private void StopIdleCheckTimer()
@@ -550,6 +573,15 @@ namespace Emby.Server.Implementations.Session
             }
             }
         }
         }
 
 
+        private void StopInactiveCheckTimer()
+        {
+            if (_inactiveTimer is not null)
+            {
+                _inactiveTimer.Dispose();
+                _inactiveTimer = null;
+            }
+        }
+
         private async void CheckForIdlePlayback(object state)
         private async void CheckForIdlePlayback(object state)
         {
         {
             var playingSessions = Sessions.Where(i => i.NowPlayingItem is not null)
             var playingSessions = Sessions.Where(i => i.NowPlayingItem is not null)
@@ -585,13 +617,50 @@ namespace Emby.Server.Implementations.Session
                 playingSessions = Sessions.Where(i => i.NowPlayingItem is not null)
                 playingSessions = Sessions.Where(i => i.NowPlayingItem is not null)
                     .ToList();
                     .ToList();
             }
             }
-
-            if (playingSessions.Count == 0)
+            else
             {
             {
                 StopIdleCheckTimer();
                 StopIdleCheckTimer();
             }
             }
         }
         }
 
 
+        private async void CheckForInactiveSteams(object state)
+        {
+            var inactiveSessions = Sessions.Where(i =>
+                    i.NowPlayingItem is not null
+                    && i.PlayState.IsPaused
+                    && (DateTime.UtcNow - i.LastPausedDate).Value.TotalMinutes > _config.Configuration.InactiveSessionThreshold);
+
+            foreach (var session in inactiveSessions)
+            {
+                _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);
+                }
+            }
+
+            bool playingSessions = Sessions.Any(i => i.NowPlayingItem is not null);
+
+            if (!playingSessions)
+            {
+                StopInactiveCheckTimer();
+            }
+        }
+
         private BaseItem GetNowPlayingItem(SessionInfo session, Guid itemId)
         private BaseItem GetNowPlayingItem(SessionInfo session, Guid itemId)
         {
         {
             var item = session.FullNowPlayingItem;
             var item = session.FullNowPlayingItem;
@@ -668,7 +737,7 @@ namespace Emby.Server.Implementations.Session
                 eventArgs,
                 eventArgs,
                 _logger);
                 _logger);
 
 
-            StartIdleCheckTimer();
+            StartCheckTimers();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -762,7 +831,7 @@ namespace Emby.Server.Implementations.Session
                 session.StartAutomaticProgress(info);
                 session.StartAutomaticProgress(info);
             }
             }
 
 
-            StartIdleCheckTimer();
+            StartCheckTimers();
         }
         }
 
 
         private void OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info)
         private void OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info)
@@ -1798,6 +1867,12 @@ namespace Emby.Server.Implementations.Session
                 _idleTimer = null;
                 _idleTimer = null;
             }
             }
 
 
+            if (_inactiveTimer is not null)
+            {
+                await _inactiveTimer.DisposeAsync().ConfigureAwait(false);
+                _inactiveTimer = null;
+            }
+
             await _shutdownCallback.DisposeAsync().ConfigureAwait(false);
             await _shutdownCallback.DisposeAsync().ConfigureAwait(false);
 
 
             _deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated;
             _deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated;

+ 6 - 0
MediaBrowser.Controller/Session/SessionInfo.cs

@@ -109,6 +109,12 @@ namespace MediaBrowser.Controller.Session
         /// <value>The last playback check in.</value>
         /// <value>The last playback check in.</value>
         public DateTime LastPlaybackCheckIn { get; set; }
         public DateTime LastPlaybackCheckIn { get; set; }
 
 
+        /// <summary>
+        /// Gets or sets the last paused date.
+        /// </summary>
+        /// <value>The last paused date.</value>
+        public DateTime? LastPausedDate { get; set; }
+
         /// <summary>
         /// <summary>
         /// Gets or sets the name of the device.
         /// Gets or sets the name of the device.
         /// </summary>
         /// </summary>

+ 7 - 0
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -158,6 +158,13 @@ public class ServerConfiguration : BaseApplicationConfiguration
     /// <value>The remaining time in minutes.</value>
     /// <value>The remaining time in minutes.</value>
     public int MaxAudiobookResume { get; set; } = 5;
     public int MaxAudiobookResume { get; set; } = 5;
 
 
+    /// <summary>
+    /// 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.
+    /// </summary>
+    /// <value>The close inactive session threshold in minutes. 0 to disable.</value>
+    public int InactiveSessionThreshold { get; set; } = 10;
+
     /// <summary>
     /// <summary>
     /// 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
     /// 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
     /// Some delay is necessary with some items because their creation is not atomic.  It involves the creation of several