Fix code issues
This commit is contained in:
parent
0b974d09ca
commit
6e22e9222b
|
@ -238,10 +238,10 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
return _socket.SendAsync(text, true, cancellationToken);
|
return _socket.SendAsync(text, true, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SendKeepAliveResponse()
|
private void SendKeepAliveResponse()
|
||||||
{
|
{
|
||||||
LastKeepAliveDate = DateTime.UtcNow;
|
LastKeepAliveDate = DateTime.UtcNow;
|
||||||
return SendAsync(new WebSocketMessage<string>
|
SendAsync(new WebSocketMessage<string>
|
||||||
{
|
{
|
||||||
MessageType = "KeepAlive"
|
MessageType = "KeepAlive"
|
||||||
}, CancellationToken.None);
|
}, CancellationToken.None);
|
||||||
|
|
|
@ -84,10 +84,10 @@ namespace Emby.Server.Implementations.Session
|
||||||
_logger = loggerFactory.CreateLogger(GetType().Name);
|
_logger = loggerFactory.CreateLogger(GetType().Name);
|
||||||
_json = json;
|
_json = json;
|
||||||
_httpServer = httpServer;
|
_httpServer = httpServer;
|
||||||
httpServer.WebSocketConnected += _serverManager_WebSocketConnected;
|
httpServer.WebSocketConnected += OnServerManagerWebSocketConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _serverManager_WebSocketConnected(object sender, GenericEventArgs<IWebSocketConnection> e)
|
void OnServerManagerWebSocketConnected(object sender, GenericEventArgs<IWebSocketConnection> e)
|
||||||
{
|
{
|
||||||
var session = GetSession(e.Argument.QueryString, e.Argument.RemoteEndPoint);
|
var session = GetSession(e.Argument.QueryString, e.Argument.RemoteEndPoint);
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ namespace Emby.Server.Implementations.Session
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_httpServer.WebSocketConnected -= _serverManager_WebSocketConnected;
|
_httpServer.WebSocketConnected -= OnServerManagerWebSocketConnected;
|
||||||
StopKeepAlive();
|
StopKeepAlive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ namespace Emby.Server.Implementations.Session
|
||||||
private void OnWebSocketClosed(object sender, EventArgs e)
|
private void OnWebSocketClosed(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var webSocket = (IWebSocketConnection) sender;
|
var webSocket = (IWebSocketConnection) sender;
|
||||||
_logger.LogDebug("WebSockets {0} closed.", webSocket);
|
_logger.LogDebug("WebSocket {0} is closed.", webSocket);
|
||||||
RemoveWebSocket(webSocket);
|
RemoveWebSocket(webSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ namespace Emby.Server.Implementations.Session
|
||||||
/// Adds a WebSocket to the KeepAlive watchlist.
|
/// Adds a WebSocket to the KeepAlive watchlist.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="webSocket">The WebSocket to monitor.</param>
|
/// <param name="webSocket">The WebSocket to monitor.</param>
|
||||||
private async Task KeepAliveWebSocket(IWebSocketConnection webSocket)
|
private void KeepAliveWebSocket(IWebSocketConnection webSocket)
|
||||||
{
|
{
|
||||||
lock (_webSocketsLock)
|
lock (_webSocketsLock)
|
||||||
{
|
{
|
||||||
|
@ -175,11 +175,11 @@ namespace Emby.Server.Implementations.Session
|
||||||
// Notify WebSocket about timeout
|
// Notify WebSocket about timeout
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await SendForceKeepAlive(webSocket);
|
SendForceKeepAlive(webSocket).Wait();
|
||||||
}
|
}
|
||||||
catch (WebSocketException exception)
|
catch (WebSocketException exception)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(exception, "Error sending ForceKeepAlive message to WebSocket {0}.", webSocket);
|
_logger.LogWarning(exception, "Cannot send ForceKeepAlive message to WebSocket {0}.", webSocket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +213,8 @@ namespace Emby.Server.Implementations.Session
|
||||||
{
|
{
|
||||||
_keepAliveCancellationToken = new CancellationTokenSource();
|
_keepAliveCancellationToken = new CancellationTokenSource();
|
||||||
// Start KeepAlive watcher
|
// Start KeepAlive watcher
|
||||||
KeepAliveSockets(
|
var task = RepeatAsyncCallbackEvery(
|
||||||
|
KeepAliveSockets,
|
||||||
TimeSpan.FromSeconds(WebSocketLostTimeout * IntervalFactor),
|
TimeSpan.FromSeconds(WebSocketLostTimeout * IntervalFactor),
|
||||||
_keepAliveCancellationToken.Token);
|
_keepAliveCancellationToken.Token);
|
||||||
}
|
}
|
||||||
|
@ -245,73 +246,58 @@ namespace Emby.Server.Implementations.Session
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks status of KeepAlive of WebSockets once every the specified interval time.
|
/// Checks status of KeepAlive of WebSockets.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="interval">The interval.</param>
|
private async Task KeepAliveSockets()
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
private async Task KeepAliveSockets(TimeSpan interval, CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
while (true)
|
IEnumerable<IWebSocketConnection> inactive;
|
||||||
|
IEnumerable<IWebSocketConnection> lost;
|
||||||
|
|
||||||
|
lock (_webSocketsLock)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Watching {0} WebSockets.", _webSockets.Count());
|
_logger.LogDebug("Watching {0} WebSockets.", _webSockets.Count);
|
||||||
|
|
||||||
IEnumerable<IWebSocketConnection> inactive;
|
inactive = _webSockets.Where(i =>
|
||||||
IEnumerable<IWebSocketConnection> lost;
|
|
||||||
lock (_webSocketsLock)
|
|
||||||
{
|
{
|
||||||
inactive = _webSockets.Where(i =>
|
var elapsed = (DateTime.UtcNow - i.LastKeepAliveDate).TotalSeconds;
|
||||||
{
|
return (elapsed > WebSocketLostTimeout * ForceKeepAliveFactor) && (elapsed < WebSocketLostTimeout);
|
||||||
var elapsed = (DateTime.UtcNow - i.LastKeepAliveDate).TotalSeconds;
|
});
|
||||||
return (elapsed > WebSocketLostTimeout * ForceKeepAliveFactor) && (elapsed < WebSocketLostTimeout);
|
lost = _webSockets.Where(i => (DateTime.UtcNow - i.LastKeepAliveDate).TotalSeconds >= WebSocketLostTimeout);
|
||||||
});
|
}
|
||||||
lost = _webSockets.Where(i => (DateTime.UtcNow - i.LastKeepAliveDate).TotalSeconds >= WebSocketLostTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inactive.Any())
|
if (inactive.Any())
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Sending ForceKeepAlive message to {0} inactive WebSockets.", inactive.Count());
|
_logger.LogInformation("Sending ForceKeepAlive message to {0} inactive WebSockets.", inactive.Count());
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var webSocket in inactive)
|
foreach (var webSocket in inactive)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
await SendForceKeepAlive(webSocket);
|
|
||||||
}
|
|
||||||
catch (WebSocketException exception)
|
|
||||||
{
|
|
||||||
_logger.LogInformation(exception, "Error sending ForceKeepAlive message to WebSocket.");
|
|
||||||
lost = lost.Append(webSocket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_webSocketsLock)
|
|
||||||
{
|
|
||||||
if (lost.Any())
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Lost {0} WebSockets.", lost.Count());
|
|
||||||
foreach (var webSocket in lost.ToList())
|
|
||||||
{
|
|
||||||
// TODO: handle session relative to the lost webSocket
|
|
||||||
RemoveWebSocket(webSocket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_webSockets.Any())
|
|
||||||
{
|
|
||||||
StopKeepAlive();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for next interval
|
|
||||||
Task task = Task.Delay(interval, cancellationToken);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await task;
|
await SendForceKeepAlive(webSocket);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (WebSocketException exception)
|
||||||
{
|
{
|
||||||
return;
|
_logger.LogInformation(exception, "Error sending ForceKeepAlive message to WebSocket.");
|
||||||
|
lost = lost.Append(webSocket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_webSocketsLock)
|
||||||
|
{
|
||||||
|
if (lost.Any())
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Lost {0} WebSockets.", lost.Count());
|
||||||
|
foreach (var webSocket in lost.ToList())
|
||||||
|
{
|
||||||
|
// TODO: handle session relative to the lost webSocket
|
||||||
|
RemoveWebSocket(webSocket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_webSockets.Any())
|
||||||
|
{
|
||||||
|
StopKeepAlive();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,5 +315,30 @@ namespace Emby.Server.Implementations.Session
|
||||||
Data = WebSocketLostTimeout
|
Data = WebSocketLostTimeout
|
||||||
}, CancellationToken.None);
|
}, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs a given async callback once every specified interval time, until cancelled.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback">The async callback.</param>
|
||||||
|
/// <param name="interval">The interval time.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>Task.</returns>
|
||||||
|
private async Task RepeatAsyncCallbackEvery(Func<Task> callback, TimeSpan interval, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await callback();
|
||||||
|
Task task = Task.Delay(interval, cancellationToken);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,15 @@ using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Controller.Syncplay;
|
using MediaBrowser.Controller.Syncplay;
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
using MediaBrowser.Model.Syncplay;
|
using MediaBrowser.Model.Syncplay;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Syncplay
|
namespace Emby.Server.Implementations.Syncplay
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class SyncplayController.
|
/// Class SyncplayController.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Class is not thread-safe, external locking is required when accessing methods.
|
||||||
|
/// </remarks>
|
||||||
public class SyncplayController : ISyncplayController, IDisposable
|
public class SyncplayController : ISyncplayController, IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -39,11 +41,6 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
AllReady = 3
|
AllReady = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The logger.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The session manager.
|
/// The session manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -71,11 +68,9 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
private bool _disposed = false;
|
private bool _disposed = false;
|
||||||
|
|
||||||
public SyncplayController(
|
public SyncplayController(
|
||||||
ILogger logger,
|
|
||||||
ISessionManager sessionManager,
|
ISessionManager sessionManager,
|
||||||
ISyncplayManager syncplayManager)
|
ISyncplayManager syncplayManager)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
|
||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
_syncplayManager = syncplayManager;
|
_syncplayManager = syncplayManager;
|
||||||
}
|
}
|
||||||
|
@ -110,6 +105,16 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts DateTime to UTC string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="date">The date to convert.</param>
|
||||||
|
/// <value>The UTC string.</value>
|
||||||
|
private string DateToUTCString(DateTime date)
|
||||||
|
{
|
||||||
|
return date.ToUniversalTime().ToString("o");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Filters sessions of this group.
|
/// Filters sessions of this group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -149,15 +154,16 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
/// <param name="from">The current session.</param>
|
/// <param name="from">The current session.</param>
|
||||||
/// <param name="type">The filtering type.</param>
|
/// <param name="type">The filtering type.</param>
|
||||||
/// <param name="message">The message to send.</param>
|
/// <param name="message">The message to send.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <value>The task.</value>
|
/// <value>The task.</value>
|
||||||
private Task SendGroupUpdate<T>(SessionInfo from, BroadcastType type, GroupUpdate<T> message)
|
private Task SendGroupUpdate<T>(SessionInfo from, BroadcastType type, GroupUpdate<T> message, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
IEnumerable<Task> GetTasks()
|
IEnumerable<Task> GetTasks()
|
||||||
{
|
{
|
||||||
SessionInfo[] sessions = FilterSessions(from, type);
|
SessionInfo[] sessions = FilterSessions(from, type);
|
||||||
foreach (var session in sessions)
|
foreach (var session in sessions)
|
||||||
{
|
{
|
||||||
yield return _sessionManager.SendSyncplayGroupUpdate(session.Id.ToString(), message, CancellationToken.None);
|
yield return _sessionManager.SendSyncplayGroupUpdate(session.Id.ToString(), message, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,15 +176,16 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
/// <param name="from">The current session.</param>
|
/// <param name="from">The current session.</param>
|
||||||
/// <param name="type">The filtering type.</param>
|
/// <param name="type">The filtering type.</param>
|
||||||
/// <param name="message">The message to send.</param>
|
/// <param name="message">The message to send.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <value>The task.</value>
|
/// <value>The task.</value>
|
||||||
private Task SendCommand(SessionInfo from, BroadcastType type, SendCommand message)
|
private Task SendCommand(SessionInfo from, BroadcastType type, SendCommand message, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
IEnumerable<Task> GetTasks()
|
IEnumerable<Task> GetTasks()
|
||||||
{
|
{
|
||||||
SessionInfo[] sessions = FilterSessions(from, type);
|
SessionInfo[] sessions = FilterSessions(from, type);
|
||||||
foreach (var session in sessions)
|
foreach (var session in sessions)
|
||||||
{
|
{
|
||||||
yield return _sessionManager.SendSyncplayCommand(session.Id.ToString(), message, CancellationToken.None);
|
yield return _sessionManager.SendSyncplayCommand(session.Id.ToString(), message, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,8 +204,8 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
GroupId = _group.GroupId.ToString(),
|
GroupId = _group.GroupId.ToString(),
|
||||||
Command = type,
|
Command = type,
|
||||||
PositionTicks = _group.PositionTicks,
|
PositionTicks = _group.PositionTicks,
|
||||||
When = _group.LastActivity.ToUniversalTime().ToString("o"),
|
When = DateToUTCString(_group.LastActivity),
|
||||||
EmittedAt = DateTime.UtcNow.ToUniversalTime().ToString("o")
|
EmittedAt = DateToUTCString(DateTime.UtcNow)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,46 +226,46 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void InitGroup(SessionInfo session)
|
public void InitGroup(SessionInfo session, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_group.AddSession(session);
|
_group.AddSession(session);
|
||||||
_syncplayManager.AddSessionToGroup(session, this);
|
_syncplayManager.AddSessionToGroup(session, this);
|
||||||
|
|
||||||
_group.PlayingItem = session.FullNowPlayingItem;
|
_group.PlayingItem = session.FullNowPlayingItem;
|
||||||
_group.IsPaused = true;
|
_group.IsPaused = true;
|
||||||
_group.PositionTicks = session.PlayState.PositionTicks ??= 0;
|
_group.PositionTicks = session.PlayState.PositionTicks ?? 0;
|
||||||
_group.LastActivity = DateTime.UtcNow;
|
_group.LastActivity = DateTime.UtcNow;
|
||||||
|
|
||||||
var updateSession = NewSyncplayGroupUpdate(GroupUpdateType.GroupJoined, DateTime.UtcNow.ToUniversalTime().ToString("o"));
|
var updateSession = NewSyncplayGroupUpdate(GroupUpdateType.GroupJoined, DateToUTCString(DateTime.UtcNow));
|
||||||
SendGroupUpdate(session, BroadcastType.CurrentSession, updateSession);
|
SendGroupUpdate(session, BroadcastType.CurrentSession, updateSession, cancellationToken);
|
||||||
var pauseCommand = NewSyncplayCommand(SendCommandType.Pause);
|
var pauseCommand = NewSyncplayCommand(SendCommandType.Pause);
|
||||||
SendCommand(session, BroadcastType.CurrentSession, pauseCommand);
|
SendCommand(session, BroadcastType.CurrentSession, pauseCommand, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void SessionJoin(SessionInfo session, JoinGroupRequest request)
|
public void SessionJoin(SessionInfo session, JoinGroupRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (session.NowPlayingItem?.Id == _group.PlayingItem.Id && request.PlayingItemId == _group.PlayingItem.Id)
|
if (session.NowPlayingItem?.Id == _group.PlayingItem.Id && request.PlayingItemId == _group.PlayingItem.Id)
|
||||||
{
|
{
|
||||||
_group.AddSession(session);
|
_group.AddSession(session);
|
||||||
_syncplayManager.AddSessionToGroup(session, this);
|
_syncplayManager.AddSessionToGroup(session, this);
|
||||||
|
|
||||||
var updateSession = NewSyncplayGroupUpdate(GroupUpdateType.GroupJoined, DateTime.UtcNow.ToUniversalTime().ToString("o"));
|
var updateSession = NewSyncplayGroupUpdate(GroupUpdateType.GroupJoined, DateToUTCString(DateTime.UtcNow));
|
||||||
SendGroupUpdate(session, BroadcastType.CurrentSession, updateSession);
|
SendGroupUpdate(session, BroadcastType.CurrentSession, updateSession, cancellationToken);
|
||||||
|
|
||||||
var updateOthers = NewSyncplayGroupUpdate(GroupUpdateType.UserJoined, session.UserName);
|
var updateOthers = NewSyncplayGroupUpdate(GroupUpdateType.UserJoined, session.UserName);
|
||||||
SendGroupUpdate(session, BroadcastType.AllExceptCurrentSession, updateOthers);
|
SendGroupUpdate(session, BroadcastType.AllExceptCurrentSession, updateOthers, cancellationToken);
|
||||||
|
|
||||||
// Client join and play, syncing will happen client side
|
// Client join and play, syncing will happen client side
|
||||||
if (!_group.IsPaused)
|
if (!_group.IsPaused)
|
||||||
{
|
{
|
||||||
var playCommand = NewSyncplayCommand(SendCommandType.Play);
|
var playCommand = NewSyncplayCommand(SendCommandType.Play);
|
||||||
SendCommand(session, BroadcastType.CurrentSession, playCommand);
|
SendCommand(session, BroadcastType.CurrentSession, playCommand, cancellationToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var pauseCommand = NewSyncplayCommand(SendCommandType.Pause);
|
var pauseCommand = NewSyncplayCommand(SendCommandType.Pause);
|
||||||
SendCommand(session, BroadcastType.CurrentSession, pauseCommand);
|
SendCommand(session, BroadcastType.CurrentSession, pauseCommand, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -267,25 +274,25 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
playRequest.ItemIds = new Guid[] { _group.PlayingItem.Id };
|
playRequest.ItemIds = new Guid[] { _group.PlayingItem.Id };
|
||||||
playRequest.StartPositionTicks = _group.PositionTicks;
|
playRequest.StartPositionTicks = _group.PositionTicks;
|
||||||
var update = NewSyncplayGroupUpdate(GroupUpdateType.PrepareSession, playRequest);
|
var update = NewSyncplayGroupUpdate(GroupUpdateType.PrepareSession, playRequest);
|
||||||
SendGroupUpdate(session, BroadcastType.CurrentSession, update);
|
SendGroupUpdate(session, BroadcastType.CurrentSession, update, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void SessionLeave(SessionInfo session)
|
public void SessionLeave(SessionInfo session, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_group.RemoveSession(session);
|
_group.RemoveSession(session);
|
||||||
_syncplayManager.RemoveSessionFromGroup(session, this);
|
_syncplayManager.RemoveSessionFromGroup(session, this);
|
||||||
|
|
||||||
var updateSession = NewSyncplayGroupUpdate(GroupUpdateType.GroupLeft, _group.PositionTicks);
|
var updateSession = NewSyncplayGroupUpdate(GroupUpdateType.GroupLeft, _group.PositionTicks);
|
||||||
SendGroupUpdate(session, BroadcastType.CurrentSession, updateSession);
|
SendGroupUpdate(session, BroadcastType.CurrentSession, updateSession, cancellationToken);
|
||||||
|
|
||||||
var updateOthers = NewSyncplayGroupUpdate(GroupUpdateType.UserLeft, session.UserName);
|
var updateOthers = NewSyncplayGroupUpdate(GroupUpdateType.UserLeft, session.UserName);
|
||||||
SendGroupUpdate(session, BroadcastType.AllExceptCurrentSession, updateOthers);
|
SendGroupUpdate(session, BroadcastType.AllExceptCurrentSession, updateOthers, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void HandleRequest(SessionInfo session, PlaybackRequest request)
|
public void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// The server's job is to mantain a consistent state to which clients refer to,
|
// The server's job is to mantain a consistent state to which clients refer to,
|
||||||
// as also to notify clients of state changes.
|
// as also to notify clients of state changes.
|
||||||
|
@ -294,19 +301,19 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
switch (request.Type)
|
switch (request.Type)
|
||||||
{
|
{
|
||||||
case PlaybackRequestType.Play:
|
case PlaybackRequestType.Play:
|
||||||
HandlePlayRequest(session, request);
|
HandlePlayRequest(session, request, cancellationToken);
|
||||||
break;
|
break;
|
||||||
case PlaybackRequestType.Pause:
|
case PlaybackRequestType.Pause:
|
||||||
HandlePauseRequest(session, request);
|
HandlePauseRequest(session, request, cancellationToken);
|
||||||
break;
|
break;
|
||||||
case PlaybackRequestType.Seek:
|
case PlaybackRequestType.Seek:
|
||||||
HandleSeekRequest(session, request);
|
HandleSeekRequest(session, request, cancellationToken);
|
||||||
break;
|
break;
|
||||||
case PlaybackRequestType.Buffering:
|
case PlaybackRequestType.Buffering:
|
||||||
HandleBufferingRequest(session, request);
|
HandleBufferingRequest(session, request, cancellationToken);
|
||||||
break;
|
break;
|
||||||
case PlaybackRequestType.BufferingDone:
|
case PlaybackRequestType.BufferingDone:
|
||||||
HandleBufferingDoneRequest(session, request);
|
HandleBufferingDoneRequest(session, request, cancellationToken);
|
||||||
break;
|
break;
|
||||||
case PlaybackRequestType.UpdatePing:
|
case PlaybackRequestType.UpdatePing:
|
||||||
HandlePingUpdateRequest(session, request);
|
HandlePingUpdateRequest(session, request);
|
||||||
|
@ -319,7 +326,8 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
/// <param name="request">The play action.</param>
|
/// <param name="request">The play action.</param>
|
||||||
private void HandlePlayRequest(SessionInfo session, PlaybackRequest request)
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
private void HandlePlayRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (_group.IsPaused)
|
if (_group.IsPaused)
|
||||||
{
|
{
|
||||||
|
@ -337,13 +345,13 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
);
|
);
|
||||||
|
|
||||||
var command = NewSyncplayCommand(SendCommandType.Play);
|
var command = NewSyncplayCommand(SendCommandType.Play);
|
||||||
SendCommand(session, BroadcastType.AllGroup, command);
|
SendCommand(session, BroadcastType.AllGroup, command, cancellationToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Client got lost, sending current state
|
// Client got lost, sending current state
|
||||||
var command = NewSyncplayCommand(SendCommandType.Play);
|
var command = NewSyncplayCommand(SendCommandType.Play);
|
||||||
SendCommand(session, BroadcastType.CurrentSession, command);
|
SendCommand(session, BroadcastType.CurrentSession, command, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,7 +360,8 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
/// <param name="request">The pause action.</param>
|
/// <param name="request">The pause action.</param>
|
||||||
private void HandlePauseRequest(SessionInfo session, PlaybackRequest request)
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
private void HandlePauseRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!_group.IsPaused)
|
if (!_group.IsPaused)
|
||||||
{
|
{
|
||||||
|
@ -366,13 +375,13 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
_group.PositionTicks += elapsedTime.Ticks > 0 ? elapsedTime.Ticks : 0;
|
_group.PositionTicks += elapsedTime.Ticks > 0 ? elapsedTime.Ticks : 0;
|
||||||
|
|
||||||
var command = NewSyncplayCommand(SendCommandType.Pause);
|
var command = NewSyncplayCommand(SendCommandType.Pause);
|
||||||
SendCommand(session, BroadcastType.AllGroup, command);
|
SendCommand(session, BroadcastType.AllGroup, command, cancellationToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Client got lost, sending current state
|
// Client got lost, sending current state
|
||||||
var command = NewSyncplayCommand(SendCommandType.Pause);
|
var command = NewSyncplayCommand(SendCommandType.Pause);
|
||||||
SendCommand(session, BroadcastType.CurrentSession, command);
|
SendCommand(session, BroadcastType.CurrentSession, command, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,16 +390,11 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
/// <param name="request">The seek action.</param>
|
/// <param name="request">The seek action.</param>
|
||||||
private void HandleSeekRequest(SessionInfo session, PlaybackRequest request)
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
private void HandleSeekRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Sanitize PositionTicks
|
// Sanitize PositionTicks
|
||||||
var ticks = request.PositionTicks ??= 0;
|
var ticks = SanitizePositionTicks(request.PositionTicks);
|
||||||
ticks = ticks >= 0 ? ticks : 0;
|
|
||||||
if (_group.PlayingItem.RunTimeTicks != null)
|
|
||||||
{
|
|
||||||
var runTimeTicks = _group.PlayingItem.RunTimeTicks ??= 0;
|
|
||||||
ticks = ticks > runTimeTicks ? runTimeTicks : ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pause and seek
|
// Pause and seek
|
||||||
_group.IsPaused = true;
|
_group.IsPaused = true;
|
||||||
|
@ -398,7 +402,7 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
_group.LastActivity = DateTime.UtcNow;
|
_group.LastActivity = DateTime.UtcNow;
|
||||||
|
|
||||||
var command = NewSyncplayCommand(SendCommandType.Seek);
|
var command = NewSyncplayCommand(SendCommandType.Seek);
|
||||||
SendCommand(session, BroadcastType.AllGroup, command);
|
SendCommand(session, BroadcastType.AllGroup, command, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -406,7 +410,8 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
/// <param name="request">The buffering action.</param>
|
/// <param name="request">The buffering action.</param>
|
||||||
private void HandleBufferingRequest(SessionInfo session, PlaybackRequest request)
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
private void HandleBufferingRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!_group.IsPaused)
|
if (!_group.IsPaused)
|
||||||
{
|
{
|
||||||
|
@ -421,16 +426,16 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
|
|
||||||
// Send pause command to all non-buffering sessions
|
// Send pause command to all non-buffering sessions
|
||||||
var command = NewSyncplayCommand(SendCommandType.Pause);
|
var command = NewSyncplayCommand(SendCommandType.Pause);
|
||||||
SendCommand(session, BroadcastType.AllReady, command);
|
SendCommand(session, BroadcastType.AllReady, command, cancellationToken);
|
||||||
|
|
||||||
var updateOthers = NewSyncplayGroupUpdate(GroupUpdateType.GroupWait, session.UserName);
|
var updateOthers = NewSyncplayGroupUpdate(GroupUpdateType.GroupWait, session.UserName);
|
||||||
SendGroupUpdate(session, BroadcastType.AllExceptCurrentSession, updateOthers);
|
SendGroupUpdate(session, BroadcastType.AllExceptCurrentSession, updateOthers, cancellationToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Client got lost, sending current state
|
// Client got lost, sending current state
|
||||||
var command = NewSyncplayCommand(SendCommandType.Pause);
|
var command = NewSyncplayCommand(SendCommandType.Pause);
|
||||||
SendCommand(session, BroadcastType.CurrentSession, command);
|
SendCommand(session, BroadcastType.CurrentSession, command, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,26 +444,28 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
/// <param name="request">The buffering-done action.</param>
|
/// <param name="request">The buffering-done action.</param>
|
||||||
private void HandleBufferingDoneRequest(SessionInfo session, PlaybackRequest request)
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
private void HandleBufferingDoneRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (_group.IsPaused)
|
if (_group.IsPaused)
|
||||||
{
|
{
|
||||||
_group.SetBuffering(session, false);
|
_group.SetBuffering(session, false);
|
||||||
|
|
||||||
var when = request.When ??= DateTime.UtcNow;
|
var requestTicks = SanitizePositionTicks(request.PositionTicks);
|
||||||
|
|
||||||
|
var when = request.When ?? DateTime.UtcNow;
|
||||||
var currentTime = DateTime.UtcNow;
|
var currentTime = DateTime.UtcNow;
|
||||||
var elapsedTime = currentTime - when;
|
var elapsedTime = currentTime - when;
|
||||||
var clientPosition = TimeSpan.FromTicks(request.PositionTicks ??= 0) + elapsedTime;
|
var clientPosition = TimeSpan.FromTicks(requestTicks) + elapsedTime;
|
||||||
var delay = _group.PositionTicks - clientPosition.Ticks;
|
var delay = _group.PositionTicks - clientPosition.Ticks;
|
||||||
|
|
||||||
if (_group.IsBuffering())
|
if (_group.IsBuffering())
|
||||||
{
|
{
|
||||||
// Others are buffering, tell this client to pause when ready
|
// Others are still buffering, tell this client to pause when ready
|
||||||
var command = NewSyncplayCommand(SendCommandType.Pause);
|
var command = NewSyncplayCommand(SendCommandType.Pause);
|
||||||
command.When = currentTime.AddMilliseconds(
|
var pauseAtTime = currentTime.AddMilliseconds(delay);
|
||||||
delay
|
command.When = DateToUTCString(pauseAtTime);
|
||||||
).ToUniversalTime().ToString("o");
|
SendCommand(session, BroadcastType.CurrentSession, command, cancellationToken);
|
||||||
SendCommand(session, BroadcastType.CurrentSession, command);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -472,7 +479,7 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
delay
|
delay
|
||||||
);
|
);
|
||||||
var command = NewSyncplayCommand(SendCommandType.Play);
|
var command = NewSyncplayCommand(SendCommandType.Play);
|
||||||
SendCommand(session, BroadcastType.AllExceptCurrentSession, command);
|
SendCommand(session, BroadcastType.AllExceptCurrentSession, command, cancellationToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -485,7 +492,7 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
);
|
);
|
||||||
|
|
||||||
var command = NewSyncplayCommand(SendCommandType.Play);
|
var command = NewSyncplayCommand(SendCommandType.Play);
|
||||||
SendCommand(session, BroadcastType.AllGroup, command);
|
SendCommand(session, BroadcastType.AllGroup, command, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,10 +500,27 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
{
|
{
|
||||||
// Group was not waiting, make sure client has latest state
|
// Group was not waiting, make sure client has latest state
|
||||||
var command = NewSyncplayCommand(SendCommandType.Play);
|
var command = NewSyncplayCommand(SendCommandType.Play);
|
||||||
SendCommand(session, BroadcastType.CurrentSession, command);
|
SendCommand(session, BroadcastType.CurrentSession, command, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sanitizes the PositionTicks, considers the current playing item when available.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="positionTicks">The PositionTicks.</param>
|
||||||
|
/// <value>The sanitized PositionTicks.</value>
|
||||||
|
private long SanitizePositionTicks(long? positionTicks)
|
||||||
|
{
|
||||||
|
var ticks = positionTicks ?? 0;
|
||||||
|
ticks = ticks >= 0 ? ticks : 0;
|
||||||
|
if (_group.PlayingItem != null)
|
||||||
|
{
|
||||||
|
var runTimeTicks = _group.PlayingItem.RunTimeTicks ?? 0;
|
||||||
|
ticks = ticks > runTimeTicks ? runTimeTicks : ticks;
|
||||||
|
}
|
||||||
|
return ticks;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates ping of a session.
|
/// Updates ping of a session.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -505,7 +529,7 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
private void HandlePingUpdateRequest(SessionInfo session, PlaybackRequest request)
|
private void HandlePingUpdateRequest(SessionInfo session, PlaybackRequest request)
|
||||||
{
|
{
|
||||||
// Collected pings are used to account for network latency when unpausing playback
|
// Collected pings are used to account for network latency when unpausing playback
|
||||||
_group.UpdatePing(session, request.Ping ??= _group.DefaulPing);
|
_group.UpdatePing(session, request.Ping ?? _group.DefaulPing);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -517,7 +541,7 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
PlayingItemName = _group.PlayingItem.Name,
|
PlayingItemName = _group.PlayingItem.Name,
|
||||||
PlayingItemId = _group.PlayingItem.Id.ToString(),
|
PlayingItemId = _group.PlayingItem.Id.ToString(),
|
||||||
PositionTicks = _group.PositionTicks,
|
PositionTicks = _group.PositionTicks,
|
||||||
Participants = _group.Participants.Values.Select(session => session.Session.UserName).ToArray()
|
Participants = _group.Participants.Values.Select(session => session.Session.UserName).Distinct().ToArray()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,14 +114,14 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
{
|
{
|
||||||
var session = e.SessionInfo;
|
var session = e.SessionInfo;
|
||||||
if (!IsSessionInGroup(session)) return;
|
if (!IsSessionInGroup(session)) return;
|
||||||
LeaveGroup(session);
|
LeaveGroup(session, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSessionManagerPlaybackStopped(object sender, PlaybackStopEventArgs e)
|
private void OnSessionManagerPlaybackStopped(object sender, PlaybackStopEventArgs e)
|
||||||
{
|
{
|
||||||
var session = e.Session;
|
var session = e.Session;
|
||||||
if (!IsSessionInGroup(session)) return;
|
if (!IsSessionInGroup(session)) return;
|
||||||
LeaveGroup(session);
|
LeaveGroup(session, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsSessionInGroup(SessionInfo session)
|
private bool IsSessionInGroup(SessionInfo session)
|
||||||
|
@ -132,7 +132,13 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
private bool HasAccessToItem(User user, Guid itemId)
|
private bool HasAccessToItem(User user, Guid itemId)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetItemById(itemId);
|
var item = _libraryManager.GetItemById(itemId);
|
||||||
var hasParentalRatingAccess = user.Policy.MaxParentalRating.HasValue ? item.InheritedParentalRatingValue <= user.Policy.MaxParentalRating : true;
|
|
||||||
|
// Check ParentalRating access
|
||||||
|
var hasParentalRatingAccess = true;
|
||||||
|
if (user.Policy.MaxParentalRating.HasValue)
|
||||||
|
{
|
||||||
|
hasParentalRatingAccess = item.InheritedParentalRatingValue <= user.Policy.MaxParentalRating;
|
||||||
|
}
|
||||||
|
|
||||||
if (!user.Policy.EnableAllFolders && hasParentalRatingAccess)
|
if (!user.Policy.EnableAllFolders && hasParentalRatingAccess)
|
||||||
{
|
{
|
||||||
|
@ -140,7 +146,7 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
folder => folder.Id.ToString("N", CultureInfo.InvariantCulture)
|
folder => folder.Id.ToString("N", CultureInfo.InvariantCulture)
|
||||||
);
|
);
|
||||||
var intersect = collections.Intersect(user.Policy.EnabledFolders);
|
var intersect = collections.Intersect(user.Policy.EnabledFolders);
|
||||||
return intersect.Count() > 0;
|
return intersect.Any();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -163,13 +169,13 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void NewGroup(SessionInfo session)
|
public void NewGroup(SessionInfo session, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(session.UserId);
|
var user = _userManager.GetUserById(session.UserId);
|
||||||
|
|
||||||
if (user.Policy.SyncplayAccess != SyncplayAccess.CreateAndJoinGroups)
|
if (user.Policy.SyncplayAccess != SyncplayAccess.CreateAndJoinGroups)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Syncplaymanager NewGroup: {0} does not have permission to create groups.", session.Id);
|
_logger.LogWarning("NewGroup: {0} does not have permission to create groups.", session.Id);
|
||||||
|
|
||||||
var error = new GroupUpdate<string>()
|
var error = new GroupUpdate<string>()
|
||||||
{
|
{
|
||||||
|
@ -183,24 +189,24 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
{
|
{
|
||||||
if (IsSessionInGroup(session))
|
if (IsSessionInGroup(session))
|
||||||
{
|
{
|
||||||
LeaveGroup(session);
|
LeaveGroup(session, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
var group = new SyncplayController(_logger, _sessionManager, this);
|
var group = new SyncplayController(_sessionManager, this);
|
||||||
_groups[group.GetGroupId().ToString()] = group;
|
_groups[group.GetGroupId().ToString()] = group;
|
||||||
|
|
||||||
group.InitGroup(session);
|
group.InitGroup(session, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void JoinGroup(SessionInfo session, string groupId, JoinGroupRequest request)
|
public void JoinGroup(SessionInfo session, string groupId, JoinGroupRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(session.UserId);
|
var user = _userManager.GetUserById(session.UserId);
|
||||||
|
|
||||||
if (user.Policy.SyncplayAccess == SyncplayAccess.None)
|
if (user.Policy.SyncplayAccess == SyncplayAccess.None)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Syncplaymanager JoinGroup: {0} does not have access to Syncplay.", session.Id);
|
_logger.LogWarning("JoinGroup: {0} does not have access to Syncplay.", session.Id);
|
||||||
|
|
||||||
var error = new GroupUpdate<string>()
|
var error = new GroupUpdate<string>()
|
||||||
{
|
{
|
||||||
|
@ -217,11 +223,11 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
|
|
||||||
if (group == null)
|
if (group == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Syncplaymanager JoinGroup: {0} tried to join group {0} that does not exist.", session.Id, groupId);
|
_logger.LogWarning("JoinGroup: {0} tried to join group {0} that does not exist.", session.Id, groupId);
|
||||||
|
|
||||||
var error = new GroupUpdate<string>()
|
var error = new GroupUpdate<string>()
|
||||||
{
|
{
|
||||||
Type = GroupUpdateType.GroupNotJoined
|
Type = GroupUpdateType.GroupDoesNotExist
|
||||||
};
|
};
|
||||||
_sessionManager.SendSyncplayGroupUpdate(session.Id.ToString(), error, CancellationToken.None);
|
_sessionManager.SendSyncplayGroupUpdate(session.Id.ToString(), error, CancellationToken.None);
|
||||||
return;
|
return;
|
||||||
|
@ -229,7 +235,7 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
|
|
||||||
if (!HasAccessToItem(user, group.GetPlayingItemId()))
|
if (!HasAccessToItem(user, group.GetPlayingItemId()))
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Syncplaymanager JoinGroup: {0} does not have access to {1}.", session.Id, group.GetPlayingItemId());
|
_logger.LogWarning("JoinGroup: {0} does not have access to {1}.", session.Id, group.GetPlayingItemId());
|
||||||
|
|
||||||
var error = new GroupUpdate<string>()
|
var error = new GroupUpdate<string>()
|
||||||
{
|
{
|
||||||
|
@ -243,15 +249,15 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
if (IsSessionInGroup(session))
|
if (IsSessionInGroup(session))
|
||||||
{
|
{
|
||||||
if (GetSessionGroup(session).Equals(groupId)) return;
|
if (GetSessionGroup(session).Equals(groupId)) return;
|
||||||
LeaveGroup(session);
|
LeaveGroup(session, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
group.SessionJoin(session, request);
|
group.SessionJoin(session, request, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void LeaveGroup(SessionInfo session)
|
public void LeaveGroup(SessionInfo session, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// TODO: determine what happens to users that are in a group and get their permissions revoked
|
// TODO: determine what happens to users that are in a group and get their permissions revoked
|
||||||
lock (_groupsLock)
|
lock (_groupsLock)
|
||||||
|
@ -261,7 +267,7 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
|
|
||||||
if (group == null)
|
if (group == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Syncplaymanager LeaveGroup: {0} does not belong to any group.", session.Id);
|
_logger.LogWarning("LeaveGroup: {0} does not belong to any group.", session.Id);
|
||||||
|
|
||||||
var error = new GroupUpdate<string>()
|
var error = new GroupUpdate<string>()
|
||||||
{
|
{
|
||||||
|
@ -271,17 +277,18 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
group.SessionLeave(session);
|
group.SessionLeave(session, cancellationToken);
|
||||||
|
|
||||||
if (group.IsGroupEmpty())
|
if (group.IsGroupEmpty())
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation("LeaveGroup: removing empty group {0}.", group.GetGroupId());
|
||||||
_groups.Remove(group.GetGroupId().ToString(), out _);
|
_groups.Remove(group.GetGroupId().ToString(), out _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public List<GroupInfoView> ListGroups(SessionInfo session)
|
public List<GroupInfoView> ListGroups(SessionInfo session, Guid filterItemId)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(session.UserId);
|
var user = _userManager.GetUserById(session.UserId);
|
||||||
|
|
||||||
|
@ -290,11 +297,11 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
return new List<GroupInfoView>();
|
return new List<GroupInfoView>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter by playing item if the user is viewing something already
|
// Filter by item if requested
|
||||||
if (session.NowPlayingItem != null)
|
if (!filterItemId.Equals(Guid.Empty))
|
||||||
{
|
{
|
||||||
return _groups.Values.Where(
|
return _groups.Values.Where(
|
||||||
group => group.GetPlayingItemId().Equals(session.FullNowPlayingItem.Id) && HasAccessToItem(user, group.GetPlayingItemId())
|
group => group.GetPlayingItemId().Equals(filterItemId) && HasAccessToItem(user, group.GetPlayingItemId())
|
||||||
).Select(
|
).Select(
|
||||||
group => group.GetInfo()
|
group => group.GetInfo()
|
||||||
).ToList();
|
).ToList();
|
||||||
|
@ -311,13 +318,13 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void HandleRequest(SessionInfo session, PlaybackRequest request)
|
public void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(session.UserId);
|
var user = _userManager.GetUserById(session.UserId);
|
||||||
|
|
||||||
if (user.Policy.SyncplayAccess == SyncplayAccess.None)
|
if (user.Policy.SyncplayAccess == SyncplayAccess.None)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Syncplaymanager HandleRequest: {0} does not have access to Syncplay.", session.Id);
|
_logger.LogWarning("HandleRequest: {0} does not have access to Syncplay.", session.Id);
|
||||||
|
|
||||||
var error = new GroupUpdate<string>()
|
var error = new GroupUpdate<string>()
|
||||||
{
|
{
|
||||||
|
@ -334,7 +341,7 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
|
|
||||||
if (group == null)
|
if (group == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Syncplaymanager HandleRequest: {0} does not belong to any group.", session.Id);
|
_logger.LogWarning("HandleRequest: {0} does not belong to any group.", session.Id);
|
||||||
|
|
||||||
var error = new GroupUpdate<string>()
|
var error = new GroupUpdate<string>()
|
||||||
{
|
{
|
||||||
|
@ -344,7 +351,7 @@ namespace Emby.Server.Implementations.Syncplay
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
group.HandleRequest(session, request);
|
group.HandleRequest(session, request, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Threading;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
@ -48,12 +49,19 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
public string SessionId { get; set; }
|
public string SessionId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Syncplay/{SessionId}/ListGroups", "POST", Summary = "List Syncplay groups playing same item")]
|
[Route("/Syncplay/{SessionId}/ListGroups", "POST", Summary = "List Syncplay groups")]
|
||||||
[Authenticated]
|
[Authenticated]
|
||||||
public class SyncplayListGroups : IReturnVoid
|
public class SyncplayListGroups : IReturnVoid
|
||||||
{
|
{
|
||||||
[ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
[ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||||
public string SessionId { get; set; }
|
public string SessionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the filter item id.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The filter item id.</value>
|
||||||
|
[ApiMember(Name = "FilterItemId", Description = "Filter by item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||||
|
public string FilterItemId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Syncplay/{SessionId}/PlayRequest", "POST", Summary = "Request play in Syncplay group")]
|
[Route("/Syncplay/{SessionId}/PlayRequest", "POST", Summary = "Request play in Syncplay group")]
|
||||||
|
@ -104,8 +112,8 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
/// Gets or sets whether this is a buffering or a buffering-done request.
|
/// Gets or sets whether this is a buffering or a buffering-done request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value><c>true</c> if buffering is complete; <c>false</c> otherwise.</value>
|
/// <value><c>true</c> if buffering is complete; <c>false</c> otherwise.</value>
|
||||||
[ApiMember(Name = "Resume", IsRequired = true, DataType = "bool", ParameterType = "query", Verb = "POST")]
|
[ApiMember(Name = "BufferingDone", IsRequired = true, DataType = "bool", ParameterType = "query", Verb = "POST")]
|
||||||
public bool Resume { get; set; }
|
public bool BufferingDone { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Syncplay/{SessionId}/UpdatePing", "POST", Summary = "Update session ping")]
|
[Route("/Syncplay/{SessionId}/UpdatePing", "POST", Summary = "Update session ping")]
|
||||||
|
@ -124,11 +132,6 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SyncplayService : BaseApiService
|
public class SyncplayService : BaseApiService
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The session manager.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ISessionManager _sessionManager;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The session context.
|
/// The session context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -143,12 +146,10 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
ILogger<SyncplayService> logger,
|
ILogger<SyncplayService> logger,
|
||||||
IServerConfigurationManager serverConfigurationManager,
|
IServerConfigurationManager serverConfigurationManager,
|
||||||
IHttpResultFactory httpResultFactory,
|
IHttpResultFactory httpResultFactory,
|
||||||
ISessionManager sessionManager,
|
|
||||||
ISessionContext sessionContext,
|
ISessionContext sessionContext,
|
||||||
ISyncplayManager syncplayManager)
|
ISyncplayManager syncplayManager)
|
||||||
: base(logger, serverConfigurationManager, httpResultFactory)
|
: base(logger, serverConfigurationManager, httpResultFactory)
|
||||||
{
|
{
|
||||||
_sessionManager = sessionManager;
|
|
||||||
_sessionContext = sessionContext;
|
_sessionContext = sessionContext;
|
||||||
_syncplayManager = syncplayManager;
|
_syncplayManager = syncplayManager;
|
||||||
}
|
}
|
||||||
|
@ -160,7 +161,7 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
public void Post(SyncplayNewGroup request)
|
public void Post(SyncplayNewGroup request)
|
||||||
{
|
{
|
||||||
var currentSession = GetSession(_sessionContext);
|
var currentSession = GetSession(_sessionContext);
|
||||||
_syncplayManager.NewGroup(currentSession);
|
_syncplayManager.NewGroup(currentSession, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -174,19 +175,27 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
{
|
{
|
||||||
GroupId = Guid.Parse(request.GroupId)
|
GroupId = Guid.Parse(request.GroupId)
|
||||||
};
|
};
|
||||||
try
|
|
||||||
|
// Both null and empty strings mean that client isn't playing anything
|
||||||
|
if (!String.IsNullOrEmpty(request.PlayingItemId))
|
||||||
{
|
{
|
||||||
joinRequest.PlayingItemId = Guid.Parse(request.PlayingItemId);
|
try
|
||||||
|
{
|
||||||
|
joinRequest.PlayingItemId = Guid.Parse(request.PlayingItemId);
|
||||||
|
}
|
||||||
|
catch (ArgumentNullException)
|
||||||
|
{
|
||||||
|
// Should never happen, but just in case
|
||||||
|
Logger.LogError("JoinGroup: null value for PlayingItemId. Ignoring request.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
Logger.LogError("JoinGroup: {0} is not a valid format for PlayingItemId. Ignoring request.", request.PlayingItemId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (ArgumentNullException)
|
_syncplayManager.JoinGroup(currentSession, request.GroupId, joinRequest, CancellationToken.None);
|
||||||
{
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
catch (FormatException)
|
|
||||||
{
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
_syncplayManager.JoinGroup(currentSession, request.GroupId, joinRequest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -196,7 +205,7 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
public void Post(SyncplayLeaveGroup request)
|
public void Post(SyncplayLeaveGroup request)
|
||||||
{
|
{
|
||||||
var currentSession = GetSession(_sessionContext);
|
var currentSession = GetSession(_sessionContext);
|
||||||
_syncplayManager.LeaveGroup(currentSession);
|
_syncplayManager.LeaveGroup(currentSession, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -207,7 +216,23 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
public List<GroupInfoView> Post(SyncplayListGroups request)
|
public List<GroupInfoView> Post(SyncplayListGroups request)
|
||||||
{
|
{
|
||||||
var currentSession = GetSession(_sessionContext);
|
var currentSession = GetSession(_sessionContext);
|
||||||
return _syncplayManager.ListGroups(currentSession);
|
var filterItemId = Guid.Empty;
|
||||||
|
if (!String.IsNullOrEmpty(request.FilterItemId))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
filterItemId = Guid.Parse(request.FilterItemId);
|
||||||
|
}
|
||||||
|
catch (ArgumentNullException)
|
||||||
|
{
|
||||||
|
Logger.LogWarning("ListGroups: null value for FilterItemId. Ignoring filter.");
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
Logger.LogWarning("ListGroups: {0} is not a valid format for FilterItemId. Ignoring filter.", request.FilterItemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _syncplayManager.ListGroups(currentSession, filterItemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -221,7 +246,7 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
{
|
{
|
||||||
Type = PlaybackRequestType.Play
|
Type = PlaybackRequestType.Play
|
||||||
};
|
};
|
||||||
_syncplayManager.HandleRequest(currentSession, syncplayRequest);
|
_syncplayManager.HandleRequest(currentSession, syncplayRequest, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -235,7 +260,7 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
{
|
{
|
||||||
Type = PlaybackRequestType.Pause
|
Type = PlaybackRequestType.Pause
|
||||||
};
|
};
|
||||||
_syncplayManager.HandleRequest(currentSession, syncplayRequest);
|
_syncplayManager.HandleRequest(currentSession, syncplayRequest, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -250,7 +275,7 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
Type = PlaybackRequestType.Seek,
|
Type = PlaybackRequestType.Seek,
|
||||||
PositionTicks = request.PositionTicks
|
PositionTicks = request.PositionTicks
|
||||||
};
|
};
|
||||||
_syncplayManager.HandleRequest(currentSession, syncplayRequest);
|
_syncplayManager.HandleRequest(currentSession, syncplayRequest, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -262,11 +287,11 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
var currentSession = GetSession(_sessionContext);
|
var currentSession = GetSession(_sessionContext);
|
||||||
var syncplayRequest = new PlaybackRequest()
|
var syncplayRequest = new PlaybackRequest()
|
||||||
{
|
{
|
||||||
Type = request.Resume ? PlaybackRequestType.BufferingDone : PlaybackRequestType.Buffering,
|
Type = request.BufferingDone ? PlaybackRequestType.BufferingDone : PlaybackRequestType.Buffering,
|
||||||
When = DateTime.Parse(request.When),
|
When = DateTime.Parse(request.When),
|
||||||
PositionTicks = request.PositionTicks
|
PositionTicks = request.PositionTicks
|
||||||
};
|
};
|
||||||
_syncplayManager.HandleRequest(currentSession, syncplayRequest);
|
_syncplayManager.HandleRequest(currentSession, syncplayRequest, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -281,7 +306,7 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
Type = PlaybackRequestType.UpdatePing,
|
Type = PlaybackRequestType.UpdatePing,
|
||||||
Ping = Convert.ToInt64(request.Ping)
|
Ping = Convert.ToInt64(request.Ping)
|
||||||
};
|
};
|
||||||
_syncplayManager.HandleRequest(currentSession, syncplayRequest);
|
_syncplayManager.HandleRequest(currentSession, syncplayRequest, CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Controller.Session;
|
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
using MediaBrowser.Model.Syncplay;
|
using MediaBrowser.Model.Syncplay;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -19,16 +18,6 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TimeSyncService : BaseApiService
|
public class TimeSyncService : BaseApiService
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The session manager.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ISessionManager _sessionManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The session context.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ISessionContext _sessionContext;
|
|
||||||
|
|
||||||
public TimeSyncService(
|
public TimeSyncService(
|
||||||
ILogger<TimeSyncService> logger,
|
ILogger<TimeSyncService> logger,
|
||||||
IServerConfigurationManager serverConfigurationManager,
|
IServerConfigurationManager serverConfigurationManager,
|
||||||
|
@ -55,7 +44,7 @@ namespace MediaBrowser.Api.Syncplay
|
||||||
var responseTransmissionTime = DateTime.UtcNow.ToUniversalTime().ToString("o");
|
var responseTransmissionTime = DateTime.UtcNow.ToUniversalTime().ToString("o");
|
||||||
response.ResponseTransmissionTime = responseTransmissionTime;
|
response.ResponseTransmissionTime = responseTransmissionTime;
|
||||||
|
|
||||||
// Implementing NTP on such a high level results in this useless
|
// Implementing NTP on such a high level results in this useless
|
||||||
// information being sent. On the other hand it enables future additions.
|
// information being sent. On the other hand it enables future additions.
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
|
@ -9,6 +8,9 @@ namespace MediaBrowser.Controller.Syncplay
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class GroupInfo.
|
/// Class GroupInfo.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Class is not thread-safe, external locking is required when accessing methods.
|
||||||
|
/// </remarks>
|
||||||
public class GroupInfo
|
public class GroupInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -49,8 +51,8 @@ namespace MediaBrowser.Controller.Syncplay
|
||||||
/// Gets the participants.
|
/// Gets the participants.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The participants, or members of the group.</value>
|
/// <value>The participants, or members of the group.</value>
|
||||||
public readonly ConcurrentDictionary<string, GroupMember> Participants =
|
public readonly Dictionary<string, GroupMember> Participants =
|
||||||
new ConcurrentDictionary<string, GroupMember>(StringComparer.OrdinalIgnoreCase);
|
new Dictionary<string, GroupMember>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if a session is in this group.
|
/// Checks if a session is in this group.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.Syncplay;
|
using MediaBrowser.Model.Syncplay;
|
||||||
|
|
||||||
|
@ -31,27 +32,31 @@ namespace MediaBrowser.Controller.Syncplay
|
||||||
/// Initializes the group with the session's info.
|
/// Initializes the group with the session's info.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
void InitGroup(SessionInfo session);
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
void InitGroup(SessionInfo session, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the session to the group.
|
/// Adds the session to the group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
void SessionJoin(SessionInfo session, JoinGroupRequest request);
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
void SessionJoin(SessionInfo session, JoinGroupRequest request, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the session from the group.
|
/// Removes the session from the group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
void SessionLeave(SessionInfo session);
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
void SessionLeave(SessionInfo session, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles the requested action by the session.
|
/// Handles the requested action by the session.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
/// <param name="request">The requested action.</param>
|
/// <param name="request">The requested action.</param>
|
||||||
void HandleRequest(SessionInfo session, PlaybackRequest request);
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the info about the group for the clients.
|
/// Gets the info about the group for the clients.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.Syncplay;
|
using MediaBrowser.Model.Syncplay;
|
||||||
|
|
||||||
|
@ -14,7 +15,8 @@ namespace MediaBrowser.Controller.Syncplay
|
||||||
/// Creates a new group.
|
/// Creates a new group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session that's creating the group.</param>
|
/// <param name="session">The session that's creating the group.</param>
|
||||||
void NewGroup(SessionInfo session);
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
void NewGroup(SessionInfo session, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the session to a group.
|
/// Adds the session to a group.
|
||||||
|
@ -22,27 +24,31 @@ namespace MediaBrowser.Controller.Syncplay
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
/// <param name="groupId">The group id.</param>
|
/// <param name="groupId">The group id.</param>
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
void JoinGroup(SessionInfo session, string groupId, JoinGroupRequest request);
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
void JoinGroup(SessionInfo session, string groupId, JoinGroupRequest request, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the session from a group.
|
/// Removes the session from a group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
void LeaveGroup(SessionInfo session);
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
void LeaveGroup(SessionInfo session, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets list of available groups for a session.
|
/// Gets list of available groups for a session.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
|
/// <param name="filterItemId">The item id to filter by.</param>
|
||||||
/// <value>The list of available groups.</value>
|
/// <value>The list of available groups.</value>
|
||||||
List<GroupInfoView> ListGroups(SessionInfo session);
|
List<GroupInfoView> ListGroups(SessionInfo session, Guid filterItemId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle a request by a session in a group.
|
/// Handle a request by a session in a group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
void HandleRequest(SessionInfo session, PlaybackRequest request);
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps a session to a group.
|
/// Maps a session to a group.
|
||||||
|
|
|
@ -30,13 +30,13 @@ namespace MediaBrowser.Model.Syncplay
|
||||||
/// </summary>
|
/// </summary>
|
||||||
PrepareSession,
|
PrepareSession,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The not-in-group error. Tells a user that it doesn't belong to a group.
|
/// The not-in-group error. Tells a user that they don't belong to a group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
NotInGroup,
|
NotInGroup,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The group-not-joined error. Sent when a request to join a group fails.
|
/// The group-does-not-exist error. Sent when trying to join a non-existing group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
GroupNotJoined,
|
GroupDoesNotExist,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The create-group-denied error. Sent when a user tries to create a group without required permissions.
|
/// The create-group-denied error. Sent when a user tries to create a group without required permissions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace MediaBrowser.Model.Syncplay
|
||||||
/// Gets or sets the request type.
|
/// Gets or sets the request type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The request type.</value>
|
/// <value>The request type.</value>
|
||||||
public PlaybackRequestType Type;
|
public PlaybackRequestType Type { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets when the request has been made by the client.
|
/// Gets or sets when the request has been made by the client.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user