repo creation with initial code after cloning public repo

This commit is contained in:
2026-02-19 07:36:25 -05:00
commit 460884fea3
2860 changed files with 650825 additions and 0 deletions
@@ -0,0 +1,69 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Jellyfin.Database.Implementations.Entities;
namespace MediaBrowser.Controller.Net
{
/// <summary>
/// The request authorization info.
/// </summary>
public class AuthorizationInfo
{
/// <summary>
/// Gets the user identifier.
/// </summary>
/// <value>The user identifier.</value>
public Guid UserId => User?.Id ?? Guid.Empty;
/// <summary>
/// Gets or sets the device identifier.
/// </summary>
/// <value>The device identifier.</value>
public string? DeviceId { get; set; }
/// <summary>
/// Gets or sets the device.
/// </summary>
/// <value>The device.</value>
public string? Device { get; set; }
/// <summary>
/// Gets or sets the client.
/// </summary>
/// <value>The client.</value>
public string? Client { get; set; }
/// <summary>
/// Gets or sets the version.
/// </summary>
/// <value>The version.</value>
public string? Version { get; set; }
/// <summary>
/// Gets or sets the token.
/// </summary>
/// <value>The token.</value>
public string? Token { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the authorization is from an api key.
/// </summary>
public bool IsApiKey { get; set; }
/// <summary>
/// Gets or sets the user making the request.
/// </summary>
public User? User { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the token is authenticated.
/// </summary>
public bool IsAuthenticated { get; set; }
/// <summary>
/// Gets a value indicating whether the request has a token.
/// </summary>
[MemberNotNullWhen(true, nameof(Token))]
public bool HasToken => !string.IsNullOrWhiteSpace(Token);
}
}
@@ -0,0 +1,316 @@
#nullable disable
#pragma warning disable CS1591, SA1306, SA1401
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using MediaBrowser.Controller.Net.WebSocketMessages;
using MediaBrowser.Model.Session;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.Net
{
/// <summary>
/// Starts sending data over a web socket periodically when a message is received, and then stops when a corresponding stop message is received.
/// </summary>
/// <typeparam name="TReturnDataType">The type of the T return data type.</typeparam>
/// <typeparam name="TStateType">The type of the T state type.</typeparam>
public abstract class BasePeriodicWebSocketListener<TReturnDataType, TStateType> : IWebSocketListener, IAsyncDisposable
where TStateType : WebSocketListenerState, new()
where TReturnDataType : class
{
private readonly Channel<bool> _channel = Channel.CreateUnbounded<bool>(new UnboundedChannelOptions
{
AllowSynchronousContinuations = false,
SingleReader = true,
SingleWriter = false
});
private readonly Lock _activeConnectionsLock = new();
/// <summary>
/// The _active connections.
/// </summary>
private readonly List<(IWebSocketConnection Connection, CancellationTokenSource CancellationTokenSource, TStateType State)> _activeConnections = new();
/// <summary>
/// The logger.
/// </summary>
protected readonly ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> Logger;
private readonly Task _messageConsumerTask;
protected BasePeriodicWebSocketListener(ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> logger)
{
ArgumentNullException.ThrowIfNull(logger);
Logger = logger;
_messageConsumerTask = HandleMessages();
}
/// <summary>
/// Gets the type used for the messages sent to the client.
/// </summary>
/// <value>The type.</value>
protected abstract SessionMessageType Type { get; }
/// <summary>
/// Gets the message type received from the client to start sending messages.
/// </summary>
/// <value>The type.</value>
protected abstract SessionMessageType StartType { get; }
/// <summary>
/// Gets the message type received from the client to stop sending messages.
/// </summary>
/// <value>The type.</value>
protected abstract SessionMessageType StopType { get; }
/// <summary>
/// Gets the data to send.
/// </summary>
/// <returns>Task{`1}.</returns>
protected abstract Task<TReturnDataType> GetDataToSend();
/// <summary>
/// Gets the data to send for a specific connection.
/// </summary>
/// <param name="connection">The connection.</param>
/// <returns>Task{`1}.</returns>
protected virtual Task<TReturnDataType> GetDataToSendForConnection(IWebSocketConnection connection)
{
return GetDataToSend();
}
/// <summary>
/// Processes the message.
/// </summary>
/// <param name="message">The message.</param>
/// <returns>Task.</returns>
public Task ProcessMessageAsync(WebSocketMessageInfo message)
{
ArgumentNullException.ThrowIfNull(message);
if (message.MessageType == StartType)
{
Start(message);
}
if (message.MessageType == StopType)
{
Stop(message);
}
return Task.CompletedTask;
}
/// <inheritdoc />
public Task ProcessWebSocketConnectedAsync(IWebSocketConnection connection, HttpContext httpContext) => Task.CompletedTask;
/// <summary>
/// Starts sending messages over a web socket.
/// </summary>
/// <param name="message">The message.</param>
protected virtual void Start(WebSocketMessageInfo message)
{
var vals = message.Data.Split(',');
var dueTimeMs = long.Parse(vals[0], CultureInfo.InvariantCulture);
var periodMs = long.Parse(vals[1], CultureInfo.InvariantCulture);
var cancellationTokenSource = new CancellationTokenSource();
Logger.LogDebug("WS {1} begin transmitting to {0}", message.Connection.RemoteEndPoint, GetType().Name);
var state = new TStateType
{
IntervalMs = periodMs,
InitialDelayMs = dueTimeMs
};
lock (_activeConnectionsLock)
{
_activeConnections.Add((message.Connection, cancellationTokenSource, state));
}
}
protected void SendData(bool force)
{
_channel.Writer.TryWrite(force);
}
private async Task HandleMessages()
{
while (await _channel.Reader.WaitToReadAsync().ConfigureAwait(false))
{
while (_channel.Reader.TryRead(out var force))
{
try
{
(IWebSocketConnection Connection, CancellationTokenSource CancellationTokenSource, TStateType State)[] tuples;
var now = DateTime.UtcNow;
lock (_activeConnectionsLock)
{
if (_activeConnections.Count == 0)
{
continue;
}
tuples = _activeConnections
.Where(c =>
{
if (c.Connection.State != WebSocketState.Open || c.CancellationTokenSource.IsCancellationRequested)
{
return false;
}
var state = c.State;
return force || (now - state.DateLastSendUtc).TotalMilliseconds >= state.IntervalMs;
})
.ToArray();
}
if (tuples.Length == 0)
{
continue;
}
IEnumerable<Task> GetTasks()
{
foreach (var tuple in tuples)
{
yield return SendDataForConnectionAsync(tuple);
}
}
await Task.WhenAll(GetTasks()).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.LogError(ex, "Failed to send updates to websockets");
}
}
}
}
private async Task SendDataForConnectionAsync((IWebSocketConnection Connection, CancellationTokenSource CancellationTokenSource, TStateType State) tuple)
{
try
{
var (connection, cts, state) = tuple;
var cancellationToken = cts.Token;
var data = await GetDataToSendForConnection(connection).ConfigureAwait(false);
if (data is null)
{
return;
}
await connection.SendAsync(
new OutboundWebSocketMessage<TReturnDataType> { MessageType = Type, Data = data },
cancellationToken).ConfigureAwait(false);
state.DateLastSendUtc = DateTime.UtcNow;
}
catch (OperationCanceledException)
{
if (tuple.CancellationTokenSource.IsCancellationRequested)
{
DisposeConnection(tuple);
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Error sending web socket message {Name}", Type);
DisposeConnection(tuple);
}
}
/// <summary>
/// Stops sending messages over a web socket.
/// </summary>
/// <param name="message">The message.</param>
private void Stop(WebSocketMessageInfo message)
{
lock (_activeConnectionsLock)
{
var connection = _activeConnections.FirstOrDefault(c => c.Connection == message.Connection);
if (connection != default)
{
DisposeConnection(connection);
}
}
}
/// <summary>
/// Disposes the connection.
/// </summary>
/// <param name="connection">The connection.</param>
private void DisposeConnection((IWebSocketConnection Connection, CancellationTokenSource CancellationTokenSource, TStateType State) connection)
{
Logger.LogDebug("WS {1} stop transmitting to {0}", connection.Connection.RemoteEndPoint, GetType().Name);
// TODO disposing the connection seems to break websockets in subtle ways, so what is the purpose of this function really...
// connection.Item1.Dispose();
try
{
connection.CancellationTokenSource.Cancel();
connection.CancellationTokenSource.Dispose();
}
catch (ObjectDisposedException ex)
{
// TODO Investigate and properly fix.
Logger.LogError(ex, "Object Disposed");
}
catch (Exception ex)
{
// TODO Investigate and properly fix.
Logger.LogError(ex, "Error disposing websocket");
}
lock (_activeConnectionsLock)
{
_activeConnections.Remove(connection);
}
}
protected virtual async ValueTask DisposeAsyncCore()
{
try
{
_channel.Writer.TryComplete();
await _messageConsumerTask.ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.LogError(ex, "Disposing the message consumer failed");
}
lock (_activeConnectionsLock)
{
foreach (var connection in _activeConnections.ToList())
{
DisposeConnection(connection);
}
}
}
/// <inheritdoc />
public async ValueTask DisposeAsync()
{
await DisposeAsyncCore().ConfigureAwait(false);
GC.SuppressFinalize(this);
}
}
}
@@ -0,0 +1,18 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Controller.Net
{
/// <summary>
/// IAuthService.
/// </summary>
public interface IAuthService
{
/// <summary>
/// Authenticate request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Authorization information. Null if unauthenticated.</returns>
Task<AuthorizationInfo> Authenticate(HttpRequest request);
}
}
@@ -0,0 +1,25 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Controller.Net
{
/// <summary>
/// IAuthorization context.
/// </summary>
public interface IAuthorizationContext
{
/// <summary>
/// Gets the authorization information.
/// </summary>
/// <param name="requestContext">The request context.</param>
/// <returns>A task containing the authorization info.</returns>
Task<AuthorizationInfo> GetAuthorizationInfo(HttpContext requestContext);
/// <summary>
/// Gets the authorization information.
/// </summary>
/// <param name="requestContext">The request context.</param>
/// <returns>A <see cref="Task"/> containing the authorization info.</returns>
Task<AuthorizationInfo> GetAuthorizationInfo(HttpRequest requestContext);
}
}
@@ -0,0 +1,81 @@
using System;
using System.Net;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Net.WebSocketMessages;
namespace MediaBrowser.Controller.Net
{
/// <summary>
/// Interface for WebSocket connections.
/// </summary>
public interface IWebSocketConnection : IAsyncDisposable, IDisposable
{
/// <summary>
/// Occurs when [closed].
/// </summary>
event EventHandler<EventArgs>? Closed;
/// <summary>
/// Gets the last activity date.
/// </summary>
/// <value>The last activity date.</value>
DateTime LastActivityDate { get; }
/// <summary>
/// Gets or sets the date of last Keepalive received.
/// </summary>
/// <value>The date of last Keepalive received.</value>
DateTime LastKeepAliveDate { get; set; }
/// <summary>
/// Gets or sets the receive action.
/// </summary>
/// <value>The receive action.</value>
Func<WebSocketMessageInfo, Task>? OnReceive { get; set; }
/// <summary>
/// Gets the state.
/// </summary>
/// <value>The state.</value>
WebSocketState State { get; }
/// <summary>
/// Gets the authorization information.
/// </summary>
public AuthorizationInfo AuthorizationInfo { get; }
/// <summary>
/// Gets the remote end point.
/// </summary>
/// <value>The remote end point.</value>
IPAddress? RemoteEndPoint { get; }
/// <summary>
/// Sends a message asynchronously.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">The message is null.</exception>
Task SendAsync(OutboundWebSocketMessage message, CancellationToken cancellationToken);
/// <summary>
/// Sends a message asynchronously.
/// </summary>
/// <typeparam name="T">The type of websocket message data.</typeparam>
/// <param name="message">The message.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">The message is null.</exception>
Task SendAsync<T>(OutboundWebSocketMessage<T> message, CancellationToken cancellationToken);
/// <summary>
/// Receives a message asynchronously.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task ReceiveAsync(CancellationToken cancellationToken = default);
}
}
@@ -0,0 +1,26 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Controller.Net
{
/// <summary>
/// Interface for listening to messages coming through a web socket connection.
/// </summary>
public interface IWebSocketListener
{
/// <summary>
/// Processes the message.
/// </summary>
/// <param name="message">The message.</param>
/// <returns>Task.</returns>
Task ProcessMessageAsync(WebSocketMessageInfo message);
/// <summary>
/// Processes a new web socket connection.
/// </summary>
/// <param name="connection">An instance of the <see cref="IWebSocketConnection"/> interface.</param>
/// <param name="httpContext">The current http context.</param>
/// <returns>Task.</returns>
Task ProcessWebSocketConnectedAsync(IWebSocketConnection connection, HttpContext httpContext);
}
}
@@ -0,0 +1,18 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Controller.Net
{
/// <summary>
/// Interface IHttpServer.
/// </summary>
public interface IWebSocketManager
{
/// <summary>
/// The HTTP request handler.
/// </summary>
/// <param name="context">The current HTTP context.</param>
/// <returns>The task.</returns>
Task WebSocketRequestHandler(HttpContext context);
}
}
@@ -0,0 +1,37 @@
using System;
namespace MediaBrowser.Controller.Net
{
/// <summary>
/// The exception that is thrown when a user is authenticated, but not authorized to access a requested resource.
/// </summary>
public class SecurityException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="SecurityException"/> class.
/// </summary>
public SecurityException()
: base()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SecurityException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public SecurityException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SecurityException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
public SecurityException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}
@@ -0,0 +1,17 @@
#nullable disable
#pragma warning disable CS1591
using System;
namespace MediaBrowser.Controller.Net
{
public class WebSocketListenerState
{
public DateTime DateLastSendUtc { get; set; }
public long InitialDelayMs { get; set; }
public long IntervalMs { get; set; }
}
}
@@ -0,0 +1,22 @@
using System.Text.Json.Serialization;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net;
/// <summary>
/// Websocket message without data.
/// </summary>
public abstract class WebSocketMessage
{
/// <summary>
/// Gets or sets the type of the message.
/// TODO make this abstract and get only.
/// </summary>
public virtual SessionMessageType MessageType { get; set; }
/// <summary>
/// Gets or sets the server id.
/// </summary>
[JsonIgnore]
public string? ServerId { get; set; }
}
@@ -0,0 +1,18 @@
#nullable disable
using MediaBrowser.Controller.Net.WebSocketMessages;
namespace MediaBrowser.Controller.Net
{
/// <summary>
/// Class WebSocketMessageInfo.
/// </summary>
public class WebSocketMessageInfo : InboundWebSocketMessage<string>
{
/// <summary>
/// Gets or sets the connection.
/// </summary>
/// <value>The connection.</value>
public IWebSocketConnection Connection { get; set; }
}
}
@@ -0,0 +1,32 @@
#pragma warning disable SA1649 // File name must equal class name.
namespace MediaBrowser.Controller.Net;
/// <summary>
/// Class WebSocketMessage.
/// </summary>
/// <typeparam name="T">The type of the data.</typeparam>
public abstract class WebSocketMessage<T> : WebSocketMessage
{
/// <summary>
/// Initializes a new instance of the <see cref="WebSocketMessage{T}"/> class.
/// </summary>
protected WebSocketMessage()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WebSocketMessage{T}"/> class.
/// </summary>
/// <param name="data">The data to send.</param>
protected WebSocketMessage(T data)
{
Data = data;
}
/// <summary>
/// Gets or sets the data.
/// </summary>
// TODO make this set only.
public T? Data { get; set; }
}
@@ -0,0 +1,10 @@
#pragma warning disable CA1040
namespace MediaBrowser.Controller.Net.WebSocketMessages;
/// <summary>
/// Interface representing that the websocket message is inbound.
/// </summary>
public interface IInboundWebSocketMessage
{
}
@@ -0,0 +1,10 @@
#pragma warning disable CA1040
namespace MediaBrowser.Controller.Net.WebSocketMessages;
/// <summary>
/// Interface representing that the websocket message is outbound.
/// </summary>
public interface IOutboundWebSocketMessage
{
}
@@ -0,0 +1,25 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Inbound;
/// <summary>
/// Activity log entry start message.
/// Data is the timing data encoded as "$initialDelay,$interval" in ms.
/// </summary>
public class ActivityLogEntryStartMessage : InboundWebSocketMessage<string>
{
/// <summary>
/// Initializes a new instance of the <see cref="ActivityLogEntryStartMessage"/> class.
/// Data is the timing data encoded as "$initialDelay,$interval" in ms.
/// </summary>
/// <param name="data">The timing data encoded as "$initialDelay,$interval".</param>
public ActivityLogEntryStartMessage(string data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.ActivityLogEntryStart)]
public override SessionMessageType MessageType => SessionMessageType.ActivityLogEntryStart;
}
@@ -0,0 +1,14 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Inbound;
/// <summary>
/// Activity log entry stop message.
/// </summary>
public class ActivityLogEntryStopMessage : InboundWebSocketMessage
{
/// <inheritdoc />
[DefaultValue(SessionMessageType.ActivityLogEntryStop)]
public override SessionMessageType MessageType => SessionMessageType.ActivityLogEntryStop;
}
@@ -0,0 +1,14 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Inbound;
/// <summary>
/// Keep alive websocket messages.
/// </summary>
public class InboundKeepAliveMessage : InboundWebSocketMessage
{
/// <inheritdoc />
[DefaultValue(SessionMessageType.KeepAlive)]
public override SessionMessageType MessageType => SessionMessageType.KeepAlive;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Inbound;
/// <summary>
/// Scheduled tasks info start message.
/// Data is the timing data encoded as "$initialDelay,$interval" in ms.
/// </summary>
public class ScheduledTasksInfoStartMessage : InboundWebSocketMessage<string>
{
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledTasksInfoStartMessage"/> class.
/// </summary>
/// <param name="data">The timing data encoded as $initialDelay,$interval.</param>
public ScheduledTasksInfoStartMessage(string data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.ScheduledTasksInfoStart)]
public override SessionMessageType MessageType => SessionMessageType.ScheduledTasksInfoStart;
}
@@ -0,0 +1,14 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Inbound;
/// <summary>
/// Scheduled tasks info stop message.
/// </summary>
public class ScheduledTasksInfoStopMessage : InboundWebSocketMessage
{
/// <inheritdoc />
[DefaultValue(SessionMessageType.ScheduledTasksInfoStop)]
public override SessionMessageType MessageType => SessionMessageType.ScheduledTasksInfoStop;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Inbound;
/// <summary>
/// Sessions start message.
/// Data is the timing data encoded as "$initialDelay,$interval" in ms.
/// </summary>
public class SessionsStartMessage : InboundWebSocketMessage<string>
{
/// <summary>
/// Initializes a new instance of the <see cref="SessionsStartMessage"/> class.
/// </summary>
/// <param name="data">The timing data encoded as $initialDelay,$interval.</param>
public SessionsStartMessage(string data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.SessionsStart)]
public override SessionMessageType MessageType => SessionMessageType.SessionsStart;
}
@@ -0,0 +1,14 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Inbound;
/// <summary>
/// Sessions stop message.
/// </summary>
public class SessionsStopMessage : InboundWebSocketMessage
{
/// <inheritdoc />
[DefaultValue(SessionMessageType.SessionsStop)]
public override SessionMessageType MessageType => SessionMessageType.SessionsStop;
}
@@ -0,0 +1,8 @@
namespace MediaBrowser.Controller.Net.WebSocketMessages;
/// <summary>
/// Inbound websocket message.
/// </summary>
public class InboundWebSocketMessage : WebSocketMessage, IInboundWebSocketMessage
{
}
@@ -0,0 +1,26 @@
#pragma warning disable SA1649 // File name must equal class name.
namespace MediaBrowser.Controller.Net.WebSocketMessages;
/// <summary>
/// Inbound websocket message with data.
/// </summary>
/// <typeparam name="T">The data type.</typeparam>
public class InboundWebSocketMessage<T> : WebSocketMessage<T>, IInboundWebSocketMessage
{
/// <summary>
/// Initializes a new instance of the <see cref="InboundWebSocketMessage{T}"/> class.
/// </summary>
public InboundWebSocketMessage()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="InboundWebSocketMessage{T}"/> class.
/// </summary>
/// <param name="data">The data to send.</param>
protected InboundWebSocketMessage(T data)
{
Data = data;
}
}
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.ComponentModel;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Activity log created message.
/// </summary>
public class ActivityLogEntryMessage : OutboundWebSocketMessage<IReadOnlyList<ActivityLogEntry>>
{
/// <summary>
/// Initializes a new instance of the <see cref="ActivityLogEntryMessage"/> class.
/// </summary>
/// <param name="data">List of activity log entries.</param>
public ActivityLogEntryMessage(IReadOnlyList<ActivityLogEntry> data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.ActivityLogEntry)]
public override SessionMessageType MessageType => SessionMessageType.ActivityLogEntry;
}
@@ -0,0 +1,23 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Force keep alive websocket messages.
/// </summary>
public class ForceKeepAliveMessage : OutboundWebSocketMessage<int>
{
/// <summary>
/// Initializes a new instance of the <see cref="ForceKeepAliveMessage"/> class.
/// </summary>
/// <param name="data">The timeout in seconds.</param>
public ForceKeepAliveMessage(int data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.ForceKeepAlive)]
public override SessionMessageType MessageType => SessionMessageType.ForceKeepAlive;
}
@@ -0,0 +1,23 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// General command websocket message.
/// </summary>
public class GeneralCommandMessage : OutboundWebSocketMessage<GeneralCommand>
{
/// <summary>
/// Initializes a new instance of the <see cref="GeneralCommandMessage"/> class.
/// </summary>
/// <param name="data">The general command.</param>
public GeneralCommandMessage(GeneralCommand data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.GeneralCommand)]
public override SessionMessageType MessageType => SessionMessageType.GeneralCommand;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Library changed message.
/// </summary>
public class LibraryChangedMessage : OutboundWebSocketMessage<LibraryUpdateInfo>
{
/// <summary>
/// Initializes a new instance of the <see cref="LibraryChangedMessage"/> class.
/// </summary>
/// <param name="data">The library update info.</param>
public LibraryChangedMessage(LibraryUpdateInfo data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.LibraryChanged)]
public override SessionMessageType MessageType => SessionMessageType.LibraryChanged;
}
@@ -0,0 +1,14 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Keep alive websocket messages.
/// </summary>
public class OutboundKeepAliveMessage : OutboundWebSocketMessage
{
/// <inheritdoc />
[DefaultValue(SessionMessageType.KeepAlive)]
public override SessionMessageType MessageType => SessionMessageType.KeepAlive;
}
@@ -0,0 +1,23 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Play command websocket message.
/// </summary>
public class PlayMessage : OutboundWebSocketMessage<PlayRequest>
{
/// <summary>
/// Initializes a new instance of the <see cref="PlayMessage"/> class.
/// </summary>
/// <param name="data">The play request.</param>
public PlayMessage(PlayRequest data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.Play)]
public override SessionMessageType MessageType => SessionMessageType.Play;
}
@@ -0,0 +1,23 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Playstate message.
/// </summary>
public class PlaystateMessage : OutboundWebSocketMessage<PlaystateRequest>
{
/// <summary>
/// Initializes a new instance of the <see cref="PlaystateMessage"/> class.
/// </summary>
/// <param name="data">Playstate request data.</param>
public PlaystateMessage(PlaystateRequest data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.Playstate)]
public override SessionMessageType MessageType => SessionMessageType.Playstate;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.Updates;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Plugin installation cancelled message.
/// </summary>
public class PluginInstallationCancelledMessage : OutboundWebSocketMessage<InstallationInfo>
{
/// <summary>
/// Initializes a new instance of the <see cref="PluginInstallationCancelledMessage"/> class.
/// </summary>
/// <param name="data">Installation info.</param>
public PluginInstallationCancelledMessage(InstallationInfo data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.PackageInstallationCancelled)]
public override SessionMessageType MessageType => SessionMessageType.PackageInstallationCancelled;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.Updates;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Plugin installation completed message.
/// </summary>
public class PluginInstallationCompletedMessage : OutboundWebSocketMessage<InstallationInfo>
{
/// <summary>
/// Initializes a new instance of the <see cref="PluginInstallationCompletedMessage"/> class.
/// </summary>
/// <param name="data">Installation info.</param>
public PluginInstallationCompletedMessage(InstallationInfo data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.PackageInstallationCompleted)]
public override SessionMessageType MessageType => SessionMessageType.PackageInstallationCompleted;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.Updates;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Plugin installation failed message.
/// </summary>
public class PluginInstallationFailedMessage : OutboundWebSocketMessage<InstallationInfo>
{
/// <summary>
/// Initializes a new instance of the <see cref="PluginInstallationFailedMessage"/> class.
/// </summary>
/// <param name="data">Installation info.</param>
public PluginInstallationFailedMessage(InstallationInfo data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.PackageInstallationFailed)]
public override SessionMessageType MessageType => SessionMessageType.PackageInstallationFailed;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.Updates;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Package installing message.
/// </summary>
public class PluginInstallingMessage : OutboundWebSocketMessage<InstallationInfo>
{
/// <summary>
/// Initializes a new instance of the <see cref="PluginInstallingMessage"/> class.
/// </summary>
/// <param name="data">Installation info.</param>
public PluginInstallingMessage(InstallationInfo data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.PackageInstalling)]
public override SessionMessageType MessageType => SessionMessageType.PackageInstalling;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Plugin uninstalled message.
/// </summary>
public class PluginUninstalledMessage : OutboundWebSocketMessage<PluginInfo>
{
/// <summary>
/// Initializes a new instance of the <see cref="PluginUninstalledMessage"/> class.
/// </summary>
/// <param name="data">Plugin info.</param>
public PluginUninstalledMessage(PluginInfo data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.PackageUninstalled)]
public override SessionMessageType MessageType => SessionMessageType.PackageUninstalled;
}
@@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Refresh progress message.
/// </summary>
public class RefreshProgressMessage : OutboundWebSocketMessage<Dictionary<string, string>>
{
/// <summary>
/// Initializes a new instance of the <see cref="RefreshProgressMessage"/> class.
/// </summary>
/// <param name="data">Refresh progress data.</param>
public RefreshProgressMessage(Dictionary<string, string> data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.RefreshProgress)]
public override SessionMessageType MessageType => SessionMessageType.RefreshProgress;
}
@@ -0,0 +1,14 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Restart required.
/// </summary>
public class RestartRequiredMessage : OutboundWebSocketMessage
{
/// <inheritdoc />
[DefaultValue(SessionMessageType.RestartRequired)]
public override SessionMessageType MessageType => SessionMessageType.RestartRequired;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.Tasks;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Scheduled task ended message.
/// </summary>
public class ScheduledTaskEndedMessage : OutboundWebSocketMessage<TaskResult>
{
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledTaskEndedMessage"/> class.
/// </summary>
/// <param name="data">Task result.</param>
public ScheduledTaskEndedMessage(TaskResult data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.ScheduledTaskEnded)]
public override SessionMessageType MessageType => SessionMessageType.ScheduledTaskEnded;
}
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.ComponentModel;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.Tasks;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Scheduled tasks info message.
/// </summary>
public class ScheduledTasksInfoMessage : OutboundWebSocketMessage<IReadOnlyList<TaskInfo>>
{
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledTasksInfoMessage"/> class.
/// </summary>
/// <param name="data">List of task infos.</param>
public ScheduledTasksInfoMessage(IReadOnlyList<TaskInfo> data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.ScheduledTasksInfo)]
public override SessionMessageType MessageType => SessionMessageType.ScheduledTasksInfo;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Series timer cancelled message.
/// </summary>
public class SeriesTimerCancelledMessage : OutboundWebSocketMessage<TimerEventInfo>
{
/// <summary>
/// Initializes a new instance of the <see cref="SeriesTimerCancelledMessage"/> class.
/// </summary>
/// <param name="data">The timer event info.</param>
public SeriesTimerCancelledMessage(TimerEventInfo data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.SeriesTimerCancelled)]
public override SessionMessageType MessageType => SessionMessageType.SeriesTimerCancelled;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Series timer created message.
/// </summary>
public class SeriesTimerCreatedMessage : OutboundWebSocketMessage<TimerEventInfo>
{
/// <summary>
/// Initializes a new instance of the <see cref="SeriesTimerCreatedMessage"/> class.
/// </summary>
/// <param name="data">timer event info.</param>
public SeriesTimerCreatedMessage(TimerEventInfo data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.SeriesTimerCreated)]
public override SessionMessageType MessageType => SessionMessageType.SeriesTimerCreated;
}
@@ -0,0 +1,14 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Server restarting down message.
/// </summary>
public class ServerRestartingMessage : OutboundWebSocketMessage
{
/// <inheritdoc />
[DefaultValue(SessionMessageType.ServerRestarting)]
public override SessionMessageType MessageType => SessionMessageType.ServerRestarting;
}
@@ -0,0 +1,14 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Server shutting down message.
/// </summary>
public class ServerShuttingDownMessage : OutboundWebSocketMessage
{
/// <inheritdoc />
[DefaultValue(SessionMessageType.ServerShuttingDown)]
public override SessionMessageType MessageType => SessionMessageType.ServerShuttingDown;
}
@@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.ComponentModel;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Sessions message.
/// </summary>
public class SessionsMessage : OutboundWebSocketMessage<IReadOnlyList<SessionInfoDto>>
{
/// <summary>
/// Initializes a new instance of the <see cref="SessionsMessage"/> class.
/// </summary>
/// <param name="data">Session info.</param>
public SessionsMessage(IReadOnlyList<SessionInfoDto> data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.Sessions)]
public override SessionMessageType MessageType => SessionMessageType.Sessions;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.SyncPlay;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Sync play command.
/// </summary>
public class SyncPlayCommandMessage : OutboundWebSocketMessage<SendCommand>
{
/// <summary>
/// Initializes a new instance of the <see cref="SyncPlayCommandMessage"/> class.
/// </summary>
/// <param name="data">The send command.</param>
public SyncPlayCommandMessage(SendCommand data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.SyncPlayCommand)]
public override SessionMessageType MessageType => SessionMessageType.SyncPlayCommand;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Timer cancelled message.
/// </summary>
public class TimerCancelledMessage : OutboundWebSocketMessage<TimerEventInfo>
{
/// <summary>
/// Initializes a new instance of the <see cref="TimerCancelledMessage"/> class.
/// </summary>
/// <param name="data">Timer event info.</param>
public TimerCancelledMessage(TimerEventInfo data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.TimerCancelled)]
public override SessionMessageType MessageType => SessionMessageType.TimerCancelled;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// Timer created message.
/// </summary>
public class TimerCreatedMessage : OutboundWebSocketMessage<TimerEventInfo>
{
/// <summary>
/// Initializes a new instance of the <see cref="TimerCreatedMessage"/> class.
/// </summary>
/// <param name="data">Timer event info.</param>
public TimerCreatedMessage(TimerEventInfo data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.TimerCreated)]
public override SessionMessageType MessageType => SessionMessageType.TimerCreated;
}
@@ -0,0 +1,23 @@
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// User data changed message.
/// </summary>
public class UserDataChangedMessage : OutboundWebSocketMessage<UserDataChangeInfo>
{
/// <summary>
/// Initializes a new instance of the <see cref="UserDataChangedMessage"/> class.
/// </summary>
/// <param name="data">The data change info.</param>
public UserDataChangedMessage(UserDataChangeInfo data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.UserDataChanged)]
public override SessionMessageType MessageType => SessionMessageType.UserDataChanged;
}
@@ -0,0 +1,24 @@
using System;
using System.ComponentModel;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// User deleted message.
/// </summary>
public class UserDeletedMessage : OutboundWebSocketMessage<Guid>
{
/// <summary>
/// Initializes a new instance of the <see cref="UserDeletedMessage"/> class.
/// </summary>
/// <param name="data">The user id.</param>
public UserDeletedMessage(Guid data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.UserDeleted)]
public override SessionMessageType MessageType => SessionMessageType.UserDeleted;
}
@@ -0,0 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
/// <summary>
/// User updated message.
/// </summary>
public class UserUpdatedMessage : OutboundWebSocketMessage<UserDto>
{
/// <summary>
/// Initializes a new instance of the <see cref="UserUpdatedMessage"/> class.
/// </summary>
/// <param name="data">The user dto.</param>
public UserUpdatedMessage(UserDto data)
: base(data)
{
}
/// <inheritdoc />
[DefaultValue(SessionMessageType.UserUpdated)]
public override SessionMessageType MessageType => SessionMessageType.UserUpdated;
}
@@ -0,0 +1,14 @@
using System;
namespace MediaBrowser.Controller.Net.WebSocketMessages;
/// <summary>
/// Outbound websocket message.
/// </summary>
public class OutboundWebSocketMessage : WebSocketMessage, IOutboundWebSocketMessage
{
/// <summary>
/// Gets or sets the message id.
/// </summary>
public Guid MessageId { get; set; } = Guid.NewGuid();
}
@@ -0,0 +1,33 @@
#pragma warning disable SA1649 // File name must equal class name.
using System;
namespace MediaBrowser.Controller.Net.WebSocketMessages;
/// <summary>
/// Outbound websocket message with data.
/// </summary>
/// <typeparam name="T">The data type.</typeparam>
public class OutboundWebSocketMessage<T> : WebSocketMessage<T>, IOutboundWebSocketMessage
{
/// <summary>
/// Initializes a new instance of the <see cref="OutboundWebSocketMessage{T}"/> class.
/// </summary>
public OutboundWebSocketMessage()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="OutboundWebSocketMessage{T}"/> class.
/// </summary>
/// <param name="data">The data to send.</param>
protected OutboundWebSocketMessage(T data)
{
Data = data;
}
/// <summary>
/// Gets or sets the message id.
/// </summary>
public Guid MessageId { get; set; } = Guid.NewGuid();
}