using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Jellyfin.Data; using Jellyfin.Database.Implementations.Enums; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; namespace Jellyfin.Api.WebSocketListeners; /// /// Class SessionInfoWebSocketListener. /// public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener, WebSocketListenerState> { private readonly ISessionManager _sessionManager; private bool _disposed; /// /// Initializes a new instance of the class. /// /// Instance of the interface. /// Instance of the interface. public SessionInfoWebSocketListener(ILogger logger, ISessionManager sessionManager) : base(logger) { _sessionManager = sessionManager; _sessionManager.SessionStarted += OnSessionManagerSessionStarted; _sessionManager.SessionEnded += OnSessionManagerSessionEnded; _sessionManager.PlaybackStart += OnSessionManagerPlaybackStart; _sessionManager.PlaybackStopped += OnSessionManagerPlaybackStopped; _sessionManager.PlaybackProgress += OnSessionManagerPlaybackProgress; _sessionManager.CapabilitiesChanged += OnSessionManagerCapabilitiesChanged; _sessionManager.SessionActivity += OnSessionManagerSessionActivity; } /// protected override SessionMessageType Type => SessionMessageType.Sessions; /// protected override SessionMessageType StartType => SessionMessageType.SessionsStart; /// protected override SessionMessageType StopType => SessionMessageType.SessionsStop; /// /// Gets the data to send. /// /// Task{SystemInfo}. protected override Task> GetDataToSend() { return Task.FromResult(_sessionManager.Sessions); } /// protected override Task> GetDataToSendForConnection(IWebSocketConnection connection) { // For non-admin users, filter the sessions to only include their own sessions if (connection.AuthorizationInfo?.User is not null && !connection.AuthorizationInfo.IsApiKey && !connection.AuthorizationInfo.User.HasPermission(PermissionKind.IsAdministrator)) { var userId = connection.AuthorizationInfo.User.Id; return Task.FromResult(_sessionManager.Sessions.Where(s => s.UserId.Equals(userId) || s.ContainsUser(userId))); } return Task.FromResult(_sessionManager.Sessions); } /// protected override async ValueTask DisposeAsyncCore() { if (!_disposed) { _sessionManager.SessionStarted -= OnSessionManagerSessionStarted; _sessionManager.SessionEnded -= OnSessionManagerSessionEnded; _sessionManager.PlaybackStart -= OnSessionManagerPlaybackStart; _sessionManager.PlaybackStopped -= OnSessionManagerPlaybackStopped; _sessionManager.PlaybackProgress -= OnSessionManagerPlaybackProgress; _sessionManager.CapabilitiesChanged -= OnSessionManagerCapabilitiesChanged; _sessionManager.SessionActivity -= OnSessionManagerSessionActivity; _disposed = true; } await base.DisposeAsyncCore().ConfigureAwait(false); } /// /// Starts sending messages over a session info web socket. /// /// The message. protected override void Start(WebSocketMessageInfo message) { // Allow all authenticated users to subscribe to session information if (message.Connection.AuthorizationInfo.User is null && !message.Connection.AuthorizationInfo.IsApiKey) { throw new AuthenticationException("User must be authenticated to subscribe to session Information."); } base.Start(message); } private void OnSessionManagerSessionActivity(object? sender, SessionEventArgs e) { SendData(false); } private void OnSessionManagerCapabilitiesChanged(object? sender, SessionEventArgs e) { SendData(true); } private void OnSessionManagerPlaybackProgress(object? sender, PlaybackProgressEventArgs e) { SendData(!e.IsAutomated); } private void OnSessionManagerPlaybackStopped(object? sender, PlaybackStopEventArgs e) { SendData(true); } private void OnSessionManagerPlaybackStart(object? sender, PlaybackProgressEventArgs e) { SendData(true); } private void OnSessionManagerSessionEnded(object? sender, SessionEventArgs e) { SendData(true); } private void OnSessionManagerSessionStarted(object? sender, SessionEventArgs e) { SendData(true); } }