diff --git a/buildPy2exe.py b/buildPy2exe.py index c59729a..20e671b 100644 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -661,6 +661,7 @@ guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock 'resources/film_edit.png', 'resources/shield_edit.png', 'resources/shield_add.png', + 'resources/email_go.png', 'resources/world_add.png', 'resources/film_add.png', 'resources/delete.png', 'resources/spinner.mng' ] resources = ["resources/icon.ico", "resources/syncplay.png"] @@ -684,7 +685,7 @@ info = dict( options={'py2exe': { 'dist_dir': OUT_DIR, 'packages': 'PySide.QtUiTools', - 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide, liburl, ast', + 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide, liburl, ast, unicodedata', 'excludes': 'venv, _ssl, doctest, pdb, unittest, win32clipboard, win32file, win32pdh, win32security, win32trace, win32ui, winxpgui, win32pipe, win32process, Tkinter', 'dll_excludes': 'msvcr71.dll, MSVCP90.dll, POWRPROF.dll', 'optimize': 2, diff --git a/resources/email_go.png b/resources/email_go.png new file mode 100644 index 0000000..4a6c5d3 Binary files /dev/null and b/resources/email_go.png differ diff --git a/syncplay/__init__.py b/syncplay/__init__.py index ad1e46d..af7090d 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ -version = '1.4.0' +version = '1.5.0' milestone = 'Yoitsu' -release_number = '38' +release_number = '39' projectURL = 'http://syncplay.pl/' diff --git a/syncplay/client.py b/syncplay/client.py index 40a44c3..28a6b7c 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -39,7 +39,7 @@ class SyncClientFactory(ClientFactory): self._timesTried += 1 self._client.ui.showMessage(getMessage("reconnection-attempt-notification")) self.reconnecting = True - reactor.callLater(0.1 * (2 ** self._timesTried), connector.connect) + reactor.callLater(0.1 * (2 ** min(self._timesTried,5)), connector.connect) else: message = getMessage("disconnection-notification") self._client.ui.showErrorMessage(message) @@ -71,6 +71,8 @@ class SyncplayClient(object): self.lastControlPasswordAttempt = None self.serverVersion = "0.0.0" + self.serverFeatures = {} + self.lastRewindTime = None self.lastLeftTime = 0 self.lastPausedOnLeaveTime = None @@ -201,7 +203,7 @@ class SyncplayClient(object): if pauseChange and paused and currentLength > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH\ and abs(position - currentLength ) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD: self.playlist.advancePlaylistCheck() - elif pauseChange and utils.meetsMinVersion(self.serverVersion, constants.USER_READY_MIN_VERSION): + elif pauseChange and self.serverFeatures["readiness"]: if currentLength == 0 or currentLength == -1 or\ not (not self.playlist.notJustChangedPlaylist() and abs(position - currentLength ) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD): pauseChange = self._toggleReady(pauseChange, paused) @@ -532,13 +534,25 @@ class SyncplayClient(object): size = 0 return filename, size - def setServerVersion(self, version): + def setServerVersion(self, version, featureList): self.serverVersion = version - self.checkForFeatureSupport() + self.checkForFeatureSupport(featureList) - def checkForFeatureSupport(self): + def checkForFeatureSupport(self, featureList): + self.serverFeatures = { + "featureList": utils.meetsMinVersion(self.serverVersion, constants.FEATURE_LIST_MIN_VERSION), + "sharedPlaylists": utils.meetsMinVersion(self.serverVersion, constants.SHARED_PLAYLIST_MIN_VERSION), + "chat": utils.meetsMinVersion(self.serverVersion, constants.CHAT_MIN_VERSION), + "readiness": utils.meetsMinVersion(self.serverVersion, constants.USER_READY_MIN_VERSION), + "managedRooms": utils.meetsMinVersion(self.serverVersion, constants.CONTROLLED_ROOMS_MIN_VERSION) + } + if featureList: + self.serverFeatures.update(featureList) if not utils.meetsMinVersion(self.serverVersion, constants.SHARED_PLAYLIST_MIN_VERSION): self.ui.showErrorMessage(getMessage("shared-playlists-not-supported-by-server-error").format(constants.SHARED_PLAYLIST_MIN_VERSION, self.serverVersion)) + elif not self.serverFeatures["sharedPlaylists"]: + self.ui.showErrorMessage(getMessage("shared-playlists-disabled-by-server-error")) + self.ui.setFeatures(self.serverFeatures) def getSanitizedCurrentUserFile(self): if self.userlist.currentUser.file: @@ -571,7 +585,12 @@ class SyncplayClient(object): self.userlist.currentUser.room = roomName if resetAutoplay: self.resetAutoPlayState() - + + def sendChat(self,message): + if self._protocol and self._protocol.logged: + message = utils.truncateText(message,constants.MAX_CHAT_MESSAGE_LENGTH) + self._protocol.sendChatMessage(message) + def sendRoom(self): room = self.userlist.currentUser.room if self._protocol and self._protocol.logged and room: @@ -590,7 +609,11 @@ class SyncplayClient(object): return self._protocol and self._protocol.logged and self.userlist.currentUser.room def sharedPlaylistIsEnabled(self): - return self._config['sharedPlaylistEnabled'] + if self.serverFeatures.has_key("sharedPlaylists") and not self.serverFeatures["sharedPlaylists"]: + sharedPlaylistEnabled = False + else: + sharedPlaylistEnabled = self._config['sharedPlaylistEnabled'] + return sharedPlaylistEnabled def connected(self): readyState = self._config['readyAtStart'] if self.userlist.currentUser.isReady() is None else self.userlist.currentUser.isReady() @@ -1025,13 +1048,12 @@ class SyncplayUserlist(object): self._roomUsersChanged = True def isReadinessSupported(self): - # TODO: Return False if server is run with --disable-ready if not utils.meetsMinVersion(self._client.serverVersion,constants.USER_READY_MIN_VERSION): return False elif self.onlyUserInRoomWhoSupportsReadiness(): return False else: - return True + return self._client.serverFeatures["readiness"] def isRoomSame(self, room): if room and self.currentUser.room and self.currentUser.room == room: @@ -1314,6 +1336,9 @@ class UiManager(object): def fileSwitchFoundFiles(self): self.__ui.fileSwitchFoundFiles() + def setFeatures(self, featureList): + self.__ui.setFeatures(featureList) + def showDebugMessage(self, message): if constants.DEBUG_MODE and message.rstrip(): sys.stderr.write("{}{}\n".format(time.strftime(constants.UI_TIME_FORMAT, time.localtime()),message.rstrip())) diff --git a/syncplay/constants.py b/syncplay/constants.py index 1898ce2..b010a1d 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -46,7 +46,7 @@ MINIMUM_SLOWDOWN_THRESHOLD = 1.3 SLOWDOWN_RESET_THRESHOLD = 0.1 DIFFERENT_DURATION_THRESHOLD = 2.5 PROTOCOL_TIMEOUT = 12.5 -RECONNECT_RETRIES = 10 +RECONNECT_RETRIES = 999 SERVER_STATE_INTERVAL = 1 WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1 AUTOPLAY_DELAY = 3.0 @@ -55,6 +55,12 @@ SYNC_ON_PAUSE = True # Client seek to global position - subtitles may disappear PLAYLIST_MAX_CHARACTERS = 10000 PLAYLIST_MAX_ITEMS = 250 +# Maximum character lengths (for client and server) +MAX_CHAT_MESSAGE_LENGTH = 50 # Number of displayed characters +MAX_USERNAME_LENGTH = 12 # Number of displayed characters +MAX_ROOM_NAME_LENGTH = 35 # Number of displayed characters +MAX_FILENAME_LENGTH = 250 # Number of displayed characters + # Options for the File Switch feature: FOLDER_SEARCH_FIRST_FILE_TIMEOUT = 15.0 # Secs - How long to wait to find the first file in folder search (to take account of HDD spin up) FOLDER_SEARCH_TIMEOUT = 6.0 # Secs - How long to wait until searches in folder to update cache are aborted (after first file is found) @@ -66,6 +72,7 @@ FILENAME_STRIP_REGEX = u"[-~_\.\[\](): ]" CONTROL_PASSWORD_STRIP_REGEX = u"[^a-zA-Z0-9\-]" ROOM_NAME_STRIP_REGEX = u"^(\+)(?P.*)(:)(\w{12})$" COMMANDS_UNDO = ["u", "undo", "revert"] +COMMANDS_CHAT = ["ch","chat"] COMMANDS_LIST = ["l", "list", "users"] COMMANDS_PAUSE = ["p", "play", "pause"] COMMANDS_ROOM = ["r", "room"] @@ -81,6 +88,8 @@ MPV_UNRESPONSIVE_THRESHOLD = 60.0 CONTROLLED_ROOMS_MIN_VERSION = "1.3.0" USER_READY_MIN_VERSION = "1.3.0" SHARED_PLAYLIST_MIN_VERSION = "1.4.0" +CHAT_MIN_VERSION = "1.5.0" +FEATURE_LIST_MIN_VERSION = "1.5.0" MPC_PATHS = [ r"c:\program files (x86)\mpc-hc\mpc-hc.exe", r"c:\program files\mpc-hc\mpc-hc.exe", @@ -143,7 +152,8 @@ STYLE_READY_PUSHBUTTON = "QPushButton { text-align: left; padding: 10px 5px 10px STYLE_AUTO_PLAY_PUSHBUTTON = "QPushButton { text-align: left; padding: 5px 5px 5px 5px; }" STYLE_NOTIFICATIONBOX = "Username { color: #367AA9; font-weight:bold; }" STYLE_CONTACT_INFO = u"{}

" # Contact info message -STYLE_USERNAME = "color: #367AA9; font-weight:bold;" +STYLE_USER_MESSAGE = u"<{}> {}" +STYLE_USERNAME = u"color: #367AA9; font-weight:bold;" STYLE_ERRORNOTIFICATION = "color: red;" STYLE_DIFFERENTITEM_COLOR = 'red' STYLE_NOFILEITEM_COLOR = 'blue' @@ -170,6 +180,7 @@ UI_COMMAND_REGEX = r"^(?P[^\ ]+)(?:\ (?P.+))?" UI_OFFSET_REGEX = r"^(?:o|offset)\ ?(?P[/+-])?(?P