| 
					
				 | 
			
			
				@@ -63,6 +63,9 @@ namespace Emby.Server.Implementations.Session 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private readonly ConcurrentDictionary<string, SessionInfo> _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(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         /// <summary> 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -790,7 +856,7 @@ namespace Emby.Server.Implementations.Session 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 session.StartAutomaticProgress(info); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            StartIdleCheckTimer(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            StartCheckTimers(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private void OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info) 
			 |