aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Session
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Session')
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs56
-rw-r--r--Emby.Server.Implementations/Session/SessionWebSocketListener.cs15
2 files changed, 67 insertions, 4 deletions
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 19823dff37..828bdd6859 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -730,6 +730,31 @@ namespace Emby.Server.Implementations.Session
}
/// <summary>
+ /// Resolves the item whose user data (playback position, played status) should be updated
+ /// for a playback report. When an alternate version is played the client reports the displayed
+ /// item as <c>ItemId</c> and the played version as <c>MediaSourceId</c>.
+ /// </summary>
+ /// <param name="libraryItem">The now playing (displayed) item.</param>
+ /// <param name="mediaSourceId">The reported media source id.</param>
+ /// <returns>The item to track progress against.</returns>
+ private BaseItem GetProgressItem(BaseItem libraryItem, string mediaSourceId)
+ {
+ if (libraryItem is Video libraryVideo
+ && !string.IsNullOrEmpty(mediaSourceId)
+ && Guid.TryParse(mediaSourceId, out var mediaSourceItemId)
+ && !mediaSourceItemId.Equals(libraryVideo.Id))
+ {
+ var versionItem = libraryVideo.GetAlternateVersion(mediaSourceItemId);
+ if (versionItem is not null)
+ {
+ return versionItem;
+ }
+ }
+
+ return libraryItem;
+ }
+
+ /// <summary>
/// Used to report that playback has started for an item.
/// </summary>
/// <param name="info">The info.</param>
@@ -760,9 +785,10 @@ namespace Emby.Server.Implementations.Session
if (libraryItem is not null)
{
+ var progressItem = GetProgressItem(libraryItem, info.MediaSourceId);
foreach (var user in users)
{
- OnPlaybackStart(user, libraryItem);
+ OnPlaybackStart(user, progressItem);
}
}
@@ -894,9 +920,10 @@ namespace Emby.Server.Implementations.Session
// only update saved user data on actual check-ins, not automated ones
if (libraryItem is not null && !isAutomated)
{
+ var progressItem = GetProgressItem(libraryItem, info.MediaSourceId);
foreach (var user in users)
{
- OnPlaybackProgress(user, libraryItem, info);
+ OnPlaybackProgress(user, progressItem, info);
}
}
@@ -956,6 +983,20 @@ namespace Emby.Server.Implementations.Session
if (changed)
{
_userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None);
+
+ // A completed version marks every alternate version played and clears their resume points, so the
+ // whole movie leaves Continue Watching and reads as watched everywhere. (Per-version resume positions
+ // only persist while nothing has been completed yet.)
+ if (data.Played == true && item is Video playedVideo)
+ {
+ playedVideo.PropagatePlayedState(user, true);
+ }
+ }
+
+ if ((!user.RememberAudioSelections && info.AudioStreamIndex.HasValue)
+ || (!user.RememberSubtitleSelections && info.SubtitleStreamIndex.HasValue))
+ {
+ _userDataManager.ResetPlaybackStreamSelections(user, item);
}
}
@@ -1087,9 +1128,10 @@ namespace Emby.Server.Implementations.Session
if (libraryItem is not null)
{
+ var progressItem = GetProgressItem(libraryItem, info.MediaSourceId);
foreach (var user in users)
{
- playedToCompletion = OnPlaybackStopped(user, libraryItem, info.PositionTicks, info.Failed);
+ playedToCompletion = OnPlaybackStopped(user, progressItem, info.PositionTicks, info.Failed);
}
}
@@ -1142,6 +1184,14 @@ namespace Emby.Server.Implementations.Session
_userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None);
+ // A completed version marks every alternate version played and clears their resume points, so the
+ // whole movie leaves Continue Watching and reads as watched everywhere. (Per-version resume positions
+ // only persist while nothing has been completed yet.)
+ if (data.Played == true && item is Video playedVideo)
+ {
+ playedVideo.PropagatePlayedState(user, true);
+ }
+
return playedToCompletion;
}
diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
index 6a26e92e14..2582ed9df0 100644
--- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -246,8 +246,21 @@ namespace Emby.Server.Implementations.Session
_logger.LogInformation("Lost {0} WebSockets.", lost.Count);
foreach (var webSocket in lost)
{
- // TODO: handle session relative to the lost webSocket
RemoveWebSocket(webSocket);
+
+ // The connection stopped answering keep-alives, so a close frame will
+ // never arrive and the pending receive loop would hang forever, keeping
+ // the session (and e.g. its SyncPlay group membership) alive. Disposing
+ // the connection aborts the receive loop, which raises Closed and lets
+ // the session end normally.
+ try
+ {
+ webSocket.Dispose();
+ }
+ catch (Exception exception)
+ {
+ _logger.LogWarning(exception, "Error disposing lost WebSocket from {RemoteEndPoint}.", webSocket.RemoteEndPoint);
+ }
}
}
}