2020-09-24 14:04:21 -07:00
using System ;
using System.Threading ;
using MediaBrowser.Controller.Session ;
using MediaBrowser.Model.SyncPlay ;
using Microsoft.Extensions.Logging ;
namespace MediaBrowser.Controller.SyncPlay
{
/// <summary>
/// Class WaitingGroupState.
/// </summary>
/// <remarks>
/// Class is not thread-safe, external locking is required when accessing methods.
/// </remarks>
public class WaitingGroupState : AbstractGroupState
{
/// <summary>
/// Tells the state to switch to after buffering is done.
/// </summary>
public bool ResumePlaying { get ; set ; } = false ;
/// <summary>
/// Whether the initial state has been set.
/// </summary>
private bool InitialStateSet { get ; set ; } = false ;
/// <summary>
/// The group state before the first ever event.
/// </summary>
private GroupState InitialState { get ; set ; }
/// <summary>
/// Default constructor.
/// </summary>
2020-10-22 06:51:58 -07:00
public WaitingGroupState ( ILogger logger )
: base ( logger )
2020-09-24 14:04:21 -07:00
{
2020-10-21 06:46:50 -07:00
// Do nothing.
2020-09-24 14:04:21 -07:00
}
/// <inheritdoc />
public override GroupState GetGroupState ( )
{
return GroupState . Waiting ;
}
/// <inheritdoc />
public override void SessionJoined ( ISyncPlayStateContext context , GroupState prevState , SessionInfo session , CancellationToken cancellationToken )
{
2020-10-21 06:46:50 -07:00
// Save state if first event.
2020-09-24 14:04:21 -07:00
if ( ! InitialStateSet )
{
InitialState = prevState ;
InitialStateSet = true ;
}
if ( prevState . Equals ( GroupState . Playing ) ) {
ResumePlaying = true ;
2020-10-21 06:46:50 -07:00
// Pause group and compute the media playback position.
2020-09-24 14:04:21 -07:00
var currentTime = DateTime . UtcNow ;
var elapsedTime = currentTime - context . LastActivity ;
context . LastActivity = currentTime ;
2020-10-22 06:51:58 -07:00
// Elapsed time is negative if event happens
// during the delay added to account for latency.
// In this phase clients haven't started the playback yet.
// In other words, LastActivity is in the future,
// when playback unpause is supposed to happen.
2020-10-21 06:46:50 -07:00
// Seek only if playback actually started.
2020-10-21 07:42:57 -07:00
context . PositionTicks + = Math . Max ( elapsedTime . Ticks , 0 ) ;
2020-09-24 14:04:21 -07:00
}
2020-10-21 06:46:50 -07:00
// Prepare new session.
2020-09-24 14:04:21 -07:00
var playQueueUpdate = context . GetPlayQueueUpdate ( PlayQueueUpdateReason . NewPlaylist ) ;
var update = context . NewSyncPlayGroupUpdate ( GroupUpdateType . PlayQueue , playQueueUpdate ) ;
context . SendGroupUpdate ( session , SyncPlayBroadcastType . CurrentSession , update , cancellationToken ) ;
context . SetBuffering ( session , true ) ;
2020-10-21 06:46:50 -07:00
// Send pause command to all non-buffering sessions.
2020-09-24 14:04:21 -07:00
var command = context . NewSyncPlayCommand ( SendCommandType . Pause ) ;
context . SendCommand ( session , SyncPlayBroadcastType . AllReady , command , cancellationToken ) ;
}
/// <inheritdoc />
public override void SessionLeaving ( ISyncPlayStateContext context , GroupState prevState , SessionInfo session , CancellationToken cancellationToken )
{
2020-10-21 06:46:50 -07:00
// Save state if first event.
2020-09-24 14:04:21 -07:00
if ( ! InitialStateSet )
{
InitialState = prevState ;
InitialStateSet = true ;
}
context . SetBuffering ( session , false ) ;
if ( ! context . IsBuffering ( ) )
{
if ( ResumePlaying )
{
2020-10-21 06:46:50 -07:00
// Client, that was buffering, left the group.
2020-09-24 14:04:21 -07:00
var playingState = new PlayingGroupState ( _logger ) ;
context . SetState ( playingState ) ;
var unpauseRequest = new UnpauseGroupRequest ( ) ;
playingState . HandleRequest ( context , GetGroupState ( ) , unpauseRequest , session , cancellationToken ) ;
_logger . LogDebug ( "SessionLeaving: {0} left the group {1}, notifying others to resume." , session . Id . ToString ( ) , context . GroupId . ToString ( ) ) ;
}
else
{
2020-10-21 06:46:50 -07:00
// Group is ready, returning to previous state.
2020-09-24 14:04:21 -07:00
var pausedState = new PausedGroupState ( _logger ) ;
context . SetState ( pausedState ) ;
_logger . LogDebug ( "SessionLeaving: {0} left the group {1}, returning to previous state." , session . Id . ToString ( ) , context . GroupId . ToString ( ) ) ;
}
}
}
/// <inheritdoc />
public override void HandleRequest ( ISyncPlayStateContext context , GroupState prevState , PlayGroupRequest request , SessionInfo session , CancellationToken cancellationToken )
{
2020-10-21 06:46:50 -07:00
// Save state if first event.
2020-09-24 14:04:21 -07:00
if ( ! InitialStateSet )
{
InitialState = prevState ;
InitialStateSet = true ;
}
ResumePlaying = true ;
var setQueueStatus = context . SetPlayQueue ( request . PlayingQueue , request . PlayingItemPosition , request . StartPositionTicks ) ;
if ( ! setQueueStatus )
{
_logger . LogError ( "HandleRequest: {0} in group {1}, unable to set playing queue." , request . GetRequestType ( ) , context . GroupId . ToString ( ) ) ;
2020-10-21 06:46:50 -07:00
// Ignore request and return to previous state.
2020-09-24 14:04:21 -07:00
ISyncPlayState newState ;
switch ( prevState )
{
case GroupState . Playing :
newState = new PlayingGroupState ( _logger ) ;
break ;
case GroupState . Paused :
newState = new PausedGroupState ( _logger ) ;
break ;
default :
newState = new IdleGroupState ( _logger ) ;
break ;
}
context . SetState ( newState ) ;
return ;
}
var playQueueUpdate = context . GetPlayQueueUpdate ( PlayQueueUpdateReason . NewPlaylist ) ;
var update = context . NewSyncPlayGroupUpdate ( GroupUpdateType . PlayQueue , playQueueUpdate ) ;
context . SendGroupUpdate ( session , SyncPlayBroadcastType . AllGroup , update , cancellationToken ) ;
2020-10-21 07:42:57 -07:00
// Reset status of sessions and await for all Ready events.
2020-09-24 14:04:21 -07:00
context . SetAllBuffering ( true ) ;
_logger . LogDebug ( "HandleRequest: {0} in group {1}, {2} set a new play queue." , request . GetRequestType ( ) , context . GroupId . ToString ( ) , session . Id . ToString ( ) ) ;
}
/// <inheritdoc />
public override void HandleRequest ( ISyncPlayStateContext context , GroupState prevState , SetPlaylistItemGroupRequest request , SessionInfo session , CancellationToken cancellationToken )
{
2020-10-21 06:46:50 -07:00
// Save state if first event.
2020-09-24 14:04:21 -07:00
if ( ! InitialStateSet )
{
InitialState = prevState ;
InitialStateSet = true ;
}
ResumePlaying = true ;
var result = context . SetPlayingItem ( request . PlaylistItemId ) ;
if ( result )
{
var playQueueUpdate = context . GetPlayQueueUpdate ( PlayQueueUpdateReason . SetCurrentItem ) ;
var update = context . NewSyncPlayGroupUpdate ( GroupUpdateType . PlayQueue , playQueueUpdate ) ;
context . SendGroupUpdate ( session , SyncPlayBroadcastType . AllGroup , update , cancellationToken ) ;
2020-10-21 07:42:57 -07:00
// Reset status of sessions and await for all Ready events.
2020-09-24 14:04:21 -07:00
context . SetAllBuffering ( true ) ;
}
else
{
2020-10-21 06:46:50 -07:00
// Return to old state.
2020-09-24 14:04:21 -07:00
ISyncPlayState newState ;
switch ( prevState )
{
case GroupState . Playing :
newState = new PlayingGroupState ( _logger ) ;
break ;
case GroupState . Paused :
newState = new PausedGroupState ( _logger ) ;
break ;
default :
newState = new IdleGroupState ( _logger ) ;
break ;
}
context . SetState ( newState ) ;
_logger . LogDebug ( "HandleRequest: {0} in group {1}, unable to change current playing item." , request . GetRequestType ( ) , context . GroupId . ToString ( ) ) ;
}
}
/// <inheritdoc />
public override void HandleRequest ( ISyncPlayStateContext context , GroupState prevState , UnpauseGroupRequest request , SessionInfo session , CancellationToken cancellationToken )
{
2020-10-21 06:46:50 -07:00
// Save state if first event.
2020-09-24 14:04:21 -07:00
if ( ! InitialStateSet )
{
InitialState = prevState ;
InitialStateSet = true ;
}
if ( prevState . Equals ( GroupState . Idle ) )
{
ResumePlaying = true ;
context . RestartCurrentItem ( ) ;
var playQueueUpdate = context . GetPlayQueueUpdate ( PlayQueueUpdateReason . NewPlaylist ) ;
var update = context . NewSyncPlayGroupUpdate ( GroupUpdateType . PlayQueue , playQueueUpdate ) ;
context . SendGroupUpdate ( session , SyncPlayBroadcastType . AllGroup , update , cancellationToken ) ;
2020-10-21 07:42:57 -07:00
// Reset status of sessions and await for all Ready events.
2020-09-24 14:04:21 -07:00
context . SetAllBuffering ( true ) ;
_logger . LogDebug ( "HandleRequest: {0} in group {1}, waiting for all ready events." , request . GetRequestType ( ) , context . GroupId . ToString ( ) ) ;
}
else
{
if ( ResumePlaying )
{
_logger . LogDebug ( "HandleRequest: {0} in group {1}, ignoring sessions that are not ready and forcing the playback to start." , request . GetRequestType ( ) , context . GroupId . ToString ( ) ) ;
2020-10-21 06:46:50 -07:00
// An Unpause request is forcing the playback to start, ignoring sessions that are not ready.
2020-09-24 14:04:21 -07:00
context . SetAllBuffering ( false ) ;
2020-10-21 06:46:50 -07:00
// Change state.
2020-09-24 14:04:21 -07:00
var playingState = new PlayingGroupState ( _logger ) ;
playingState . IgnoreBuffering = true ;
context . SetState ( playingState ) ;
playingState . HandleRequest ( context , GetGroupState ( ) , request , session , cancellationToken ) ;
}
else
{
2020-10-21 06:46:50 -07:00
// Group would have gone to paused state, now will go to playing state when ready.
2020-09-24 14:04:21 -07:00
ResumePlaying = true ;
2020-10-21 06:46:50 -07:00
// Notify relevant state change event.
2020-09-24 14:04:21 -07:00
SendGroupStateUpdate ( context , request , session , cancellationToken ) ;
}
}
}
/// <inheritdoc />
public override void HandleRequest ( ISyncPlayStateContext context , GroupState prevState , PauseGroupRequest request , SessionInfo session , CancellationToken cancellationToken )
{
2020-10-21 06:46:50 -07:00
// Save state if first event.
2020-09-24 14:04:21 -07:00
if ( ! InitialStateSet )
{
InitialState = prevState ;
InitialStateSet = true ;
}
2020-10-21 06:46:50 -07:00
// Wait for sessions to be ready, then switch to paused state.
2020-09-24 14:04:21 -07:00
ResumePlaying = false ;
2020-10-21 06:46:50 -07:00
// Notify relevant state change event.
2020-09-24 14:04:21 -07:00
SendGroupStateUpdate ( context , request , session , cancellationToken ) ;
}
/// <inheritdoc />
public override void HandleRequest ( ISyncPlayStateContext context , GroupState prevState , StopGroupRequest request , SessionInfo session , CancellationToken cancellationToken )
{
2020-10-21 06:46:50 -07:00
// Save state if first event.
2020-09-24 14:04:21 -07:00
if ( ! InitialStateSet )
{
InitialState = prevState ;
InitialStateSet = true ;
}
2020-10-21 06:46:50 -07:00
// Change state.
2020-09-24 14:04:21 -07:00
var idleState = new IdleGroupState ( _logger ) ;
context . SetState ( idleState ) ;
idleState . HandleRequest ( context , GetGroupState ( ) , request , session , cancellationToken ) ;
}
/// <inheritdoc />
public override void HandleRequest ( ISyncPlayStateContext context , GroupState prevState , SeekGroupRequest request , SessionInfo session , CancellationToken cancellationToken )
{
2020-10-21 06:46:50 -07:00
// Save state if first event.
2020-09-24 14:04:21 -07:00
if ( ! InitialStateSet )
{
InitialState = prevState ;
InitialStateSet = true ;
}
if ( prevState . Equals ( GroupState . Playing ) )
{
ResumePlaying = true ;
}
else if ( prevState . Equals ( GroupState . Paused ) )
{
ResumePlaying = false ;
}
2020-10-21 06:46:50 -07:00
// Sanitize PositionTicks.
2020-09-24 14:04:21 -07:00
var ticks = context . SanitizePositionTicks ( request . PositionTicks ) ;
2020-10-21 06:46:50 -07:00
// Seek.
2020-09-24 14:04:21 -07:00
context . PositionTicks = ticks ;
context . LastActivity = DateTime . UtcNow ;
var command = context . NewSyncPlayCommand ( SendCommandType . Seek ) ;
context . SendCommand ( session , SyncPlayBroadcastType . AllGroup , command , cancellationToken ) ;
2020-10-21 07:42:57 -07:00
// Reset status of sessions and await for all Ready events.
2020-09-24 14:04:21 -07:00
context . SetAllBuffering ( true ) ;
2020-10-21 06:46:50 -07:00
// Notify relevant state change event.
2020-09-24 14:04:21 -07:00
SendGroupStateUpdate ( context , request , session , cancellationToken ) ;
}
/// <inheritdoc />
public override void HandleRequest ( ISyncPlayStateContext context , GroupState prevState , BufferGroupRequest request , SessionInfo session , CancellationToken cancellationToken )
{
2020-10-21 06:46:50 -07:00
// Save state if first event.
2020-09-24 14:04:21 -07:00
if ( ! InitialStateSet )
{
InitialState = prevState ;
InitialStateSet = true ;
}
2020-10-21 06:46:50 -07:00
// Make sure the client is playing the correct item.
2020-09-24 14:04:21 -07:00
if ( ! request . PlaylistItemId . Equals ( context . PlayQueue . GetPlayingItemPlaylistId ( ) ) )
{
_logger . LogDebug ( "HandleRequest: {0} in group {1}, {2} has wrong playlist item." , request . GetRequestType ( ) , context . GroupId . ToString ( ) , session . Id . ToString ( ) ) ;
var playQueueUpdate = context . GetPlayQueueUpdate ( PlayQueueUpdateReason . SetCurrentItem ) ;
var updateSession = context . NewSyncPlayGroupUpdate ( GroupUpdateType . PlayQueue , playQueueUpdate ) ;
context . SendGroupUpdate ( session , SyncPlayBroadcastType . CurrentSession , updateSession , cancellationToken ) ;
context . SetBuffering ( session , true ) ;
return ;
}
if ( prevState . Equals ( GroupState . Playing ) )
{
2020-10-21 06:46:50 -07:00
// Resume playback when all ready.
2020-09-24 14:04:21 -07:00
ResumePlaying = true ;
context . SetBuffering ( session , true ) ;
2020-10-21 06:46:50 -07:00
// Pause group and compute the media playback position.
2020-09-24 14:04:21 -07:00
var currentTime = DateTime . UtcNow ;
var elapsedTime = currentTime - context . LastActivity ;
context . LastActivity = currentTime ;
2020-10-22 06:51:58 -07:00
// Elapsed time is negative if event happens
// during the delay added to account for latency.
// In this phase clients haven't started the playback yet.
// In other words, LastActivity is in the future,
// when playback unpause is supposed to happen.
// Seek only if playback actually started.
2020-10-21 07:42:57 -07:00
context . PositionTicks + = Math . Max ( elapsedTime . Ticks , 0 ) ;
2020-09-24 14:04:21 -07:00
2020-10-21 06:46:50 -07:00
// Send pause command to all non-buffering sessions.
2020-09-24 14:04:21 -07:00
var command = context . NewSyncPlayCommand ( SendCommandType . Pause ) ;
context . SendCommand ( session , SyncPlayBroadcastType . AllReady , command , cancellationToken ) ;
}
else if ( prevState . Equals ( GroupState . Paused ) )
{
2020-10-21 06:46:50 -07:00
// Don't resume playback when all ready.
2020-09-24 14:04:21 -07:00
ResumePlaying = false ;
context . SetBuffering ( session , true ) ;
2020-10-21 06:46:50 -07:00
// Send pause command to buffering session.
2020-09-24 14:04:21 -07:00
var command = context . NewSyncPlayCommand ( SendCommandType . Pause ) ;
context . SendCommand ( session , SyncPlayBroadcastType . CurrentSession , command , cancellationToken ) ;
}
else if ( prevState . Equals ( GroupState . Waiting ) )
{
2020-10-21 06:46:50 -07:00
// Another session is now buffering.
2020-09-24 14:04:21 -07:00
context . SetBuffering ( session , true ) ;
if ( ! ResumePlaying )
{
2020-10-21 06:46:50 -07:00
// Force update for this session that should be paused.
2020-09-24 14:04:21 -07:00
var command = context . NewSyncPlayCommand ( SendCommandType . Pause ) ;
context . SendCommand ( session , SyncPlayBroadcastType . CurrentSession , command , cancellationToken ) ;
}
}
2020-10-21 06:46:50 -07:00
// Notify relevant state change event.
2020-09-24 14:04:21 -07:00
SendGroupStateUpdate ( context , request , session , cancellationToken ) ;
}
/// <inheritdoc />
public override void HandleRequest ( ISyncPlayStateContext context , GroupState prevState , ReadyGroupRequest request , SessionInfo session , CancellationToken cancellationToken )
{
2020-10-21 06:46:50 -07:00
// Save state if first event.
2020-09-24 14:04:21 -07:00
if ( ! InitialStateSet )
{
InitialState = prevState ;
InitialStateSet = true ;
}
2020-10-21 06:46:50 -07:00
// Make sure the client is playing the correct item.
2020-09-24 14:04:21 -07:00
if ( ! request . PlaylistItemId . Equals ( context . PlayQueue . GetPlayingItemPlaylistId ( ) ) )
{
_logger . LogDebug ( "HandleRequest: {0} in group {1}, {2} has wrong playlist item." , request . GetRequestType ( ) , context . GroupId . ToString ( ) , session . Id . ToString ( ) ) ;
var playQueueUpdate = context . GetPlayQueueUpdate ( PlayQueueUpdateReason . SetCurrentItem ) ;
var update = context . NewSyncPlayGroupUpdate ( GroupUpdateType . PlayQueue , playQueueUpdate ) ;
context . SendGroupUpdate ( session , SyncPlayBroadcastType . CurrentSession , update , cancellationToken ) ;
context . SetBuffering ( session , true ) ;
return ;
}
var requestTicks = context . SanitizePositionTicks ( request . PositionTicks ) ;
var currentTime = DateTime . UtcNow ;
var elapsedTime = currentTime - request . When ;
if ( ! request . IsPlaying )
{
elapsedTime = TimeSpan . Zero ;
}
var clientPosition = TimeSpan . FromTicks ( requestTicks ) + elapsedTime ;
var delayTicks = context . PositionTicks - clientPosition . Ticks ;
if ( delayTicks > TimeSpan . FromSeconds ( 5 ) . Ticks )
{
// The client is really behind, other participants will have to wait a lot of time...
_logger . LogWarning ( "HandleRequest: {0} in group {1}, {2} got lost in time." , request . GetRequestType ( ) , context . GroupId . ToString ( ) , session . Id . ToString ( ) ) ;
}
if ( ResumePlaying )
{
// Handle case where session reported as ready but in reality
2020-10-21 06:46:50 -07:00
// it has no clue of the real position nor the playback state.
2020-09-24 14:04:21 -07:00
if ( ! request . IsPlaying & & Math . Abs ( context . PositionTicks - requestTicks ) > TimeSpan . FromSeconds ( 0.5 ) . Ticks ) {
2020-10-21 06:46:50 -07:00
// Session not ready at all.
2020-09-24 14:04:21 -07:00
context . SetBuffering ( session , true ) ;
2020-10-21 06:46:50 -07:00
// Correcting session's position.
2020-09-24 14:04:21 -07:00
var command = context . NewSyncPlayCommand ( SendCommandType . Seek ) ;
context . SendCommand ( session , SyncPlayBroadcastType . CurrentSession , command , cancellationToken ) ;
2020-10-21 06:46:50 -07:00
// Notify relevant state change event.
2020-09-24 14:04:21 -07:00
SendGroupStateUpdate ( context , request , session , cancellationToken ) ;
_logger . LogDebug ( "HandleRequest: {0} in group {1}, {2} got lost in time, correcting." , request . GetRequestType ( ) , context . GroupId . ToString ( ) , session . Id . ToString ( ) ) ;
return ;
}
2020-10-21 06:46:50 -07:00
// Session is ready.
2020-09-24 14:04:21 -07:00
context . SetBuffering ( session , false ) ;
if ( context . IsBuffering ( ) )
{
2020-10-21 06:46:50 -07:00
// Others are still buffering, tell this client to pause when ready.
2020-09-24 14:04:21 -07:00
var command = context . NewSyncPlayCommand ( SendCommandType . Pause ) ;
var pauseAtTime = currentTime . AddTicks ( delayTicks ) ;
command . When = context . DateToUTCString ( pauseAtTime ) ;
context . SendCommand ( session , SyncPlayBroadcastType . CurrentSession , command , cancellationToken ) ;
_logger . LogDebug ( "HandleRequest: {0} in group {1}, others still buffering, {2} will pause when ready." , request . GetRequestType ( ) , context . GroupId . ToString ( ) , session . Id . ToString ( ) ) ;
}
else
{
2020-10-21 06:46:50 -07:00
// If all ready, then start playback.
// Let other clients resume as soon as the buffering client catches up.
2020-09-24 14:04:21 -07:00
if ( delayTicks > context . GetHighestPing ( ) * 2 * TimeSpan . TicksPerMillisecond )
{
2020-10-21 06:46:50 -07:00
// Client that was buffering is recovering, notifying others to resume.
2020-09-24 14:04:21 -07:00
context . LastActivity = currentTime . AddTicks ( delayTicks ) ;
var command = context . NewSyncPlayCommand ( SendCommandType . Unpause ) ;
var filter = SyncPlayBroadcastType . AllExceptCurrentSession ;
if ( ! request . IsPlaying )
{
filter = SyncPlayBroadcastType . AllGroup ;
}
context . SendCommand ( session , filter , command , cancellationToken ) ;
_logger . LogDebug ( "HandleRequest: {0} in group {1}, {2} is recovering, notifying others to resume." , request . GetRequestType ( ) , context . GroupId . ToString ( ) , session . Id . ToString ( ) ) ;
}
else
{
2020-10-21 06:46:50 -07:00
// Client, that was buffering, resumed playback but did not update others in time.
2020-09-24 14:04:21 -07:00
delayTicks = context . GetHighestPing ( ) * 2 * TimeSpan . TicksPerMillisecond ;
2020-10-22 06:51:58 -07:00
delayTicks = Math . Max ( delayTicks , context . DefaultPing ) ;
2020-09-24 14:04:21 -07:00
context . LastActivity = currentTime . AddTicks ( delayTicks ) ;
var command = context . NewSyncPlayCommand ( SendCommandType . Unpause ) ;
context . SendCommand ( session , SyncPlayBroadcastType . AllGroup , command , cancellationToken ) ;
_logger . LogDebug ( "HandleRequest: {0} in group {1}, {2} resumed playback but did not update others in time." , request . GetRequestType ( ) , context . GroupId . ToString ( ) , session . Id . ToString ( ) ) ;
}
2020-10-21 06:46:50 -07:00
// Change state.
2020-09-24 14:04:21 -07:00
var playingState = new PlayingGroupState ( _logger ) ;
context . SetState ( playingState ) ;
playingState . HandleRequest ( context , GetGroupState ( ) , request , session , cancellationToken ) ;
}
}
else
{
2020-10-21 06:46:50 -07:00
// Check that session is really ready, tollerate half second difference to account for player imperfections.
2020-09-24 14:04:21 -07:00
if ( Math . Abs ( context . PositionTicks - requestTicks ) > TimeSpan . FromSeconds ( 0.5 ) . Ticks )
{
2020-10-21 06:46:50 -07:00
// Session still not ready.
2020-09-24 14:04:21 -07:00
context . SetBuffering ( session , true ) ;
2020-10-21 06:46:50 -07:00
// Session is seeking to wrong position, correcting.
2020-09-24 14:04:21 -07:00
var command = context . NewSyncPlayCommand ( SendCommandType . Seek ) ;
context . SendCommand ( session , SyncPlayBroadcastType . CurrentSession , command , cancellationToken ) ;
2020-10-21 06:46:50 -07:00
// Notify relevant state change event.
2020-09-24 14:04:21 -07:00
SendGroupStateUpdate ( context , request , session , cancellationToken ) ;
_logger . LogDebug ( "HandleRequest: {0} in group {1}, {2} was seeking to wrong position, correcting." , request . GetRequestType ( ) , context . GroupId . ToString ( ) , session . Id . ToString ( ) ) ;
return ;
} else {
2020-10-21 06:46:50 -07:00
// Session is ready.
2020-09-24 14:04:21 -07:00
context . SetBuffering ( session , false ) ;
}
if ( ! context . IsBuffering ( ) )
{
2020-10-21 06:46:50 -07:00
// Group is ready, returning to previous state.
2020-09-24 14:04:21 -07:00
var pausedState = new PausedGroupState ( _logger ) ;
context . SetState ( pausedState ) ;
if ( InitialState . Equals ( GroupState . Playing ) )
{
2020-10-21 06:46:50 -07:00
// Group went from playing to waiting state and a pause request occured while waiting.
2020-09-24 14:04:21 -07:00
var pauserequest = new PauseGroupRequest ( ) ;
pausedState . HandleRequest ( context , GetGroupState ( ) , pauserequest , session , cancellationToken ) ;
}
else if ( InitialState . Equals ( GroupState . Paused ) )
{
pausedState . HandleRequest ( context , GetGroupState ( ) , request , session , cancellationToken ) ;
}
_logger . LogDebug ( "HandleRequest: {0} in group {1}, {2} is ready, returning to previous state." , request . GetRequestType ( ) , context . GroupId . ToString ( ) , session . Id . ToString ( ) ) ;
}
}
}
/// <inheritdoc />
public override void HandleRequest ( ISyncPlayStateContext context , GroupState prevState , NextTrackGroupRequest request , SessionInfo session , CancellationToken cancellationToken )
{
2020-10-21 06:46:50 -07:00
// Save state if first event.
2020-09-24 14:04:21 -07:00
if ( ! InitialStateSet )
{
InitialState = prevState ;
InitialStateSet = true ;
}
ResumePlaying = true ;
2020-10-21 06:46:50 -07:00
// Make sure the client knows the playing item, to avoid duplicate requests.
2020-09-24 14:04:21 -07:00
if ( ! request . PlaylistItemId . Equals ( context . PlayQueue . GetPlayingItemPlaylistId ( ) ) )
{
2020-10-21 07:42:57 -07:00
_logger . LogDebug ( "HandleRequest: {0} in group {1}, client provided the wrong playlist identifier." , request . GetRequestType ( ) , context . GroupId . ToString ( ) ) ;
2020-09-24 14:04:21 -07:00
return ;
}
var newItem = context . NextItemInQueue ( ) ;
if ( newItem )
{
2020-10-21 06:46:50 -07:00
// Send playing-queue update.
2020-09-24 14:04:21 -07:00
var playQueueUpdate = context . GetPlayQueueUpdate ( PlayQueueUpdateReason . NextTrack ) ;
var update = context . NewSyncPlayGroupUpdate ( GroupUpdateType . PlayQueue , playQueueUpdate ) ;
context . SendGroupUpdate ( session , SyncPlayBroadcastType . AllGroup , update , cancellationToken ) ;
2020-10-21 07:42:57 -07:00
// Reset status of sessions and await for all Ready events.
2020-09-24 14:04:21 -07:00
context . SetAllBuffering ( true ) ;
}
else
{
2020-10-21 06:46:50 -07:00
// Return to old state.
2020-09-24 14:04:21 -07:00
ISyncPlayState newState ;
switch ( prevState )
{
case GroupState . Playing :
newState = new PlayingGroupState ( _logger ) ;
break ;
case GroupState . Paused :
newState = new PausedGroupState ( _logger ) ;
break ;
default :
newState = new IdleGroupState ( _logger ) ;
break ;
}
context . SetState ( newState ) ;
_logger . LogDebug ( "HandleRequest: {0} in group {1}, no next track available." , request . GetRequestType ( ) , context . GroupId . ToString ( ) ) ;
}
}
/// <inheritdoc />
public override void HandleRequest ( ISyncPlayStateContext context , GroupState prevState , PreviousTrackGroupRequest request , SessionInfo session , CancellationToken cancellationToken )
{
2020-10-21 06:46:50 -07:00
// Save state if first event.
2020-09-24 14:04:21 -07:00
if ( ! InitialStateSet )
{
InitialState = prevState ;
InitialStateSet = true ;
}
ResumePlaying = true ;
2020-10-21 06:46:50 -07:00
// Make sure the client knows the playing item, to avoid duplicate requests.
2020-09-24 14:04:21 -07:00
if ( ! request . PlaylistItemId . Equals ( context . PlayQueue . GetPlayingItemPlaylistId ( ) ) )
{
2020-10-21 07:42:57 -07:00
_logger . LogDebug ( "HandleRequest: {0} in group {1}, client provided the wrong playlist identifier." , request . GetRequestType ( ) , context . GroupId . ToString ( ) ) ;
2020-09-24 14:04:21 -07:00
return ;
}
var newItem = context . PreviousItemInQueue ( ) ;
if ( newItem )
{
2020-10-21 06:46:50 -07:00
// Send playing-queue update.
2020-09-24 14:04:21 -07:00
var playQueueUpdate = context . GetPlayQueueUpdate ( PlayQueueUpdateReason . PreviousTrack ) ;
var update = context . NewSyncPlayGroupUpdate ( GroupUpdateType . PlayQueue , playQueueUpdate ) ;
context . SendGroupUpdate ( session , SyncPlayBroadcastType . AllGroup , update , cancellationToken ) ;
2020-10-21 07:42:57 -07:00
// Reset status of sessions and await for all Ready events.
2020-09-24 14:04:21 -07:00
context . SetAllBuffering ( true ) ;
}
else
{
2020-10-21 06:46:50 -07:00
// Return to old state.
2020-09-24 14:04:21 -07:00
ISyncPlayState newState ;
switch ( prevState )
{
case GroupState . Playing :
newState = new PlayingGroupState ( _logger ) ;
break ;
case GroupState . Paused :
newState = new PausedGroupState ( _logger ) ;
break ;
default :
newState = new IdleGroupState ( _logger ) ;
break ;
}
context . SetState ( newState ) ;
_logger . LogDebug ( "HandleRequest: {0} in group {1}, no previous track available." , request . GetRequestType ( ) , context . GroupId . ToString ( ) ) ;
}
}
}
}