Re-work handling of sync on join and playlist change (#280)
This commit is contained in:
parent
7d1fda5521
commit
f8b8032ef6
@ -76,9 +76,12 @@ class SyncplayClient(object):
|
|||||||
self.serverFeatures = {}
|
self.serverFeatures = {}
|
||||||
|
|
||||||
self.lastRewindTime = None
|
self.lastRewindTime = None
|
||||||
|
self.lastUpdatedFileTime = None
|
||||||
self.lastAdvanceTime = None
|
self.lastAdvanceTime = None
|
||||||
self.lastConnectTime = None
|
self.lastConnectTime = None
|
||||||
self.lastSetRoomTime = None
|
self.lastSetRoomTime = None
|
||||||
|
self.hadFirstPlaylistIndex = False
|
||||||
|
self.hadFirstStateUpdate = False
|
||||||
self.lastLeftTime = 0
|
self.lastLeftTime = 0
|
||||||
self.lastPausedOnLeaveTime = None
|
self.lastPausedOnLeaveTime = None
|
||||||
self.lastLeftUser = ""
|
self.lastLeftUser = ""
|
||||||
@ -186,21 +189,23 @@ class SyncplayClient(object):
|
|||||||
pauseChange = self.getPlayerPaused() != paused and self.getGlobalPaused() != paused
|
pauseChange = self.getPlayerPaused() != paused and self.getGlobalPaused() != paused
|
||||||
_playerDiff = abs(self.getPlayerPosition() - position)
|
_playerDiff = abs(self.getPlayerPosition() - position)
|
||||||
_globalDiff = abs(self.getGlobalPosition() - position)
|
_globalDiff = abs(self.getGlobalPosition() - position)
|
||||||
seeked = _playerDiff > constants.SEEK_THRESHOLD and _globalDiff > constants.SEEK_THRESHOLD and not self.recentlySetRoom()
|
seeked = _playerDiff > constants.SEEK_THRESHOLD and _globalDiff > constants.SEEK_THRESHOLD
|
||||||
return pauseChange, seeked
|
return pauseChange, seeked
|
||||||
|
|
||||||
def rewindFile(self):
|
def rewindFile(self):
|
||||||
self.setPosition(-1)
|
self.setPosition(0)
|
||||||
self.establishRewindDoubleCheck()
|
self.establishRewindDoubleCheck()
|
||||||
|
|
||||||
def establishRewindDoubleCheck(self):
|
def establishRewindDoubleCheck(self):
|
||||||
reactor.callLater(0.5, self.doubleCheckRewindFile,)
|
if constants.DOUBLE_CHECK_REWIND:
|
||||||
reactor.callLater(1, self.doubleCheckRewindFile,)
|
reactor.callLater(0.5, self.doubleCheckRewindFile,)
|
||||||
reactor.callLater(1.5, self.doubleCheckRewindFile,)
|
reactor.callLater(1, self.doubleCheckRewindFile,)
|
||||||
|
reactor.callLater(1.5, self.doubleCheckRewindFile,)
|
||||||
|
return
|
||||||
|
|
||||||
def doubleCheckRewindFile(self):
|
def doubleCheckRewindFile(self):
|
||||||
if self.getStoredPlayerPosition() > 5:
|
if self.getStoredPlayerPosition() > 5:
|
||||||
self.setPosition(-1)
|
self.setPosition(0)
|
||||||
self.ui.showDebugMessage("Rewinded after double-check")
|
self.ui.showDebugMessage("Rewinded after double-check")
|
||||||
|
|
||||||
def isPlayingMusic(self):
|
def isPlayingMusic(self):
|
||||||
@ -251,7 +256,7 @@ class SyncplayClient(object):
|
|||||||
|
|
||||||
def _recentlyAdvanced(self):
|
def _recentlyAdvanced(self):
|
||||||
lastAdvandedDiff = time.time() - self.lastAdvanceTime if self.lastAdvanceTime else None
|
lastAdvandedDiff = time.time() - self.lastAdvanceTime if self.lastAdvanceTime else None
|
||||||
if lastAdvandedDiff is not None and lastAdvandedDiff < constants.LAST_PAUSED_DIFF_THRESHOLD:
|
if lastAdvandedDiff is not None and lastAdvandedDiff < constants.AUTOPLAY_DELAY + 5:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def recentlyConnected(self):
|
def recentlyConnected(self):
|
||||||
@ -259,15 +264,17 @@ class SyncplayClient(object):
|
|||||||
if connectDiff is None or connectDiff < constants.LAST_PAUSED_DIFF_THRESHOLD:
|
if connectDiff is None or connectDiff < constants.LAST_PAUSED_DIFF_THRESHOLD:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def recentlySetRoom(self):
|
def recentlyRewound(self, recentRewindThreshold = 5.0):
|
||||||
setroomDiff = time.time() - self.lastSetRoomTime if self.lastSetRoomTime else None
|
lastRewindTime = self.lastRewindTime
|
||||||
if setroomDiff is None or setroomDiff < constants.LAST_PAUSED_DIFF_THRESHOLD:
|
if lastRewindTime and self.lastUpdatedFileTime and self.lastUpdatedFileTime > lastRewindTime:
|
||||||
return True
|
lastRewindTime = self.lastRewindTime - 4.5
|
||||||
|
return lastRewindTime is not None and abs(time.time() - lastRewindTime) < recentRewindThreshold
|
||||||
|
|
||||||
def _toggleReady(self, pauseChange, paused):
|
def _toggleReady(self, pauseChange, paused):
|
||||||
if not self.userlist.currentUser.canControl():
|
if not self.userlist.currentUser.canControl():
|
||||||
self._player.setPaused(self._globalPaused)
|
self._player.setPaused(self._globalPaused)
|
||||||
self.toggleReady(manuallyInitiated=True)
|
if not self.recentlyRewound() and not ((self._globalPaused == True) and not self._recentlyAdvanced()):
|
||||||
|
self.toggleReady(manuallyInitiated=True)
|
||||||
self._playerPaused = self._globalPaused
|
self._playerPaused = self._globalPaused
|
||||||
pauseChange = False
|
pauseChange = False
|
||||||
if self.userlist.currentUser.isReady():
|
if self.userlist.currentUser.isReady():
|
||||||
@ -278,6 +285,10 @@ class SyncplayClient(object):
|
|||||||
self.ui.showDebugMessage("Readiness toggle ignored due to seamless music override")
|
self.ui.showDebugMessage("Readiness toggle ignored due to seamless music override")
|
||||||
self._player.setPaused(paused)
|
self._player.setPaused(paused)
|
||||||
self._playerPaused = paused
|
self._playerPaused = paused
|
||||||
|
elif (self.recentlyRewound() and (self._globalPaused == True) and not self._recentlyAdvanced()):
|
||||||
|
self._player.setPaused(self._globalPaused)
|
||||||
|
self._playerPaused = self._globalPaused
|
||||||
|
pauseChange = False
|
||||||
elif not paused and not self.instaplayConditionsMet():
|
elif not paused and not self.instaplayConditionsMet():
|
||||||
paused = True
|
paused = True
|
||||||
self._player.setPaused(paused)
|
self._player.setPaused(paused)
|
||||||
@ -489,6 +500,7 @@ class SyncplayClient(object):
|
|||||||
return self._globalPaused
|
return self._globalPaused
|
||||||
|
|
||||||
def updateFile(self, filename, duration, path):
|
def updateFile(self, filename, duration, path):
|
||||||
|
self.lastUpdatedFileTime = time.time()
|
||||||
newPath = ""
|
newPath = ""
|
||||||
if utils.isURL(path):
|
if utils.isURL(path):
|
||||||
filename = path
|
filename = path
|
||||||
@ -550,6 +562,7 @@ class SyncplayClient(object):
|
|||||||
self.playlist.openedFile()
|
self.playlist.openedFile()
|
||||||
self._player.openFile(filePath, resetPosition)
|
self._player.openFile(filePath, resetPosition)
|
||||||
if resetPosition:
|
if resetPosition:
|
||||||
|
self.rewindFile()
|
||||||
self.establishRewindDoubleCheck()
|
self.establishRewindDoubleCheck()
|
||||||
self.lastRewindTime = time.time()
|
self.lastRewindTime = time.time()
|
||||||
self.autoplayCheck()
|
self.autoplayCheck()
|
||||||
@ -911,12 +924,12 @@ class SyncplayClient(object):
|
|||||||
def autoplayConditionsMet(self):
|
def autoplayConditionsMet(self):
|
||||||
if self.seamlessMusicOveride():
|
if self.seamlessMusicOveride():
|
||||||
self.setPaused(False)
|
self.setPaused(False)
|
||||||
recentlyReset = (self.lastRewindTime is not None and abs(time.time() - self.lastRewindTime) < 10) and self._playerPosition < 3
|
recentlyAdvanced = self._recentlyAdvanced()
|
||||||
return (
|
return (
|
||||||
self._playerPaused and (self.autoPlay or recentlyReset) and
|
self._playerPaused and (self.autoPlay or recentlyAdvanced) and
|
||||||
self.userlist.currentUser.canControl() and self.userlist.isReadinessSupported()
|
self.userlist.currentUser.canControl() and self.userlist.isReadinessSupported()
|
||||||
and self.userlist.areAllUsersInRoomReady(requireSameFilenames=self._config["autoplayRequireSameFilenames"])
|
and self.userlist.areAllUsersInRoomReady(requireSameFilenames=self._config["autoplayRequireSameFilenames"])
|
||||||
and ((self.autoPlayThreshold and self.userlist.usersInRoomCount() >= self.autoPlayThreshold) or recentlyReset)
|
and ((self.autoPlayThreshold and self.userlist.usersInRoomCount() >= self.autoPlayThreshold) or recentlyAdvanced)
|
||||||
)
|
)
|
||||||
|
|
||||||
def autoplayTimerIsRunning(self):
|
def autoplayTimerIsRunning(self):
|
||||||
@ -1618,6 +1631,7 @@ class UiManager(object):
|
|||||||
|
|
||||||
class SyncplayPlaylist():
|
class SyncplayPlaylist():
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
||||||
|
self.queuedIndex = None
|
||||||
self._client = client
|
self._client = client
|
||||||
self._ui = self._client.ui
|
self._ui = self._client.ui
|
||||||
self._previousPlaylist = None
|
self._previousPlaylist = None
|
||||||
@ -1643,11 +1657,15 @@ class SyncplayPlaylist():
|
|||||||
try:
|
try:
|
||||||
index = self._playlist.index(filename)
|
index = self._playlist.index(filename)
|
||||||
if index != self._playlistIndex:
|
if index != self._playlistIndex:
|
||||||
self.changeToPlaylistIndex(index)
|
self.changeToPlaylistIndex(index, resetPosition=True)
|
||||||
|
else:
|
||||||
|
if filename == self.queuedIndexFilename:
|
||||||
|
return
|
||||||
|
self._client.rewindFile()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def changeToPlaylistIndex(self, index, username=None):
|
def changeToPlaylistIndex(self, index, username=None, resetPosition=False):
|
||||||
if self._playlist is None or len(self._playlist) == 0:
|
if self._playlist is None or len(self._playlist) == 0:
|
||||||
return
|
return
|
||||||
if index is None:
|
if index is None:
|
||||||
@ -1674,10 +1692,12 @@ class SyncplayPlaylist():
|
|||||||
self._playlistIndex = index
|
self._playlistIndex = index
|
||||||
if username is None:
|
if username is None:
|
||||||
if self._client.isConnectedAndInARoom() and self._client.sharedPlaylistIsEnabled():
|
if self._client.isConnectedAndInARoom() and self._client.sharedPlaylistIsEnabled():
|
||||||
|
if resetPosition:
|
||||||
|
self._client.rewindFile()
|
||||||
self._client.setPlaylistIndex(index)
|
self._client.setPlaylistIndex(index)
|
||||||
else:
|
elif index is not None:
|
||||||
self._ui.showMessage(getMessage("playlist-selection-changed-notification").format(username))
|
self._ui.showMessage(getMessage("playlist-selection-changed-notification").format(username))
|
||||||
self.switchToNewPlaylistIndex(index)
|
self.switchToNewPlaylistIndex(index, resetPosition=resetPosition)
|
||||||
|
|
||||||
def canSwitchToNextPlaylistIndex(self):
|
def canSwitchToNextPlaylistIndex(self):
|
||||||
if self._thereIsNextPlaylistIndex() and self._client.sharedPlaylistIsEnabled():
|
if self._thereIsNextPlaylistIndex() and self._client.sharedPlaylistIsEnabled():
|
||||||
@ -1696,7 +1716,12 @@ class SyncplayPlaylist():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
@needsSharedPlaylistsEnabled
|
@needsSharedPlaylistsEnabled
|
||||||
def switchToNewPlaylistIndex(self, index, resetPosition=False):
|
def switchToNewPlaylistIndex(self, index, resetPosition = False):
|
||||||
|
try:
|
||||||
|
self.queuedIndexFilename = self._playlist[index]
|
||||||
|
except:
|
||||||
|
self.queuedIndexFilename = None
|
||||||
|
self._ui.showDebugMessage("Failed to find index {} in plauylist".format(index))
|
||||||
self._lastPlaylistIndexChange = time.time()
|
self._lastPlaylistIndexChange = time.time()
|
||||||
if self._client.playerIsNotReady():
|
if self._client.playerIsNotReady():
|
||||||
self._client.addPlayerReadyCallback(lambda x: self.switchToNewPlaylistIndex(index, resetPosition))
|
self._client.addPlayerReadyCallback(lambda x: self.switchToNewPlaylistIndex(index, resetPosition))
|
||||||
@ -1773,6 +1798,7 @@ class SyncplayPlaylist():
|
|||||||
|
|
||||||
|
|
||||||
def changePlaylist(self, files, username=None, resetIndex=False):
|
def changePlaylist(self, files, username=None, resetIndex=False):
|
||||||
|
self.queuedIndexFilename = None
|
||||||
if self._playlist == files:
|
if self._playlist == files:
|
||||||
if self._playlistIndex != 0 and resetIndex:
|
if self._playlistIndex != 0 and resetIndex:
|
||||||
self.changeToPlaylistIndex(0)
|
self.changeToPlaylistIndex(0)
|
||||||
|
|||||||
@ -109,6 +109,7 @@ FOLDER_SEARCH_TIMEOUT = 20.0 # Secs - How long to wait until searches in folder
|
|||||||
FOLDER_SEARCH_DOUBLE_CHECK_INTERVAL = 30.0 # Secs - Frequency of updating cache
|
FOLDER_SEARCH_DOUBLE_CHECK_INTERVAL = 30.0 # Secs - Frequency of updating cache
|
||||||
|
|
||||||
# Usually there's no need to adjust these
|
# Usually there's no need to adjust these
|
||||||
|
DOUBLE_CHECK_REWIND = False
|
||||||
LAST_PAUSED_DIFF_THRESHOLD = 2
|
LAST_PAUSED_DIFF_THRESHOLD = 2
|
||||||
FILENAME_STRIP_REGEX = "[-~_\.\[\](): ]"
|
FILENAME_STRIP_REGEX = "[-~_\.\[\](): ]"
|
||||||
CONTROL_PASSWORD_STRIP_REGEX = "[^a-zA-Z0-9\-]"
|
CONTROL_PASSWORD_STRIP_REGEX = "[^a-zA-Z0-9\-]"
|
||||||
|
|||||||
@ -406,6 +406,8 @@ class MPCHCAPIPlayer(BasePlayer):
|
|||||||
|
|
||||||
def openFile(self, filePath, resetPosition=False):
|
def openFile(self, filePath, resetPosition=False):
|
||||||
self._mpcApi.openFile(filePath)
|
self._mpcApi.openFile(filePath)
|
||||||
|
if resetPosition:
|
||||||
|
self.setPosition(0)
|
||||||
|
|
||||||
def displayMessage(
|
def displayMessage(
|
||||||
self, message,
|
self, message,
|
||||||
|
|||||||
@ -73,12 +73,16 @@ class SyncClientProtocol(JSONCommandProtocol):
|
|||||||
self.clientIgnoringOnTheFly = 0
|
self.clientIgnoringOnTheFly = 0
|
||||||
self.serverIgnoringOnTheFly = 0
|
self.serverIgnoringOnTheFly = 0
|
||||||
self.logged = False
|
self.logged = False
|
||||||
|
self.hadFirstPlaylistIndex = False
|
||||||
|
self.hadFirstStateUpdate = False
|
||||||
self._pingService = PingService()
|
self._pingService = PingService()
|
||||||
|
|
||||||
def showDebugMessage(self, line):
|
def showDebugMessage(self, line):
|
||||||
self._client.ui.showDebugMessage(line)
|
self._client.ui.showDebugMessage(line)
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
|
self.hadFirstPlaylistIndex = False
|
||||||
|
self.hadFirstStateUpdate = False
|
||||||
self._client.initProtocol(self)
|
self._client.initProtocol(self)
|
||||||
if self._client._clientSupportsTLS:
|
if self._client._clientSupportsTLS:
|
||||||
if self._client._serverSupportsTLS:
|
if self._client._serverSupportsTLS:
|
||||||
@ -183,7 +187,12 @@ class SyncClientProtocol(JSONCommandProtocol):
|
|||||||
manuallyInitiated = values["manuallyInitiated"] if "manuallyInitiated" in values else True
|
manuallyInitiated = values["manuallyInitiated"] if "manuallyInitiated" in values else True
|
||||||
self._client.setReady(user, isReady, manuallyInitiated)
|
self._client.setReady(user, isReady, manuallyInitiated)
|
||||||
elif command == "playlistIndex":
|
elif command == "playlistIndex":
|
||||||
self._client.playlist.changeToPlaylistIndex(values['index'], values['user'])
|
user = values['user']
|
||||||
|
resetPosition = True
|
||||||
|
if not self.hadFirstPlaylistIndex:
|
||||||
|
self.hadFirstPlaylistIndex = True
|
||||||
|
resetPosition = False
|
||||||
|
self._client.playlist.changeToPlaylistIndex(values['index'], user, resetPosition=resetPosition)
|
||||||
elif command == "playlistChange":
|
elif command == "playlistChange":
|
||||||
self._client.playlist.changePlaylist(values['files'], values['user'])
|
self._client.playlist.changePlaylist(values['files'], values['user'])
|
||||||
elif command == "features":
|
elif command == "features":
|
||||||
@ -245,6 +254,8 @@ class SyncClientProtocol(JSONCommandProtocol):
|
|||||||
def handleState(self, state):
|
def handleState(self, state):
|
||||||
position, paused, doSeek, setBy = None, None, None, None
|
position, paused, doSeek, setBy = None, None, None, None
|
||||||
messageAge = 0
|
messageAge = 0
|
||||||
|
if not self.hadFirstStateUpdate:
|
||||||
|
self.hadFirstStateUpdate = True
|
||||||
if "ignoringOnTheFly" in state:
|
if "ignoringOnTheFly" in state:
|
||||||
ignore = state["ignoringOnTheFly"]
|
ignore = state["ignoringOnTheFly"]
|
||||||
if "server" in ignore:
|
if "server" in ignore:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user