Fixed offsets

Renamed util for utils, added proper time parsing
This commit is contained in:
Uriziel 2012-12-24 19:13:33 +01:00
parent 71ab3b386f
commit 694747397c
4 changed files with 106 additions and 52 deletions

View File

@ -5,6 +5,7 @@ import time
from twisted.internet.protocol import ClientFactory from twisted.internet.protocol import ClientFactory
from twisted.internet import reactor, task from twisted.internet import reactor, task
from syncplay.protocols import SyncClientProtocol from syncplay.protocols import SyncClientProtocol
from syncplay import utils
class SyncClientFactory(ClientFactory): class SyncClientFactory(ClientFactory):
def __init__(self, client, retry = 10): def __init__(self, client, retry = 10):
@ -78,6 +79,7 @@ class SyncplayClient(object):
self._lastGlobalUpdate = None self._lastGlobalUpdate = None
self._globalPosition = 0.0 self._globalPosition = 0.0
self._globalPaused = 0.0 self._globalPaused = 0.0
self._userOffset = 0.0
self._speedChanged = False self._speedChanged = False
def initProtocol(self, protocol): def initProtocol(self, protocol):
@ -119,7 +121,8 @@ class SyncplayClient(object):
return pauseChange, seeked return pauseChange, seeked
def updatePlayerStatus(self, paused, position): def updatePlayerStatus(self, paused, position):
pauseChange, seeked = self._determinePlayerStateChange(paused, position) position -= self.getUserOffset()
pauseChange, seeked = self._determinePlayerStateChange(paused, position)
self._playerPosition = position self._playerPosition = position
self._playerPaused = paused self._playerPaused = paused
if(self._lastGlobalUpdate): if(self._lastGlobalUpdate):
@ -139,13 +142,13 @@ class SyncplayClient(object):
return None, None, None, None return None, None, None, None
def _initPlayerState(self, position, paused): def _initPlayerState(self, position, paused):
self._player.setPosition(position) self.setPosition(position)
self._player.setPaused(paused) self._player.setPaused(paused)
madeChangeOnPlayer = True madeChangeOnPlayer = True
return madeChangeOnPlayer return madeChangeOnPlayer
def _rewindPlayerDueToTimeDifference(self, position, setBy): def _rewindPlayerDueToTimeDifference(self, position, setBy):
self._player.setPosition(position) self.setPosition(position)
message = "Rewinded due to time difference with <{}>".format(setBy) message = "Rewinded due to time difference with <{}>".format(setBy)
self.ui.showMessage(message) self.ui.showMessage(message)
madeChangeOnPlayer = True madeChangeOnPlayer = True
@ -160,7 +163,7 @@ class SyncplayClient(object):
def _serverPaused(self, setBy, diff): def _serverPaused(self, setBy, diff):
if (diff > 0): if (diff > 0):
self._player.setPosition(self.getGlobalPosition()) self.setPosition(self.getGlobalPosition())
self._player.setPaused(True) self._player.setPaused(True)
madeChangeOnPlayer = True madeChangeOnPlayer = True
message = '<{}> paused'.format(setBy) message = '<{}> paused'.format(setBy)
@ -170,11 +173,11 @@ class SyncplayClient(object):
def _serverSeeked(self, position, setBy): def _serverSeeked(self, position, setBy):
if(self.getUsername() <> setBy): if(self.getUsername() <> setBy):
self.playerPositionBeforeLastSeek = self.getPlayerPosition() self.playerPositionBeforeLastSeek = self.getPlayerPosition()
self._player.setPosition(position) self.setPosition(position)
madeChangeOnPlayer = True madeChangeOnPlayer = True
else: else:
madeChangeOnPlayer = False madeChangeOnPlayer = False
message = '<{}> jumped from {} to {}'.format(setBy, self.ui.formatTime(self.playerPositionBeforeLastSeek), self.ui.formatTime(position)) message = '<{}> jumped from {} to {}'.format(setBy, utils.formatTime(self.playerPositionBeforeLastSeek), utils.formatTime(position))
self.ui.showMessage(message) self.ui.showMessage(message)
return madeChangeOnPlayer return madeChangeOnPlayer
@ -214,12 +217,22 @@ class SyncplayClient(object):
self.__getUserlistOnLogon = False self.__getUserlistOnLogon = False
self.getUserList() self.getUserList()
madeChangeOnPlayer = False madeChangeOnPlayer = False
if(not paused): if(not paused):
position += latency position += latency
if(self._player): if(self._player):
madeChangeOnPlayer = self._changePlayerStateAccordingToGlobalState(position, paused, doSeek, setBy) madeChangeOnPlayer = self._changePlayerStateAccordingToGlobalState(position, paused, doSeek, setBy)
if(madeChangeOnPlayer): if(madeChangeOnPlayer):
self.askPlayer() self.askPlayer()
def getUserOffset(self):
return self._userOffset
def setUserOffset(self, time):
self._userOffset = time
message = "Current offset: {} seconds".format(self._userOffset)
self.setPosition(self.getGlobalPosition())
self.ui.showMessage(message)
def getPlayerPosition(self): def getPlayerPosition(self):
if(not self._lastPlayerUpdate): if(not self._lastPlayerUpdate):
@ -291,7 +304,10 @@ class SyncplayClient(object):
return self._serverPassword return self._serverPassword
def setPosition(self, position): def setPosition(self, position):
position += self.getUserOffset()
if(self._player): if(self._player):
if(position < 0):
position = 0
self._player.setPosition(position) self._player.setPosition(position)
def setPaused(self, paused): def setPaused(self, paused):
@ -360,7 +376,7 @@ class SyncplayUserlist(object):
message = "<{}> has joined the room: '{}'".format(username, room) message = "<{}> has joined the room: '{}'".format(username, room)
self.ui.showMessage(message) self.ui.showMessage(message)
elif (room and file_ and username != self.currentUser.username): elif (room and file_ and username != self.currentUser.username):
duration = self.ui.formatTime(file_['duration']) duration = utils.formatTime(file_['duration'])
message = "<{}> is playing '{}' ({})".format(username, file_['name'], duration) message = "<{}> is playing '{}' ({})".format(username, file_['name'], duration)
if(self.currentUser.room <> room or self.currentUser.username == username): if(self.currentUser.room <> room or self.currentUser.username == username):
message += " in room: '{}'".format(room) message += " in room: '{}'".format(room)
@ -411,8 +427,8 @@ class SyncplayUserlist(object):
self.addUser(username, room, file_) self.addUser(username, room, file_)
def __addUserWithFileToList(self, rooms, user): def __addUserWithFileToList(self, rooms, user):
currentPosition = self.ui.formatTime(user.lastPosition) currentPosition = utils.formatTime(user.lastPosition)
file_key = '\'{}\' ({}/{})'.format(user.file['name'], currentPosition, self.ui.formatTime(user.file['duration'])) file_key = '\'{}\' ({}/{})'.format(user.file['name'], currentPosition, utils.formatTime(user.file['duration']))
if (not rooms[user.room].has_key(file_key)): if (not rooms[user.room].has_key(file_key)):
rooms[user.room][file_key] = {} rooms[user.room][file_key] = {}
rooms[user.room][file_key][user.username] = user rooms[user.room][file_key][user.username] = user
@ -492,19 +508,3 @@ class UiManager(object):
def promptFor(self, prompt): def promptFor(self, prompt):
return self.__ui.promptFor(prompt) return self.__ui.promptFor(prompt)
def formatTime(self, timeInSeconds):
timeInSeconds = round(timeInSeconds)
weeks = timeInSeconds // 604800
days = (timeInSeconds % 604800) // 86400
hours = (timeInSeconds % 86400) // 3600
minutes = (timeInSeconds % 3600) // 60
seconds = timeInSeconds % 60
if(weeks > 0):
return '{0:.0f}w, {1:.0f}d, {2:02.0f}:{3:02.0f}:{4:02.0f}'.format(weeks, days, hours, minutes, seconds)
elif(days > 0):
return '{0:.0f}d, {1:02.0f}:{2:02.0f}:{3:02.0f}'.format(days, hours, minutes, seconds)
elif(hours > 0):
return '{0:02.0f}:{1:02.0f}:{2:02.0f}'.format(hours, minutes, seconds)
else:
return '{0:02.0f}:{1:02.0f}'.format(minutes, seconds)

View File

@ -6,7 +6,7 @@ import win32con, win32api, win32gui, ctypes, ctypes.wintypes #@UnresolvedImport
from functools import wraps from functools import wraps
from syncplay.players.basePlayer import BasePlayer from syncplay.players.basePlayer import BasePlayer
import re import re
from syncplay.util import retry from syncplay.utils import retry
class MpcHcApi: class MpcHcApi:

View File

@ -4,6 +4,7 @@ import time
import syncplay import syncplay
import os import os
import re import re
from syncplay import utils
class ConsoleUI(threading.Thread): class ConsoleUI(threading.Thread):
def __init__(self): def __init__(self):
@ -48,26 +49,52 @@ class ConsoleUI(threading.Thread):
def showErrorMessage(self, message): def showErrorMessage(self, message):
print("ERROR:\t" + message) print("ERROR:\t" + message)
def __doSeek(self, m): def _extractRegexSign(self, m):
if (m.group(4)):
t = int(m.group(5)) * 60 + int(m.group(6))
else:
t = int(m.group(2))
if(m.group(1)): if(m.group(1)):
if(m.group(1) == "-"): if(m.group(1) == "-"):
sign = -1 return -1
else: else:
sign = 1 return 1
t = self._syncplayClient.getGlobalPosition() + sign * t else:
self._syncplayClient.setPosition(t) return None
def _tryAdvancedCommands(self, data):
o = re.match(r"^(?:o|offset)\ ([+-])?\ ?(.+)$", data)
s = re.match(r"^(?:s|seek)?\ ?([+-])?\ ?(.+)$", data) #careful! s will match o as well
if(o):
sign = self._extractRegexSign(o)
t = utils.parseTime(o.group(2))
if(not t):
return
if(sign):
t = self._syncplayClient.getUserOffset() + sign * t
self._syncplayClient.setUserOffset(t)
return True
elif s:
sign = self._extractRegexSign(s)
t = utils.parseTime(s.group(2))
if(t is None):
return
if(sign):
t = self._syncplayClient.getGlobalPosition() + sign * t
self._syncplayClient.setPosition(t)
return True
return False
def _executeCommand(self, data): def _executeCommand(self, data):
m = re.match(r"^s? ?([+-])? ?((\d+)|((\d+)\D(\d+)))$", data) command = re.match(r"^(.+)(?:\ (.+))?", data)
r = re.match(r"^(r|room)( (.+))?$", data) if(not command):
if(m): return
self.__doSeek(m) if(command.group(1) in ["u", "undo", "revert"]):
elif r: tmp_pos = self._syncplayClient.getPlayerPosition()
room = r.group(3) self._syncplayClient.setPosition(self._syncplayClient.playerPositionBeforeLastSeek)
self._syncplayClient.playerPositionBeforeLastSeek = tmp_pos
elif (command.group(1) in ["l", "list", "users"]):
self._syncplayClient.getUserList()
elif (command.group(1) in ["p", "play", "pause"]):
self._syncplayClient.setPaused(not self._syncplayClient.getPlayerPaused())
elif (command.group(1) in ["r", "room"]):
room = command.group(1)
if room == None: if room == None:
if self._syncplayClient.userlist.currentUser.file: if self._syncplayClient.userlist.currentUser.file:
room = self._syncplayClient.userlist.currentUser.file["name"] room = self._syncplayClient.userlist.currentUser.file["name"]
@ -75,16 +102,10 @@ class ConsoleUI(threading.Thread):
room = self._syncplayClient.defaultRoom room = self._syncplayClient.defaultRoom
self._syncplayClient.setRoom(room) self._syncplayClient.setRoom(room)
self._syncplayClient.sendRoom() self._syncplayClient.sendRoom()
elif data == "u":
tmp_pos = self._syncplayClient.getPlayerPosition()
self._syncplayClient.setPosition(self._syncplayClient.playerPositionBeforeLastSeek)
self._syncplayClient.playerPositionBeforeLastSeek = tmp_pos
elif data == "l":
self._syncplayClient.getUserList()
elif data == "p":
self._syncplayClient.setPaused(not self._syncplayClient.getPlayerPaused())
else: else:
if data not in ['help', 'h', '?', '/?', '\?']: if(self._tryAdvancedCommands(data)):
return
if (command.group(1) not in ['help', 'h', '?', '/?', '\?']):
self.showMessage("Unrecognized command") self.showMessage("Unrecognized command")
self.showMessage("Available commands:", True) self.showMessage("Available commands:", True)
self.showMessage("\tr [name] - change room", True) self.showMessage("\tr [name] - change room", True)
@ -95,4 +116,4 @@ class ConsoleUI(threading.Thread):
self.showMessage("\th - this help", True) self.showMessage("\th - this help", True)
self.showMessage("Syncplay version: {}".format(syncplay.version), True) self.showMessage("Syncplay version: {}".format(syncplay.version), True)
self.showMessage("More info available at: {}".format(syncplay.projectURL), True) self.showMessage("More info available at: {}".format(syncplay.projectURL), True)

View File

@ -1,4 +1,6 @@
import time import time
import re
import datetime
def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None): def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
"""Retry calling the decorated function using an exponential backoff. """Retry calling the decorated function using an exponential backoff.
@ -39,4 +41,35 @@ def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
return f(*args, **kwargs) return f(*args, **kwargs)
return return
return f_retry # true decorator return f_retry # true decorator
return deco_retry return deco_retry
def parseTime(timeStr):
regex = re.compile(r'(:?(?:(?P<hours>\d+?)[^\d\.])?(?:(?P<minutes>\d+?))?[^\d\.])?(?P<seconds>\d+?)(?:\.(?P<miliseconds>\d+?))?$')
parts = regex.match(timeStr)
if not parts:
return
parts = parts.groupdict()
time_params = {}
for (name, param) in parts.iteritems():
if param:
if(name == "miliseconds"):
time_params["microseconds"] = int(param) * 1000
else:
time_params[name] = int(param)
return datetime.timedelta(**time_params).total_seconds()
def formatTime(timeInSeconds):
timeInSeconds = round(timeInSeconds)
weeks = timeInSeconds // 604800
days = (timeInSeconds % 604800) // 86400
hours = (timeInSeconds % 86400) // 3600
minutes = (timeInSeconds % 3600) // 60
seconds = timeInSeconds % 60
if(weeks > 0):
return '{0:.0f}w, {1:.0f}d, {2:02.0f}:{3:02.0f}:{4:02.0f}'.format(weeks, days, hours, minutes, seconds)
elif(days > 0):
return '{0:.0f}d, {1:02.0f}:{2:02.0f}:{3:02.0f}'.format(days, hours, minutes, seconds)
elif(hours > 0):
return '{0:02.0f}:{1:02.0f}:{2:02.0f}'.format(hours, minutes, seconds)
else:
return '{0:02.0f}:{1:02.0f}'.format(minutes, seconds)