diff options
Diffstat (limited to 'MediaBrowser.Controller/Session')
| -rw-r--r-- | MediaBrowser.Controller/Session/AuthenticationRequest.cs | 19 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Session/ISessionController.cs | 25 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Session/ISessionManager.cs | 328 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Session/SessionEventArgs.cs | 9 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Session/SessionInfo.cs | 396 |
5 files changed, 777 insertions, 0 deletions
diff --git a/MediaBrowser.Controller/Session/AuthenticationRequest.cs b/MediaBrowser.Controller/Session/AuthenticationRequest.cs new file mode 100644 index 000000000..eb64db8c3 --- /dev/null +++ b/MediaBrowser.Controller/Session/AuthenticationRequest.cs @@ -0,0 +1,19 @@ +using System; + + +namespace MediaBrowser.Controller.Session +{ + public class AuthenticationRequest + { + public string Username { get; set; } + public Guid UserId { get; set; } + public string Password { get; set; } + public string PasswordSha1 { get; set; } + public string PasswordMd5 { get; set; } + public string App { get; set; } + public string AppVersion { get; set; } + public string DeviceId { get; set; } + public string DeviceName { get; set; } + public string RemoteEndPoint { get; set; } + } +} diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs new file mode 100644 index 000000000..e1d3a7ee6 --- /dev/null +++ b/MediaBrowser.Controller/Session/ISessionController.cs @@ -0,0 +1,25 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Session +{ + public interface ISessionController + { + /// <summary> + /// Gets a value indicating whether this instance is session active. + /// </summary> + /// <value><c>true</c> if this instance is session active; otherwise, <c>false</c>.</value> + bool IsSessionActive { get; } + + /// <summary> + /// Gets a value indicating whether [supports media remote control]. + /// </summary> + /// <value><c>true</c> if [supports media remote control]; otherwise, <c>false</c>.</value> + bool SupportsMediaControl { get; } + + /// <summary> + /// Sends the message. + /// </summary> + Task SendMessage<T>(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs new file mode 100644 index 000000000..b7719e556 --- /dev/null +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -0,0 +1,328 @@ +using MediaBrowser.Controller.Authentication; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Security; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Session; +using MediaBrowser.Model.Users; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Session +{ + /// <summary> + /// Interface ISessionManager + /// </summary> + public interface ISessionManager + { + /// <summary> + /// Occurs when [playback start]. + /// </summary> + event EventHandler<PlaybackProgressEventArgs> PlaybackStart; + + /// <summary> + /// Occurs when [playback progress]. + /// </summary> + event EventHandler<PlaybackProgressEventArgs> PlaybackProgress; + + /// <summary> + /// Occurs when [playback stopped]. + /// </summary> + event EventHandler<PlaybackStopEventArgs> PlaybackStopped; + + /// <summary> + /// Occurs when [session started]. + /// </summary> + event EventHandler<SessionEventArgs> SessionStarted; + + /// <summary> + /// Occurs when [session ended]. + /// </summary> + event EventHandler<SessionEventArgs> SessionEnded; + + event EventHandler<SessionEventArgs> SessionActivity; + + /// <summary> + /// Occurs when [capabilities changed]. + /// </summary> + event EventHandler<SessionEventArgs> CapabilitiesChanged; + + /// <summary> + /// Occurs when [authentication failed]. + /// </summary> + event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed; + + /// <summary> + /// Occurs when [authentication succeeded]. + /// </summary> + event EventHandler<GenericEventArgs<AuthenticationResult>> AuthenticationSucceeded; + + /// <summary> + /// Gets the sessions. + /// </summary> + /// <value>The sessions.</value> + IEnumerable<SessionInfo> Sessions { get; } + + /// <summary> + /// Logs the user activity. + /// </summary> + /// <param name="appName">Type of the client.</param> + /// <param name="appVersion">The app version.</param> + /// <param name="deviceId">The device id.</param> + /// <param name="deviceName">Name of the device.</param> + /// <param name="remoteEndPoint">The remote end point.</param> + /// <param name="user">The user.</param> + SessionInfo LogSessionActivity(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user); + + void UpdateDeviceName(string sessionId, string reportedDeviceName); + + /// <summary> + /// Used to report that playback has started for an item + /// </summary> + /// <param name="info">The info.</param> + /// <returns>Task.</returns> + Task OnPlaybackStart(PlaybackStartInfo info); + + /// <summary> + /// Used to report playback progress for an item + /// </summary> + /// <param name="info">The info.</param> + /// <returns>Task.</returns> + /// <exception cref="System.ArgumentNullException"></exception> + Task OnPlaybackProgress(PlaybackProgressInfo info); + + Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated); + + /// <summary> + /// Used to report that playback has ended for an item + /// </summary> + /// <param name="info">The info.</param> + /// <returns>Task.</returns> + /// <exception cref="System.ArgumentNullException"></exception> + Task OnPlaybackStopped(PlaybackStopInfo info); + + /// <summary> + /// Reports the session ended. + /// </summary> + /// <param name="sessionId">The session identifier.</param> + /// <returns>Task.</returns> + void ReportSessionEnded(string sessionId); + + /// <summary> + /// Sends the general command. + /// </summary> + /// <param name="controllingSessionId">The controlling session identifier.</param> + /// <param name="sessionId">The session identifier.</param> + /// <param name="command">The command.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendGeneralCommand(string controllingSessionId, string sessionId, GeneralCommand command, CancellationToken cancellationToken); + + /// <summary> + /// Sends the message command. + /// </summary> + /// <param name="controllingSessionId">The controlling session identifier.</param> + /// <param name="sessionId">The session id.</param> + /// <param name="command">The command.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken); + + /// <summary> + /// Sends the play command. + /// </summary> + /// <param name="controllingSessionId">The controlling session identifier.</param> + /// <param name="sessionId">The session id.</param> + /// <param name="command">The command.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken); + + /// <summary> + /// Sends the browse command. + /// </summary> + /// <param name="controllingSessionId">The controlling session identifier.</param> + /// <param name="sessionId">The session id.</param> + /// <param name="command">The command.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken); + + /// <summary> + /// Sends the playstate command. + /// </summary> + /// <param name="controllingSessionId">The controlling session identifier.</param> + /// <param name="sessionId">The session id.</param> + /// <param name="command">The command.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken); + + /// <summary> + /// Sends the message to admin sessions. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="name">The name.</param> + /// <param name="data">The data.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken); + + /// <summary> + /// Sends the message to user sessions. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns>Task.</returns> + Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, T data, CancellationToken cancellationToken); + + Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, Func<T> dataFn, CancellationToken cancellationToken); + + /// <summary> + /// Sends the message to user device sessions. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="deviceId">The device identifier.</param> + /// <param name="name">The name.</param> + /// <param name="data">The data.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken); + + /// <summary> + /// Sends the restart required message. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendRestartRequiredNotification(CancellationToken cancellationToken); + + /// <summary> + /// Sends the server shutdown notification. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendServerShutdownNotification(CancellationToken cancellationToken); + + /// <summary> + /// Sends the server restart notification. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendServerRestartNotification(CancellationToken cancellationToken); + + /// <summary> + /// Adds the additional user. + /// </summary> + /// <param name="sessionId">The session identifier.</param> + /// <param name="userId">The user identifier.</param> + void AddAdditionalUser(string sessionId, Guid userId); + + /// <summary> + /// Removes the additional user. + /// </summary> + /// <param name="sessionId">The session identifier.</param> + /// <param name="userId">The user identifier.</param> + void RemoveAdditionalUser(string sessionId, Guid userId); + + /// <summary> + /// Reports the now viewing item. + /// </summary> + /// <param name="sessionId">The session identifier.</param> + /// <param name="itemId">The item identifier.</param> + void ReportNowViewingItem(string sessionId, string itemId); + + /// <summary> + /// Reports the now viewing item. + /// </summary> + /// <param name="sessionId">The session identifier.</param> + /// <param name="item">The item.</param> + void ReportNowViewingItem(string sessionId, BaseItemDto item); + + /// <summary> + /// Authenticates the new session. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>Task{SessionInfo}.</returns> + Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request); + + /// <summary> + /// Creates the new session. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>Task<AuthenticationResult>.</returns> + Task<AuthenticationResult> CreateNewSession(AuthenticationRequest request); + + /// <summary> + /// Reports the capabilities. + /// </summary> + /// <param name="sessionId">The session identifier.</param> + /// <param name="capabilities">The capabilities.</param> + void ReportCapabilities(string sessionId, ClientCapabilities capabilities); + + /// <summary> + /// Reports the transcoding information. + /// </summary> + /// <param name="deviceId">The device identifier.</param> + /// <param name="info">The information.</param> + void ReportTranscodingInfo(string deviceId, TranscodingInfo info); + + /// <summary> + /// Clears the transcoding information. + /// </summary> + /// <param name="deviceId">The device identifier.</param> + void ClearTranscodingInfo(string deviceId); + + /// <summary> + /// Gets the session. + /// </summary> + /// <param name="deviceId">The device identifier.</param> + /// <param name="client">The client.</param> + /// <param name="version">The version.</param> + /// <returns>SessionInfo.</returns> + SessionInfo GetSession(string deviceId, string client, string version); + + /// <summary> + /// Gets the session by authentication token. + /// </summary> + /// <param name="token">The token.</param> + /// <param name="deviceId">The device identifier.</param> + /// <param name="remoteEndpoint">The remote endpoint.</param> + /// <returns>SessionInfo.</returns> + SessionInfo GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint); + + /// <summary> + /// Gets the session by authentication token. + /// </summary> + /// <param name="info">The information.</param> + /// <param name="deviceId">The device identifier.</param> + /// <param name="remoteEndpoint">The remote endpoint.</param> + /// <param name="appVersion">The application version.</param> + /// <returns>Task<SessionInfo>.</returns> + SessionInfo GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion); + + /// <summary> + /// Logouts the specified access token. + /// </summary> + /// <param name="accessToken">The access token.</param> + /// <returns>Task.</returns> + void Logout(string accessToken); + void Logout(AuthenticationInfo accessToken); + + /// <summary> + /// Revokes the user tokens. + /// </summary> + /// <returns>Task.</returns> + void RevokeUserTokens(Guid userId, string currentAccessToken); + + /// <summary> + /// Revokes the token. + /// </summary> + /// <param name="id">The identifier.</param> + /// <returns>Task.</returns> + void RevokeToken(string id); + + void CloseIfNeeded(SessionInfo session); + } +} diff --git a/MediaBrowser.Controller/Session/SessionEventArgs.cs b/MediaBrowser.Controller/Session/SessionEventArgs.cs new file mode 100644 index 000000000..96daa6ec9 --- /dev/null +++ b/MediaBrowser.Controller/Session/SessionEventArgs.cs @@ -0,0 +1,9 @@ +using System; + +namespace MediaBrowser.Controller.Session +{ + public class SessionEventArgs : EventArgs + { + public SessionInfo SessionInfo { get; set; } + } +} diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs new file mode 100644 index 000000000..869d3fcb0 --- /dev/null +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -0,0 +1,396 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Session; +using MediaBrowser.Model.Threading; +using System.Linq; +using System; + +namespace MediaBrowser.Controller.Session +{ + /// <summary> + /// Class SessionInfo + /// </summary> + public class SessionInfo : IDisposable + { + private ISessionManager _sessionManager; + private readonly ILogger _logger; + + public SessionInfo(ISessionManager sessionManager, ILogger logger) + { + _sessionManager = sessionManager; + _logger = logger; + + AdditionalUsers = new SessionUserInfo[] { }; + PlayState = new PlayerStateInfo(); + SessionControllers = new ISessionController[] { }; + } + + public PlayerStateInfo PlayState { get; set; } + + public SessionUserInfo[] AdditionalUsers { get; set; } + + public ClientCapabilities Capabilities { get; set; } + + /// <summary> + /// Gets or sets the remote end point. + /// </summary> + /// <value>The remote end point.</value> + public string RemoteEndPoint { get; set; } + + /// <summary> + /// Gets or sets the playable media types. + /// </summary> + /// <value>The playable media types.</value> + public string[] PlayableMediaTypes + { + get + { + if (Capabilities == null) + { + return new string[] {}; + } + return Capabilities.PlayableMediaTypes; + } + } + + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + public string Id { get; set; } + + /// <summary> + /// Gets or sets the user id. + /// </summary> + /// <value>The user id.</value> + public Guid UserId { get; set; } + + /// <summary> + /// Gets or sets the username. + /// </summary> + /// <value>The username.</value> + public string UserName { get; set; } + + /// <summary> + /// Gets or sets the type of the client. + /// </summary> + /// <value>The type of the client.</value> + public string Client { get; set; } + + /// <summary> + /// Gets or sets the last activity date. + /// </summary> + /// <value>The last activity date.</value> + public DateTime LastActivityDate { get; set; } + + /// <summary> + /// Gets or sets the last playback check in. + /// </summary> + /// <value>The last playback check in.</value> + public DateTime LastPlaybackCheckIn { get; set; } + + /// <summary> + /// Gets or sets the name of the device. + /// </summary> + /// <value>The name of the device.</value> + public string DeviceName { get; set; } + + public string DeviceType { get; set; } + + /// <summary> + /// Gets or sets the now playing item. + /// </summary> + /// <value>The now playing item.</value> + public BaseItemDto NowPlayingItem { get; set; } + + public BaseItem FullNowPlayingItem { get; set; } + + /// <summary> + /// Gets or sets the device id. + /// </summary> + /// <value>The device id.</value> + public string DeviceId { get; set; } + + /// <summary> + /// Gets or sets the application version. + /// </summary> + /// <value>The application version.</value> + public string ApplicationVersion { get; set; } + + /// <summary> + /// Gets or sets the session controller. + /// </summary> + /// <value>The session controller.</value> + [IgnoreDataMember] + public ISessionController[] SessionControllers { get; set; } + + /// <summary> + /// Gets or sets the application icon URL. + /// </summary> + /// <value>The application icon URL.</value> + public string AppIconUrl { get; set; } + + /// <summary> + /// Gets or sets the supported commands. + /// </summary> + /// <value>The supported commands.</value> + public string[] SupportedCommands + { + get + { + if (Capabilities == null) + { + return new string[] {}; + } + return Capabilities.SupportedCommands; + } + } + + public TranscodingInfo TranscodingInfo { get; set; } + + /// <summary> + /// Gets a value indicating whether this instance is active. + /// </summary> + /// <value><c>true</c> if this instance is active; otherwise, <c>false</c>.</value> + public bool IsActive + { + get + { + var controllers = SessionControllers; + foreach (var controller in controllers) + { + if (controller.IsSessionActive) + { + return true; + } + } + if (controllers.Length > 0) + { + return false; + } + + return true; + } + } + + public bool SupportsMediaControl + { + get + { + if (Capabilities == null || !Capabilities.SupportsMediaControl) + { + return false; + } + + var controllers = SessionControllers; + foreach (var controller in controllers) + { + if (controller.SupportsMediaControl) + { + return true; + } + } + + return false; + } + } + + public bool SupportsRemoteControl + { + get + { + if (Capabilities == null || !Capabilities.SupportsMediaControl) + { + return false; + } + + var controllers = SessionControllers; + foreach (var controller in controllers) + { + if (controller.SupportsMediaControl) + { + return true; + } + } + + return false; + } + } + + public Tuple<ISessionController, bool> EnsureController<T>(Func<SessionInfo, ISessionController> factory) + { + var controllers = SessionControllers.ToList(); + foreach (var controller in controllers) + { + if (controller is T) + { + return new Tuple<ISessionController, bool>(controller, false); + } + } + + var newController = factory(this); + _logger.Debug("Creating new {0}", newController.GetType().Name); + controllers.Add(newController); + + SessionControllers = controllers.ToArray(); + return new Tuple<ISessionController, bool>(newController, true); + } + + public void AddController(ISessionController controller) + { + var controllers = SessionControllers.ToList(); + controllers.Add(controller); + SessionControllers = controllers.ToArray(); + } + + public bool ContainsUser(string userId) + { + return ContainsUser(new Guid(userId)); + } + + public bool ContainsUser(Guid userId) + { + if (UserId.Equals(userId)) + { + return true; + } + + foreach (var additionalUser in AdditionalUsers) + { + if (userId.Equals(userId)) + { + return true; + } + } + return false; + } + + private readonly object _progressLock = new object(); + private ITimer _progressTimer; + private PlaybackProgressInfo _lastProgressInfo; + + public void StartAutomaticProgress(ITimerFactory timerFactory, PlaybackProgressInfo progressInfo) + { + if (_disposed) + { + return; + } + + lock (_progressLock) + { + _lastProgressInfo = progressInfo; + + if (_progressTimer == null) + { + _progressTimer = timerFactory.Create(OnProgressTimerCallback, null, 1000, 1000); + } + else + { + _progressTimer.Change(1000, 1000); + } + } + } + + // 1 second + private const long ProgressIncrement = 10000000; + + private async void OnProgressTimerCallback(object state) + { + if (_disposed) + { + return; + } + + var progressInfo = _lastProgressInfo; + if (progressInfo == null) + { + return; + } + if (progressInfo.IsPaused) + { + return; + } + + var positionTicks = progressInfo.PositionTicks ?? 0; + if (positionTicks < 0) + { + positionTicks = 0; + } + + var newPositionTicks = positionTicks + ProgressIncrement; + var item = progressInfo.Item; + long? runtimeTicks = item == null ? null : item.RunTimeTicks; + + // Don't report beyond the runtime + if (runtimeTicks.HasValue && newPositionTicks >= runtimeTicks.Value) + { + return; + } + + progressInfo.PositionTicks = newPositionTicks; + + try + { + await _sessionManager.OnPlaybackProgress(progressInfo, true).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error reporting playback progress", ex); + } + } + + public void StopAutomaticProgress() + { + lock (_progressLock) + { + if (_progressTimer != null) + { + _progressTimer.Dispose(); + _progressTimer = null; + } + _lastProgressInfo = null; + } + } + + private bool _disposed = false; + + public void Dispose() + { + _disposed = true; + + StopAutomaticProgress(); + + var controllers = SessionControllers.ToList(); + SessionControllers = new ISessionController[] { }; + + foreach (var controller in controllers) + { + var disposable = controller as IDisposable; + + if (disposable != null) + { + _logger.Debug("Disposing session controller {0}", disposable.GetType().Name); + + try + { + disposable.Dispose(); + } + catch (Exception ex) + { + _logger.ErrorException("Error disposing session controller", ex); + } + } + } + + _sessionManager = null; + } + + public QueueItem[] NowPlayingQueue { get; set; } + public bool HasCustomDeviceName { get; set; } + public string PlaylistItemId { get; set; } + public string ServerId { get; set; } + public string UserPrimaryImageTag { get; set; } + } +} |
