aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Session/SessionManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Session/SessionManager.cs')
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs126
1 files changed, 104 insertions, 22 deletions
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 030da6f73..cf2ca047c 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -7,11 +7,13 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Jellyfin.Data.Entities;
-using Jellyfin.Data.Entities.Security;
+using Jellyfin.Data;
using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using Jellyfin.Data.Queries;
+using Jellyfin.Database.Implementations.Entities;
+using Jellyfin.Database.Implementations.Entities.Security;
+using Jellyfin.Database.Implementations.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
@@ -62,6 +64,9 @@ namespace Emby.Server.Implementations.Session
private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections
= new(StringComparer.OrdinalIgnoreCase);
+ private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, string>> _activeLiveStreamSessions
+ = new(StringComparer.OrdinalIgnoreCase);
+
private Timer _idleTimer;
private Timer _inactiveTimer;
@@ -309,7 +314,7 @@ namespace Emby.Server.Implementations.Session
_activeConnections.TryRemove(key, out _);
if (!string.IsNullOrEmpty(session.PlayState?.LiveStreamId))
{
- await _mediaSourceManager.CloseLiveStream(session.PlayState.LiveStreamId).ConfigureAwait(false);
+ await CloseLiveStreamIfNeededAsync(session.PlayState.LiveStreamId, session.Id).ConfigureAwait(false);
}
await OnSessionEnded(session).ConfigureAwait(false);
@@ -317,6 +322,42 @@ namespace Emby.Server.Implementations.Session
}
/// <inheritdoc />
+ public async Task CloseLiveStreamIfNeededAsync(string liveStreamId, string sessionIdOrPlaySessionId)
+ {
+ bool liveStreamNeedsToBeClosed = false;
+
+ if (_activeLiveStreamSessions.TryGetValue(liveStreamId, out var activeSessionMappings))
+ {
+ if (activeSessionMappings.TryRemove(sessionIdOrPlaySessionId, out var correspondingId))
+ {
+ if (!string.IsNullOrEmpty(correspondingId))
+ {
+ activeSessionMappings.TryRemove(correspondingId, out _);
+ }
+
+ liveStreamNeedsToBeClosed = true;
+ }
+
+ if (activeSessionMappings.IsEmpty)
+ {
+ _activeLiveStreamSessions.TryRemove(liveStreamId, out _);
+ }
+ }
+
+ if (liveStreamNeedsToBeClosed)
+ {
+ try
+ {
+ await _mediaSourceManager.CloseLiveStream(liveStreamId).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error closing live stream");
+ }
+ }
+ }
+
+ /// <inheritdoc />
public async ValueTask ReportSessionEnded(string sessionId)
{
CheckDisposed();
@@ -343,6 +384,11 @@ namespace Emby.Server.Implementations.Session
/// <returns>Task.</returns>
private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem, bool updateLastCheckInTime)
{
+ if (session is null)
+ {
+ return;
+ }
+
if (string.IsNullOrEmpty(info.MediaSourceId))
{
info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture);
@@ -410,7 +456,7 @@ namespace Emby.Server.Implementations.Session
var nowPlayingQueue = info.NowPlayingQueue;
- if (nowPlayingQueue?.Length > 0)
+ if (nowPlayingQueue?.Length > 0 && !nowPlayingQueue.SequenceEqual(session.NowPlayingQueue))
{
session.NowPlayingQueue = nowPlayingQueue;
@@ -428,6 +474,7 @@ namespace Emby.Server.Implementations.Session
private void RemoveNowPlayingItem(SessionInfo session)
{
session.NowPlayingItem = null;
+ session.FullNowPlayingItem = null;
session.PlayState = new PlayerStateInfo();
if (!string.IsNullOrEmpty(session.DeviceId))
@@ -462,13 +509,11 @@ namespace Emby.Server.Implementations.Session
ArgumentException.ThrowIfNullOrEmpty(deviceId);
var key = GetSessionKey(appName, deviceId);
-
- CheckDisposed();
-
- if (!_activeConnections.TryGetValue(key, out var sessionInfo))
+ SessionInfo newSession = CreateSessionInfo(key, appName, appVersion, deviceId, deviceName, remoteEndPoint, user);
+ SessionInfo sessionInfo = _activeConnections.GetOrAdd(key, newSession);
+ if (ReferenceEquals(newSession, sessionInfo))
{
- sessionInfo = CreateSession(key, appName, appVersion, deviceId, deviceName, remoteEndPoint, user);
- _activeConnections[key] = sessionInfo;
+ OnSessionStarted(newSession);
}
sessionInfo.UserId = user?.Id ?? Guid.Empty;
@@ -492,7 +537,7 @@ namespace Emby.Server.Implementations.Session
return sessionInfo;
}
- private SessionInfo CreateSession(
+ private SessionInfo CreateSessionInfo(
string key,
string appName,
string appVersion,
@@ -536,7 +581,6 @@ namespace Emby.Server.Implementations.Session
sessionInfo.HasCustomDeviceName = true;
}
- OnSessionStarted(sessionInfo);
return sessionInfo;
}
@@ -675,6 +719,11 @@ namespace Emby.Server.Implementations.Session
private BaseItem GetNowPlayingItem(SessionInfo session, Guid itemId)
{
+ if (session is null)
+ {
+ return null;
+ }
+
var item = session.FullNowPlayingItem;
if (item is not null && item.Id.Equals(itemId))
{
@@ -725,6 +774,11 @@ namespace Emby.Server.Implementations.Session
}
}
+ if (!string.IsNullOrEmpty(info.LiveStreamId))
+ {
+ UpdateLiveStreamActiveSessionMappings(info.LiveStreamId, info.SessionId, info.PlaySessionId);
+ }
+
var eventArgs = new PlaybackStartEventArgs
{
Item = libraryItem,
@@ -782,6 +836,32 @@ namespace Emby.Server.Implementations.Session
return OnPlaybackProgress(info, false);
}
+ private void UpdateLiveStreamActiveSessionMappings(string liveStreamId, string sessionId, string playSessionId)
+ {
+ var activeSessionMappings = _activeLiveStreamSessions.GetOrAdd(liveStreamId, _ => new ConcurrentDictionary<string, string>());
+
+ if (!string.IsNullOrEmpty(playSessionId))
+ {
+ if (!activeSessionMappings.TryGetValue(sessionId, out var currentPlaySessionId) || currentPlaySessionId != playSessionId)
+ {
+ if (!string.IsNullOrEmpty(currentPlaySessionId))
+ {
+ activeSessionMappings.TryRemove(currentPlaySessionId, out _);
+ }
+
+ activeSessionMappings[sessionId] = playSessionId;
+ activeSessionMappings[playSessionId] = sessionId;
+ }
+ }
+ else
+ {
+ if (!activeSessionMappings.TryGetValue(sessionId, out _))
+ {
+ activeSessionMappings[sessionId] = string.Empty;
+ }
+ }
+ }
+
/// <summary>
/// Used to report playback progress for an item.
/// </summary>
@@ -794,7 +874,11 @@ namespace Emby.Server.Implementations.Session
ArgumentNullException.ThrowIfNull(info);
- var session = GetSession(info.SessionId);
+ var session = GetSession(info.SessionId, false);
+ if (session is null)
+ {
+ return;
+ }
var libraryItem = info.ItemId.IsEmpty()
? null
@@ -818,6 +902,11 @@ namespace Emby.Server.Implementations.Session
}
}
+ if (!string.IsNullOrEmpty(info.LiveStreamId))
+ {
+ UpdateLiveStreamActiveSessionMappings(info.LiveStreamId, info.SessionId, info.PlaySessionId);
+ }
+
var eventArgs = new PlaybackProgressEventArgs
{
Item = libraryItem,
@@ -1000,14 +1089,7 @@ namespace Emby.Server.Implementations.Session
if (!string.IsNullOrEmpty(info.LiveStreamId))
{
- try
- {
- await _mediaSourceManager.CloseLiveStream(info.LiveStreamId).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error closing live stream");
- }
+ await CloseLiveStreamIfNeededAsync(info.LiveStreamId, session.Id).ConfigureAwait(false);
}
var eventArgs = new PlaybackStopEventArgs
@@ -1724,7 +1806,6 @@ namespace Emby.Server.Implementations.Session
fields.Remove(ItemFields.DateLastSaved);
fields.Remove(ItemFields.DisplayPreferencesId);
fields.Remove(ItemFields.Etag);
- fields.Remove(ItemFields.InheritedParentalRatingValue);
fields.Remove(ItemFields.ItemCounts);
fields.Remove(ItemFields.MediaSourceCount);
fields.Remove(ItemFields.MediaStreams);
@@ -2055,6 +2136,7 @@ namespace Emby.Server.Implementations.Session
}
_activeConnections.Clear();
+ _activeLiveStreamSessions.Clear();
}
}
}