From b827a57cabf7952ea78e76049f1f35a54b2e92e4 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Tue, 24 Apr 2018 00:44:19 +0200 Subject: [PATCH 01/95] 2to3 conversion and initial VLC support --- appdmg.py | 2 +- buildPy2app.py | 0 buildPy2exe.py | 16 +- syncplay/__init__.py | 0 syncplay/client.py | 154 +++-- syncplay/clientManager.py | 0 syncplay/constants.py | 30 +- syncplay/messages.py | 34 +- syncplay/messages_de.py | 762 +++++++++++------------ syncplay/messages_en.py | 328 +++++----- syncplay/messages_it.py | 772 +++++++++++------------ syncplay/messages_ru.py | 772 +++++++++++------------ syncplay/players/__init__.py | 32 +- syncplay/players/basePlayer.py | 258 ++++---- syncplay/players/mpc.py | 80 +-- syncplay/players/mpcbe.py | 18 +- syncplay/players/mplayer.py | 40 +- syncplay/players/mpv.py | 40 +- syncplay/players/playerFactory.py | 0 syncplay/players/vlc.py | 958 +++++++++++++++-------------- syncplay/protocols.py | 110 ++-- syncplay/server.py | 16 +- syncplay/ui/ConfigurationGetter.py | 42 +- syncplay/ui/GuiConfiguration.py | 92 +-- syncplay/ui/__init__.py | 0 syncplay/ui/consoleUI.py | 20 +- syncplay/ui/gui.py | 170 ++--- syncplay/utils.py | 50 +- syncplay/vendor/Qt.py | 18 +- syncplay/vendor/__init__.py | 0 syncplay/vendor/qt5reactor.py | 6 +- syncplayClient.py | 12 +- syncplayServer.py | 12 +- 33 files changed, 2422 insertions(+), 2422 deletions(-) mode change 100644 => 100755 appdmg.py mode change 100644 => 100755 buildPy2app.py mode change 100644 => 100755 syncplay/__init__.py mode change 100644 => 100755 syncplay/client.py mode change 100644 => 100755 syncplay/clientManager.py mode change 100644 => 100755 syncplay/constants.py mode change 100644 => 100755 syncplay/messages_de.py mode change 100644 => 100755 syncplay/messages_en.py mode change 100644 => 100755 syncplay/messages_it.py mode change 100644 => 100755 syncplay/messages_ru.py mode change 100644 => 100755 syncplay/players/__init__.py mode change 100644 => 100755 syncplay/players/basePlayer.py mode change 100644 => 100755 syncplay/players/mpc.py mode change 100644 => 100755 syncplay/players/mpcbe.py mode change 100644 => 100755 syncplay/players/mplayer.py mode change 100644 => 100755 syncplay/players/mpv.py mode change 100644 => 100755 syncplay/players/playerFactory.py mode change 100644 => 100755 syncplay/protocols.py mode change 100644 => 100755 syncplay/server.py mode change 100644 => 100755 syncplay/ui/__init__.py mode change 100644 => 100755 syncplay/ui/consoleUI.py mode change 100644 => 100755 syncplay/utils.py mode change 100644 => 100755 syncplay/vendor/__init__.py mode change 100644 => 100755 syncplay/vendor/qt5reactor.py diff --git a/appdmg.py b/appdmg.py old mode 100644 new mode 100755 index 3e2dc8c..705ff7b --- a/appdmg.py +++ b/appdmg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + import biplist import os.path diff --git a/buildPy2app.py b/buildPy2app.py old mode 100644 new mode 100755 diff --git a/buildPy2exe.py b/buildPy2exe.py index 13975f8..c96b22c 100755 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -33,8 +33,8 @@ if missingStrings is not None and missingStrings is not "": def get_nsis_path(): bin_name = "makensis.exe" - from _winreg import HKEY_LOCAL_MACHINE as HKLM - from _winreg import KEY_READ, KEY_WOW64_32KEY, OpenKey, QueryValueEx + from winreg import HKEY_LOCAL_MACHINE as HKLM + from winreg import KEY_READ, KEY_WOW64_32KEY, OpenKey, QueryValueEx try: nsisreg = OpenKey(HKLM, "Software\\NSIS", 0, KEY_READ | KEY_WOW64_32KEY) @@ -616,7 +616,7 @@ NSIS_SCRIPT_TEMPLATE = r""" class NSISScript(object): def create(self): fileList, totalSize = self.getBuildDirContents(OUT_DIR) - print "Total size eq: {}".format(totalSize) + print("Total size eq: {}".format(totalSize)) installFiles = self.prepareInstallListTemplate(fileList) uninstallFiles = self.prepareDeleteListTemplate(fileList) @@ -648,14 +648,14 @@ class NSISScript(object): totalSize += sum(os.path.getsize(os.path.join(root, file_)) for file_ in files) for file_ in files: new_root = root.replace(OUT_DIR, "").strip("\\") - if not fileList.has_key(new_root): + if new_root not in fileList: fileList[new_root] = [] fileList[new_root].append(file_) return fileList, totalSize def prepareInstallListTemplate(self, fileList): create = [] - for dir_ in fileList.iterkeys(): + for dir_ in fileList.keys(): create.append('SetOutPath "$INSTDIR\\{}"'.format(dir_)) for file_ in fileList[dir_]: create.append('FILE "{}\\{}\\{}"'.format(OUT_DIR, dir_, file_)) @@ -663,7 +663,7 @@ class NSISScript(object): def prepareDeleteListTemplate(self, fileList): delete = [] - for dir_ in fileList.iterkeys(): + for dir_ in fileList.keys(): for file_ in fileList[dir_]: delete.append('DELETE "$INSTDIR\\{}\\{}"'.format(dir_, file_)) delete.append('RMdir "$INSTDIR\\{}"'.format(file_)) @@ -674,9 +674,9 @@ class build_installer(py2exe): py2exe.run(self) script = NSISScript() script.create() - print "*** compiling the NSIS setup script***" + print("*** compiling the NSIS setup script***") script.compile() - print "*** DONE ***" + print("*** DONE ***") guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock_go.png', 'resources/control_pause_blue.png', 'resources/cross.png', 'resources/door_in.png', diff --git a/syncplay/__init__.py b/syncplay/__init__.py old mode 100644 new mode 100755 diff --git a/syncplay/client.py b/syncplay/client.py old mode 100644 new mode 100755 index e8cb8b5..cdce3e9 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -76,7 +76,7 @@ class SyncplayClient(object): self.lastRewindTime = None self.lastLeftTime = 0 self.lastPausedOnLeaveTime = None - self.lastLeftUser = u"" + self.lastLeftUser = "" self.protocolFactory = SyncClientFactory(self) self.ui = UiManager(self, ui) self.userlist = SyncplayUserlist(self.ui, self) @@ -90,9 +90,9 @@ class SyncplayClient(object): self.setUsername(config['name']) self.setRoom(config['room']) if config['password']: - config['password'] = hashlib.md5(config['password']).hexdigest() + config['password'] = hashlib.md5(config['password'].encode('utf-8')).hexdigest() self._serverPassword = config['password'] - self._host = u"{}:{}".format(config['host'],config['port']) + self._host = "{}:{}".format(config['host'],config['port']) self._publicServers = config["publicServers"] if not config['file']: self.__getUserlistOnLogon = True @@ -126,14 +126,14 @@ class SyncplayClient(object): self.fileSwitch = FileSwitchManager(self) self.playlist = SyncplayPlaylist(self) - if constants.LIST_RELATIVE_CONFIGS and self._config.has_key('loadedRelativePaths') and self._config['loadedRelativePaths']: + if constants.LIST_RELATIVE_CONFIGS and 'loadedRelativePaths' in self._config and self._config['loadedRelativePaths']: paths = "; ".join(self._config['loadedRelativePaths']) self.ui.showMessage(getMessage("relative-config-notification").format(paths), noPlayer=True, noTimestamp=True) if constants.DEBUG_MODE and constants.WARN_ABOUT_MISSING_STRINGS: missingStrings = getMissingStrings() if missingStrings is not None and missingStrings is not "": - self.ui.showDebugMessage(u"MISSING/UNUSED STRINGS DETECTED:\n{}".format(missingStrings)) + self.ui.showDebugMessage("MISSING/UNUSED STRINGS DETECTED:\n{}".format(missingStrings)) def initProtocol(self, protocol): self._protocol = protocol @@ -205,7 +205,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 self.serverFeatures.has_key("readiness") and self.serverFeatures["readiness"]: + elif pauseChange and "readiness" in self.serverFeatures 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) @@ -299,7 +299,7 @@ class SyncplayClient(object): def _serverPaused(self, setBy): hideFromOSD = not constants.SHOW_SAME_ROOM_OSD - if constants.SYNC_ON_PAUSE and self.getUsername() <> setBy: + if constants.SYNC_ON_PAUSE and self.getUsername() != setBy: self.setPosition(self.getGlobalPosition()) self._player.setPaused(True) madeChangeOnPlayer = True @@ -311,7 +311,7 @@ class SyncplayClient(object): def _serverSeeked(self, position, setBy): hideFromOSD = not constants.SHOW_SAME_ROOM_OSD - if self.getUsername() <> setBy: + if self.getUsername() != setBy: self.playerPositionBeforeLastSeek = self.getPlayerPosition() self.setPosition(position) madeChangeOnPlayer = True @@ -445,7 +445,7 @@ class SyncplayClient(object): return self._globalPaused def updateFile(self, filename, duration, path): - newPath = u"" + newPath = "" if utils.isURL(path): try: filename = path.encode('utf-8') @@ -472,7 +472,7 @@ class SyncplayClient(object): from syncplay.ui.ConfigurationGetter import ConfigurationGetter ConfigurationGetter().setConfigOption("trustedDomains", newTrustedDomains) oldTrustedDomains = self._config['trustedDomains'] - if oldTrustedDomains <> newTrustedDomains: + if oldTrustedDomains != newTrustedDomains: self._config['trustedDomains'] = newTrustedDomains self.fileSwitchFoundFiles() self.ui.showMessage("Trusted domains updated") @@ -487,13 +487,13 @@ class SyncplayClient(object): return False def isURITrusted(self, URIToTest): - URIToTest = URIToTest+u"/" + URIToTest = URIToTest+"/" for trustedProtocol in constants.TRUSTABLE_WEB_PROTOCOLS: if URIToTest.startswith(trustedProtocol): if self._config['onlySwitchToTrustedDomains']: if self._config['trustedDomains']: for trustedDomain in self._config['trustedDomains']: - trustableURI = ''.join([trustedProtocol,trustedDomain,u"/"]) + trustableURI = ''.join([trustedProtocol,trustedDomain,"/"]) if URIToTest.startswith(trustableURI): return True return False @@ -569,9 +569,9 @@ class SyncplayClient(object): if self.serverFeatures["maxFilenameLength"] is not None: constants.MAX_FILENAME_LENGTH = self.serverFeatures["maxFilenameLength"] constants.MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND = ["MaxChatMessageLength={}".format(constants.MAX_CHAT_MESSAGE_LENGTH), - u"inputPromptStartCharacter={}".format(constants.MPV_INPUT_PROMPT_START_CHARACTER), - u"inputPromptEndCharacter={}".format(constants.MPV_INPUT_PROMPT_END_CHARACTER), - u"backslashSubstituteCharacter={}".format( + "inputPromptStartCharacter={}".format(constants.MPV_INPUT_PROMPT_START_CHARACTER), + "inputPromptEndCharacter={}".format(constants.MPV_INPUT_PROMPT_END_CHARACTER), + "backslashSubstituteCharacter={}".format( constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER)] self.ui.setFeatures(self.serverFeatures) if self._player: @@ -582,7 +582,7 @@ class SyncplayClient(object): file_ = deepcopy(self.userlist.currentUser.file) if constants.PRIVATE_FILE_FIELDS: for PrivateField in constants.PRIVATE_FILE_FIELDS: - if file_.has_key(PrivateField): + if PrivateField in file_: file_.pop(PrivateField) return file_ else: @@ -595,7 +595,7 @@ class SyncplayClient(object): self._protocol.sendFileSetting(file_) def setUsername(self, username): - if username and username <> "": + if username and username != "": self.userlist.currentUser.username = username else: random_number = random.randrange(1000, 9999) @@ -645,7 +645,7 @@ class SyncplayClient(object): return self._protocol and self._protocol.logged and self.userlist.currentUser.room def sharedPlaylistIsEnabled(self): - if self.serverFeatures.has_key("sharedPlaylists") and not self.serverFeatures["sharedPlaylists"]: + if "sharedPlaylists" in self.serverFeatures and not self.serverFeatures["sharedPlaylists"]: sharedPlaylistEnabled = False else: sharedPlaylistEnabled = self._config['sharedPlaylistEnabled'] @@ -742,8 +742,8 @@ class SyncplayClient(object): self.ui.showDebugMessage( "Tried to check server version too soon (testing support for: {})".format(featureRequired)) return None - if not self.serverFeatures.has_key(featureRequired) or not self.serverFeatures[featureRequired]: - featureName = getMessage(u"feature-{}".format(featureRequired)) + if featureRequired not in self.serverFeatures or not self.serverFeatures[featureRequired]: + featureName = getMessage("feature-{}".format(featureRequired)) self.ui.showErrorMessage(getMessage("not-supported-by-server-error").format(featureName)) return return f(self, *args, **kwds) @@ -824,7 +824,7 @@ class SyncplayClient(object): return allReadyMessage = getMessage("all-users-ready").format(self.userlist.readyUserCount()) autoplayingMessage = getMessage("autoplaying-notification").format(int(self.autoplayTimeLeft)) - countdownMessage = u"{}{}{}".format(allReadyMessage,self._player.osdMessageSeparator, autoplayingMessage) + countdownMessage = "{}{}{}".format(allReadyMessage,self._player.osdMessageSeparator, autoplayingMessage) self.ui.showOSDMessage(countdownMessage, 1, OSDType=constants.OSD_ALERT, mood=constants.MESSAGE_GOODNEWS) if self.autoplayTimeLeft <= 0: self.setPaused(False) @@ -905,16 +905,16 @@ class SyncplayClient(object): self.controlpasswords[room] = password def getControlledRoomPassword(self, room): - if self.controlpasswords.has_key(room): + if room in self.controlpasswords: return self.controlpasswords[room] def checkForUpdate(self, userInitiated): try: - import urllib, syncplay, sys, messages, json - params = urllib.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, + import urllib.request, urllib.parse, urllib.error, syncplay, sys, messages, json + params = urllib.parse.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, 'language': messages.messages["CURRENT"], 'platform': sys.platform, 'userInitiated': userInitiated}) - f = urllib.urlopen(constants.SYNCPLAY_UPDATE_URL.format(params)) + f = urllib.request.urlopen(constants.SYNCPLAY_UPDATE_URL.format(params)) response = f.read() response = response.replace("

","").replace("

","").replace("
","").replace("“","\"").replace("”","\"") # Fix Wordpress response = json.loads(response) @@ -923,8 +923,8 @@ class SyncplayClient(object): publicServers = response["public-servers"].\ replace("”","'").replace(":’","'").replace("’","'").replace("′","'").replace("\n","").replace("\r","") publicServers = ast.literal_eval(publicServers) - return response["version-status"], response["version-message"] if response.has_key("version-message")\ - else None, response["version-url"] if response.has_key("version-url") else None, publicServers + return response["version-status"], response["version-message"] if "version-message" in response\ + else None, response["version-url"] if "version-url" in response else None, publicServers except: return "failed", getMessage("update-check-failed-notification").format(syncplay.version), constants.SYNCPLAY_DOWNLOAD_URL, None @@ -1012,10 +1012,10 @@ class SyncplayClient(object): if self._userlist.currentUser.canControl() and self._userlist.isReadinessSupported(): if self._userlist.areAllUsersInRoomReady(): allReadyMessage = getMessage("all-users-ready").format(self._userlist.readyUserCount()) - osdMessage = u"{}{}{}".format(fileDifferencesMessage, self._client._player.osdMessageSeparator, allReadyMessage) + osdMessage = "{}{}{}".format(fileDifferencesMessage, self._client._player.osdMessageSeparator, allReadyMessage) else: notAllReadyMessage = getMessage("not-all-ready").format(self._userlist.usersInRoomNotReady()) - osdMessage = u"{}{}{}".format(fileDifferencesMessage, self._client._player.osdMessageSeparator, notAllReadyMessage) + osdMessage = "{}{}{}".format(fileDifferencesMessage, self._client._player.osdMessageSeparator, notAllReadyMessage) else: osdMessage = fileDifferencesMessage elif self._userlist.isReadinessSupported(): @@ -1067,7 +1067,7 @@ class SyncplayUser(object): def isFileSame(self, file_): if not self.file: - return False + return False sameName = utils.sameFilename(self.file['name'], file_['name']) sameSize = utils.sameFilesize(self.file['size'], file_['size']) sameDuration = utils.sameFileduration(self.file['duration'], file_['duration']) @@ -1081,9 +1081,9 @@ class SyncplayUser(object): def __repr__(self, *args, **kwargs): if self.file: - return u"{}: {} ({}, {})".format(self.username, self.file['name'], self.file['duration'], self.file['size']) + return "{}: {} ({}, {})".format(self.username, self.file['name'], self.file['duration'], self.file['size']) else: - return u"{}".format(self.username) + return "{}".format(self.username) def setControllerStatus(self, isController): self._controller = isController @@ -1148,7 +1148,7 @@ class SyncplayUserlist(object): else: duration = utils.formatTime(file_['duration']) message = getMessage("playing-notification").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 += getMessage("playing-notification/room-addendum").format(room) self.ui.showMessage(message, hideFromOSD) if self.currentUser.file and not self.currentUser.isFileSame(file_) and self.currentUser.room == room: @@ -1176,7 +1176,7 @@ class SyncplayUserlist(object): differentName = False differentSize = False differentDuration = False - for otherUser in self._users.itervalues(): + for otherUser in self._users.values(): if otherUser.room == self.currentUser.room and otherUser.file: if not utils.sameFilename(self.currentUser.file['name'], otherUser.file['name']): differentName = True @@ -1207,12 +1207,12 @@ class SyncplayUserlist(object): def removeUser(self, username): hideFromOSD = not constants.SHOW_DIFFERENT_ROOM_OSD - if self._users.has_key(username): + if username in self._users: user = self._users[username] if user.room: if self.isRoomSame(user.room): hideFromOSD = not constants.SHOW_SAME_ROOM_OSD - if self._users.has_key(username): + if username in self._users: self._users.pop(username) message = getMessage("left-notification").format(username) self.ui.showMessage(message, hideFromOSD) @@ -1227,7 +1227,7 @@ class SyncplayUserlist(object): self.__showUserChangeMessage(username, room, None, oldRoom) def modUser(self, username, room, file_): - if self._users.has_key(username): + if username in self._users: user = self._users[username] oldRoom = user.room if user.room else None if user.room != room: @@ -1245,7 +1245,7 @@ class SyncplayUserlist(object): def setUserAsController(self, username): if self.currentUser.username == username: self.currentUser.setControllerStatus(True) - elif self._users.has_key(username): + elif username in self._users: user = self._users[username] user.setControllerStatus(True) @@ -1254,7 +1254,7 @@ class SyncplayUserlist(object): return True if not self.currentUser.isReady(): return False - for user in self._users.itervalues(): + for user in self._users.values(): if user.room == self.currentUser.room: if user.isReadyWithFile() == False: return False @@ -1266,7 +1266,7 @@ class SyncplayUserlist(object): return True def areAllOtherUsersInRoomReady(self): - for user in self._users.itervalues(): + for user in self._users.values(): if user.room == self.currentUser.room and user.isReadyWithFile() == False: return False return True @@ -1275,14 +1275,14 @@ class SyncplayUserlist(object): readyCount = 0 if self.currentUser.isReady(): readyCount += 1 - for user in self._users.itervalues(): + for user in self._users.values(): if user.room == self.currentUser.room and user.isReadyWithFile(): readyCount += 1 return readyCount def usersInRoomCount(self): userCount = 1 - for user in self._users.itervalues(): + for user in self._users.values(): if user.room == self.currentUser.room and user.isReadyWithFile(): userCount += 1 return userCount @@ -1291,33 +1291,33 @@ class SyncplayUserlist(object): notReady = [] if not self.currentUser.isReady(): notReady.append(self.currentUser.username) - for user in self._users.itervalues(): + for user in self._users.values(): if user.room == self.currentUser.room and user.isReadyWithFile() == False: notReady.append(user.username) return ", ".join(notReady) def areAllFilesInRoomSame(self): if self.currentUser.file: - for user in self._users.itervalues(): + for user in self._users.values(): if user.room == self.currentUser.room and user.file and not self.currentUser.isFileSame(user.file): if user.canControl(): return False return True def areYouAloneInRoom(self): - for user in self._users.itervalues(): + for user in self._users.values(): if user.room == self.currentUser.room: return False return True def onlyUserInRoomWhoSupportsReadiness(self): - for user in self._users.itervalues(): + for user in self._users.values(): if user.room == self.currentUser.room and user.isReadyWithFile() is not None: return False return True def isUserInYourRoom(self, username): - for user in self._users.itervalues(): + for user in self._users.values(): if user.username == username and user.room == self.currentUser.room: return True return False @@ -1326,7 +1326,7 @@ class SyncplayUserlist(object): if self.currentUser.username == username and self.currentUser.canControl(): return True - for user in self._users.itervalues(): + for user in self._users.values(): if user.username == username and user.canControl(): return True return False @@ -1335,7 +1335,7 @@ class SyncplayUserlist(object): if self.currentUser.username == username: return self.currentUser.isReadyWithFile() - for user in self._users.itervalues(): + for user in self._users.values(): if user.username == username: return user.isReadyWithFile() return None @@ -1344,7 +1344,7 @@ class SyncplayUserlist(object): if self.currentUser.username == username: return self.currentUser.isReady() - for user in self._users.itervalues(): + for user in self._users.values(): if user.username == username: return user.isReady() return None @@ -1352,7 +1352,7 @@ class SyncplayUserlist(object): def setReady(self, username, isReady): if self.currentUser.username == username: self.currentUser.setReady(isReady) - elif self._users.has_key(username): + elif username in self._users: self._users[username].setReady(isReady) self._client.autoplayCheck() @@ -1369,7 +1369,7 @@ class SyncplayUserlist(object): def showUserList(self, altUI=None): rooms = {} - for user in self._users.itervalues(): + for user in self._users.values(): if user.room not in rooms: rooms[user.room] = [] rooms[user.room].append(user) @@ -1389,7 +1389,7 @@ class SyncplayUserlist(object): def sortList(self, rooms): for room in rooms: rooms[room] = sorted(rooms[room]) - rooms = collections.OrderedDict(sorted(rooms.items(), key=lambda s: s[0].lower())) + rooms = collections.OrderedDict(sorted(list(rooms.items()), key=lambda s: s[0].lower())) return rooms class UiManager(object): @@ -1416,10 +1416,10 @@ class UiManager(object): def showDebugMessage(self, message): if constants.DEBUG_MODE and message.rstrip(): - sys.stderr.write("{}{}\n".format(time.strftime(constants.UI_TIME_FORMAT, time.localtime()).decode('utf-8'),message.rstrip())) + sys.stderr.write("{}{}\n".format(time.strftime(constants.UI_TIME_FORMAT, time.localtime()),message.rstrip())) def showChatMessage(self, username, userMessage): - messageString = u"<{}> {}".format(username, userMessage) + messageString = "<{}> {}".format(username, userMessage) if self._client._player.chatOSDSupported and self._client._config["chatOutputEnabled"]: self._client._player.displayChatMessage(username,userMessage) else: @@ -1451,19 +1451,19 @@ class UiManager(object): else: self.lastAlertOSDEndTime = time.time() + constants.NO_ALERT_OSD_WARNING_DURATION if self.lastNotificationOSDEndTime and time.time() < self.lastNotificationOSDEndTime: - message = u"{}{}{}".format(message, self._client._player.osdMessageSeparator, self.lastNotificatinOSDMessage) + message = "{}{}{}".format(message, self._client._player.osdMessageSeparator, self.lastNotificatinOSDMessage) else: self.lastNotificatinOSDMessage = message self.lastNotificationOSDEndTime = time.time() + constants.OSD_DURATION if self.lastAlertOSDEndTime and time.time() < self.lastAlertOSDEndTime: - message = u"{}{}{}".format(self.lastAlertOSDMessage, self._client._player.osdMessageSeparator, message) + message = "{}{}{}".format(self.lastAlertOSDMessage, self._client._player.osdMessageSeparator, message) self._client._player.displayMessage(message, int(duration * 1000), OSDType, mood) def setControllerStatus(self, username, isController): self.__ui.setControllerStatus(username, isController) def showErrorMessage(self, message, criticalerror=False): - if message <> self.lastError: # Avoid double call bug + if message != self.lastError: # Avoid double call bug self.lastError = message self.__ui.showErrorMessage(message, criticalerror) @@ -1500,7 +1500,7 @@ class SyncplayPlaylist(): @wraps(f) def wrapper(self, *args, **kwds): if not self._client.sharedPlaylistIsEnabled(): - self._ui.showDebugMessage(u"Tried to use shared playlists when it was disabled!") + self._ui.showDebugMessage("Tried to use shared playlists when it was disabled!") return return f(self, *args, **kwds) return wrapper @@ -1511,7 +1511,7 @@ class SyncplayPlaylist(): def changeToPlaylistIndexFromFilename(self, filename): try: index = self._playlist.index(filename) - if index <> self._playlistIndex: + if index != self._playlistIndex: self.changeToPlaylistIndex(index) except ValueError: pass @@ -1573,7 +1573,7 @@ class SyncplayPlaylist(): try: if index is None: - self._ui.showDebugMessage(u"Cannot switch to None index in playlist") + self._ui.showDebugMessage("Cannot switch to None index in playlist") return filename = self._playlist[index] # TODO: Handle isse with index being None @@ -1624,7 +1624,7 @@ class SyncplayPlaylist(): def changePlaylist(self, files, username = None, resetIndex=False): if self._playlist == files: - if self._playlistIndex <> 0 and resetIndex: + if self._playlistIndex != 0 and resetIndex: self.changeToPlaylistIndex(0) return @@ -1674,7 +1674,7 @@ class SyncplayPlaylist(): self.switchToNewPlaylistIndex(0, resetPosition=True) def canUndoPlaylist(self, currentPlaylist): - return self._previousPlaylist is not None and currentPlaylist <> self._previousPlaylist + return self._previousPlaylist is not None and currentPlaylist != self._previousPlaylist def loadCurrentPlaylistIndex(self): if self._notPlayingCurrentIndex(): @@ -1719,13 +1719,13 @@ class SyncplayPlaylist(): def _notPlayingCurrentIndex(self): if self._playlistIndex is None or self._playlist is None or len(self._playlist) <= self._playlistIndex: - self._ui.showDebugMessage(u"Not playing current index - Index none or length issue") + self._ui.showDebugMessage("Not playing current index - Index none or length issue") return True currentPlaylistFilename = self._playlist[self._playlistIndex] if self._client.userlist.currentUser.file and currentPlaylistFilename == self._client.userlist.currentUser.file['name']: return False else: - self._ui.showDebugMessage(u"Not playing current index - Filename mismatch or no file") + self._ui.showDebugMessage("Not playing current index - Filename mismatch or no file") return True def _thereIsNextPlaylistIndex(self): @@ -1748,14 +1748,14 @@ class SyncplayPlaylist(): return len(self._playlist) <= self._playlistIndex+1 def _playlistBufferIsFromOldRoom(self, newRoom): - return self._previousPlaylistRoom <> newRoom + return self._previousPlaylistRoom != newRoom def _movePlaylistBufferToNewRoom(self, currentRoom): self._previousPlaylist = None self._previousPlaylistRoom = currentRoom def _playlistBufferNeedsUpdating(self, newPlaylist): - return self._previousPlaylist <> self._playlist and self._playlist <> newPlaylist + return self._previousPlaylist != self._playlist and self._playlist != newPlaylist class FileSwitchManager(object): def __init__(self, client): @@ -1821,15 +1821,15 @@ class FileSwitchManager(object): if dirsToSearch: # Spin up hard drives to prevent premature timeout - randomFilename = u"RandomFile"+unicode(random.randrange(10000, 99999))+u".txt" + randomFilename = "RandomFile"+str(random.randrange(10000, 99999))+".txt" for directory in dirsToSearch: if not os.path.isdir(directory): self.directorySearchError = getMessage("cannot-find-directory-error").format(directory) startTime = time.time() if os.path.isfile(os.path.join(directory, randomFilename)): - randomFilename = u"RandomFile"+unicode(random.randrange(10000, 99999))+u".txt" - print "Found random file (?)" + randomFilename = "RandomFile"+str(random.randrange(10000, 99999))+".txt" + print("Found random file (?)") if time.time() - startTime > constants.FOLDER_SEARCH_FIRST_FILE_TIMEOUT: self.folderSearchEnabled = False self.directorySearchError = getMessage("folder-search-first-file-timeout-error").format(directory) @@ -1846,7 +1846,7 @@ class FileSwitchManager(object): self.folderSearchEnabled = False return - if self.mediaFilesCache <> newMediaFilesCache: + if self.mediaFilesCache != newMediaFilesCache: self.mediaFilesCache = newMediaFilesCache self.newInfo = True finally: @@ -1873,12 +1873,12 @@ class FileSwitchManager(object): if highPriority and self.folderSearchEnabled and self.mediaDirectories is not None: directoryList = self.mediaDirectories # Spin up hard drives to prevent premature timeout - randomFilename = u"RandomFile"+unicode(random.randrange(10000, 99999))+u".txt" + randomFilename = "RandomFile"+str(random.randrange(10000, 99999))+".txt" for directory in directoryList: startTime = time.time() if os.path.isfile(os.path.join(directory, randomFilename)): - randomFilename = u"RandomFile"+unicode(random.randrange(10000, 99999))+u".txt" - print "Found random file (?)" + randomFilename = "RandomFile"+str(random.randrange(10000, 99999))+".txt" + print("Found random file (?)") if not self.folderSearchEnabled: return if time.time() - startTime > constants.FOLDER_SEARCH_FIRST_FILE_TIMEOUT: @@ -1929,10 +1929,6 @@ class FileSwitchManager(object): return False def notifyUserIfFileNotInMediaDirectory(self, filenameToFind, path): - try: - path = path.decode('utf-8') - except UnicodeEncodeError: - pass directoryToFind = os.path.dirname(path) if directoryToFind in self.mediaDirectoriesNotFound: return @@ -1947,6 +1943,6 @@ class FileSwitchManager(object): return if self.isDirectoryInList(directoryToFind, self.mediaDirectories): return - directoryToFind = unicode(directoryToFind) + directoryToFind = str(directoryToFind) self._client.ui.showErrorMessage(getMessage("added-file-not-in-media-directory-error").format(directoryToFind)) self.mediaDirectoriesNotFound.append(directoryToFind) diff --git a/syncplay/clientManager.py b/syncplay/clientManager.py old mode 100644 new mode 100755 diff --git a/syncplay/constants.py b/syncplay/constants.py old mode 100644 new mode 100755 index 122b491..00ec2cf --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -17,7 +17,7 @@ SHOW_CONTACT_INFO = True # Displays dev contact details below list in GUI SHOW_TOOLTIPS = True WARN_ABOUT_MISSING_STRINGS = False # (If debug mode is enabled) FALLBACK_INITIAL_LANGUAGE = "en" -FALLBACK_PUBLIC_SYNCPLAY_SERVERS = [[u'syncplay.pl:8995 (France)', u'syncplay.pl:8995'],[u'syncplay.pl:8996 (France)', u'syncplay.pl:8996'],[u'syncplay.pl:8997 (France)', u'syncplay.pl:8997'],[u'syncplay.pl:8998 (France)', u'syncplay.pl:8998'],[u'syncplay.pl:8999 (France)', u'syncplay.pl:8999']] +FALLBACK_PUBLIC_SYNCPLAY_SERVERS = [['syncplay.pl:8995 (France)', 'syncplay.pl:8995'],['syncplay.pl:8996 (France)', 'syncplay.pl:8996'],['syncplay.pl:8997 (France)', 'syncplay.pl:8997'],['syncplay.pl:8998 (France)', 'syncplay.pl:8998'],['syncplay.pl:8999 (France)', 'syncplay.pl:8999']] PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH = 10 # Seconds PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD = 5 # Seconds (only triggered if file is paused, e.g. due to EOF) @@ -82,9 +82,9 @@ FOLDER_SEARCH_DOUBLE_CHECK_INTERVAL = 30.0 # Secs - Frequency of updating cache #Usually there's no need to adjust these LAST_PAUSED_DIFF_THRESHOLD = 2 -FILENAME_STRIP_REGEX = u"[-~_\.\[\](): ]" -CONTROL_PASSWORD_STRIP_REGEX = u"[^a-zA-Z0-9\-]" -ROOM_NAME_STRIP_REGEX = u"^(\+)(?P.*)(:)(\w{12})$" +FILENAME_STRIP_REGEX = "[-~_\.\[\](): ]" +CONTROL_PASSWORD_STRIP_REGEX = "[^a-zA-Z0-9\-]" +ROOM_NAME_STRIP_REGEX = "^(\+)(?P.*)(:)(\w{12})$" COMMANDS_UNDO = ["u", "undo", "revert"] COMMANDS_CHAT = ["ch","chat"] COMMANDS_LIST = ["l", "list", "users"] @@ -173,9 +173,9 @@ STYLE_SUCCESSLABEL = "QLabel { color : black; border-style: outset; border-width STYLE_READY_PUSHBUTTON = "QPushButton { text-align: left; padding: 10px 5px 10px 5px;}" 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_USER_MESSAGE = u"<{}> {}" -STYLE_USERNAME = u"color: #367AA9; font-weight:bold;" +STYLE_CONTACT_INFO = "{}

" # Contact info message +STYLE_USER_MESSAGE = "<{}> {}" +STYLE_USERNAME = "color: #367AA9; font-weight:bold;" STYLE_ERRORNOTIFICATION = "color: red;" STYLE_DIFFERENTITEM_COLOR = 'red' STYLE_NOFILEITEM_COLOR = 'blue' @@ -192,16 +192,16 @@ MPV_SLAVE_ARGS = ['--msg-level=all=error,cplayer=info,term-msg=info', '--input-t MPV_SLAVE_ARGS_NEW = ['--term-playing-msg=\nANS_filename=${filename}\nANS_length=${=duration:${=length:0}}\nANS_path=${path}\n', '--terminal=yes'] MPV_NEW_VERSION = False MPV_OSC_VISIBILITY_CHANGE_VERSION = False -MPV_INPUT_PROMPT_START_CHARACTER = u"〉" -MPV_INPUT_PROMPT_END_CHARACTER = u" 〈" -MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER = u"\" +MPV_INPUT_PROMPT_START_CHARACTER = "〉" +MPV_INPUT_PROMPT_END_CHARACTER = " 〈" +MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER = "\" MPV_SYNCPLAYINTF_OPTIONS_TO_SEND = ["chatInputEnabled","chatInputFontFamily", "chatInputRelativeFontSize", "chatInputFontWeight","chatInputFontUnderline", "chatInputFontColor", "chatInputPosition","chatOutputFontFamily","chatOutputRelativeFontSize", "chatOutputFontWeight","chatOutputFontUnderline","chatOutputMode","chatMaxLines", "chatTopMargin","chatLeftMargin","chatBottomMargin","chatDirectInput", "notificationTimeout","alertTimeout","chatTimeout","chatOutputEnabled"] -MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND = ["MaxChatMessageLength={}".format(MAX_CHAT_MESSAGE_LENGTH),u"inputPromptStartCharacter={}".format(MPV_INPUT_PROMPT_START_CHARACTER),u"inputPromptEndCharacter={}".format(MPV_INPUT_PROMPT_END_CHARACTER),u"backslashSubstituteCharacter={}".format(MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER)] +MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND = ["MaxChatMessageLength={}".format(MAX_CHAT_MESSAGE_LENGTH),"inputPromptStartCharacter={}".format(MPV_INPUT_PROMPT_START_CHARACTER),"inputPromptEndCharacter={}".format(MPV_INPUT_PROMPT_END_CHARACTER),"backslashSubstituteCharacter={}".format(MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER)] # Note: Constants updated in client.py->checkForFeatureSupport MPV_SYNCPLAYINTF_LANGUAGE_TO_SEND = ["mpv-key-tab-hint","mpv-key-hint", "alphakey-mode-warning-first-line", "alphakey-mode-warning-second-line"] VLC_SLAVE_ARGS = ['--extraintf=luaintf', '--lua-intf=syncplay', '--no-quiet', '--no-input-fast-seek', @@ -256,12 +256,12 @@ OSD_CHAT = "chat" CHATROOM_MODE = "Chatroom" SCROLLING_MODE = "Scrolling" -SYNCPLAY_UPDATE_URL = u"https://syncplay.pl/checkforupdate?{}" # Params +SYNCPLAY_UPDATE_URL = "https://syncplay.pl/checkforupdate?{}" # Params SYNCPLAY_DOWNLOAD_URL = "https://syncplay.pl/download/" -SYNCPLAY_PUBLIC_SERVER_LIST_URL = u"https://syncplay.pl/listpublicservers?{}" # Params +SYNCPLAY_PUBLIC_SERVER_LIST_URL = "https://syncplay.pl/listpublicservers?{}" # Params -DEFAULT_TRUSTED_DOMAINS = [u"youtube.com",u"youtu.be"] -TRUSTABLE_WEB_PROTOCOLS = [u"http://www.",u"https://www.",u"http://",u"https://"] +DEFAULT_TRUSTED_DOMAINS = ["youtube.com","youtu.be"] +TRUSTABLE_WEB_PROTOCOLS = ["http://www.","https://www.","http://","https://"] PRIVATE_FILE_FIELDS = ["path"] diff --git a/syncplay/messages.py b/syncplay/messages.py index 63c72a0..2199909 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -1,10 +1,10 @@ # coding:utf8 from syncplay import constants -import messages_en -import messages_ru -import messages_de -import messages_it +from . import messages_en +from . import messages_ru +from . import messages_de +from . import messages_it messages = { "en": messages_en.en, @@ -29,10 +29,10 @@ def getMissingStrings(): for lang in messages: if lang != "en" and lang != "CURRENT": for message in messages["en"]: - if not messages[lang].has_key(message): + if message not in messages[lang]: missingStrings = missingStrings + "({}) Missing: {}\n".format(lang, message) for message in messages[lang]: - if not messages["en"].has_key(message): + if message not in messages["en"]: missingStrings = missingStrings + "({}) Unused: {}\n".format(lang, message) return missingStrings @@ -41,14 +41,14 @@ def getInitialLanguage(): import locale try: initialLanguage = locale.getdefaultlocale()[0].split("_")[0] - if not messages.has_key(initialLanguage): + if initialLanguage not in messages: initialLanguage = constants.FALLBACK_INITIAL_LANGUAGE except: initialLanguage = constants.FALLBACK_INITIAL_LANGUAGE return initialLanguage def isValidLanguage(language): - return messages.has_key(language) + return language in messages def getMessage(type_, locale=None): if constants.SHOW_TOOLTIPS == False: @@ -59,15 +59,15 @@ def getMessage(type_, locale=None): setLanguage(getInitialLanguage()) lang = messages["CURRENT"] - if locale and messages.has_key(locale): - if messages[locale].has_key(type_): - return unicode(messages[locale][type_]) - if lang and messages.has_key(lang): - if messages[lang].has_key(type_): - return unicode(messages[lang][type_]) - if messages["en"].has_key(type_): - return unicode(messages["en"][type_]) + if locale and locale in messages: + if type_ in messages[locale]: + return str(messages[locale][type_]) + if lang and lang in messages: + if type_ in messages[lang]: + return str(messages[lang][type_]) + if type_ in messages["en"]: + return str(messages["en"][type_]) else: - print u"WARNING: Cannot find message '{}'!".format(type_) + print("WARNING: Cannot find message '{}'!".format(type_)) return "!{}".format(type_) # TODO: Remove #raise KeyError(type_) diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py old mode 100644 new mode 100755 index eac5b70..08bf649 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -3,472 +3,472 @@ """Deutsch dictionary""" de = { - "LANGUAGE" : u"Deutsch", # (German) + "LANGUAGE" : "Deutsch", # (German) # Client notifications - "config-cleared-notification" : u"Einstellungen gelöscht. Änderungen werden gespeichert, wenn du eine gültige Konfiguration speicherst.", + "config-cleared-notification" : "Einstellungen gelöscht. Änderungen werden gespeichert, wenn du eine gültige Konfiguration speicherst.", - "relative-config-notification" : u"Relative Konfigurationsdatei(en) geladen: {}", + "relative-config-notification" : "Relative Konfigurationsdatei(en) geladen: {}", - "connection-attempt-notification" : u"Verbinde mit {}:{}", # Port, IP - "reconnection-attempt-notification" : u"Verbindung zum Server verloren, versuche erneut", - "disconnection-notification" : u"Verbindung zum Server beendet", - "connection-failed-notification" : u"Verbindung zum Server fehlgeschlagen", - "connected-successful-notification" : u"Erfolgreich mit Server verbunden", - "retrying-notification" : u"%s, versuche erneut in %d Sekunden...", # Seconds + "connection-attempt-notification" : "Verbinde mit {}:{}", # Port, IP + "reconnection-attempt-notification" : "Verbindung zum Server verloren, versuche erneut", + "disconnection-notification" : "Verbindung zum Server beendet", + "connection-failed-notification" : "Verbindung zum Server fehlgeschlagen", + "connected-successful-notification" : "Erfolgreich mit Server verbunden", + "retrying-notification" : "%s, versuche erneut in %d Sekunden...", # Seconds - "rewind-notification" : u"Zurückgespult wegen Zeitdifferenz mit {}", # User - "fastforward-notification" : u"Vorgespult wegen Zeitdifferenz mit {}", # User - "slowdown-notification" : u"Verlangsamt wegen Zeitdifferenz mit {}", # User - "revert-notification" : u"Normalgeschwindigkeit", + "rewind-notification" : "Zurückgespult wegen Zeitdifferenz mit {}", # User + "fastforward-notification" : "Vorgespult wegen Zeitdifferenz mit {}", # User + "slowdown-notification" : "Verlangsamt wegen Zeitdifferenz mit {}", # User + "revert-notification" : "Normalgeschwindigkeit", - "pause-notification" : u"{} pausierte", # User - "unpause-notification" : u"{} startete", # User - "seek-notification" : u"{} sprang von {} nach {}", # User, from time, to time + "pause-notification" : "{} pausierte", # User + "unpause-notification" : "{} startete", # User + "seek-notification" : "{} sprang von {} nach {}", # User, from time, to time - "current-offset-notification" : u"Aktueller Offset: {} Sekunden", # Offset + "current-offset-notification" : "Aktueller Offset: {} Sekunden", # Offset - "media-directory-list-updated-notification" : u"Syncplay media directories have been updated.", # TODO: Translate + "media-directory-list-updated-notification" : "Syncplay media directories have been updated.", # TODO: Translate - "room-join-notification" : u"{} hat den Raum '{}' betreten", # User - "left-notification" : u"{} ist gegangen", # User - "left-paused-notification" : u"{} ist gegangen, {} pausierte", # User who left, User who paused - "playing-notification" : u"{} spielt '{}' ({})", # User, file, duration - "playing-notification/room-addendum" : u" in Raum: '{}'", # Room + "room-join-notification" : "{} hat den Raum '{}' betreten", # User + "left-notification" : "{} ist gegangen", # User + "left-paused-notification" : "{} ist gegangen, {} pausierte", # User who left, User who paused + "playing-notification" : "{} spielt '{}' ({})", # User, file, duration + "playing-notification/room-addendum" : " in Raum: '{}'", # Room - "not-all-ready" : u"Noch nicht bereit: {}", # Usernames - "all-users-ready" : u"Alle sind bereit ({} Nutzer)", #Number of ready users - "ready-to-unpause-notification" : u"Du bist bereit - noch einmal fortsetzen klicken zum abspielen", - "set-as-ready-notification" : u"Du bist bereit", - "set-as-not-ready-notification" : u"Du bist nicht bereit", - "autoplaying-notification" : u"Starte in {}...", # Number of seconds until playback will start + "not-all-ready" : "Noch nicht bereit: {}", # Usernames + "all-users-ready" : "Alle sind bereit ({} Nutzer)", #Number of ready users + "ready-to-unpause-notification" : "Du bist bereit - noch einmal fortsetzen klicken zum abspielen", + "set-as-ready-notification" : "Du bist bereit", + "set-as-not-ready-notification" : "Du bist nicht bereit", + "autoplaying-notification" : "Starte in {}...", # Number of seconds until playback will start - "identifying-as-controller-notification" : u"Identifiziere als Raumleiter mit Passwort '{}'...", # TODO: find a better translation to "room operator" - "failed-to-identify-as-controller-notification" : u"{} konnte sich nicht als Raumleiter identifizieren.", - "authenticated-as-controller-notification" : u"{} authentifizierte sich als Raumleiter", - "created-controlled-room-notification" : u"Gesteuerten Raum '{}' mit Passwort '{}' erstellt. Bitte diese Informationen für die Zukunft aufheben!", # RoomName, operatorPassword + "identifying-as-controller-notification" : "Identifiziere als Raumleiter mit Passwort '{}'...", # TODO: find a better translation to "room operator" + "failed-to-identify-as-controller-notification" : "{} konnte sich nicht als Raumleiter identifizieren.", + "authenticated-as-controller-notification" : "{} authentifizierte sich als Raumleiter", + "created-controlled-room-notification" : "Gesteuerten Raum '{}' mit Passwort '{}' erstellt. Bitte diese Informationen für die Zukunft aufheben!", # RoomName, operatorPassword - "file-different-notification" : u"Deine Datei scheint sich von {}s zu unterscheiden", # User - "file-differences-notification" : u"Deine Datei unterscheidet sich auf folgende Art: {}", - "room-file-differences" : u"Unterschiedlich in: {}", # File differences (filename, size, and/or duration) - "file-difference-filename" : u"Name", - "file-difference-filesize" : u"Größe", - "file-difference-duration" : u"Dauer", - "alone-in-the-room": u"Du bist alleine im Raum", + "file-different-notification" : "Deine Datei scheint sich von {}s zu unterscheiden", # User + "file-differences-notification" : "Deine Datei unterscheidet sich auf folgende Art: {}", + "room-file-differences" : "Unterschiedlich in: {}", # File differences (filename, size, and/or duration) + "file-difference-filename" : "Name", + "file-difference-filesize" : "Größe", + "file-difference-duration" : "Dauer", + "alone-in-the-room": "Du bist alleine im Raum", - "different-filesize-notification" : u" (ihre Dateigröße ist anders als deine!)", - "userlist-playing-notification" : u"{} spielt:", #Username - "file-played-by-notification" : u"Datei: {} wird gespielt von:", # File - "no-file-played-notification" : u"{} spielt keine Datei ab", # Username - "notplaying-notification" : u"Personen im Raum, die keine Dateien spielen:", - "userlist-room-notification" : u"In Raum '{}':", # Room - "userlist-file-notification" : u"Datei", - "controller-userlist-userflag" : u"Raumleiter", - "ready-userlist-userflag" : u"Bereit", + "different-filesize-notification" : " (ihre Dateigröße ist anders als deine!)", + "userlist-playing-notification" : "{} spielt:", #Username + "file-played-by-notification" : "Datei: {} wird gespielt von:", # File + "no-file-played-notification" : "{} spielt keine Datei ab", # Username + "notplaying-notification" : "Personen im Raum, die keine Dateien spielen:", + "userlist-room-notification" : "In Raum '{}':", # Room + "userlist-file-notification" : "Datei", + "controller-userlist-userflag" : "Raumleiter", + "ready-userlist-userflag" : "Bereit", - "update-check-failed-notification" : u"Konnte nicht automatisch prüfen, ob Syncplay {} aktuell ist. Soll https://syncplay.pl/ geöffnet werden, um manuell nach Updates zu suchen?", #Syncplay version - "syncplay-uptodate-notification" : u"Syncplay ist aktuell", - "syncplay-updateavailable-notification" : u"Eine neuere Version von Syncplay ist verfügbar. Soll die Download-Seite geöffnet werden?", + "update-check-failed-notification" : "Konnte nicht automatisch prüfen, ob Syncplay {} aktuell ist. Soll https://syncplay.pl/ geöffnet werden, um manuell nach Updates zu suchen?", #Syncplay version + "syncplay-uptodate-notification" : "Syncplay ist aktuell", + "syncplay-updateavailable-notification" : "Eine neuere Version von Syncplay ist verfügbar. Soll die Download-Seite geöffnet werden?", - "mplayer-file-required-notification" : u"Syncplay für mplayer benötigt eine Dateiangabe beim Start", - "mplayer-file-required-notification/example" : u"Anwendungsbeispiel: syncplay [optionen] [url|pfad/]Dateiname", - "mplayer2-required" : u"Syncplay ist inkompatibel zu MPlayer 1.x, bitte nutze MPlayer2 oder mpv", + "mplayer-file-required-notification" : "Syncplay für mplayer benötigt eine Dateiangabe beim Start", + "mplayer-file-required-notification/example" : "Anwendungsbeispiel: syncplay [optionen] [url|pfad/]Dateiname", + "mplayer2-required" : "Syncplay ist inkompatibel zu MPlayer 1.x, bitte nutze MPlayer2 oder mpv", - "unrecognized-command-notification" : u"Unbekannter Befehl", - "commandlist-notification" : u"Verfügbare Befehle:", - "commandlist-notification/room" : u"\tr [Name] - Raum ändern", - "commandlist-notification/list" : u"\tl - Nutzerliste anzeigen", - "commandlist-notification/undo" : u"\tu - Letzter Zeitsprung rückgängig", - "commandlist-notification/pause" : u"\tp - Pausieren / weiter", - "commandlist-notification/seek" : u"\t[s][+-]Zeit - zu einer bestimmten Zeit spulen, ohne + oder - wird als absolute Zeit gewertet; Angabe in Sekunden oder Minuten:Sekunden", - "commandlist-notification/help" : u"\th - Diese Hilfe", - "commandlist-notification/toggle" : u"\tt - Bereitschaftsanzeige umschalten", - "commandlist-notification/create" : u"\tc [name] - erstelle zentral gesteuerten Raum mit dem aktuellen Raumnamen", - "commandlist-notification/auth" : u"\ta [password] - authentifiziere als Raumleiter mit Passwort", + "unrecognized-command-notification" : "Unbekannter Befehl", + "commandlist-notification" : "Verfügbare Befehle:", + "commandlist-notification/room" : "\tr [Name] - Raum ändern", + "commandlist-notification/list" : "\tl - Nutzerliste anzeigen", + "commandlist-notification/undo" : "\tu - Letzter Zeitsprung rückgängig", + "commandlist-notification/pause" : "\tp - Pausieren / weiter", + "commandlist-notification/seek" : "\t[s][+-]Zeit - zu einer bestimmten Zeit spulen, ohne + oder - wird als absolute Zeit gewertet; Angabe in Sekunden oder Minuten:Sekunden", + "commandlist-notification/help" : "\th - Diese Hilfe", + "commandlist-notification/toggle" : "\tt - Bereitschaftsanzeige umschalten", + "commandlist-notification/create" : "\tc [name] - erstelle zentral gesteuerten Raum mit dem aktuellen Raumnamen", + "commandlist-notification/auth" : "\ta [password] - authentifiziere als Raumleiter mit Passwort", "commandlist-notification/chat" : "\tch [message] - send a chat message in a room", # TODO: Translate - "syncplay-version-notification" : u"Syncplay Version: {}", # syncplay.version - "more-info-notification" : u"Weitere Informationen auf: {}", # projectURL + "syncplay-version-notification" : "Syncplay Version: {}", # syncplay.version + "more-info-notification" : "Weitere Informationen auf: {}", # projectURL - "gui-data-cleared-notification" : u"Syncplay hat die Pfad und Fensterdaten der Syncplay-GUI zurückgesetzt.", - "language-changed-msgbox-label" : u"Die Sprache wird geändert, wenn du Syncplay neu startest.", - "promptforupdate-label" : u"Soll Syncplay regelmäßig nach Updates suchen?", + "gui-data-cleared-notification" : "Syncplay hat die Pfad und Fensterdaten der Syncplay-GUI zurückgesetzt.", + "language-changed-msgbox-label" : "Die Sprache wird geändert, wenn du Syncplay neu startest.", + "promptforupdate-label" : "Soll Syncplay regelmäßig nach Updates suchen?", - "vlc-version-mismatch": u"This version of VLC does not support Syncplay. VLC {}+ supports Syncplay but VLC 3 does not. Please use an alternative media player.", # VLC min version # TODO: Translate - "vlc-interface-version-mismatch": u"Du nutzt Version {} des VLC-Syncplay Interface-Moduls, Syncplay benötigt aber mindestens Version {}. In der Syncplay-Anleitung unter https://syncplay.pl/guide/ [Englisch] findest du Details zur Installation des syncplay.lua-Skripts.", # VLC interface version, VLC interface min version - "vlc-interface-oldversion-warning": u"Warnung: Es ist eine alte Version des Syncplay Interface-Moduls für VLC im VLC-Verzeichnis installiert. In der Syncplay-Anleitung unter https://syncplay.pl/guide/ [Englisch] findest du Details zur Installation des syncplay.lua-Skripts.", - "vlc-interface-not-installed": u"Warnung: Es wurde kein Syncplay Interface-Modul für VLC im VLC-Verzeichnis gefunden. Daher wird, wenn du VLC 2.0 nutzt, die syncplay.lua die mit Syncplay mitgeliefert wurde, verwendet. Dies bedeutet allerdings, dass keine anderen Interface-Skripts und Erweiterungen geladen werden. In der Syncplay-Anleitung unter https://syncplay.pl/guide/ [Englisch] findest du Details zur Installation des syncplay.lua-Skripts.", - "media-player-latency-warning": u"Warnung: Der Mediaplayer brauchte {} Sekunden zum Antworten. Wenn Probleme bei der Synchronisation auftreten, schließe bitte andere Anwendungen, um Ressourcen freizugeben. Sollte das nicht funktionieren, versuche es mit einem anderen Media-Player.", # Seconds to respond - "mpv-unresponsive-error": u"MPV hat für {} Sekunden nicht geantwortet und scheint abgestürzt zu sein. Bitte starte Syncplay neu.", # Seconds to respond + "vlc-version-mismatch": "This version of VLC does not support Syncplay. VLC {}+ supports Syncplay but VLC 3 does not. Please use an alternative media player.", # VLC min version # TODO: Translate + "vlc-interface-version-mismatch": "Du nutzt Version {} des VLC-Syncplay Interface-Moduls, Syncplay benötigt aber mindestens Version {}. In der Syncplay-Anleitung unter https://syncplay.pl/guide/ [Englisch] findest du Details zur Installation des syncplay.lua-Skripts.", # VLC interface version, VLC interface min version + "vlc-interface-oldversion-warning": "Warnung: Es ist eine alte Version des Syncplay Interface-Moduls für VLC im VLC-Verzeichnis installiert. In der Syncplay-Anleitung unter https://syncplay.pl/guide/ [Englisch] findest du Details zur Installation des syncplay.lua-Skripts.", + "vlc-interface-not-installed": "Warnung: Es wurde kein Syncplay Interface-Modul für VLC im VLC-Verzeichnis gefunden. Daher wird, wenn du VLC 2.0 nutzt, die syncplay.lua die mit Syncplay mitgeliefert wurde, verwendet. Dies bedeutet allerdings, dass keine anderen Interface-Skripts und Erweiterungen geladen werden. In der Syncplay-Anleitung unter https://syncplay.pl/guide/ [Englisch] findest du Details zur Installation des syncplay.lua-Skripts.", + "media-player-latency-warning": "Warnung: Der Mediaplayer brauchte {} Sekunden zum Antworten. Wenn Probleme bei der Synchronisation auftreten, schließe bitte andere Anwendungen, um Ressourcen freizugeben. Sollte das nicht funktionieren, versuche es mit einem anderen Media-Player.", # Seconds to respond + "mpv-unresponsive-error": "MPV hat für {} Sekunden nicht geantwortet und scheint abgestürzt zu sein. Bitte starte Syncplay neu.", # Seconds to respond # Client prompts - "enter-to-exit-prompt" : u"Enter drücken zum Beenden\n", + "enter-to-exit-prompt" : "Enter drücken zum Beenden\n", # Client errors - "missing-arguments-error" : u"Notwendige Argumente fehlen, siehe --help", - "server-timeout-error" : u"Timeout: Verbindung zum Server fehlgeschlagen", - "mpc-slave-error" : u"Kann MPC nicht im Slave-Modus starten!", - "mpc-version-insufficient-error" : u"MPC-Version nicht ausreichend, bitte nutze `mpc-hc` >= `{}`", - "mpc-be-version-insufficient-error" : u"MPC-Version nicht ausreichend, bitte nutze `mpc-be` >= `{}`", - "mpv-version-error" : u"Syncplay ist nicht kompatibel mit dieser Version von mpv. Bitte benutze eine andere Version (z.B. Git HEAD).", - "player-file-open-error" : u"Fehler beim Öffnen der Datei durch den Player", - "player-path-error" : u"Ungültiger Player-Pfad. Supported players are: mpv, VLC, MPC-HC, MPC-BE and mplayer2", # To do: Translate end - "hostname-empty-error" : u"Hostname darf nicht leer sein", - "empty-error" : u"{} darf nicht leer sein", # Configuration - "media-player-error": u"Player-Fehler: \"{}\"", # Error line - "unable-import-gui-error": u"Konnte die GUI-Bibliotheken nicht importieren. PySide muss installiert sein, damit die grafische Oberfläche funktioniert.", + "missing-arguments-error" : "Notwendige Argumente fehlen, siehe --help", + "server-timeout-error" : "Timeout: Verbindung zum Server fehlgeschlagen", + "mpc-slave-error" : "Kann MPC nicht im Slave-Modus starten!", + "mpc-version-insufficient-error" : "MPC-Version nicht ausreichend, bitte nutze `mpc-hc` >= `{}`", + "mpc-be-version-insufficient-error" : "MPC-Version nicht ausreichend, bitte nutze `mpc-be` >= `{}`", + "mpv-version-error" : "Syncplay ist nicht kompatibel mit dieser Version von mpv. Bitte benutze eine andere Version (z.B. Git HEAD).", + "player-file-open-error" : "Fehler beim Öffnen der Datei durch den Player", + "player-path-error" : "Ungültiger Player-Pfad. Supported players are: mpv, VLC, MPC-HC, MPC-BE and mplayer2", # To do: Translate end + "hostname-empty-error" : "Hostname darf nicht leer sein", + "empty-error" : "{} darf nicht leer sein", # Configuration + "media-player-error": "Player-Fehler: \"{}\"", # Error line + "unable-import-gui-error": "Konnte die GUI-Bibliotheken nicht importieren. PySide muss installiert sein, damit die grafische Oberfläche funktioniert.", - "arguments-missing-error" : u"Notwendige Argumente fehlen, siehe --help", + "arguments-missing-error" : "Notwendige Argumente fehlen, siehe --help", - "unable-to-start-client-error" : u"Client kann nicht gestartet werden", + "unable-to-start-client-error" : "Client kann nicht gestartet werden", - "player-path-config-error": u"Player-Pfad ist nicht ordnungsgemäß gesetzt. Supported players are: mpv, VLC, MPC-HC, MPC-BE and mplayer2.", # To do: Translate end - "no-file-path-config-error": u"Es muss eine Datei ausgewählt werden, bevor der Player gestartet wird.", - "no-hostname-config-error": u"Hostname darf nicht leer sein", - "invalid-port-config-error" : u"Port muss gültig sein", - "empty-value-config-error" : u"{} darf nicht leer sein", # Config option + "player-path-config-error": "Player-Pfad ist nicht ordnungsgemäß gesetzt. Supported players are: mpv, VLC, MPC-HC, MPC-BE and mplayer2.", # To do: Translate end + "no-file-path-config-error": "Es muss eine Datei ausgewählt werden, bevor der Player gestartet wird.", + "no-hostname-config-error": "Hostname darf nicht leer sein", + "invalid-port-config-error" : "Port muss gültig sein", + "empty-value-config-error" : "{} darf nicht leer sein", # Config option - "not-json-error" : u"Kein JSON-String\n", - "hello-arguments-error" : u"Zu wenige Hello-Argumente\n", - "version-mismatch-error" : u"Verschiedene Versionen auf Client und Server\n", - "vlc-failed-connection": u"Kann nicht zu VLC verbinden. Wenn du syncplay.lua nicht installiert hast, findest du auf https://syncplay.pl/LUA/ [Englisch] eine Anleitung.", - "vlc-failed-noscript": u"Laut VLC ist das syncplay.lua Interface-Skript nicht installiert. Auf https://syncplay.pl/LUA/ [Englisch] findest du eine Anleitung.", - "vlc-failed-versioncheck": u"Diese VLC-Version wird von Syncplay nicht unterstützt. Bitte nutze VLC 2.0", - "vlc-failed-other" : u"Beim Laden des syncplay.lua Interface-Skripts durch VLC trat folgender Fehler auf: {}", # Syncplay Error + "not-json-error" : "Kein JSON-String\n", + "hello-arguments-error" : "Zu wenige Hello-Argumente\n", + "version-mismatch-error" : "Verschiedene Versionen auf Client und Server\n", + "vlc-failed-connection": "Kann nicht zu VLC verbinden. Wenn du syncplay.lua nicht installiert hast, findest du auf https://syncplay.pl/LUA/ [Englisch] eine Anleitung.", + "vlc-failed-noscript": "Laut VLC ist das syncplay.lua Interface-Skript nicht installiert. Auf https://syncplay.pl/LUA/ [Englisch] findest du eine Anleitung.", + "vlc-failed-versioncheck": "Diese VLC-Version wird von Syncplay nicht unterstützt. Bitte nutze VLC 2.0", + "vlc-failed-other" : "Beim Laden des syncplay.lua Interface-Skripts durch VLC trat folgender Fehler auf: {}", # Syncplay Error - "not-supported-by-server-error" : u"Dieses Feature wird vom Server nicht unterstützt. Es wird ein Server mit Syncplay Version {}+ benötigt, aktuell verwendet wird jedoch Version {}.", #minVersion, serverVersion + "not-supported-by-server-error" : "Dieses Feature wird vom Server nicht unterstützt. Es wird ein Server mit Syncplay Version {}+ benötigt, aktuell verwendet wird jedoch Version {}.", #minVersion, serverVersion "shared-playlists-not-supported-by-server-error" : "The shared playlists feature may not be supported by the server. To ensure that it works correctly requires a server running Syncplay {}+, but the server is running Syncplay {}.", #minVersion, serverVersion # TODO: Translate "shared-playlists-disabled-by-server-error" : "The shared playlist feature has been disabled in the server configuration. To use this feature you will need to connect to a different server.", # TODO: Translate - "invalid-seek-value" : u"Ungültige Zeitangabe", - "invalid-offset-value" : u"Ungültiger Offset-Wert", + "invalid-seek-value" : "Ungültige Zeitangabe", + "invalid-offset-value" : "Ungültiger Offset-Wert", - "switch-file-not-found-error" : u"Konnte nicht zur Datei '{0}' wechseln. Syncplay looks in the specified media directories.", # File not found, folder it was not found in # TODO: Re-translate "Syncplay sucht im Ordner der aktuellen Datei und angegebenen Medien-Verzeichnissen." to reference to checking in "current media directory" - "folder-search-timeout-error" : u"The search for media in media directories was aborted as it took too long to search through '{}'. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. For automatic file switching to work again please select File->Set Media Directories in the menu bar and remove this directory or replace it with an appropriate sub-folder. If the folder is actually fine then you can re-enable it by selecting File->Set Media Directories and pressing 'OK'.", #Folder # TODO: Translate - "folder-search-first-file-timeout-error" : u"The search for media in '{}' was aborted as it took too long to access the directory. This could happen if it is a network drive or if you configure your drive to spin down after a period of inactivity. For automatic file switching to work again please go to File->Set Media Directories and either remove the directory or resolve the issue (e.g. by changing power saving settings).", #Folder # TODO: Translate - "added-file-not-in-media-directory-error" : u"You loaded a file in '{}' which is not a known media directory. You can add this as a media directory by selecting File->Set Media Directories in the menu bar.", #Folder # TODO: Translate - "no-media-directories-error" : u"No media directories have been set. For shared playlist and file switching features to work properly please select File->Set Media Directories and specify where Syncplay should look to find media files.", # TODO: Translate - "cannot-find-directory-error" : u"Could not find media directory '{}'. To update your list of media directories please select File->Set Media Directories from the menu bar and specify where Syncplay should look to find media files.", # TODO: Translate + "switch-file-not-found-error" : "Konnte nicht zur Datei '{0}' wechseln. Syncplay looks in the specified media directories.", # File not found, folder it was not found in # TODO: Re-translate "Syncplay sucht im Ordner der aktuellen Datei und angegebenen Medien-Verzeichnissen." to reference to checking in "current media directory" + "folder-search-timeout-error" : "The search for media in media directories was aborted as it took too long to search through '{}'. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. For automatic file switching to work again please select File->Set Media Directories in the menu bar and remove this directory or replace it with an appropriate sub-folder. If the folder is actually fine then you can re-enable it by selecting File->Set Media Directories and pressing 'OK'.", #Folder # TODO: Translate + "folder-search-first-file-timeout-error" : "The search for media in '{}' was aborted as it took too long to access the directory. This could happen if it is a network drive or if you configure your drive to spin down after a period of inactivity. For automatic file switching to work again please go to File->Set Media Directories and either remove the directory or resolve the issue (e.g. by changing power saving settings).", #Folder # TODO: Translate + "added-file-not-in-media-directory-error" : "You loaded a file in '{}' which is not a known media directory. You can add this as a media directory by selecting File->Set Media Directories in the menu bar.", #Folder # TODO: Translate + "no-media-directories-error" : "No media directories have been set. For shared playlist and file switching features to work properly please select File->Set Media Directories and specify where Syncplay should look to find media files.", # TODO: Translate + "cannot-find-directory-error" : "Could not find media directory '{}'. To update your list of media directories please select File->Set Media Directories from the menu bar and specify where Syncplay should look to find media files.", # TODO: Translate - "failed-to-load-server-list-error" : u"Konnte die Liste der öffentlichen Server nicht laden. Bitte besuche http://www.syncplay.pl/ [Englisch] mit deinem Browser.", + "failed-to-load-server-list-error" : "Konnte die Liste der öffentlichen Server nicht laden. Bitte besuche http://www.syncplay.pl/ [Englisch] mit deinem Browser.", # Client arguments - "argument-description" : u'Syncplay ist eine Anwendung um mehrere MPlayer, MPC-HC, MPC-BE und VLC-Instanzen über das Internet zu synchronisieren.', - "argument-epilog" : u'Wenn keine Optionen angegeben sind, werden die _config-Werte verwendet', - "nogui-argument" : u'Keine GUI anzeigen', - "host-argument" : u'Server-Adresse', - "name-argument" : u'Gewünschter Nutzername', - "debug-argument" : u'Debug-Modus', - "force-gui-prompt-argument" : u'Einstellungsfenster anzeigen', - "no-store-argument" : u'keine Werte in .syncplay speichern', - "room-argument" : u'Standard-Raum', - "password-argument" : u'Server-Passwort', - "player-path-argument" : u'Pfad zum Player', - "file-argument" : u'Abzuspielende Datei', - "args-argument" : u'Player-Einstellungen; Wenn du Einstellungen, die mit - beginnen, nutzen willst, stelle ein einzelnes \'--\'-Argument davor', - "clear-gui-data-argument" : u'Setzt die Pfad- und GUI-Fenster-Daten die in den QSettings gespeichert sind zurück', - "language-argument" : u'Sprache für Syncplay-Nachrichten (de/en/ru)', + "argument-description" : 'Syncplay ist eine Anwendung um mehrere MPlayer, MPC-HC, MPC-BE und VLC-Instanzen über das Internet zu synchronisieren.', + "argument-epilog" : 'Wenn keine Optionen angegeben sind, werden die _config-Werte verwendet', + "nogui-argument" : 'Keine GUI anzeigen', + "host-argument" : 'Server-Adresse', + "name-argument" : 'Gewünschter Nutzername', + "debug-argument" : 'Debug-Modus', + "force-gui-prompt-argument" : 'Einstellungsfenster anzeigen', + "no-store-argument" : 'keine Werte in .syncplay speichern', + "room-argument" : 'Standard-Raum', + "password-argument" : 'Server-Passwort', + "player-path-argument" : 'Pfad zum Player', + "file-argument" : 'Abzuspielende Datei', + "args-argument" : 'Player-Einstellungen; Wenn du Einstellungen, die mit - beginnen, nutzen willst, stelle ein einzelnes \'--\'-Argument davor', + "clear-gui-data-argument" : 'Setzt die Pfad- und GUI-Fenster-Daten die in den QSettings gespeichert sind zurück', + "language-argument" : 'Sprache für Syncplay-Nachrichten (de/en/ru)', - "version-argument" : u'gibt die aktuelle Version aus', - "version-message" : u"Du verwendest Syncplay v. {} ({})", + "version-argument" : 'gibt die aktuelle Version aus', + "version-message" : "Du verwendest Syncplay v. {} ({})", # Client labels - "config-window-title" : u"Syncplay Konfiguration", + "config-window-title" : "Syncplay Konfiguration", - "connection-group-title" : u"Verbindungseinstellungen", - "host-label" : u"Server-Adresse:", - "name-label" : u"Benutzername (optional):", - "password-label" : u"Server-Passwort (falls nötig):", - "room-label" : u"Standard-Raum:", + "connection-group-title" : "Verbindungseinstellungen", + "host-label" : "Server-Adresse:", + "name-label" : "Benutzername (optional):", + "password-label" : "Server-Passwort (falls nötig):", + "room-label" : "Standard-Raum:", - "media-setting-title" : u"Media-Player Einstellungen", - "executable-path-label" : u"Pfad zum Media-Player:", - "media-path-label" : u"Pfad zur Datei:", # Todo: Translate to 'Path to video (optional)' - "player-arguments-label" : u"Playerparameter:", - "browse-label" : u"Durchsuchen", - "update-server-list-label" : u"Liste aktualisieren", + "media-setting-title" : "Media-Player Einstellungen", + "executable-path-label" : "Pfad zum Media-Player:", + "media-path-label" : "Pfad zur Datei:", # Todo: Translate to 'Path to video (optional)' + "player-arguments-label" : "Playerparameter:", + "browse-label" : "Durchsuchen", + "update-server-list-label" : "Liste aktualisieren", - "more-title" : u"Mehr Einstellungen zeigen", - "never-rewind-value" : u"Niemals", - "seconds-suffix" : u" sek", - "privacy-sendraw-option" : u"Klartext senden", - "privacy-sendhashed-option" : u"Hash senden", - "privacy-dontsend-option" : u"Nicht senden", - "filename-privacy-label" : u"Dateiname:", - "filesize-privacy-label" : u"Dateigröße:", - "checkforupdatesautomatically-label" : u"Automatisch nach Updates suchen", - "slowondesync-label" : u"Verlangsamen wenn nicht synchron (nicht unterstützt mit MPC-HC/BE)", - "dontslowdownwithme-label" : u"Nie verlangsamen oder andere zurückspulen (Experimentell)", - "pausing-title" : u"Pausing", # TODO: Translate - "pauseonleave-label" : u"Pausieren wenn ein Benutzer austritt", - "readiness-title" : u"Initial readiness state", # TODO: Translate - "readyatstart-label" : u"Standardmäßig auf \'Bereit\' stellen", - "forceguiprompt-label" : u"Diesen Dialog nicht mehr anzeigen", - "showosd-label" : u"OSD-Nachrichten anzeigen", + "more-title" : "Mehr Einstellungen zeigen", + "never-rewind-value" : "Niemals", + "seconds-suffix" : " sek", + "privacy-sendraw-option" : "Klartext senden", + "privacy-sendhashed-option" : "Hash senden", + "privacy-dontsend-option" : "Nicht senden", + "filename-privacy-label" : "Dateiname:", + "filesize-privacy-label" : "Dateigröße:", + "checkforupdatesautomatically-label" : "Automatisch nach Updates suchen", + "slowondesync-label" : "Verlangsamen wenn nicht synchron (nicht unterstützt mit MPC-HC/BE)", + "dontslowdownwithme-label" : "Nie verlangsamen oder andere zurückspulen (Experimentell)", + "pausing-title" : "Pausing", # TODO: Translate + "pauseonleave-label" : "Pausieren wenn ein Benutzer austritt", + "readiness-title" : "Initial readiness state", # TODO: Translate + "readyatstart-label" : "Standardmäßig auf \'Bereit\' stellen", + "forceguiprompt-label" : "Diesen Dialog nicht mehr anzeigen", + "showosd-label" : "OSD-Nachrichten anzeigen", - "showosdwarnings-label" : u"Zeige Warnungen (z.B. wenn Dateien verschieden)", - "showsameroomosd-label" : u"Zeige Ereignisse in deinem Raum", - "shownoncontrollerosd-label" : u"Zeige Ereignisse von nicht geführten Räumen in geführten Räumen.", - "showdifferentroomosd-label" : u"Zeige Ereignisse in anderen Räumen", - "showslowdownosd-label" : u"Zeige Verlangsamungs/Zurücksetzungs-Benachrichtigung", - "language-label" : u"Sprache:", - "automatic-language" : u"Automatisch ({})", # Default language - "showdurationnotification-label" : u"Zeige Warnung wegen unterschiedlicher Dauer", - "basics-label" : u"Grundlagen", - "readiness-label" : u"Play/Pause", - "misc-label" : u"Diverse", - "core-behaviour-title" : u"Verhalten des Raumes", - "syncplay-internals-title" : u"Syncplay intern", - "syncplay-mediasearchdirectories-title" : u"In diesen Verzeichnissen nach Medien suchen (ein Pfad pro Zeile)", - "sync-label" : u"Synchronisation", - "sync-otherslagging-title" : u"Wenn andere laggen...", - "sync-youlaggging-title" : u"Wenn du laggst...", - "messages-label" : u"Nachrichten", - "messages-osd-title" : u"OSD-(OnScreenDisplay)-Einstellungen", - "messages-other-title" : u"Weitere Display-Einstellungen", - "chat-label" : u"Chat", # TODO: Translate - "privacy-label" : u"Privatsphäre", - "privacy-title" : u"Privatsphäreneinstellungen", - "unpause-title" : u"Wenn du Play drückst, auf Bereit setzen und:", - "unpause-ifalreadyready-option" : u"Wiedergeben wenn bereits als Bereit gesetzt", - "unpause-ifothersready-option" : u"Wiedergeben wenn bereits als Bereit gesetzt oder alle anderen bereit sind (Standard)", - "unpause-ifminusersready-option" : u"Wiedergeben wenn bereits als Bereit gesetzt oder die minimale Anzahl anderer Nutzer bereit ist", - "unpause-always" : u"Immer wiedergeben", - "syncplay-trusteddomains-title": u"Trusted domains (for streaming services and hosted content)", # TODO: Translate into German + "showosdwarnings-label" : "Zeige Warnungen (z.B. wenn Dateien verschieden)", + "showsameroomosd-label" : "Zeige Ereignisse in deinem Raum", + "shownoncontrollerosd-label" : "Zeige Ereignisse von nicht geführten Räumen in geführten Räumen.", + "showdifferentroomosd-label" : "Zeige Ereignisse in anderen Räumen", + "showslowdownosd-label" : "Zeige Verlangsamungs/Zurücksetzungs-Benachrichtigung", + "language-label" : "Sprache:", + "automatic-language" : "Automatisch ({})", # Default language + "showdurationnotification-label" : "Zeige Warnung wegen unterschiedlicher Dauer", + "basics-label" : "Grundlagen", + "readiness-label" : "Play/Pause", + "misc-label" : "Diverse", + "core-behaviour-title" : "Verhalten des Raumes", + "syncplay-internals-title" : "Syncplay intern", + "syncplay-mediasearchdirectories-title" : "In diesen Verzeichnissen nach Medien suchen (ein Pfad pro Zeile)", + "sync-label" : "Synchronisation", + "sync-otherslagging-title" : "Wenn andere laggen...", + "sync-youlaggging-title" : "Wenn du laggst...", + "messages-label" : "Nachrichten", + "messages-osd-title" : "OSD-(OnScreenDisplay)-Einstellungen", + "messages-other-title" : "Weitere Display-Einstellungen", + "chat-label" : "Chat", # TODO: Translate + "privacy-label" : "Privatsphäre", + "privacy-title" : "Privatsphäreneinstellungen", + "unpause-title" : "Wenn du Play drückst, auf Bereit setzen und:", + "unpause-ifalreadyready-option" : "Wiedergeben wenn bereits als Bereit gesetzt", + "unpause-ifothersready-option" : "Wiedergeben wenn bereits als Bereit gesetzt oder alle anderen bereit sind (Standard)", + "unpause-ifminusersready-option" : "Wiedergeben wenn bereits als Bereit gesetzt oder die minimale Anzahl anderer Nutzer bereit ist", + "unpause-always" : "Immer wiedergeben", + "syncplay-trusteddomains-title": "Trusted domains (for streaming services and hosted content)", # TODO: Translate into German - "chat-title": u"Chat message input", # TODO: Translate - "chatinputenabled-label": u"Enable chat input via mpv (using enter key)", # TODO: Translate - "chatdirectinput-label" : u"Allow instant chat input (bypass having to press enter key to chat)", # TODO: Translate - "chatinputfont-label": u"Chat input font", # TODO: Translate - "chatfont-label": u"Set font", # TODO: Translate - "chatcolour-label": u"Set colour", # TODO: Translate - "chatinputposition-label": u"Position of message input area in mpv", # TODO: Translate - "chat-top-option": u"Top", # TODO: Translate - "chat-middle-option": u"Middle", # TODO: Translate - "chat-bottom-option": u"Bottom", # TODO: Translate - "chatoutputheader-label" : u"Chat message output", # TODO: Translate - "chatoutputfont-label": u"Chat output font", # TODO: Translate - "chatoutputenabled-label": u"Enable chat output in media player (mpv only for now)", # TODO: Translate - "chatoutputposition-label": u"Output mode", # TODO: Translate - "chat-chatroom-option": u"Chatroom style", # TODO: Translate - "chat-scrolling-option": u"Scrolling style", # TODO: Translate + "chat-title": "Chat message input", # TODO: Translate + "chatinputenabled-label": "Enable chat input via mpv (using enter key)", # TODO: Translate + "chatdirectinput-label" : "Allow instant chat input (bypass having to press enter key to chat)", # TODO: Translate + "chatinputfont-label": "Chat input font", # TODO: Translate + "chatfont-label": "Set font", # TODO: Translate + "chatcolour-label": "Set colour", # TODO: Translate + "chatinputposition-label": "Position of message input area in mpv", # TODO: Translate + "chat-top-option": "Top", # TODO: Translate + "chat-middle-option": "Middle", # TODO: Translate + "chat-bottom-option": "Bottom", # TODO: Translate + "chatoutputheader-label" : "Chat message output", # TODO: Translate + "chatoutputfont-label": "Chat output font", # TODO: Translate + "chatoutputenabled-label": "Enable chat output in media player (mpv only for now)", # TODO: Translate + "chatoutputposition-label": "Output mode", # TODO: Translate + "chat-chatroom-option": "Chatroom style", # TODO: Translate + "chat-scrolling-option": "Scrolling style", # TODO: Translate - "mpv-key-tab-hint": u"[TAB] to toggle access to alphabet row key shortcuts.", # TODO: Translate - "mpv-key-hint": u"[ENTER] to send message. [ESC] to escape chat mode.", # TODO: Translate - "alphakey-mode-warning-first-line": u"You can temporarily use old mpv bindings with a-z keys.", # TODO: Translate - "alphakey-mode-warning-second-line": u"Press [TAB] to return to Syncplay chat mode.", # TODO: Translate + "mpv-key-tab-hint": "[TAB] to toggle access to alphabet row key shortcuts.", # TODO: Translate + "mpv-key-hint": "[ENTER] to send message. [ESC] to escape chat mode.", # TODO: Translate + "alphakey-mode-warning-first-line": "You can temporarily use old mpv bindings with a-z keys.", # TODO: Translate + "alphakey-mode-warning-second-line": "Press [TAB] to return to Syncplay chat mode.", # TODO: Translate - "help-label" : u"Hilfe", - "reset-label" : u"Standardwerte zurücksetzen", - "run-label" : u"Syncplay starten", - "storeandrun-label" : u"Konfiguration speichern und Syncplay starten", + "help-label" : "Hilfe", + "reset-label" : "Standardwerte zurücksetzen", + "run-label" : "Syncplay starten", + "storeandrun-label" : "Konfiguration speichern und Syncplay starten", - "contact-label" : u"Du hast eine Idee, einen Bug gefunden oder möchtest Feedback geben? Sende eine E-Mail an dev@syncplay.pl, chatte auf dem #Syncplay IRC-Kanal auf irc.freenode.net oder öffne eine Fehlermeldung auf GitHub. Außerdem findest du auf https://syncplay.pl/ weitere Informationen, Hilfestellungen und Updates. OTE: Chat messages are not encrypted so do not use Syncplay to send sensitive information.", # TODO: Translate last sentence + "contact-label" : "Du hast eine Idee, einen Bug gefunden oder möchtest Feedback geben? Sende eine E-Mail an dev@syncplay.pl, chatte auf dem #Syncplay IRC-Kanal auf irc.freenode.net oder öffne eine Fehlermeldung auf GitHub. Außerdem findest du auf https://syncplay.pl/ weitere Informationen, Hilfestellungen und Updates. OTE: Chat messages are not encrypted so do not use Syncplay to send sensitive information.", # TODO: Translate last sentence - "joinroom-label" : u"Raum beitreten", - "joinroom-menu-label" : u"Raum beitreten {}", #TODO: Might want to fix this - "seektime-menu-label" : u"Spule zu Zeit", - "undoseek-menu-label" : u"Rückgängig", - "play-menu-label" : u"Wiedergabe", - "pause-menu-label" : u"Pause", - "playbackbuttons-menu-label" : u"Wiedergabesteuerung anzeigen", - "autoplay-menu-label" : u"Auto-Play-Knopf anzeigen", - "autoplay-guipushbuttonlabel" : u"Automatisch abspielen wenn alle bereit sind", - "autoplay-minimum-label" : u"Minimum an Nutzern:", + "joinroom-label" : "Raum beitreten", + "joinroom-menu-label" : "Raum beitreten {}", #TODO: Might want to fix this + "seektime-menu-label" : "Spule zu Zeit", + "undoseek-menu-label" : "Rückgängig", + "play-menu-label" : "Wiedergabe", + "pause-menu-label" : "Pause", + "playbackbuttons-menu-label" : "Wiedergabesteuerung anzeigen", + "autoplay-menu-label" : "Auto-Play-Knopf anzeigen", + "autoplay-guipushbuttonlabel" : "Automatisch abspielen wenn alle bereit sind", + "autoplay-minimum-label" : "Minimum an Nutzern:", - "sendmessage-label" : u"Send", # TODO: Translate + "sendmessage-label" : "Send", # TODO: Translate - "ready-guipushbuttonlabel" : u"Ich bin bereit den Film anzuschauen!", + "ready-guipushbuttonlabel" : "Ich bin bereit den Film anzuschauen!", - "roomuser-heading-label" : u"Raum / Benutzer", - "size-heading-label" : u"Größe", - "duration-heading-label" : u"Länge", - "filename-heading-label" : u"Dateiname", - "notifications-heading-label" : u"Benachrichtigungen", - "userlist-heading-label" : u"Liste der gespielten Dateien", + "roomuser-heading-label" : "Raum / Benutzer", + "size-heading-label" : "Größe", + "duration-heading-label" : "Länge", + "filename-heading-label" : "Dateiname", + "notifications-heading-label" : "Benachrichtigungen", + "userlist-heading-label" : "Liste der gespielten Dateien", - "browseformedia-label" : u"Nach Mediendateien durchsuchen", + "browseformedia-label" : "Nach Mediendateien durchsuchen", - "file-menu-label" : u"&Datei", # & precedes shortcut key - "openmedia-menu-label" : u"&Mediendatei öffnen...", - "openstreamurl-menu-label" : u"&Stream URL öffnen", - "setmediadirectories-menu-label" : u"Set media &directories", # TODO: Translate - "exit-menu-label" : u"&Beenden", - "advanced-menu-label" : u"&Erweitert", - "window-menu-label" : u"&Fenster", - "setoffset-menu-label" : u"&Offset einstellen", - "createcontrolledroom-menu-label" : u"&Zentral gesteuerten Raum erstellen", - "identifyascontroller-menu-label" : u"Als Raumleiter &identifizieren", - "settrusteddomains-menu-label" : u"Set &trusted domains", # TODO: Translate - "addtrusteddomain-menu-label" : u"Add {} as trusted domain", # Domain # TODO: Translate + "file-menu-label" : "&Datei", # & precedes shortcut key + "openmedia-menu-label" : "&Mediendatei öffnen...", + "openstreamurl-menu-label" : "&Stream URL öffnen", + "setmediadirectories-menu-label" : "Set media &directories", # TODO: Translate + "exit-menu-label" : "&Beenden", + "advanced-menu-label" : "&Erweitert", + "window-menu-label" : "&Fenster", + "setoffset-menu-label" : "&Offset einstellen", + "createcontrolledroom-menu-label" : "&Zentral gesteuerten Raum erstellen", + "identifyascontroller-menu-label" : "Als Raumleiter &identifizieren", + "settrusteddomains-menu-label" : "Set &trusted domains", # TODO: Translate + "addtrusteddomain-menu-label" : "Add {} as trusted domain", # Domain # TODO: Translate - "playback-menu-label" : u"&Wiedergabe", + "playback-menu-label" : "&Wiedergabe", - "help-menu-label" : u"&Hilfe", - "userguide-menu-label" : u"&Benutzerhandbuch öffnen", - "update-menu-label" : u"auf &Aktualisierung prüfen", + "help-menu-label" : "&Hilfe", + "userguide-menu-label" : "&Benutzerhandbuch öffnen", + "update-menu-label" : "auf &Aktualisierung prüfen", #About dialog - TODO: Translate - "about-menu-label": u"&About Syncplay", - "about-dialog-title": u"About Syncplay", - "about-dialog-release": u"Version {} release {} on {}", - "about-dialog-license-text" : u"Licensed under the Apache License, Version 2.0", - "about-dialog-license-button": u"License", - "about-dialog-dependencies": u"Dependencies", + "about-menu-label": "&About Syncplay", + "about-dialog-title": "About Syncplay", + "about-dialog-release": "Version {} release {} on {}", + "about-dialog-license-text" : "Licensed under the Apache License, Version 2.0", + "about-dialog-license-button": "License", + "about-dialog-dependencies": "Dependencies", - "setoffset-msgbox-label" : u"Offset einstellen", - "offsetinfo-msgbox-label" : u"Offset (siehe https://syncplay.pl/guide/ für eine Anleitung [Englisch]):", + "setoffset-msgbox-label" : "Offset einstellen", + "offsetinfo-msgbox-label" : "Offset (siehe https://syncplay.pl/guide/ für eine Anleitung [Englisch]):", - "promptforstreamurl-msgbox-label" : u"Stream URL öffnen", - "promptforstreamurlinfo-msgbox-label" : u"Stream URL", + "promptforstreamurl-msgbox-label" : "Stream URL öffnen", + "promptforstreamurlinfo-msgbox-label" : "Stream URL", - "addfolder-label" : u"Add folder", # TODO: Translate + "addfolder-label" : "Add folder", # TODO: Translate - "adduris-msgbox-label" : u"Add URLs to playlist (one per line)", # TODO: Translate - "editplaylist-msgbox-label": u"Set playlist (one per line)", # TODO: Translate - "trusteddomains-msgbox-label" : u"Domains it is okay to automatically switch to (one per line)", # TODO: Translate + "adduris-msgbox-label" : "Add URLs to playlist (one per line)", # TODO: Translate + "editplaylist-msgbox-label": "Set playlist (one per line)", # TODO: Translate + "trusteddomains-msgbox-label" : "Domains it is okay to automatically switch to (one per line)", # TODO: Translate - "createcontrolledroom-msgbox-label" : u"Zentral gesteuerten Raum erstellen", - "controlledroominfo-msgbox-label" : u"Namen des zentral gesteuerten Raums eingeben\r\n(siehe https://syncplay.pl/guide/ für eine Anleitung [Englisch]):", + "createcontrolledroom-msgbox-label" : "Zentral gesteuerten Raum erstellen", + "controlledroominfo-msgbox-label" : "Namen des zentral gesteuerten Raums eingeben\r\n(siehe https://syncplay.pl/guide/ für eine Anleitung [Englisch]):", - "identifyascontroller-msgbox-label" : u"Als Raumleiter identifizieren", - "identifyinfo-msgbox-label" : u"Passwort des zentral gesteuerten Raums eingeben\r\n(siehe https://syncplay.pl/guide/ für eine Anleitung [Englisch]):", + "identifyascontroller-msgbox-label" : "Als Raumleiter identifizieren", + "identifyinfo-msgbox-label" : "Passwort des zentral gesteuerten Raums eingeben\r\n(siehe https://syncplay.pl/guide/ für eine Anleitung [Englisch]):", - "public-server-msgbox-label" : u"Einen öffentlichen Server für diese Sitzung auswählen", + "public-server-msgbox-label" : "Einen öffentlichen Server für diese Sitzung auswählen", - "megabyte-suffix" : u" MB", + "megabyte-suffix" : " MB", # Tooltips - "host-tooltip" : u"Hostname oder IP zu der verbunden werden soll. Optional mit Port (z.B.. syncplay.pl:8999). Synchronisation findet nur mit Personen auf dem selben Server und Port statt.", - "name-tooltip" : u"Dein Benutzername. Keine Registrierung, kann einfach geändert werden. Bei fehlender Angabe wird ein zufälliger Name generiert.", - "password-tooltip" : u"Passwörter sind nur bei Verbindung zu privaten Servern nötig.", - "room-tooltip" : u"Der Raum, der betreten werden soll, kann ein x-beliebiger sein. Allerdings werden nur Clients im selben Raum synchronisiert.", + "host-tooltip" : "Hostname oder IP zu der verbunden werden soll. Optional mit Port (z.B.. syncplay.pl:8999). Synchronisation findet nur mit Personen auf dem selben Server und Port statt.", + "name-tooltip" : "Dein Benutzername. Keine Registrierung, kann einfach geändert werden. Bei fehlender Angabe wird ein zufälliger Name generiert.", + "password-tooltip" : "Passwörter sind nur bei Verbindung zu privaten Servern nötig.", + "room-tooltip" : "Der Raum, der betreten werden soll, kann ein x-beliebiger sein. Allerdings werden nur Clients im selben Raum synchronisiert.", - "executable-path-tooltip" : u"Pfad zum ausgewählten, unterstützten Mediaplayer (MPC-HC, MPC-BE, VLC, mplayer2 or mpv).", - "media-path-tooltip" : u"Pfad zum wiederzugebenden Video oder Stream. Notwendig für mplayer2.", # TODO: Confirm translation - "player-arguments-tooltip" : u"Zusätzliche Kommandozeilenparameter / -schalter für diesen Mediaplayer.", - "mediasearcdirectories-arguments-tooltip" : u"Verzeichnisse, in denen Syncplay nach Mediendateien suchen soll, z.B. wenn du das Click-to-switch-Feature verwendest. Syncplay wird rekursiv Unterordner durchsuchen.", # TODO: Translate Click-to-switch? (or use as name for feature) + "executable-path-tooltip" : "Pfad zum ausgewählten, unterstützten Mediaplayer (MPC-HC, MPC-BE, VLC, mplayer2 or mpv).", + "media-path-tooltip" : "Pfad zum wiederzugebenden Video oder Stream. Notwendig für mplayer2.", # TODO: Confirm translation + "player-arguments-tooltip" : "Zusätzliche Kommandozeilenparameter / -schalter für diesen Mediaplayer.", + "mediasearcdirectories-arguments-tooltip" : "Verzeichnisse, in denen Syncplay nach Mediendateien suchen soll, z.B. wenn du das Click-to-switch-Feature verwendest. Syncplay wird rekursiv Unterordner durchsuchen.", # TODO: Translate Click-to-switch? (or use as name for feature) - "more-tooltip" : u"Weitere Einstellungen anzeigen.", - "filename-privacy-tooltip" : u"Privatheitsmodus beim Senden des Namens der aktuellen Datei zum Server.", - "filesize-privacy-tooltip" : u"Privatheitsmodus beim Senden der Größe der aktuellen Datei zum Server.", - "privacy-sendraw-tooltip" : u"Die Information im Klartext übertragen. Dies ist die Standard-Einstellung mit der besten Funktionalität.", - "privacy-sendhashed-tooltip" : u"Die Informationen gehasht übertragen, um sie für andere Clients schwerer lesbar zu machen.", - "privacy-dontsend-tooltip" : u"Diese Information nicht übertragen. Dies garantiert den größtmöglichen Datanschutz.", - "checkforupdatesautomatically-tooltip" : u"Regelmäßig auf der Syncplay-Website nach Updates suchen.", - "slowondesync-tooltip" : u"Reduziert die Abspielgeschwindigkeit zeitweise, um die Synchronität zu den anderen Clients wiederherzustellen.", - "rewindondesync-label" : u"Zurückspulen bei großer Zeitdifferenz (empfohlen)", - "fastforwardondesync-label" : u"Vorspulen wenn das Video laggt (empfohlen)", - "dontslowdownwithme-tooltip" : u"Lässt andere nicht langsamer werden oder zurückspringen, wenn deine Wiedergabe hängt.", - "pauseonleave-tooltip" : u"Wiedergabe anhalten, wenn deine Verbindung verloren geht oder jemand den Raum verlässt.", - "readyatstart-tooltip" : u"Zu Beginn auf 'Bereit' setzen (sonst bist du als 'Nicht Bereit' gesetzt, bis du den Status änderst)", - "forceguiprompt-tooltip" : u"Der Konfigurationsdialog wird nicht angezeigt, wenn eine Datei mit Syncplay geöffnet wird.", - "nostore-tooltip" : u"Syncplay mit den angegebenen Einstellungen starten, diese aber nicht dauerhaft speichern.", - "rewindondesync-tooltip" : u"Zum Wiederherstellen der Synchronität in der Zeit zurückspringen (empfohlen)", - "fastforwardondesync-tooltip" : u"Nach vorne springen, wenn asynchron zum Raumleiter (oder deine vorgetäuschte Position, falls 'Niemals verlangsamen oder andere zurückspulen' aktiviert ist).", - "showosd-tooltip" : u"Syncplay-Nachrichten auf dem OSD (= OnScreenDisplay, ein eingeblendetes Textfeld) des Players anzeigen.", - "showosdwarnings-tooltip" : u"Warnungen bei Unterschiedlichen Dateien oder Alleinsein im Raum anzeigen.", - "showsameroomosd-tooltip" : u"OSD-Meldungen über Ereignisse im selben Raum anzeigen.", - "shownoncontrollerosd-tooltip" : u"OSD-Meldungen bei Ereignissen verursacht durch nicht-Raumleiter in zentral gesteuerten Räumen anzeigen.", - "showdifferentroomosd-tooltip" : u"OSD-Meldungen zu anderen Räumen als dem aktuell betretenen anzeigen.", - "showslowdownosd-tooltip" : u"Meldungen bei Geschwindigkeitsänderung anzeigen.", - "showdurationnotification-tooltip" : u"Nützlich, wenn z.B. ein Teil eines mehrteiligen Videos fehlt, kann jedoch auch fehlerhaft anschlagen.", - "language-tooltip" : u"Die verwendete Sprache von Syncplay", - "unpause-always-tooltip" : u"Wiedergabe startet immer (anstatt nur den Bereitschaftsstatus zu ändern)", - "unpause-ifalreadyready-tooltip" : u"Wenn du nicht bereit bist und Play drückst wirst du als bereit gesetzt - zum Starten der Wiedergabe nochmal drücken.", - "unpause-ifothersready-tooltip" : u"Wenn du Play drückst und nicht bereit bist, wird nur gestartet, wenn alle anderen bereit sind.", - "unpause-ifminusersready-tooltip" : u"Wenn du Play drückst und nicht bereit bist, wird nur gestartet, wenn die minimale Anzahl anderer Benutzer bereit ist.", - "trusteddomains-arguments-tooltip" : u"Domains that it is okay for Syncplay to automatically switch to when shared playlists is enabled.", # TODO: Translate into German + "more-tooltip" : "Weitere Einstellungen anzeigen.", + "filename-privacy-tooltip" : "Privatheitsmodus beim Senden des Namens der aktuellen Datei zum Server.", + "filesize-privacy-tooltip" : "Privatheitsmodus beim Senden der Größe der aktuellen Datei zum Server.", + "privacy-sendraw-tooltip" : "Die Information im Klartext übertragen. Dies ist die Standard-Einstellung mit der besten Funktionalität.", + "privacy-sendhashed-tooltip" : "Die Informationen gehasht übertragen, um sie für andere Clients schwerer lesbar zu machen.", + "privacy-dontsend-tooltip" : "Diese Information nicht übertragen. Dies garantiert den größtmöglichen Datanschutz.", + "checkforupdatesautomatically-tooltip" : "Regelmäßig auf der Syncplay-Website nach Updates suchen.", + "slowondesync-tooltip" : "Reduziert die Abspielgeschwindigkeit zeitweise, um die Synchronität zu den anderen Clients wiederherzustellen.", + "rewindondesync-label" : "Zurückspulen bei großer Zeitdifferenz (empfohlen)", + "fastforwardondesync-label" : "Vorspulen wenn das Video laggt (empfohlen)", + "dontslowdownwithme-tooltip" : "Lässt andere nicht langsamer werden oder zurückspringen, wenn deine Wiedergabe hängt.", + "pauseonleave-tooltip" : "Wiedergabe anhalten, wenn deine Verbindung verloren geht oder jemand den Raum verlässt.", + "readyatstart-tooltip" : "Zu Beginn auf 'Bereit' setzen (sonst bist du als 'Nicht Bereit' gesetzt, bis du den Status änderst)", + "forceguiprompt-tooltip" : "Der Konfigurationsdialog wird nicht angezeigt, wenn eine Datei mit Syncplay geöffnet wird.", + "nostore-tooltip" : "Syncplay mit den angegebenen Einstellungen starten, diese aber nicht dauerhaft speichern.", + "rewindondesync-tooltip" : "Zum Wiederherstellen der Synchronität in der Zeit zurückspringen (empfohlen)", + "fastforwardondesync-tooltip" : "Nach vorne springen, wenn asynchron zum Raumleiter (oder deine vorgetäuschte Position, falls 'Niemals verlangsamen oder andere zurückspulen' aktiviert ist).", + "showosd-tooltip" : "Syncplay-Nachrichten auf dem OSD (= OnScreenDisplay, ein eingeblendetes Textfeld) des Players anzeigen.", + "showosdwarnings-tooltip" : "Warnungen bei Unterschiedlichen Dateien oder Alleinsein im Raum anzeigen.", + "showsameroomosd-tooltip" : "OSD-Meldungen über Ereignisse im selben Raum anzeigen.", + "shownoncontrollerosd-tooltip" : "OSD-Meldungen bei Ereignissen verursacht durch nicht-Raumleiter in zentral gesteuerten Räumen anzeigen.", + "showdifferentroomosd-tooltip" : "OSD-Meldungen zu anderen Räumen als dem aktuell betretenen anzeigen.", + "showslowdownosd-tooltip" : "Meldungen bei Geschwindigkeitsänderung anzeigen.", + "showdurationnotification-tooltip" : "Nützlich, wenn z.B. ein Teil eines mehrteiligen Videos fehlt, kann jedoch auch fehlerhaft anschlagen.", + "language-tooltip" : "Die verwendete Sprache von Syncplay", + "unpause-always-tooltip" : "Wiedergabe startet immer (anstatt nur den Bereitschaftsstatus zu ändern)", + "unpause-ifalreadyready-tooltip" : "Wenn du nicht bereit bist und Play drückst wirst du als bereit gesetzt - zum Starten der Wiedergabe nochmal drücken.", + "unpause-ifothersready-tooltip" : "Wenn du Play drückst und nicht bereit bist, wird nur gestartet, wenn alle anderen bereit sind.", + "unpause-ifminusersready-tooltip" : "Wenn du Play drückst und nicht bereit bist, wird nur gestartet, wenn die minimale Anzahl anderer Benutzer bereit ist.", + "trusteddomains-arguments-tooltip" : "Domains that it is okay for Syncplay to automatically switch to when shared playlists is enabled.", # TODO: Translate into German - "chatinputenabled-tooltip": u"Enable chat input in mpv (press enter to chat, enter to send, escape to cancel)", # TODO: Translate - "chatdirectinput-tooltip" : u"Skip having to press 'enter' to go into chat input mode in mpv. Press TAB in mpv to temporarily disable this feature.", # TODO: Translate - "font-label-tooltip": u"Font used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", # TODO: Translate - "set-input-font-tooltip": u"Font family used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", # TODO: Translate - "set-input-colour-tooltip": u"Font colour used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", # TODO: Translate - "chatinputposition-tooltip": u"Location in mpv where chat input text will appear when you press enter and type.", # TODO: Translate - "chatinputposition-top-tooltip": u"Place chat input at top of mpv window.", # TODO: Translate - "chatinputposition-middle-tooltip": u"Place chat input in dead centre of mpv window.", # TODO: Translate - "chatinputposition-bottom-tooltip": u"Place chat input at bottom of mpv window.", # TODO: Translate - "chatoutputenabled-tooltip": u"Show chat messages in OSD (if supported by media player).", # TODO: Translate - "font-output-label-tooltip": u"Chat output font.", # TODO: Translate - "set-output-font-tooltip": u"Font used for when displaying chat messages.", # TODO: Translate - "chatoutputmode-tooltip": u"How chat messages are displayed.", # TODO: Translate - "chatoutputmode-chatroom-tooltip": u"Display new lines of chat directly below previous line.", # TODO: Translate - "chatoutputmode-scrolling-tooltip": u"Scroll chat text from right to left.", # TODO: Translate + "chatinputenabled-tooltip": "Enable chat input in mpv (press enter to chat, enter to send, escape to cancel)", # TODO: Translate + "chatdirectinput-tooltip" : "Skip having to press 'enter' to go into chat input mode in mpv. Press TAB in mpv to temporarily disable this feature.", # TODO: Translate + "font-label-tooltip": "Font used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", # TODO: Translate + "set-input-font-tooltip": "Font family used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", # TODO: Translate + "set-input-colour-tooltip": "Font colour used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", # TODO: Translate + "chatinputposition-tooltip": "Location in mpv where chat input text will appear when you press enter and type.", # TODO: Translate + "chatinputposition-top-tooltip": "Place chat input at top of mpv window.", # TODO: Translate + "chatinputposition-middle-tooltip": "Place chat input in dead centre of mpv window.", # TODO: Translate + "chatinputposition-bottom-tooltip": "Place chat input at bottom of mpv window.", # TODO: Translate + "chatoutputenabled-tooltip": "Show chat messages in OSD (if supported by media player).", # TODO: Translate + "font-output-label-tooltip": "Chat output font.", # TODO: Translate + "set-output-font-tooltip": "Font used for when displaying chat messages.", # TODO: Translate + "chatoutputmode-tooltip": "How chat messages are displayed.", # TODO: Translate + "chatoutputmode-chatroom-tooltip": "Display new lines of chat directly below previous line.", # TODO: Translate + "chatoutputmode-scrolling-tooltip": "Scroll chat text from right to left.", # TODO: Translate - "help-tooltip" : u"Öffnet Hilfe auf syncplay.pl [Englisch]", - "reset-tooltip" : u"Alle Einstellungen auf Standardwerte zurücksetzen.", - "update-server-list-tooltip" : u"Mit syncplay.pl verbinden um die Liste öffentlicher Server zu aktualisieren.", + "help-tooltip" : "Öffnet Hilfe auf syncplay.pl [Englisch]", + "reset-tooltip" : "Alle Einstellungen auf Standardwerte zurücksetzen.", + "update-server-list-tooltip" : "Mit syncplay.pl verbinden um die Liste öffentlicher Server zu aktualisieren.", - "joinroom-tooltip" : u"Den aktuellen Raum verlassen und stattdessen den angegebenen betreten.", - "seektime-msgbox-label" : u"Springe zur angegebenen Zeit (in Sekunden oder min:sek). Verwende +/- zum relativen Springen.", - "ready-tooltip" : u"Zeigt an, ob du bereit zum anschauen bist", - "autoplay-tooltip" : u"Automatisch abspielen, wenn alle Nutzer bereit sind oder die minimale Nutzerzahl erreicht ist.", - "switch-to-file-tooltip" : u"Doppelklicken um zu {} zu wechseln", # Filename - "sendmessage-tooltip" : u"Send message to room", # TODO: Translate + "joinroom-tooltip" : "Den aktuellen Raum verlassen und stattdessen den angegebenen betreten.", + "seektime-msgbox-label" : "Springe zur angegebenen Zeit (in Sekunden oder min:sek). Verwende +/- zum relativen Springen.", + "ready-tooltip" : "Zeigt an, ob du bereit zum anschauen bist", + "autoplay-tooltip" : "Automatisch abspielen, wenn alle Nutzer bereit sind oder die minimale Nutzerzahl erreicht ist.", + "switch-to-file-tooltip" : "Doppelklicken um zu {} zu wechseln", # Filename + "sendmessage-tooltip" : "Send message to room", # TODO: Translate # In-userlist notes (GUI) - "differentsize-note" : u"Verschiedene Größe!", - "differentsizeandduration-note" : u"Verschiedene Größe und Dauer!", - "differentduration-note" : u"Verschiedene Dauer!", - "nofile-note" : u"(keine Datei wird abgespielt)", + "differentsize-note" : "Verschiedene Größe!", + "differentsizeandduration-note" : "Verschiedene Größe und Dauer!", + "differentduration-note" : "Verschiedene Dauer!", + "nofile-note" : "(keine Datei wird abgespielt)", # Server messages to client - "new-syncplay-available-motd-message" : u" Du nutzt Syncplay Version {}, aber es gibt eine neuere Version auf https://syncplay.pl", # ClientVersion + "new-syncplay-available-motd-message" : " Du nutzt Syncplay Version {}, aber es gibt eine neuere Version auf https://syncplay.pl", # ClientVersion # Server notifications - "welcome-server-notification" : u"Willkommen zum Syncplay-Server, v. {0}", # version - "client-connected-room-server-notification" : u"{0}({2}) hat den Raum '{1}' betreten", # username, host, room - "client-left-server-notification" : u"{0} hat den Server verlassen", # name - "no-salt-notification" : u"WICHTIGER HINWEIS: Damit von dem Server generierte Passwörter für geführte Räume auch nach einem Serverneustart funktionieren, starte den Server mit dem folgenden Parameter: --salt {}", #Salt + "welcome-server-notification" : "Willkommen zum Syncplay-Server, v. {0}", # version + "client-connected-room-server-notification" : "{0}({2}) hat den Raum '{1}' betreten", # username, host, room + "client-left-server-notification" : "{0} hat den Server verlassen", # name + "no-salt-notification" : "WICHTIGER HINWEIS: Damit von dem Server generierte Passwörter für geführte Räume auch nach einem Serverneustart funktionieren, starte den Server mit dem folgenden Parameter: --salt {}", #Salt # Server arguments - "server-argument-description" : u'Anwendung, um mehrere MPlayer, MPC-HC/BE und VLC-Instanzen über das Internet zu synchronisieren. Server', - "server-argument-epilog" : u'Wenn keine Optionen angegeben sind, werden die _config-Werte verwendet', - "server-port-argument" : u'Server TCP-Port', - "server-password-argument" : u'Server Passwort', - "server-isolate-room-argument" : u'Sollen die Räume isoliert sein?', - "server-salt-argument" : u"zufällige Zeichenkette, die zur Erstellung von Passwörtern verwendet wird", - "server-disable-ready-argument" : u"Bereitschaftsfeature deaktivieren", - "server-motd-argument": u"Pfad zur Datei, von der die Nachricht des Tages geladen wird", + "server-argument-description" : 'Anwendung, um mehrere MPlayer, MPC-HC/BE und VLC-Instanzen über das Internet zu synchronisieren. Server', + "server-argument-epilog" : 'Wenn keine Optionen angegeben sind, werden die _config-Werte verwendet', + "server-port-argument" : 'Server TCP-Port', + "server-password-argument" : 'Server Passwort', + "server-isolate-room-argument" : 'Sollen die Räume isoliert sein?', + "server-salt-argument" : "zufällige Zeichenkette, die zur Erstellung von Passwörtern verwendet wird", + "server-disable-ready-argument" : "Bereitschaftsfeature deaktivieren", + "server-motd-argument": "Pfad zur Datei, von der die Nachricht des Tages geladen wird", "server-chat-argument" : "Should chat be disabled?", # TODO: Translate - "server-chat-maxchars-argument": u"Maximum number of characters in a chat message (default is {})", # TODO: Translate - "server-maxusernamelength-argument" : u"Maximum number of charactrs in a username (default is {})", # TODO: Translate - "server-messed-up-motd-unescaped-placeholders": u"Die Nachricht des Tages hat unmaskierte Platzhalter. Alle $-Zeichen sollten verdoppelt werden ($$).", - "server-messed-up-motd-too-long": u"Die Nachricht des Tages ist zu lang - Maximal {} Zeichen, aktuell {}.", + "server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # TODO: Translate + "server-maxusernamelength-argument" : "Maximum number of charactrs in a username (default is {})", # TODO: Translate + "server-messed-up-motd-unescaped-placeholders": "Die Nachricht des Tages hat unmaskierte Platzhalter. Alle $-Zeichen sollten verdoppelt werden ($$).", + "server-messed-up-motd-too-long": "Die Nachricht des Tages ist zu lang - Maximal {} Zeichen, aktuell {}.", # Server errors - "unknown-command-server-error" : u"Unbekannter Befehl {}", # message - "not-json-server-error" : u"Kein JSON-String {}", # message - "not-known-server-error" : u"Der Server muss dich kennen, bevor du diesen Befehl nutzen kannst", - "client-drop-server-error" : u"Client verloren: {} -- {}", # host, error - "password-required-server-error" : u"Passwort nötig", - "wrong-password-server-error" : u"Ungültiges Passwort", - "hello-server-error" : u"Zu wenige Hello-Argumente", + "unknown-command-server-error" : "Unbekannter Befehl {}", # message + "not-json-server-error" : "Kein JSON-String {}", # message + "not-known-server-error" : "Der Server muss dich kennen, bevor du diesen Befehl nutzen kannst", + "client-drop-server-error" : "Client verloren: {} -- {}", # host, error + "password-required-server-error" : "Passwort nötig", + "wrong-password-server-error" : "Ungültiges Passwort", + "hello-server-error" : "Zu wenige Hello-Argumente", # Playlists TODO: Translate all this to German - "playlist-selection-changed-notification" : u"{} changed the playlist selection", # Username - "playlist-contents-changed-notification" : u"{} updated the playlist", # Username - "cannot-find-file-for-playlist-switch-error" : u"Could not find file {} in media directories for playlist switch!", # Filename - "cannot-add-duplicate-error" : u"Could not add second entry for '{}' to the playlist as no duplicates are allowed.", #Filename - "cannot-add-unsafe-path-error" : u"Could not automatically load {} because it is not on a trusted domain. You can switch to the URL manually by double clicking it in the playlist, and add trusted domains via File->Advanced->Set Trusted Domains. If you right click on a URL then you can add its domain as a trusted domain via the context menu.", # Filename - "sharedplaylistenabled-label" : u"Enable shared playlists", - "removefromplaylist-menu-label" : u"Remove from playlist", - "shuffleremainingplaylist-menu-label" : u"Shuffle remaining playlist", - "shuffleentireplaylist-menu-label" : u"Shuffle entire playlist", - "undoplaylist-menu-label" : u"Undo last change to playlist", - "addfilestoplaylist-menu-label" : u"Add file(s) to bottom of playlist", - "addurlstoplaylist-menu-label" : u"Add URL(s) to bottom of playlist", - "editplaylist-menu-label": u"Edit playlist", + "playlist-selection-changed-notification" : "{} changed the playlist selection", # Username + "playlist-contents-changed-notification" : "{} updated the playlist", # Username + "cannot-find-file-for-playlist-switch-error" : "Could not find file {} in media directories for playlist switch!", # Filename + "cannot-add-duplicate-error" : "Could not add second entry for '{}' to the playlist as no duplicates are allowed.", #Filename + "cannot-add-unsafe-path-error" : "Could not automatically load {} because it is not on a trusted domain. You can switch to the URL manually by double clicking it in the playlist, and add trusted domains via File->Advanced->Set Trusted Domains. If you right click on a URL then you can add its domain as a trusted domain via the context menu.", # Filename + "sharedplaylistenabled-label" : "Enable shared playlists", + "removefromplaylist-menu-label" : "Remove from playlist", + "shuffleremainingplaylist-menu-label" : "Shuffle remaining playlist", + "shuffleentireplaylist-menu-label" : "Shuffle entire playlist", + "undoplaylist-menu-label" : "Undo last change to playlist", + "addfilestoplaylist-menu-label" : "Add file(s) to bottom of playlist", + "addurlstoplaylist-menu-label" : "Add URL(s) to bottom of playlist", + "editplaylist-menu-label": "Edit playlist", - "open-containing-folder": u"Open folder containing this file", - "addusersfiletoplaylist-menu-label" : u"Add {} file to playlist", # item owner indicator - "addusersstreamstoplaylist-menu-label" : u"Add {} stream to playlist", # item owner indicator - "openusersstream-menu-label" : u"Open {} stream", # [username]'s - "openusersfile-menu-label" : u"Open {} file", # [username]'s - "item-is-yours-indicator" : u"your", # Goes with addusersfiletoplaylist/addusersstreamstoplaylist - "item-is-others-indicator" : u"{}'s", # username - goes with addusersfiletoplaylist/addusersstreamstoplaylist + "open-containing-folder": "Open folder containing this file", + "addusersfiletoplaylist-menu-label" : "Add {} file to playlist", # item owner indicator + "addusersstreamstoplaylist-menu-label" : "Add {} stream to playlist", # item owner indicator + "openusersstream-menu-label" : "Open {} stream", # [username]'s + "openusersfile-menu-label" : "Open {} file", # [username]'s + "item-is-yours-indicator" : "your", # Goes with addusersfiletoplaylist/addusersstreamstoplaylist + "item-is-others-indicator" : "{}'s", # username - goes with addusersfiletoplaylist/addusersstreamstoplaylist - "playlist-instruction-item-message" : u"Drag file here to add it to the shared playlist.", - "sharedplaylistenabled-tooltip" : u"Room operators can add files to a synced playlist to make it easy for everyone to watching the same thing. Configure media directories under 'Misc'.", + "playlist-instruction-item-message" : "Drag file here to add it to the shared playlist.", + "sharedplaylistenabled-tooltip" : "Room operators can add files to a synced playlist to make it easy for everyone to watching the same thing. Configure media directories under 'Misc'.", } diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py old mode 100644 new mode 100755 index 2dc14a5..41343e6 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -8,7 +8,7 @@ en = { # Client notifications "config-cleared-notification" : "Settings cleared. Changes will be saved when you store a valid configuration.", - "relative-config-notification" : u"Loaded relative configuration file(s): {}", + "relative-config-notification" : "Loaded relative configuration file(s): {}", "connection-attempt-notification" : "Attempting to connect to {}:{}", # Port, IP "reconnection-attempt-notification" : "Connection with server lost, attempting to reconnect", @@ -22,53 +22,53 @@ en = { "slowdown-notification" : "Slowing down due to time difference with {}", # User "revert-notification" : "Reverting speed back to normal", - "pause-notification" : u"{} paused", # User - "unpause-notification" : u"{} unpaused", # User - "seek-notification" : u"{} jumped from {} to {}", # User, from time, to time + "pause-notification" : "{} paused", # User + "unpause-notification" : "{} unpaused", # User + "seek-notification" : "{} jumped from {} to {}", # User, from time, to time "current-offset-notification" : "Current offset: {} seconds", # Offset - "media-directory-list-updated-notification" : u"Syncplay media directories have been updated.", + "media-directory-list-updated-notification" : "Syncplay media directories have been updated.", - "room-join-notification" : u"{} has joined the room: '{}'", # User - "left-notification" : u"{} has left", # User - "left-paused-notification" : u"{} left, {} paused", # User who left, User who paused - "playing-notification" : u"{} is playing '{}' ({})", # User, file, duration - "playing-notification/room-addendum" : u" in room: '{}'", # Room + "room-join-notification" : "{} has joined the room: '{}'", # User + "left-notification" : "{} has left", # User + "left-paused-notification" : "{} left, {} paused", # User who left, User who paused + "playing-notification" : "{} is playing '{}' ({})", # User, file, duration + "playing-notification/room-addendum" : " in room: '{}'", # Room - "not-all-ready" : u"Not ready: {}", # Usernames - "all-users-ready" : u"Everyone is ready ({} users)", #Number of ready users - "ready-to-unpause-notification" : u"You are now set as ready - unpause again to unpause", - "set-as-ready-notification" : u"You are now set as ready", - "set-as-not-ready-notification" : u"You are now set as not ready", - "autoplaying-notification" : u"Auto-playing in {}...", # Number of seconds until playback will start + "not-all-ready" : "Not ready: {}", # Usernames + "all-users-ready" : "Everyone is ready ({} users)", #Number of ready users + "ready-to-unpause-notification" : "You are now set as ready - unpause again to unpause", + "set-as-ready-notification" : "You are now set as ready", + "set-as-not-ready-notification" : "You are now set as not ready", + "autoplaying-notification" : "Auto-playing in {}...", # Number of seconds until playback will start - "identifying-as-controller-notification" : u"Identifying as room operator with password '{}'...", - "failed-to-identify-as-controller-notification" : u"{} failed to identify as a room operator.", - "authenticated-as-controller-notification" : u"{} authenticated as a room operator", - "created-controlled-room-notification" : u"Created managed room '{}' with password '{}'. Please save this information for future reference!", # RoomName, operatorPassword + "identifying-as-controller-notification" : "Identifying as room operator with password '{}'...", + "failed-to-identify-as-controller-notification" : "{} failed to identify as a room operator.", + "authenticated-as-controller-notification" : "{} authenticated as a room operator", + "created-controlled-room-notification" : "Created managed room '{}' with password '{}'. Please save this information for future reference!", # RoomName, operatorPassword "file-different-notification" : "File you are playing appears to be different from {}'s", # User - "file-differences-notification" : u"Your file differs in the following way(s): {}", # Differences - "room-file-differences" : u"File differences: {}", # File differences (filename, size, and/or duration) - "file-difference-filename" : u"name", - "file-difference-filesize" : u"size", - "file-difference-duration" : u"duration", - "alone-in-the-room": u"You're alone in the room", + "file-differences-notification" : "Your file differs in the following way(s): {}", # Differences + "room-file-differences" : "File differences: {}", # File differences (filename, size, and/or duration) + "file-difference-filename" : "name", + "file-difference-filesize" : "size", + "file-difference-duration" : "duration", + "alone-in-the-room": "You're alone in the room", - "different-filesize-notification" : u" (their file size is different from yours!)", - "userlist-playing-notification" : u"{} is playing:", #Username - "file-played-by-notification" : u"File: {} is being played by:", # File - "no-file-played-notification" : u"{} is not playing a file", # Username + "different-filesize-notification" : " (their file size is different from yours!)", + "userlist-playing-notification" : "{} is playing:", #Username + "file-played-by-notification" : "File: {} is being played by:", # File + "no-file-played-notification" : "{} is not playing a file", # Username "notplaying-notification" : "People who are not playing any file:", - "userlist-room-notification" : u"In room '{}':", # Room + "userlist-room-notification" : "In room '{}':", # Room "userlist-file-notification" : "File", "controller-userlist-userflag" : "Operator", "ready-userlist-userflag" : "Ready", - "update-check-failed-notification" : u"Could not automatically check whether Syncplay {} is up to date. Want to visit https://syncplay.pl/ to manually check for updates?", #Syncplay version - "syncplay-uptodate-notification" : u"Syncplay is up to date", - "syncplay-updateavailable-notification" : u"A new version of Syncplay is available. Do you want to visit the release page?", + "update-check-failed-notification" : "Could not automatically check whether Syncplay {} is up to date. Want to visit https://syncplay.pl/ to manually check for updates?", #Syncplay version + "syncplay-uptodate-notification" : "Syncplay is up to date", + "syncplay-updateavailable-notification" : "A new version of Syncplay is available. Do you want to visit the release page?", "mplayer-file-required-notification" : "Syncplay using mplayer requires you to provide file when starting", "mplayer-file-required-notification/example" : "Usage example: syncplay [options] [url|path/]filename", @@ -82,7 +82,7 @@ en = { "commandlist-notification/pause" : "\tp - toggle pause", "commandlist-notification/seek" : "\t[s][+-]time - seek to the given value of time, if + or - is not specified it's absolute time in seconds or min:sec", "commandlist-notification/help" : "\th - this help", - "commandlist-notification/toggle" : u"\tt - toggles whether you are ready to watch or not", + "commandlist-notification/toggle" : "\tt - toggles whether you are ready to watch or not", "commandlist-notification/create" : "\tc [name] - create managed room using name of current room", "commandlist-notification/auth" : "\ta [password] - authenticate as room operator with operator password", "commandlist-notification/chat" : "\tch [message] - send a chat message in a room", @@ -91,13 +91,13 @@ en = { "gui-data-cleared-notification" : "Syncplay has cleared the path and window state data used by the GUI.", "language-changed-msgbox-label" : "Language will be changed when you run Syncplay.", - "promptforupdate-label" : u"Is it okay for Syncplay to automatically check for updates from time to time?", + "promptforupdate-label" : "Is it okay for Syncplay to automatically check for updates from time to time?", "vlc-interface-version-mismatch": "You are running version {} of the Syncplay interface module for VLC, but Syncplay is designed to run with version {} and above. Please refer to the Syncplay User Guide at https://syncplay.pl/guide/ for instructions on how to install syncplay.lua.", # VLC interface version, VLC interface min version "vlc-interface-oldversion-warning": "Warning: Syncplay detected that an old version version of the Syncplay interface module for VLC was installed in the VLC directory. Please refer to the Syncplay User Guide at https://syncplay.pl/guide/ for instructions on how to install syncplay.lua.", "vlc-interface-not-installed": "Warning: The Syncplay interface module for VLC was not found in the VLC directory. As such, if you are running VLC 2.0 then VLC will use the syncplay.lua module contained within the Syncplay directory, but this will mean that other custom interface scripts and extensions will not work. Please refer to the Syncplay User Guide at https://syncplay.pl/guide/ for instructions on how to install syncplay.lua.", - "media-player-latency-warning": u"Warning: The media player took {} seconds to respond. If you experience syncing issues then close applications to free up system resources, and if that doesn't work then try a different media player.", # Seconds to respond - "mpv-unresponsive-error": u"mpv has not responded for {} seconds so appears to have malfunctioned. Please restart Syncplay.", # Seconds to respond + "media-player-latency-warning": "Warning: The media player took {} seconds to respond. If you experience syncing issues then close applications to free up system resources, and if that doesn't work then try a different media player.", # Seconds to respond + "mpv-unresponsive-error": "mpv has not responded for {} seconds so appears to have malfunctioned. Please restart Syncplay.", # Seconds to respond # Client prompts "enter-to-exit-prompt" : "Press enter to exit\n", @@ -133,26 +133,26 @@ en = { "vlc-failed-noscript": "VLC has reported that the syncplay.lua interface script has not been installed. Please refer to https://syncplay.pl/LUA/ for instructions.", "vlc-failed-versioncheck": "This version of VLC is not supported by Syncplay.", - "feature-sharedPlaylists" : u"shared playlists", # used for not-supported-by-server-error - "feature-chat" : u"chat", # used for not-supported-by-server-error - "feature-readiness" : u"readiness", # used for not-supported-by-server-error - "feature-managedRooms" : u"managed rooms", # used for not-supported-by-server-error + "feature-sharedPlaylists" : "shared playlists", # used for not-supported-by-server-error + "feature-chat" : "chat", # used for not-supported-by-server-error + "feature-readiness" : "readiness", # used for not-supported-by-server-error + "feature-managedRooms" : "managed rooms", # used for not-supported-by-server-error - "not-supported-by-server-error" : u"The {} feature is not supported by this server..", #feature + "not-supported-by-server-error" : "The {} feature is not supported by this server..", #feature "shared-playlists-not-supported-by-server-error" : "The shared playlists feature may not be supported by the server. To ensure that it works correctly requires a server running Syncplay {}+, but the server is running Syncplay {}.", #minVersion, serverVersion "shared-playlists-disabled-by-server-error" : "The shared playlist feature has been disabled in the server configuration. To use this feature you will need to connect to a different server.", - "invalid-seek-value" : u"Invalid seek value", - "invalid-offset-value" : u"Invalid offset value", + "invalid-seek-value" : "Invalid seek value", + "invalid-offset-value" : "Invalid offset value", - "switch-file-not-found-error" : u"Could not switch to file '{0}'. Syncplay looks in specified media directories.", # File not found - "folder-search-timeout-error" : u"The search for media in media directories was aborted as it took too long to search through '{}'. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. For automatic file switching to work again please select File->Set Media Directories in the menu bar and remove this directory or replace it with an appropriate sub-folder. If the folder is actually fine then you can re-enable it by selecting File->Set Media Directories and pressing 'OK'.", #Folder - "folder-search-first-file-timeout-error" : u"The search for media in '{}' was aborted as it took too long to access the directory. This could happen if it is a network drive or if you configure your drive to spin down after a period of inactivity. For automatic file switching to work again please go to File->Set Media Directories and either remove the directory or resolve the issue (e.g. by changing power saving settings).", #Folder - "added-file-not-in-media-directory-error" : u"You loaded a file in '{}' which is not a known media directory. You can add this as a media directory by selecting File->Set Media Directories in the menu bar.", #Folder - "no-media-directories-error" : u"No media directories have been set. For shared playlist and file switching features to work properly please select File->Set Media Directories and specify where Syncplay should look to find media files.", - "cannot-find-directory-error" : u"Could not find media directory '{}'. To update your list of media directories please select File->Set Media Directories from the menu bar and specify where Syncplay should look to find media files.", + "switch-file-not-found-error" : "Could not switch to file '{0}'. Syncplay looks in specified media directories.", # File not found + "folder-search-timeout-error" : "The search for media in media directories was aborted as it took too long to search through '{}'. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. For automatic file switching to work again please select File->Set Media Directories in the menu bar and remove this directory or replace it with an appropriate sub-folder. If the folder is actually fine then you can re-enable it by selecting File->Set Media Directories and pressing 'OK'.", #Folder + "folder-search-first-file-timeout-error" : "The search for media in '{}' was aborted as it took too long to access the directory. This could happen if it is a network drive or if you configure your drive to spin down after a period of inactivity. For automatic file switching to work again please go to File->Set Media Directories and either remove the directory or resolve the issue (e.g. by changing power saving settings).", #Folder + "added-file-not-in-media-directory-error" : "You loaded a file in '{}' which is not a known media directory. You can add this as a media directory by selecting File->Set Media Directories in the menu bar.", #Folder + "no-media-directories-error" : "No media directories have been set. For shared playlist and file switching features to work properly please select File->Set Media Directories and specify where Syncplay should look to find media files.", + "cannot-find-directory-error" : "Could not find media directory '{}'. To update your list of media directories please select File->Set Media Directories from the menu bar and specify where Syncplay should look to find media files.", - "failed-to-load-server-list-error" : u"Failed to load public server list. Please visit http://www.syncplay.pl/ in your browser.", + "failed-to-load-server-list-error" : "Failed to load public server list. Please visit http://www.syncplay.pl/ in your browser.", # Client arguments "argument-description" : 'Solution to synchronize playback of multiple media player instances over the network.', @@ -188,7 +188,7 @@ en = { "media-path-label" : "Path to video (optional):", "player-arguments-label" : "Player arguments (if any):", "browse-label" : "Browse", - "update-server-list-label" : u"Update list", + "update-server-list-label" : "Update list", "more-title" : "Show more settings", "never-rewind-value" : "Never", @@ -203,9 +203,9 @@ en = { "rewindondesync-label" : "Rewind on major desync (recommended)", "fastforwardondesync-label" : "Fast-forward if lagging behind (recommended)", "dontslowdownwithme-label" : "Never slow down or rewind others (experimental)", - "pausing-title" : u"Pausing", + "pausing-title" : "Pausing", "pauseonleave-label" : "Pause when user leaves (e.g. if they are disconnected)", - "readiness-title" : u"Initial readiness state", + "readiness-title" : "Initial readiness state", "readyatstart-label" : "Set me as 'ready to watch' by default", "forceguiprompt-label" : "Don't always show the Syncplay configuration window", # (Inverted) "showosd-label" : "Enable OSD Messages", @@ -216,51 +216,51 @@ en = { "showdifferentroomosd-label" : "Include events in other rooms", "showslowdownosd-label" :"Include slowing down / reverting notifications", "language-label" : "Language:", - "automatic-language" : u"Default ({})", # Default language + "automatic-language" : "Default ({})", # Default language "showdurationnotification-label" : "Warn about media duration mismatches", "basics-label" : "Basics", - "readiness-label" : u"Play/Pause", - "misc-label" : u"Misc", + "readiness-label" : "Play/Pause", + "misc-label" : "Misc", "core-behaviour-title" : "Core room behaviour", - "syncplay-internals-title" : u"Syncplay internals", - "syncplay-mediasearchdirectories-title" : u"Directories to search for media (one path per line)", + "syncplay-internals-title" : "Syncplay internals", + "syncplay-mediasearchdirectories-title" : "Directories to search for media (one path per line)", "sync-label" : "Sync", "sync-otherslagging-title" : "If others are lagging behind...", "sync-youlaggging-title" : "If you are lagging behind...", "messages-label" : "Messages", "messages-osd-title" : "On-screen Display settings", "messages-other-title" : "Other display settings", - "chat-label" : u"Chat", + "chat-label" : "Chat", "privacy-label" : "Privacy", # Currently unused, but will be brought back if more space is needed in Misc tab "privacy-title" : "Privacy settings", - "unpause-title" : u"If you press play, set as ready and:", - "unpause-ifalreadyready-option" : u"Unpause if already set as ready", - "unpause-ifothersready-option" : u"Unpause if already ready or others in room are ready (default)", - "unpause-ifminusersready-option" : u"Unpause if already ready or if all others ready and min users ready", - "unpause-always" : u"Always unpause", - "syncplay-trusteddomains-title": u"Trusted domains (for streaming services and hosted content)", + "unpause-title" : "If you press play, set as ready and:", + "unpause-ifalreadyready-option" : "Unpause if already set as ready", + "unpause-ifothersready-option" : "Unpause if already ready or others in room are ready (default)", + "unpause-ifminusersready-option" : "Unpause if already ready or if all others ready and min users ready", + "unpause-always" : "Always unpause", + "syncplay-trusteddomains-title": "Trusted domains (for streaming services and hosted content)", - "chat-title" : u"Chat message input", - "chatinputenabled-label" : u"Enable chat input via mpv", - "chatdirectinput-label" : u"Allow instant chat input (bypass having to press enter key to chat)", - "chatinputfont-label" : u"Chat input font", - "chatfont-label" : u"Set font", - "chatcolour-label" : u"Set colour", - "chatinputposition-label" : u"Position of message input area in mpv", - "chat-top-option" : u"Top", - "chat-middle-option" : u"Middle", - "chat-bottom-option" : u"Bottom", - "chatoutputheader-label" : u"Chat message output", - "chatoutputfont-label": u"Chat output font", - "chatoutputenabled-label": u"Enable chat output in media player (mpv only for now)", - "chatoutputposition-label": u"Output mode", - "chat-chatroom-option": u"Chatroom style", - "chat-scrolling-option": u"Scrolling style", + "chat-title" : "Chat message input", + "chatinputenabled-label" : "Enable chat input via mpv", + "chatdirectinput-label" : "Allow instant chat input (bypass having to press enter key to chat)", + "chatinputfont-label" : "Chat input font", + "chatfont-label" : "Set font", + "chatcolour-label" : "Set colour", + "chatinputposition-label" : "Position of message input area in mpv", + "chat-top-option" : "Top", + "chat-middle-option" : "Middle", + "chat-bottom-option" : "Bottom", + "chatoutputheader-label" : "Chat message output", + "chatoutputfont-label": "Chat output font", + "chatoutputenabled-label": "Enable chat output in media player (mpv only for now)", + "chatoutputposition-label": "Output mode", + "chat-chatroom-option": "Chatroom style", + "chat-scrolling-option": "Scrolling style", - "mpv-key-tab-hint": u"[TAB] to toggle access to alphabet row key shortcuts.", - "mpv-key-hint": u"[ENTER] to send message. [ESC] to escape chat mode.", - "alphakey-mode-warning-first-line": u"You can temporarily use old mpv bindings with a-z keys.", - "alphakey-mode-warning-second-line": u"Press [TAB] to return to Syncplay chat mode.", + "mpv-key-tab-hint": "[TAB] to toggle access to alphabet row key shortcuts.", + "mpv-key-hint": "[ENTER] to send message. [ESC] to escape chat mode.", + "alphakey-mode-warning-first-line": "You can temporarily use old mpv bindings with a-z keys.", + "alphakey-mode-warning-second-line": "Press [TAB] to return to Syncplay chat mode.", "help-label" : "Help", "reset-label" : "Restore defaults", @@ -270,19 +270,19 @@ en = { "contact-label" : "Feel free to e-mail dev@syncplay.pl, chat via the #Syncplay IRC channel on irc.freenode.net, raise an issue via GitHub, like us on Facebook, follow us on Twitter, or visit https://syncplay.pl/. NOTE: Chat messages are not encrypted so do not use Syncplay to send sensitive information.", "joinroom-label" : "Join room", - "joinroom-menu-label" : u"Join room {}", + "joinroom-menu-label" : "Join room {}", "seektime-menu-label" : "Seek to time", "undoseek-menu-label" : "Undo seek", "play-menu-label" : "Play", "pause-menu-label" : "Pause", - "playbackbuttons-menu-label" : u"Show playback buttons", - "autoplay-menu-label" : u"Show auto-play button", - "autoplay-guipushbuttonlabel" : u"Play when all ready", - "autoplay-minimum-label" : u"Min users:", + "playbackbuttons-menu-label" : "Show playback buttons", + "autoplay-menu-label" : "Show auto-play button", + "autoplay-guipushbuttonlabel" : "Play when all ready", + "autoplay-minimum-label" : "Min users:", - "sendmessage-label" : u"Send", + "sendmessage-label" : "Send", - "ready-guipushbuttonlabel" : u"I'm ready to watch!", + "ready-guipushbuttonlabel" : "I'm ready to watch!", "roomuser-heading-label" : "Room / User", "size-heading-label" : "Size", @@ -296,29 +296,29 @@ en = { "file-menu-label" : "&File", # & precedes shortcut key "openmedia-menu-label" : "&Open media file", "openstreamurl-menu-label" : "Open &media stream URL", - "setmediadirectories-menu-label" : u"Set media &directories", + "setmediadirectories-menu-label" : "Set media &directories", "exit-menu-label" : "E&xit", "advanced-menu-label" : "&Advanced", "window-menu-label" : "&Window", "setoffset-menu-label" : "Set &offset", "createcontrolledroom-menu-label" : "&Create managed room", "identifyascontroller-menu-label" : "&Identify as room operator", - "settrusteddomains-menu-label" : u"Set &trusted domains", - "addtrusteddomain-menu-label" : u"Add {} as trusted domain", # Domain + "settrusteddomains-menu-label" : "Set &trusted domains", + "addtrusteddomain-menu-label" : "Add {} as trusted domain", # Domain - "playback-menu-label" : u"&Playback", + "playback-menu-label" : "&Playback", "help-menu-label" : "&Help", "userguide-menu-label" : "Open user &guide", "update-menu-label" : "Check for &update", #About dialog - "about-menu-label": u"&About Syncplay", - "about-dialog-title": u"About Syncplay", - "about-dialog-release": u"Version {} release {} on {}", - "about-dialog-license-text" : u"Licensed under the Apache License, Version 2.0", - "about-dialog-license-button": u"License", - "about-dialog-dependencies": u"Dependencies", + "about-menu-label": "&About Syncplay", + "about-dialog-title": "About Syncplay", + "about-dialog-release": "Version {} release {} on {}", + "about-dialog-license-text" : "Licensed under the Apache License, Version 2.0", + "about-dialog-license-button": "License", + "about-dialog-dependencies": "Dependencies", "setoffset-msgbox-label" : "Set offset", "offsetinfo-msgbox-label" : "Offset (see https://syncplay.pl/guide/ for usage instructions):", @@ -326,11 +326,11 @@ en = { "promptforstreamurl-msgbox-label" : "Open media stream URL", "promptforstreamurlinfo-msgbox-label" : "Stream URL", - "addfolder-label" : u"Add folder", + "addfolder-label" : "Add folder", - "adduris-msgbox-label" : u"Add URLs to playlist (one per line)", - "editplaylist-msgbox-label" : u"Set playlist (one per line)", - "trusteddomains-msgbox-label" : u"Domains it is okay to automatically switch to (one per line)", + "adduris-msgbox-label" : "Add URLs to playlist (one per line)", + "editplaylist-msgbox-label" : "Set playlist (one per line)", + "trusteddomains-msgbox-label" : "Domains it is okay to automatically switch to (one per line)", "createcontrolledroom-msgbox-label" : "Create managed room", "controlledroominfo-msgbox-label" : "Enter name of managed room\r\n(see https://syncplay.pl/guide/ for usage instructions):", @@ -338,7 +338,7 @@ en = { "identifyascontroller-msgbox-label" : "Identify as room operator", "identifyinfo-msgbox-label" : "Enter operator password for this room\r\n(see https://syncplay.pl/guide/ for usage instructions):", - "public-server-msgbox-label" : u"Select the public server for this viewing session", + "public-server-msgbox-label" : "Select the public server for this viewing session", "megabyte-suffix" : " MB", @@ -352,7 +352,7 @@ en = { "executable-path-tooltip" : "Location of your chosen supported media player (mpv, VLC, MPC-HC/BE or mplayer2).", "media-path-tooltip" : "Location of video or stream to be opened. Necessary for mplayer2.", "player-arguments-tooltip" : "Additional command line arguments / switches to pass on to this media player.", - "mediasearcdirectories-arguments-tooltip" : u"Directories where Syncplay will search for media files, e.g. when you are using the click to switch feature. Syncplay will look recursively through sub-folders.", + "mediasearcdirectories-arguments-tooltip" : "Directories where Syncplay will search for media files, e.g. when you are using the click to switch feature. Syncplay will look recursively through sub-folders.", "more-tooltip" : "Display less frequently used settings.", "filename-privacy-tooltip" : "Privacy mode for sending currently playing filename to server.", @@ -376,39 +376,39 @@ en = { "showdifferentroomosd-tooltip" : "Show OSD notifications for events relating to room user is not in.", "showslowdownosd-tooltip" : "Show notifications of slowing down / reverting on time difference.", "showdurationnotification-tooltip" : "Useful for when a segment in a multi-part file is missing, but can result in false positives.", - "language-tooltip" : u"Language to be used by Syncplay.", - "unpause-always-tooltip" : u"If you press unpause it always sets you as ready and unpause, rather than just setting you as ready.", - "unpause-ifalreadyready-tooltip" : u"If you press unpause when not ready it will set you as ready - press unpause again to unpause.", - "unpause-ifothersready-tooltip" : u"If you press unpause when not ready, it will only upause if others are ready.", - "unpause-ifminusersready-tooltip" : u"If you press unpause when not ready, it will only unpause if others are ready and minimum users threshold is met.", - "trusteddomains-arguments-tooltip" : u"Domains that it is okay for Syncplay to automatically switch to when shared playlists is enabled.", + "language-tooltip" : "Language to be used by Syncplay.", + "unpause-always-tooltip" : "If you press unpause it always sets you as ready and unpause, rather than just setting you as ready.", + "unpause-ifalreadyready-tooltip" : "If you press unpause when not ready it will set you as ready - press unpause again to unpause.", + "unpause-ifothersready-tooltip" : "If you press unpause when not ready, it will only upause if others are ready.", + "unpause-ifminusersready-tooltip" : "If you press unpause when not ready, it will only unpause if others are ready and minimum users threshold is met.", + "trusteddomains-arguments-tooltip" : "Domains that it is okay for Syncplay to automatically switch to when shared playlists is enabled.", - "chatinputenabled-tooltip" : u"Enable chat input in mpv (press enter to chat, enter to send, escape to cancel)", - "chatdirectinput-tooltip" : u"Skip having to press 'enter' to go into chat input mode in mpv. Press TAB in mpv to temporarily disable this feature.", - "font-label-tooltip" : u"Font used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", - "set-input-font-tooltip" : u"Font family used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", - "set-input-colour-tooltip" : u"Font colour used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", - "chatinputposition-tooltip" : u"Location in mpv where chat input text will appear when you press enter and type.", - "chatinputposition-top-tooltip" : u"Place chat input at top of mpv window.", - "chatinputposition-middle-tooltip" : u"Place chat input in dead centre of mpv window.", - "chatinputposition-bottom-tooltip" : u"Place chat input at bottom of mpv window.", - "chatoutputenabled-tooltip": u"Show chat messages in OSD (if supported by media player).", - "font-output-label-tooltip": u"Chat output font.", - "set-output-font-tooltip": u"Font used for when displaying chat messages.", - "chatoutputmode-tooltip": u"How chat messages are displayed.", - "chatoutputmode-chatroom-tooltip": u"Display new lines of chat directly below previous line.", - "chatoutputmode-scrolling-tooltip": u"Scroll chat text from right to left.", + "chatinputenabled-tooltip" : "Enable chat input in mpv (press enter to chat, enter to send, escape to cancel)", + "chatdirectinput-tooltip" : "Skip having to press 'enter' to go into chat input mode in mpv. Press TAB in mpv to temporarily disable this feature.", + "font-label-tooltip" : "Font used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", + "set-input-font-tooltip" : "Font family used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", + "set-input-colour-tooltip" : "Font colour used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", + "chatinputposition-tooltip" : "Location in mpv where chat input text will appear when you press enter and type.", + "chatinputposition-top-tooltip" : "Place chat input at top of mpv window.", + "chatinputposition-middle-tooltip" : "Place chat input in dead centre of mpv window.", + "chatinputposition-bottom-tooltip" : "Place chat input at bottom of mpv window.", + "chatoutputenabled-tooltip": "Show chat messages in OSD (if supported by media player).", + "font-output-label-tooltip": "Chat output font.", + "set-output-font-tooltip": "Font used for when displaying chat messages.", + "chatoutputmode-tooltip": "How chat messages are displayed.", + "chatoutputmode-chatroom-tooltip": "Display new lines of chat directly below previous line.", + "chatoutputmode-scrolling-tooltip": "Scroll chat text from right to left.", "help-tooltip" : "Opens the Syncplay.pl user guide.", "reset-tooltip" : "Reset all settings to the default configuration.", - "update-server-list-tooltip" : u"Connect to syncplay.pl to update list of public servers.", + "update-server-list-tooltip" : "Connect to syncplay.pl to update list of public servers.", "joinroom-tooltip" : "Leave current room and joins specified room.", "seektime-msgbox-label" : "Jump to specified time (in seconds / min:sec). Use +/- for relative seek.", "ready-tooltip" : "Indicates whether you are ready to watch.", "autoplay-tooltip" : "Auto-play when all users who have readiness indicator are ready and minimum user threshold met.", - "switch-to-file-tooltip" : u"Double click to switch to {}", # Filename - "sendmessage-tooltip" : u"Send message to room", + "switch-to-file-tooltip" : "Double click to switch to {}", # Filename + "sendmessage-tooltip" : "Send message to room", # In-userlist notes (GUI) "differentsize-note" : "Different size!", @@ -421,8 +421,8 @@ en = { # Server notifications "welcome-server-notification" : "Welcome to Syncplay server, ver. {0}", # version - "client-connected-room-server-notification" : u"{0}({2}) connected to room '{1}'", # username, host, room - "client-left-server-notification" : u"{0} left server", # name + "client-connected-room-server-notification" : "{0}({2}) connected to room '{1}'", # username, host, room + "client-left-server-notification" : "{0} left server", # name "no-salt-notification" : "PLEASE NOTE: To allow room operator passwords generated by this server instance to still work when the server is restarted, please add the following command line argument when running the Syncplay server in the future: --salt {}", #Salt @@ -433,46 +433,46 @@ en = { "server-password-argument" : 'server password', "server-isolate-room-argument" : 'should rooms be isolated?', "server-salt-argument" : "random string used to generate managed room passwords", - "server-disable-ready-argument" : u"disable readiness feature", + "server-disable-ready-argument" : "disable readiness feature", "server-motd-argument": "path to file from which motd will be fetched", "server-chat-argument" : "Should chat be disabled?", - "server-chat-maxchars-argument" : u"Maximum number of characters in a chat message (default is {})", # Default number of characters - "server-maxusernamelength-argument" : u"Maximum number of charactrs in a username (default is {})", + "server-chat-maxchars-argument" : "Maximum number of characters in a chat message (default is {})", # Default number of characters + "server-maxusernamelength-argument" : "Maximum number of charactrs in a username (default is {})", "server-messed-up-motd-unescaped-placeholders": "Message of the Day has unescaped placeholders. All $ signs should be doubled ($$).", - "server-messed-up-motd-too-long": u"Message of the Day is too long - maximum of {} chars, {} given.", + "server-messed-up-motd-too-long": "Message of the Day is too long - maximum of {} chars, {} given.", # Server errors - "unknown-command-server-error" : u"Unknown command {}", # message + "unknown-command-server-error" : "Unknown command {}", # message "not-json-server-error" : "Not a json encoded string {}", # message "not-known-server-error" : "You must be known to server before sending this command", - "client-drop-server-error" : u"Client drop: {} -- {}", # host, error + "client-drop-server-error" : "Client drop: {} -- {}", # host, error "password-required-server-error" : "Password required", "wrong-password-server-error" : "Wrong password supplied", "hello-server-error" : "Not enough Hello arguments", #DO NOT TRANSLATE # Playlists - "playlist-selection-changed-notification" : u"{} changed the playlist selection", # Username - "playlist-contents-changed-notification" : u"{} updated the playlist", # Username - "cannot-find-file-for-playlist-switch-error" : u"Could not find file {} in media directories for playlist switch!", # Filename - "cannot-add-duplicate-error" : u"Could not add second entry for '{}' to the playlist as no duplicates are allowed.", #Filename - "cannot-add-unsafe-path-error" : u"Could not automatically load {} because it is not on a trusted domain. You can switch to the URL manually by double clicking it in the playlist, and add trusted domains via File->Advanced->Set Trusted Domains. If you right click on a URL then you can add its domain as a trusted domain via the context menu.", # Filename - "sharedplaylistenabled-label" : u"Enable shared playlists", - "removefromplaylist-menu-label" : u"Remove from playlist", - "shuffleremainingplaylist-menu-label" : u"Shuffle remaining playlist", - "shuffleentireplaylist-menu-label" : u"Shuffle entire playlist", - "undoplaylist-menu-label" : u"Undo last change to playlist", - "addfilestoplaylist-menu-label" : u"Add file(s) to bottom of playlist", - "addurlstoplaylist-menu-label" : u"Add URL(s) to bottom of playlist", - "editplaylist-menu-label": u"Edit playlist", + "playlist-selection-changed-notification" : "{} changed the playlist selection", # Username + "playlist-contents-changed-notification" : "{} updated the playlist", # Username + "cannot-find-file-for-playlist-switch-error" : "Could not find file {} in media directories for playlist switch!", # Filename + "cannot-add-duplicate-error" : "Could not add second entry for '{}' to the playlist as no duplicates are allowed.", #Filename + "cannot-add-unsafe-path-error" : "Could not automatically load {} because it is not on a trusted domain. You can switch to the URL manually by double clicking it in the playlist, and add trusted domains via File->Advanced->Set Trusted Domains. If you right click on a URL then you can add its domain as a trusted domain via the context menu.", # Filename + "sharedplaylistenabled-label" : "Enable shared playlists", + "removefromplaylist-menu-label" : "Remove from playlist", + "shuffleremainingplaylist-menu-label" : "Shuffle remaining playlist", + "shuffleentireplaylist-menu-label" : "Shuffle entire playlist", + "undoplaylist-menu-label" : "Undo last change to playlist", + "addfilestoplaylist-menu-label" : "Add file(s) to bottom of playlist", + "addurlstoplaylist-menu-label" : "Add URL(s) to bottom of playlist", + "editplaylist-menu-label": "Edit playlist", - "open-containing-folder": u"Open folder containing this file", - "addusersfiletoplaylist-menu-label" : u"Add {} file to playlist", # item owner indicator - "addusersstreamstoplaylist-menu-label" : u"Add {} stream to playlist", # item owner indicator - "openusersstream-menu-label" : u"Open {} stream", # [username]'s - "openusersfile-menu-label" : u"Open {} file", # [username]'s - "item-is-yours-indicator" : u"your", # Goes with addusersfiletoplaylist/addusersstreamstoplaylist - "item-is-others-indicator" : u"{}'s", # username - goes with addusersfiletoplaylist/addusersstreamstoplaylist + "open-containing-folder": "Open folder containing this file", + "addusersfiletoplaylist-menu-label" : "Add {} file to playlist", # item owner indicator + "addusersstreamstoplaylist-menu-label" : "Add {} stream to playlist", # item owner indicator + "openusersstream-menu-label" : "Open {} stream", # [username]'s + "openusersfile-menu-label" : "Open {} file", # [username]'s + "item-is-yours-indicator" : "your", # Goes with addusersfiletoplaylist/addusersstreamstoplaylist + "item-is-others-indicator" : "{}'s", # username - goes with addusersfiletoplaylist/addusersstreamstoplaylist - "playlist-instruction-item-message" : u"Drag file here to add it to the shared playlist.", - "sharedplaylistenabled-tooltip" : u"Room operators can add files to a synced playlist to make it easy for everyone to watching the same thing. Configure media directories under 'Misc'.", + "playlist-instruction-item-message" : "Drag file here to add it to the shared playlist.", + "sharedplaylistenabled-tooltip" : "Room operators can add files to a synced playlist to make it easy for everyone to watching the same thing. Configure media directories under 'Misc'.", } diff --git a/syncplay/messages_it.py b/syncplay/messages_it.py old mode 100644 new mode 100755 index 2de7b79..fac2bb2 --- a/syncplay/messages_it.py +++ b/syncplay/messages_it.py @@ -3,477 +3,477 @@ """Italian dictionary""" it = { - "LANGUAGE" : u"Italiano", + "LANGUAGE" : "Italiano", # Client notifications - "config-cleared-notification" : u"Impostazioni iniziali ripristinate. I cambiamenti saranno memorizzati quando salverai una configurazione valida.", + "config-cleared-notification" : "Impostazioni iniziali ripristinate. I cambiamenti saranno memorizzati quando salverai una configurazione valida.", - "relative-config-notification" : u"Caricato i file di configurazione relativi: {}", + "relative-config-notification" : "Caricato i file di configurazione relativi: {}", - "connection-attempt-notification" : u"Tentativo di connessione a {}:{}", # Port, IP - "reconnection-attempt-notification" : u"Connessione col server persa, tentativo di riconnesione in corso", - "disconnection-notification" : u"Disconnesso dal server", - "connection-failed-notification" : u"Connessione col server fallita", - "connected-successful-notification" : u"Connessione al server effettuata con successo", - "retrying-notification" : u"%s, Nuovo tentativo in %d secondi...", # Seconds + "connection-attempt-notification" : "Tentativo di connessione a {}:{}", # Port, IP + "reconnection-attempt-notification" : "Connessione col server persa, tentativo di riconnesione in corso", + "disconnection-notification" : "Disconnesso dal server", + "connection-failed-notification" : "Connessione col server fallita", + "connected-successful-notification" : "Connessione al server effettuata con successo", + "retrying-notification" : "%s, Nuovo tentativo in %d secondi...", # Seconds - "rewind-notification" : u"Riavvolgo a causa della differenza temporale con {}", # User - "fastforward-notification" : u"Avanzamento rapido a causa della differenza temporale con {}", # User - "slowdown-notification" : u"Rallento a causa della differenza temporale con {}", # User - "revert-notification" : u"Ripristino la velocità di riproduzione normale", + "rewind-notification" : "Riavvolgo a causa della differenza temporale con {}", # User + "fastforward-notification" : "Avanzamento rapido a causa della differenza temporale con {}", # User + "slowdown-notification" : "Rallento a causa della differenza temporale con {}", # User + "revert-notification" : "Ripristino la velocità di riproduzione normale", - "pause-notification" : u"{} ha messo in pausa", # User - "unpause-notification" : u"{} ha ripreso la riproduzione", # User - "seek-notification" : u"{} è passato da {} a {}", # User, from time, to time + "pause-notification" : "{} ha messo in pausa", # User + "unpause-notification" : "{} ha ripreso la riproduzione", # User + "seek-notification" : "{} è passato da {} a {}", # User, from time, to time - "current-offset-notification" : u"Offset corrente: {} secondi", # Offset + "current-offset-notification" : "Offset corrente: {} secondi", # Offset - "media-directory-list-updated-notification" : u"Le cartelle multimediali di Syncplay sono state aggiornate.", + "media-directory-list-updated-notification" : "Le cartelle multimediali di Syncplay sono state aggiornate.", - "room-join-notification" : u"{} è entranto nella stanza: '{}'", # User - "left-notification" : u"{} ha lasciato la stanza", # User - "left-paused-notification" : u"{} ha lasciato la stanza, {} ha messo in pausa", # User who left, User who paused - "playing-notification" : u"{} sta riproducendo '{}' ({})", # User, file, duration - "playing-notification/room-addendum" : u" nella stanza: '{}'", # Room + "room-join-notification" : "{} è entranto nella stanza: '{}'", # User + "left-notification" : "{} ha lasciato la stanza", # User + "left-paused-notification" : "{} ha lasciato la stanza, {} ha messo in pausa", # User who left, User who paused + "playing-notification" : "{} sta riproducendo '{}' ({})", # User, file, duration + "playing-notification/room-addendum" : " nella stanza: '{}'", # Room - "not-all-ready" : u"Non pronti: {}", # Usernames - "all-users-ready" : u"Tutti i partecipanti sono pronti ({} utenti)", #Number of ready users - "ready-to-unpause-notification" : u"Ora sei pronto - premi ancora una volta per riprendere la riproduzione", - "set-as-ready-notification" : u"Ora sei pronto", - "set-as-not-ready-notification" : u"Non sei pronto", - "autoplaying-notification" : u"Riproduzione automatica in {}...", # Number of seconds until playback will start + "not-all-ready" : "Non pronti: {}", # Usernames + "all-users-ready" : "Tutti i partecipanti sono pronti ({} utenti)", #Number of ready users + "ready-to-unpause-notification" : "Ora sei pronto - premi ancora una volta per riprendere la riproduzione", + "set-as-ready-notification" : "Ora sei pronto", + "set-as-not-ready-notification" : "Non sei pronto", + "autoplaying-notification" : "Riproduzione automatica in {}...", # Number of seconds until playback will start - "identifying-as-controller-notification" : u"Ti sei identificato come gestore della stanza con password '{}'...", - "failed-to-identify-as-controller-notification" : u"{} ha fallito l'identificazione come gestore della stanza.", - "authenticated-as-controller-notification" : u"{} autenticato come gestore della stanza", - "created-controlled-room-notification" : u"Stanza gestita '{}' creata con password '{}'. Per favore salva queste informazioni per una consultazione futura!", # RoomName, operatorPassword + "identifying-as-controller-notification" : "Ti sei identificato come gestore della stanza con password '{}'...", + "failed-to-identify-as-controller-notification" : "{} ha fallito l'identificazione come gestore della stanza.", + "authenticated-as-controller-notification" : "{} autenticato come gestore della stanza", + "created-controlled-room-notification" : "Stanza gestita '{}' creata con password '{}'. Per favore salva queste informazioni per una consultazione futura!", # RoomName, operatorPassword - "file-different-notification" : u"Il file che stai riproducendo sembra essere diverso da quello di {}", # User - "file-differences-notification" : u"Il tuo file mostra le seguenti differenze: {}", # Differences - "room-file-differences" : u"Differenze: {}", # File differences (filename, size, and/or duration) - "file-difference-filename" : u"nome", - "file-difference-filesize" : u"dimensione", - "file-difference-duration" : u"durata", - "alone-in-the-room": u"Non ci sono altri utenti nella stanza", + "file-different-notification" : "Il file che stai riproducendo sembra essere diverso da quello di {}", # User + "file-differences-notification" : "Il tuo file mostra le seguenti differenze: {}", # Differences + "room-file-differences" : "Differenze: {}", # File differences (filename, size, and/or duration) + "file-difference-filename" : "nome", + "file-difference-filesize" : "dimensione", + "file-difference-duration" : "durata", + "alone-in-the-room": "Non ci sono altri utenti nella stanza", - "different-filesize-notification" : u" (la dimensione del tuo file è diversa da quella degli altri partecipanti!)", - "userlist-playing-notification" : u"{} sta riproducendo:", #Username - "file-played-by-notification" : u"File: {} è in riproduzione da:", # File - "no-file-played-notification" : u"{} non sta riproducendo alcun file", # Username - "notplaying-notification" : u"Partecipanti che non stanno riproducendo alcun file:", - "userlist-room-notification" : u"Nella stanza '{}':", # Room - "userlist-file-notification" : u"File", - "controller-userlist-userflag" : u"Gestore", - "ready-userlist-userflag" : u"Pronto", + "different-filesize-notification" : " (la dimensione del tuo file è diversa da quella degli altri partecipanti!)", + "userlist-playing-notification" : "{} sta riproducendo:", #Username + "file-played-by-notification" : "File: {} è in riproduzione da:", # File + "no-file-played-notification" : "{} non sta riproducendo alcun file", # Username + "notplaying-notification" : "Partecipanti che non stanno riproducendo alcun file:", + "userlist-room-notification" : "Nella stanza '{}':", # Room + "userlist-file-notification" : "File", + "controller-userlist-userflag" : "Gestore", + "ready-userlist-userflag" : "Pronto", - "update-check-failed-notification" : u"Controllo automatico degli aggiornamenti di Syncplay {} fallito. Vuoi visitare https://syncplay.pl/ per verificare manualmente la presenza di aggiornamenti?", #Syncplay version - "syncplay-uptodate-notification" : u"Syncplay è aggiornato", - "syncplay-updateavailable-notification" : u"Una nuova versione di Syncplay è disponibile. Vuoi visitare la pagina delle release?", + "update-check-failed-notification" : "Controllo automatico degli aggiornamenti di Syncplay {} fallito. Vuoi visitare https://syncplay.pl/ per verificare manualmente la presenza di aggiornamenti?", #Syncplay version + "syncplay-uptodate-notification" : "Syncplay è aggiornato", + "syncplay-updateavailable-notification" : "Una nuova versione di Syncplay è disponibile. Vuoi visitare la pagina delle release?", - "mplayer-file-required-notification" : u"Utilizzare Syncplay con mplayer di selezionare il file all'avvio", - "mplayer-file-required-notification/example" : u"Esempio di utilizzo: syncplay [opzioni] [url|percorso/]nomefile", - "mplayer2-required" : u"Syncplay non è compatibile con MPlayer 1.x, per favore utilizza mplayer2 or mpv", + "mplayer-file-required-notification" : "Utilizzare Syncplay con mplayer di selezionare il file all'avvio", + "mplayer-file-required-notification/example" : "Esempio di utilizzo: syncplay [opzioni] [url|percorso/]nomefile", + "mplayer2-required" : "Syncplay non è compatibile con MPlayer 1.x, per favore utilizza mplayer2 or mpv", - "unrecognized-command-notification" : u"Comando non riconosciuto", - "commandlist-notification" : u"Comandi disponibili:", - "commandlist-notification/room" : u"\tr [nome] - cambia stanza", - "commandlist-notification/list" : u"\tl - mostra la lista di utenti", - "commandlist-notification/undo" : u"\tu - annulla l'ultima ricerca", - "commandlist-notification/pause" : u"\tp - attiva o disattiva la pausa", - "commandlist-notification/seek" : u"\t[s][+-]tempo - salta all'istante di tempo dato, se + o - non è specificato si considera il tempo assoluto in secondi o min:sec", - "commandlist-notification/help" : u"\th - mostra questo help", - "commandlist-notification/toggle" : u"\tt - attiva o disattiva la funzionalità \"pronto\"", - "commandlist-notification/create" : u"\tc [nome] - crea una stanza gestita usando il nome della stanza attuale", - "commandlist-notification/auth" : u"\ta [password] - autentica come gestore della stanza, utilizzando la password del gestore", - "commandlist-notification/chat" : u"\tch [message] - invia un messaggio nella chat della stanza", - "syncplay-version-notification" : u"Versione di Syncplay: {}", # syncplay.version - "more-info-notification" : u"Maggiori informazioni a: {}", # projectURL + "unrecognized-command-notification" : "Comando non riconosciuto", + "commandlist-notification" : "Comandi disponibili:", + "commandlist-notification/room" : "\tr [nome] - cambia stanza", + "commandlist-notification/list" : "\tl - mostra la lista di utenti", + "commandlist-notification/undo" : "\tu - annulla l'ultima ricerca", + "commandlist-notification/pause" : "\tp - attiva o disattiva la pausa", + "commandlist-notification/seek" : "\t[s][+-]tempo - salta all'istante di tempo dato, se + o - non è specificato si considera il tempo assoluto in secondi o min:sec", + "commandlist-notification/help" : "\th - mostra questo help", + "commandlist-notification/toggle" : "\tt - attiva o disattiva la funzionalità \"pronto\"", + "commandlist-notification/create" : "\tc [nome] - crea una stanza gestita usando il nome della stanza attuale", + "commandlist-notification/auth" : "\ta [password] - autentica come gestore della stanza, utilizzando la password del gestore", + "commandlist-notification/chat" : "\tch [message] - invia un messaggio nella chat della stanza", + "syncplay-version-notification" : "Versione di Syncplay: {}", # syncplay.version + "more-info-notification" : "Maggiori informazioni a: {}", # projectURL - "gui-data-cleared-notification" : u"Syncplay ha ripristinato i dati dell'interfaccia relativi ai percorsi e allo stato delle finestre.", - "language-changed-msgbox-label" : u"La lingua sarà cambiata quando avvierai Syncplay.", - "promptforupdate-label" : u"Ti piacerebbe che, di tanto in tanto, Syncplay controllasse automaticamente la presenza di aggiornamenti?", + "gui-data-cleared-notification" : "Syncplay ha ripristinato i dati dell'interfaccia relativi ai percorsi e allo stato delle finestre.", + "language-changed-msgbox-label" : "La lingua sarà cambiata quando avvierai Syncplay.", + "promptforupdate-label" : "Ti piacerebbe che, di tanto in tanto, Syncplay controllasse automaticamente la presenza di aggiornamenti?", - "vlc-interface-version-mismatch": u"Stai eseguendo la versione {} del modulo di interfaccia per VLC di Syncplay, ma Syncplay è progettato per essere utilizzato con la versione {} o superiore. Per favore, fai riferimento alla User Guide di Syncplay presso https://syncplay.pl/guide/ per istruzioni su come installare syncplay.lua.", # VLC interface version, VLC interface min version - "vlc-interface-oldversion-warning": u"Attenzione: Syncplay ha rilevato una vecchia versione del modulo di interfaccia per VLC di Syncplay installata nella cartella di VLC. Per favore, fai riferimento alla User Guide di Syncplay presso https://syncplay.pl/guide/ per istruzioni su come installare syncplay.lua.", - "media-player-latency-warning": u"Attenzione: il media player ha impiegato {} secondi per rispondere. Se stai avendo problemi di sincronizzazione, chiudi delle applicazioni per liberare le risorse di sistema e, se ciò non dovesse avere alcun effetto, prova un altro media player.", # Seconds to respond - "vlc-interface-not-installed": u"Attenzione: il modulo di interfaccia per VLC di Syncplay non è stato trovato nella cartella di VLC. Se stai utilizzando VLC 2.0, VLC userà il modulo syncplay.lua contenuto nella cartella di Syncplay, ma ciò significa che altri custom script di interfaccia ed estensioni non funzioneranno. Per favore, fai riferimento alla User Guide di Syncplay presso https://syncplay.pl/guide/ per istruzioni su come installare syncplay.lua.", - "mpv-unresponsive-error": u"mpv non ha risposto per {} secondi, quindi sembra non funzionare correttamente. Per favore, riavvia Syncplay.", # Seconds to respond + "vlc-interface-version-mismatch": "Stai eseguendo la versione {} del modulo di interfaccia per VLC di Syncplay, ma Syncplay è progettato per essere utilizzato con la versione {} o superiore. Per favore, fai riferimento alla User Guide di Syncplay presso https://syncplay.pl/guide/ per istruzioni su come installare syncplay.lua.", # VLC interface version, VLC interface min version + "vlc-interface-oldversion-warning": "Attenzione: Syncplay ha rilevato una vecchia versione del modulo di interfaccia per VLC di Syncplay installata nella cartella di VLC. Per favore, fai riferimento alla User Guide di Syncplay presso https://syncplay.pl/guide/ per istruzioni su come installare syncplay.lua.", + "media-player-latency-warning": "Attenzione: il media player ha impiegato {} secondi per rispondere. Se stai avendo problemi di sincronizzazione, chiudi delle applicazioni per liberare le risorse di sistema e, se ciò non dovesse avere alcun effetto, prova un altro media player.", # Seconds to respond + "vlc-interface-not-installed": "Attenzione: il modulo di interfaccia per VLC di Syncplay non è stato trovato nella cartella di VLC. Se stai utilizzando VLC 2.0, VLC userà il modulo syncplay.lua contenuto nella cartella di Syncplay, ma ciò significa che altri custom script di interfaccia ed estensioni non funzioneranno. Per favore, fai riferimento alla User Guide di Syncplay presso https://syncplay.pl/guide/ per istruzioni su come installare syncplay.lua.", + "mpv-unresponsive-error": "mpv non ha risposto per {} secondi, quindi sembra non funzionare correttamente. Per favore, riavvia Syncplay.", # Seconds to respond # Client prompts - "enter-to-exit-prompt" : u"Premi Invio per uscire\n", + "enter-to-exit-prompt" : "Premi Invio per uscire\n", # Client errors - "missing-arguments-error" : u"Alcuni argomenti obbligatori non sono stati trovati. Fai riferimento a --help", - "server-timeout-error" : u"Connessione col server scaduta", - "mpc-slave-error" : u"Non è possibile avviare MPC in modalità slave!", - "mpc-version-insufficient-error" : u"La tua versione di MPC è troppo vecchia, per favore usa `mpc-hc` >= `{}`", - "mpc-be-version-insufficient-error" : u"La tua versione di MPC è troppo vecchia, per favore usa `mpc-be` >= `{}`", - "mpv-version-error" : u"Syncplay non è compatibile con questa versione di mpv. Per favore usa un'altra versione di mpv (es. Git HEAD).", - "player-file-open-error" : u"Il player non è riuscito ad aprire il file", - "player-path-error" : u"Il path del player non è configurato correttamente. I player supportati sono: mpv, VLC, MPC-HC, MPC-BE e mplayer2", - "hostname-empty-error" : u"Il campo hostname non può essere vuoto", - "empty-error" : u"Il campo {} non può esssere vuoto", # Configuration - "media-player-error": u"Errore media player: \"{}\"", # Error line - "unable-import-gui-error": u"Non è possibile importare le librerie di interfaccia grafica. Hai bisogno di PySide per poter utilizzare l'interfaccia grafica.", + "missing-arguments-error" : "Alcuni argomenti obbligatori non sono stati trovati. Fai riferimento a --help", + "server-timeout-error" : "Connessione col server scaduta", + "mpc-slave-error" : "Non è possibile avviare MPC in modalità slave!", + "mpc-version-insufficient-error" : "La tua versione di MPC è troppo vecchia, per favore usa `mpc-hc` >= `{}`", + "mpc-be-version-insufficient-error" : "La tua versione di MPC è troppo vecchia, per favore usa `mpc-be` >= `{}`", + "mpv-version-error" : "Syncplay non è compatibile con questa versione di mpv. Per favore usa un'altra versione di mpv (es. Git HEAD).", + "player-file-open-error" : "Il player non è riuscito ad aprire il file", + "player-path-error" : "Il path del player non è configurato correttamente. I player supportati sono: mpv, VLC, MPC-HC, MPC-BE e mplayer2", + "hostname-empty-error" : "Il campo hostname non può essere vuoto", + "empty-error" : "Il campo {} non può esssere vuoto", # Configuration + "media-player-error": "Errore media player: \"{}\"", # Error line + "unable-import-gui-error": "Non è possibile importare le librerie di interfaccia grafica. Hai bisogno di PySide per poter utilizzare l'interfaccia grafica.", - "arguments-missing-error" : u"Alcuni argomenti obbligatori non sono stati trovati. Fai riferimento a --help", + "arguments-missing-error" : "Alcuni argomenti obbligatori non sono stati trovati. Fai riferimento a --help", - "unable-to-start-client-error" : u"Impossibile avviare il client", + "unable-to-start-client-error" : "Impossibile avviare il client", - "player-path-config-error": u"Il percorso del player non è configurato correttamente. I player supportati sono: mpv, VLC, MPC-HC, MPC-BE e mplayer2.", - "no-file-path-config-error" :u"Deve essere selezionato un file prima di avviare il player", - "no-hostname-config-error": u"Il campo hostname non può essere vuoto", - "invalid-port-config-error" : u"La porta deve essere valida", - "empty-value-config-error" : u"Il campo {} non può essere vuoto", # Config option + "player-path-config-error": "Il percorso del player non è configurato correttamente. I player supportati sono: mpv, VLC, MPC-HC, MPC-BE e mplayer2.", + "no-file-path-config-error" :"Deve essere selezionato un file prima di avviare il player", + "no-hostname-config-error": "Il campo hostname non può essere vuoto", + "invalid-port-config-error" : "La porta deve essere valida", + "empty-value-config-error" : "Il campo {} non può essere vuoto", # Config option - "not-json-error" : u"Non è una stringa con codifica JSON\n", + "not-json-error" : "Non è una stringa con codifica JSON\n", "hello-arguments-error" : "Not enough Hello arguments\n", # DO NOT TRANSLATE - "version-mismatch-error" : u"La versione del client è diversa da quella del server\n", - "vlc-failed-connection": u"Impossibile collegarsi a VLC. Se non hai installato syncplay.lua e stai usando l'ultima versione di VLC, fai riferimento a https://syncplay.pl/LUA/ per istruzioni.", - "vlc-failed-noscript": u"VLC ha segnalato che lo script di interfaccia syncplay.lua non è stato installato. Per favore, fai riferimento a https://syncplay.pl/LUA/ per istruzioni.", - "vlc-failed-versioncheck": u"Questa versione di VLC non è supportata da Syncplay.", + "version-mismatch-error" : "La versione del client è diversa da quella del server\n", + "vlc-failed-connection": "Impossibile collegarsi a VLC. Se non hai installato syncplay.lua e stai usando l'ultima versione di VLC, fai riferimento a https://syncplay.pl/LUA/ per istruzioni.", + "vlc-failed-noscript": "VLC ha segnalato che lo script di interfaccia syncplay.lua non è stato installato. Per favore, fai riferimento a https://syncplay.pl/LUA/ per istruzioni.", + "vlc-failed-versioncheck": "Questa versione di VLC non è supportata da Syncplay.", - "feature-sharedPlaylists" : u"playlist condivise", # used for not-supported-by-server-error - "feature-chat" : u"chat", # used for not-supported-by-server-error - "feature-readiness" : u"pronto", # used for not-supported-by-server-error - "feature-managedRooms" : u"stanze gestite", # used for not-supported-by-server-error + "feature-sharedPlaylists" : "playlist condivise", # used for not-supported-by-server-error + "feature-chat" : "chat", # used for not-supported-by-server-error + "feature-readiness" : "pronto", # used for not-supported-by-server-error + "feature-managedRooms" : "stanze gestite", # used for not-supported-by-server-error - "not-supported-by-server-error" : u"La feature {} non è supportata da questo server..", #feature - "shared-playlists-not-supported-by-server-error" : u"Le playlist condivise potrebbero non essere supportata dal server. È necessario un server con Syncplay {}+ per assicurarsi che funzionino correttamente, tuttavia il server sta utilizzando Syncplay {}.", #minVersion, serverVersion - "shared-playlists-disabled-by-server-error" : u"Le playlist condivise sono state disabilitate nella configurazione del server. Per utilizzarle, dovrai collegarti a un altro server.", + "not-supported-by-server-error" : "La feature {} non è supportata da questo server..", #feature + "shared-playlists-not-supported-by-server-error" : "Le playlist condivise potrebbero non essere supportata dal server. È necessario un server con Syncplay {}+ per assicurarsi che funzionino correttamente, tuttavia il server sta utilizzando Syncplay {}.", #minVersion, serverVersion + "shared-playlists-disabled-by-server-error" : "Le playlist condivise sono state disabilitate nella configurazione del server. Per utilizzarle, dovrai collegarti a un altro server.", - "invalid-seek-value" : u"Valore di ricerca non valido", - "invalid-offset-value" : u"Valore di offset non valido", + "invalid-seek-value" : "Valore di ricerca non valido", + "invalid-offset-value" : "Valore di offset non valido", - "switch-file-not-found-error" : u"Impossibile selezionare il file '{0}'. Syncplay osserva solo le cartelle multimediali specificate.", # File not found - "folder-search-timeout-error" : u"La ricerca nelle cartelle multimediali è stata interrotta perché l'analisi di '{}' sta impiegando troppo tempo. Ciò accade se si aggiunge nella lista di ricerca una cartella con troppe sottocartelle. Per riabilitare la selezione automatica dei file seleziona File->Imposta cartelle multimediali nella barra dei menù e rimuovi questa cartella, o sostituiscila con una sottocartella appropriata. Se la cartella è idonea, è possibile riabilitarla selezionando File->Imposta cartelle multimediali e premendo 'OK'.", #Folder - "folder-search-first-file-timeout-error" : u"La ricerca dei media in '{}' è stata interrotta perché l'accesso alla cartella sta impiegando troppo tempo. Ciò accade se questa si trova in un disco di rete oppure se hai impostato il blocco della rotazione del disco rigido dopo un certo periodo di inattività. Per riabilitare la selezione automatica dei file seleziona File->Imposta cartelle multimediali, quindi rimuovi la cartella oppure risolvi il problema (es. cambiando le impostazioni di risparmio energetico).", #Folder - "added-file-not-in-media-directory-error" : u"Hai selezionato un file in '{}', che non è impostata come cartella multimediale. Puoi aggiungerla come cartella multimediale selezionando File->Imposta cartelle multimediali nella barra dei menù.", #Folder - "no-media-directories-error" : u"Nessuna cartella multimediale è stata configurata. Per permettere il corretto funzionamento delle playlist condivise e la selezione automatica dei file, naviga in File->Imposta cartelle multimediali e specifica dove Syncplay deve ricercare i file multimediali.", - "cannot-find-directory-error" : u"Impossibile trovare la cartella multimediale '{}'. Per aggiornare la lista delle cartelle multimediali seleziona File->Imposta cartelle multimediali dalla barra dei menù e specifica dove Syncplay deve ricercare i file multimediali.", + "switch-file-not-found-error" : "Impossibile selezionare il file '{0}'. Syncplay osserva solo le cartelle multimediali specificate.", # File not found + "folder-search-timeout-error" : "La ricerca nelle cartelle multimediali è stata interrotta perché l'analisi di '{}' sta impiegando troppo tempo. Ciò accade se si aggiunge nella lista di ricerca una cartella con troppe sottocartelle. Per riabilitare la selezione automatica dei file seleziona File->Imposta cartelle multimediali nella barra dei menù e rimuovi questa cartella, o sostituiscila con una sottocartella appropriata. Se la cartella è idonea, è possibile riabilitarla selezionando File->Imposta cartelle multimediali e premendo 'OK'.", #Folder + "folder-search-first-file-timeout-error" : "La ricerca dei media in '{}' è stata interrotta perché l'accesso alla cartella sta impiegando troppo tempo. Ciò accade se questa si trova in un disco di rete oppure se hai impostato il blocco della rotazione del disco rigido dopo un certo periodo di inattività. Per riabilitare la selezione automatica dei file seleziona File->Imposta cartelle multimediali, quindi rimuovi la cartella oppure risolvi il problema (es. cambiando le impostazioni di risparmio energetico).", #Folder + "added-file-not-in-media-directory-error" : "Hai selezionato un file in '{}', che non è impostata come cartella multimediale. Puoi aggiungerla come cartella multimediale selezionando File->Imposta cartelle multimediali nella barra dei menù.", #Folder + "no-media-directories-error" : "Nessuna cartella multimediale è stata configurata. Per permettere il corretto funzionamento delle playlist condivise e la selezione automatica dei file, naviga in File->Imposta cartelle multimediali e specifica dove Syncplay deve ricercare i file multimediali.", + "cannot-find-directory-error" : "Impossibile trovare la cartella multimediale '{}'. Per aggiornare la lista delle cartelle multimediali seleziona File->Imposta cartelle multimediali dalla barra dei menù e specifica dove Syncplay deve ricercare i file multimediali.", - "failed-to-load-server-list-error" : u"Impossibile caricare la lista dei server pubblici. Per favore, visita http://www.syncplay.pl/ con il tuo browser.", + "failed-to-load-server-list-error" : "Impossibile caricare la lista dei server pubblici. Per favore, visita http://www.syncplay.pl/ con il tuo browser.", # Client arguments - "argument-description" : u'Programma per sincronizzare la riproduzione di media player multipli attraverso la rete.', - "argument-epilog" : u'Se non è specificata alcuna opzione saranno utilizzati i valori _config', - "nogui-argument" : u'non mostrare l\'interfaccia grafica', - "host-argument" : u'indirizzo del server', - "name-argument" : u'username desiderato', - "debug-argument" : u'modalità debug', - "force-gui-prompt-argument" : u'mostra la finestra di configurazione', - "no-store-argument" : u'non salvare i valori in .syncplay', - "room-argument" : u'stanza di default', - "password-argument" : u'password del server', - "player-path-argument" : u'percorso dell\'eseguibile del tuo player', - "file-argument" : u'file da riprodurre', - "args-argument" : u'opzioni del player, se hai bisogno di utilizzare opzioni che iniziano con - anteponi un singolo \'--\'', - "clear-gui-data-argument" : u'ripristina il percorso e i dati impostati tramite interfaccia grafica e salvati come QSettings', - "language-argument" : u'lingua per i messaggi di Syncplay (de/en/ru/it)', + "argument-description" : 'Programma per sincronizzare la riproduzione di media player multipli attraverso la rete.', + "argument-epilog" : 'Se non è specificata alcuna opzione saranno utilizzati i valori _config', + "nogui-argument" : 'non mostrare l\'interfaccia grafica', + "host-argument" : 'indirizzo del server', + "name-argument" : 'username desiderato', + "debug-argument" : 'modalità debug', + "force-gui-prompt-argument" : 'mostra la finestra di configurazione', + "no-store-argument" : 'non salvare i valori in .syncplay', + "room-argument" : 'stanza di default', + "password-argument" : 'password del server', + "player-path-argument" : 'percorso dell\'eseguibile del tuo player', + "file-argument" : 'file da riprodurre', + "args-argument" : 'opzioni del player, se hai bisogno di utilizzare opzioni che iniziano con - anteponi un singolo \'--\'', + "clear-gui-data-argument" : 'ripristina il percorso e i dati impostati tramite interfaccia grafica e salvati come QSettings', + "language-argument" : 'lingua per i messaggi di Syncplay (de/en/ru/it)', - "version-argument" : u'mostra la tua versione', - "version-message" : u"Stai usando la versione di Syncplay {} ({})", + "version-argument" : 'mostra la tua versione', + "version-message" : "Stai usando la versione di Syncplay {} ({})", # Client labels - "config-window-title" : u"Configurazione di Syncplay", + "config-window-title" : "Configurazione di Syncplay", - "connection-group-title" : u"Impostazioni di connessione", - "host-label" : u"Indirizzo del server: ", - "name-label" : u"Username (opzionale):", - "password-label" : u"Password del server (se necessaria):", - "room-label" : u"Stanza di default: ", + "connection-group-title" : "Impostazioni di connessione", + "host-label" : "Indirizzo del server: ", + "name-label" : "Username (opzionale):", + "password-label" : "Password del server (se necessaria):", + "room-label" : "Stanza di default: ", - "media-setting-title" : u"Impostazioni del media player", - "executable-path-label" : u"Percorso del media player:", - "media-path-label" : u"Percorso del video (opzionale):", - "player-arguments-label" : u"Opzioni del player (se necessarie):", - "browse-label" : u"Sfoglia", - "update-server-list-label" : u"Aggiorna lista", + "media-setting-title" : "Impostazioni del media player", + "executable-path-label" : "Percorso del media player:", + "media-path-label" : "Percorso del video (opzionale):", + "player-arguments-label" : "Opzioni del player (se necessarie):", + "browse-label" : "Sfoglia", + "update-server-list-label" : "Aggiorna lista", - "more-title" : u"Mostra altre impostazioni", - "never-rewind-value" : u"Mai", - "seconds-suffix" : u" sec", - "privacy-sendraw-option" : u"Invio semplice", - "privacy-sendhashed-option" : u"Invio cifrato", - "privacy-dontsend-option" : u"Non inviare", - "filename-privacy-label" : u"Nome del file:", - "filesize-privacy-label" : u"Dimensione del file:", - "checkforupdatesautomatically-label" : u"Controlla automaticamente gli aggiornamenti di Syncplay", - "slowondesync-label" : u"Rallenta in caso di sfasamento minimo (non supportato su MPC-HC/BE)", - "rewindondesync-label" : u"Riavvolgi in caso di grande sfasamento (consigliato)", - "fastforwardondesync-label" : u"Avanzamento rapido in caso di ritardo (consigliato)", - "dontslowdownwithme-label" : u"Non rallentare o riavvolgere gli altri utenti (sperimentale)", - "pausing-title" : u"Pausa", - "pauseonleave-label" : u"Metti in pausa quando gli altri utenti lasciano la stanza (es. disconnessione)", - "readiness-title" : u"Stato iniziale di 'pronto'", - "readyatstart-label" : u"Imposta sempre il mio stato come \"pronto\" a guardare", - "forceguiprompt-label" : u"Non mostrare la finestra di configurazione di Syncplay a ogni avvio", # (Inverted) - "showosd-label" : u"Abilita i messaggi OSD", + "more-title" : "Mostra altre impostazioni", + "never-rewind-value" : "Mai", + "seconds-suffix" : " sec", + "privacy-sendraw-option" : "Invio semplice", + "privacy-sendhashed-option" : "Invio cifrato", + "privacy-dontsend-option" : "Non inviare", + "filename-privacy-label" : "Nome del file:", + "filesize-privacy-label" : "Dimensione del file:", + "checkforupdatesautomatically-label" : "Controlla automaticamente gli aggiornamenti di Syncplay", + "slowondesync-label" : "Rallenta in caso di sfasamento minimo (non supportato su MPC-HC/BE)", + "rewindondesync-label" : "Riavvolgi in caso di grande sfasamento (consigliato)", + "fastforwardondesync-label" : "Avanzamento rapido in caso di ritardo (consigliato)", + "dontslowdownwithme-label" : "Non rallentare o riavvolgere gli altri utenti (sperimentale)", + "pausing-title" : "Pausa", + "pauseonleave-label" : "Metti in pausa quando gli altri utenti lasciano la stanza (es. disconnessione)", + "readiness-title" : "Stato iniziale di 'pronto'", + "readyatstart-label" : "Imposta sempre il mio stato come \"pronto\" a guardare", + "forceguiprompt-label" : "Non mostrare la finestra di configurazione di Syncplay a ogni avvio", # (Inverted) + "showosd-label" : "Abilita i messaggi OSD", - "showosdwarnings-label" : u"Mostra gli avvisi (es. file differenti, utenti non pronti)", - "showsameroomosd-label" : u"Mostra gli eventi della tua stanza", - "shownoncontrollerosd-label" : u"Mostra gli eventi dei non gestori nelle stanze gestite", - "showdifferentroomosd-label" : u"Mostra gli eventi di altre stanze", - "showslowdownosd-label" : u"Mostra le notifiche di rallentamento / riavvolgimento", - "language-label" : u"Lingua:", - "automatic-language" : u"Predefinita ({})", # Default language - "showdurationnotification-label" : u"Avvisa in caso di mancata corrispondenza della durata del file", - "basics-label" : u"Generali", - "readiness-label" : u"Play/Pausa", - "misc-label" : u"Varie", - "core-behaviour-title" : u"Comportamento principale della stanza", - "syncplay-internals-title" : u"Funzionamento di Syncplay", - "syncplay-mediasearchdirectories-title" : u"Cartelle contenenti i file multimediali", - "syncplay-mediasearchdirectories-label" : u"Cartelle contenenti i file multimediali (un solo percorso per riga)", - "sync-label" : u"Sincronia", # don't have better options as the label won't fit in the panel. - "sync-otherslagging-title" : u"Se gli altri partecipanti non sono sincronizzati...", - "sync-youlaggging-title" : u"Se tu sei non sei sincronizzato...", - "messages-label" : u"Messaggi", - "messages-osd-title" : u"Impostazioni On-Screen Display", - "messages-other-title" : u"Altre impostazioni dello schermo", - "chat-label" : u"Chat", - "privacy-label" : u"Privacy", # Currently unused, but will be brought back if more space is needed in Misc tab - "privacy-title" : u"Impostazioni privacy", - "unpause-title" : u"Premendo play, imposta il tuo stato su \"pronto\" e:", - "unpause-ifalreadyready-option" : u"Riprendi la riproduzione se eri già pronto", - "unpause-ifothersready-option" : u"Riprendi la riproduzione se eri già pronto o se gli altri partecipanti sono pronti (default)", - "unpause-ifminusersready-option" : u"Riprendi la riproduzione se eri già pronto o se un numero minimo di partecipanti è pronto", - "unpause-always" : u"Riprendi sempre la riproduzione", - "syncplay-trusteddomains-title": u"Domini fidati (per streaming e i contenuti in rete)", + "showosdwarnings-label" : "Mostra gli avvisi (es. file differenti, utenti non pronti)", + "showsameroomosd-label" : "Mostra gli eventi della tua stanza", + "shownoncontrollerosd-label" : "Mostra gli eventi dei non gestori nelle stanze gestite", + "showdifferentroomosd-label" : "Mostra gli eventi di altre stanze", + "showslowdownosd-label" : "Mostra le notifiche di rallentamento / riavvolgimento", + "language-label" : "Lingua:", + "automatic-language" : "Predefinita ({})", # Default language + "showdurationnotification-label" : "Avvisa in caso di mancata corrispondenza della durata del file", + "basics-label" : "Generali", + "readiness-label" : "Play/Pausa", + "misc-label" : "Varie", + "core-behaviour-title" : "Comportamento principale della stanza", + "syncplay-internals-title" : "Funzionamento di Syncplay", + "syncplay-mediasearchdirectories-title" : "Cartelle contenenti i file multimediali", + "syncplay-mediasearchdirectories-label" : "Cartelle contenenti i file multimediali (un solo percorso per riga)", + "sync-label" : "Sincronia", # don't have better options as the label won't fit in the panel. + "sync-otherslagging-title" : "Se gli altri partecipanti non sono sincronizzati...", + "sync-youlaggging-title" : "Se tu sei non sei sincronizzato...", + "messages-label" : "Messaggi", + "messages-osd-title" : "Impostazioni On-Screen Display", + "messages-other-title" : "Altre impostazioni dello schermo", + "chat-label" : "Chat", + "privacy-label" : "Privacy", # Currently unused, but will be brought back if more space is needed in Misc tab + "privacy-title" : "Impostazioni privacy", + "unpause-title" : "Premendo play, imposta il tuo stato su \"pronto\" e:", + "unpause-ifalreadyready-option" : "Riprendi la riproduzione se eri già pronto", + "unpause-ifothersready-option" : "Riprendi la riproduzione se eri già pronto o se gli altri partecipanti sono pronti (default)", + "unpause-ifminusersready-option" : "Riprendi la riproduzione se eri già pronto o se un numero minimo di partecipanti è pronto", + "unpause-always" : "Riprendi sempre la riproduzione", + "syncplay-trusteddomains-title": "Domini fidati (per streaming e i contenuti in rete)", - "chat-title" : u"Inserimento messaggi di chat", - "chatinputenabled-label" : u"Abilita la chat su mpv", - "chatdirectinput-label" : u"Abilita la chat istantanea (evita di dover premere Invio per chattare)", - "chatinputfont-label" : u"Font dell'input della chat", - "chatfont-label" : u"Imposta font", - "chatcolour-label" : u"Imposta colore", - "chatinputposition-label" : u"Posizione dell'area di inserimento testo in mpv", - "chat-top-option" : u"In alto", - "chat-middle-option" : u"Al centro", - "chat-bottom-option" : u"In basso", - "chatoutputheader-label" : u"Output messaggi di chat", - "chatoutputfont-label": u"Font dell'output della chat", - "chatoutputenabled-label": u"Abilita l'output della chat nel media player (al momento solo mpv è supportato)", - "chatoutputposition-label": u"Modalità di output", - "chat-chatroom-option": u"Stile chatroom", - "chat-scrolling-option": u"A scorrimento", + "chat-title" : "Inserimento messaggi di chat", + "chatinputenabled-label" : "Abilita la chat su mpv", + "chatdirectinput-label" : "Abilita la chat istantanea (evita di dover premere Invio per chattare)", + "chatinputfont-label" : "Font dell'input della chat", + "chatfont-label" : "Imposta font", + "chatcolour-label" : "Imposta colore", + "chatinputposition-label" : "Posizione dell'area di inserimento testo in mpv", + "chat-top-option" : "In alto", + "chat-middle-option" : "Al centro", + "chat-bottom-option" : "In basso", + "chatoutputheader-label" : "Output messaggi di chat", + "chatoutputfont-label": "Font dell'output della chat", + "chatoutputenabled-label": "Abilita l'output della chat nel media player (al momento solo mpv è supportato)", + "chatoutputposition-label": "Modalità di output", + "chat-chatroom-option": "Stile chatroom", + "chat-scrolling-option": "A scorrimento", - "mpv-key-tab-hint": u"[TAB] per attivare le scorciatoie da tastiera e disattivare la chat.", - "mpv-key-hint": u"[Invio] per inviare un messaggio. [Esc] per uscire dalla modalità chat.", - "alphakey-mode-warning-first-line": u"Puoi utilizzare temporaneamente i vecchi comandi di mpv con i tasti a-z.", - "alphakey-mode-warning-second-line": u"Premi [TAB] per ritornare alla modalità chat di Syncplay.", + "mpv-key-tab-hint": "[TAB] per attivare le scorciatoie da tastiera e disattivare la chat.", + "mpv-key-hint": "[Invio] per inviare un messaggio. [Esc] per uscire dalla modalità chat.", + "alphakey-mode-warning-first-line": "Puoi utilizzare temporaneamente i vecchi comandi di mpv con i tasti a-z.", + "alphakey-mode-warning-second-line": "Premi [TAB] per ritornare alla modalità chat di Syncplay.", - "help-label" : u"Aiuto", - "reset-label" : u"Elimina configurazione", - "run-label" : u"Avvia Syncplay", - "storeandrun-label" : u"Salva la configurazione e avvia Syncplay", + "help-label" : "Aiuto", + "reset-label" : "Elimina configurazione", + "run-label" : "Avvia Syncplay", + "storeandrun-label" : "Salva la configurazione e avvia Syncplay", - "contact-label" : u"Sentiti libero di inviare un'e-mail a dev@syncplay.pl, chattare tramite il canale IRC #Syncplay su irc.freenode.net, segnalare un problema su GitHub, lasciare un like sulla nostra pagina Facebook, seguirci su Twitter, o visitare https://syncplay.pl/. NOTA: i messaggi di chat non sono cifrati, quindi non usare Syncplay per inviare dati sensibili.", + "contact-label" : "Sentiti libero di inviare un'e-mail a dev@syncplay.pl, chattare tramite il canale IRC #Syncplay su irc.freenode.net, segnalare un problema su GitHub, lasciare un like sulla nostra pagina Facebook, seguirci su Twitter, o visitare https://syncplay.pl/. NOTA: i messaggi di chat non sono cifrati, quindi non usare Syncplay per inviare dati sensibili.", - "joinroom-label" : u"Entra nella stanza", - "joinroom-menu-label" : u"Entra nella stanza {}", - "seektime-menu-label" : u"Vai a...", - "undoseek-menu-label" : u"Annulla vai a...", - "play-menu-label" : u"Play", - "pause-menu-label" : u"Pausa", - "playbackbuttons-menu-label" : u"Mostra i controlli della riproduzione", - "autoplay-menu-label" : u"Mostra il tasto di riproduzione automatica", - "autoplay-guipushbuttonlabel" : u"Riproduci quando tutti sono pronti", - "autoplay-minimum-label" : u"Minimo utenti pronti:", + "joinroom-label" : "Entra nella stanza", + "joinroom-menu-label" : "Entra nella stanza {}", + "seektime-menu-label" : "Vai a...", + "undoseek-menu-label" : "Annulla vai a...", + "play-menu-label" : "Play", + "pause-menu-label" : "Pausa", + "playbackbuttons-menu-label" : "Mostra i controlli della riproduzione", + "autoplay-menu-label" : "Mostra il tasto di riproduzione automatica", + "autoplay-guipushbuttonlabel" : "Riproduci quando tutti sono pronti", + "autoplay-minimum-label" : "Minimo utenti pronti:", - "sendmessage-label" : u"Invia", + "sendmessage-label" : "Invia", - "ready-guipushbuttonlabel" : u"Sono pronto a guardare!", + "ready-guipushbuttonlabel" : "Sono pronto a guardare!", - "roomuser-heading-label" : u"Stanza / Utente", - "size-heading-label" : u"Dimensione", - "duration-heading-label" : u"Durata", - "filename-heading-label" : u"Nome del file", - "notifications-heading-label" : u"Notifiche", - "userlist-heading-label" : u"Lista degli utenti nella stanza", + "roomuser-heading-label" : "Stanza / Utente", + "size-heading-label" : "Dimensione", + "duration-heading-label" : "Durata", + "filename-heading-label" : "Nome del file", + "notifications-heading-label" : "Notifiche", + "userlist-heading-label" : "Lista degli utenti nella stanza", - "browseformedia-label" : u"Seleziona i file multimediali", + "browseformedia-label" : "Seleziona i file multimediali", - "file-menu-label" : u"&File", # & precedes shortcut key - "openmedia-menu-label" : u"&Apri file multimediali", - "openstreamurl-menu-label" : u"Apri indirizzo di &rete", - "setmediadirectories-menu-label" : u"Imposta &cartelle multimediali", - "exit-menu-label" : u"&Esci", - "advanced-menu-label" : u"&Avanzate", - "window-menu-label" : u"&Finestra", - "setoffset-menu-label" : u"Imposta &offset", - "createcontrolledroom-menu-label" : u"&Crea stanza gestita", - "identifyascontroller-menu-label" : u"&Identificati come operatore della stanza", - "settrusteddomains-menu-label" : u"Imposta &domini fidati", - "addtrusteddomain-menu-label" : u"Aggiungi {} come dominio fidato", # Domain + "file-menu-label" : "&File", # & precedes shortcut key + "openmedia-menu-label" : "&Apri file multimediali", + "openstreamurl-menu-label" : "Apri indirizzo di &rete", + "setmediadirectories-menu-label" : "Imposta &cartelle multimediali", + "exit-menu-label" : "&Esci", + "advanced-menu-label" : "&Avanzate", + "window-menu-label" : "&Finestra", + "setoffset-menu-label" : "Imposta &offset", + "createcontrolledroom-menu-label" : "&Crea stanza gestita", + "identifyascontroller-menu-label" : "&Identificati come operatore della stanza", + "settrusteddomains-menu-label" : "Imposta &domini fidati", + "addtrusteddomain-menu-label" : "Aggiungi {} come dominio fidato", # Domain - "playback-menu-label" : u"&Riproduzione", + "playback-menu-label" : "&Riproduzione", - "help-menu-label" : u"&Aiuto", - "userguide-menu-label" : u"Apri guida &utente", - "update-menu-label" : u"Controlla la presenza di &aggiornamenti", + "help-menu-label" : "&Aiuto", + "userguide-menu-label" : "Apri guida &utente", + "update-menu-label" : "Controlla la presenza di &aggiornamenti", #About dialog - "about-menu-label": u"&Informazioni su Syncplay", - "about-dialog-title": u"Informazioni su Syncplay", - "about-dialog-release": u"Versione {} release {} con {}", - "about-dialog-license-text" : u"Rilasciato sotto Apache License, Version 2.0", - "about-dialog-license-button": u"Licenza", - "about-dialog-dependencies": u"Dipendenze", + "about-menu-label": "&Informazioni su Syncplay", + "about-dialog-title": "Informazioni su Syncplay", + "about-dialog-release": "Versione {} release {} con {}", + "about-dialog-license-text" : "Rilasciato sotto Apache License, Version 2.0", + "about-dialog-license-button": "Licenza", + "about-dialog-dependencies": "Dipendenze", - "setoffset-msgbox-label" : u"Imposta offset", - "offsetinfo-msgbox-label" : u"Offset (vedi https://syncplay.pl/guide/ per istruzioni):", + "setoffset-msgbox-label" : "Imposta offset", + "offsetinfo-msgbox-label" : "Offset (vedi https://syncplay.pl/guide/ per istruzioni):", - "promptforstreamurl-msgbox-label" : u"Apri URL", - "promptforstreamurlinfo-msgbox-label" : u"Indirizzo di rete", + "promptforstreamurl-msgbox-label" : "Apri URL", + "promptforstreamurlinfo-msgbox-label" : "Indirizzo di rete", - "addfolder-label" : u"Aggiungi cartella", + "addfolder-label" : "Aggiungi cartella", - "adduris-msgbox-label" : u"Aggiungi gli indirizzi alla playlist (uno per riga)", - "editplaylist-msgbox-label" : u"Imposta playlist (una per riga)", - "trusteddomains-msgbox-label" : u"Domini a cui è lecito passare automaticamente (uno per riga)", + "adduris-msgbox-label" : "Aggiungi gli indirizzi alla playlist (uno per riga)", + "editplaylist-msgbox-label" : "Imposta playlist (una per riga)", + "trusteddomains-msgbox-label" : "Domini a cui è lecito passare automaticamente (uno per riga)", - "createcontrolledroom-msgbox-label" : u"Crea stanza gestita", - "controlledroominfo-msgbox-label" : u"Inserisci il nome della stanza gestita\r\n(vedi https://syncplay.pl/guide/ per istruzioni):", + "createcontrolledroom-msgbox-label" : "Crea stanza gestita", + "controlledroominfo-msgbox-label" : "Inserisci il nome della stanza gestita\r\n(vedi https://syncplay.pl/guide/ per istruzioni):", - "identifyascontroller-msgbox-label" : u"Identificati come operatore della stanza", - "identifyinfo-msgbox-label" : u"Inserisci la password dell'operatore per questa stanza\r\n(vedi https://syncplay.pl/guide/ per istruzioni):", + "identifyascontroller-msgbox-label" : "Identificati come operatore della stanza", + "identifyinfo-msgbox-label" : "Inserisci la password dell'operatore per questa stanza\r\n(vedi https://syncplay.pl/guide/ per istruzioni):", - "public-server-msgbox-label" : u"Seleziona il server pubblico per questa sessione", + "public-server-msgbox-label" : "Seleziona il server pubblico per questa sessione", - "megabyte-suffix" : u" MB", + "megabyte-suffix" : " MB", # Tooltips - "host-tooltip" : u"Hostname o indirizzo IP a cui collegarsi e, se necessario, includere la porta (es. syncplay.pl:8999). La sincronizzazione avviene solo con gli utenti collegati allo stesso server/porta.", - "name-tooltip" : u"Il nome utente con cui sarai riconosciuto. Nessuna registrazione necessaria, cosi potrai sempre cambiarlo. Se lasciato vuoto, viene scelto un nome casuale.", - "password-tooltip" : u"La password è necessaria solo in caso di connessione a server privati.", - "room-tooltip" : u"La stanza in cui entrare dopo la connessione. Può assumere qualsiasi nome, ma ricorda che sarai sincronizzato solo con gli utenti nella stessa stanza.", + "host-tooltip" : "Hostname o indirizzo IP a cui collegarsi e, se necessario, includere la porta (es. syncplay.pl:8999). La sincronizzazione avviene solo con gli utenti collegati allo stesso server/porta.", + "name-tooltip" : "Il nome utente con cui sarai riconosciuto. Nessuna registrazione necessaria, cosi potrai sempre cambiarlo. Se lasciato vuoto, viene scelto un nome casuale.", + "password-tooltip" : "La password è necessaria solo in caso di connessione a server privati.", + "room-tooltip" : "La stanza in cui entrare dopo la connessione. Può assumere qualsiasi nome, ma ricorda che sarai sincronizzato solo con gli utenti nella stessa stanza.", - "executable-path-tooltip" : u"Percorso del media player desiderato (scegliere tra mpv, VLC, MPC-HC/BE or mplayer2).", - "media-path-tooltip" : u"Percorso del video o stream da aprire. Necessario per mplayer2.", - "player-arguments-tooltip" : u"Argomenti da linea di comando aggiuntivi da passare al media player scelto.", - "mediasearcdirectories-arguments-tooltip" : u"Cartelle dove Syncplay cercherà i file multimediali, es. quando usi la funzione click to switch. Syncplay cercherà anche nelle sottocartelle.", + "executable-path-tooltip" : "Percorso del media player desiderato (scegliere tra mpv, VLC, MPC-HC/BE or mplayer2).", + "media-path-tooltip" : "Percorso del video o stream da aprire. Necessario per mplayer2.", + "player-arguments-tooltip" : "Argomenti da linea di comando aggiuntivi da passare al media player scelto.", + "mediasearcdirectories-arguments-tooltip" : "Cartelle dove Syncplay cercherà i file multimediali, es. quando usi la funzione click to switch. Syncplay cercherà anche nelle sottocartelle.", - "more-tooltip" : u"Mostra le impostazioni usate meno frequentemente.", - "filename-privacy-tooltip" : u"Modalità di invio al server del nome del file attualmente in riproduzione.", - "filesize-privacy-tooltip" : u"Modalità di invio al server della dimensione del file attualmente in riproduzione.", - "privacy-sendraw-tooltip" : u"Invia questa informazione in chiaro. Questa è l'impostazione predefinita per la maggior parte delle funzionalità.", - "privacy-sendhashed-tooltip" : u"Invia una versione cifrata dell'informazione, rendendola meno visibile agli altri client.", - "privacy-dontsend-tooltip" : u"Non inviare questa informazione al server. Questo garantisce massima privacy.", - "checkforupdatesautomatically-tooltip" : u"Controlla regolarmente la presenza di nuove versioni di Syncplay.", - "slowondesync-tooltip" : u"Riduce temporaneamente la velocità di riproduzione quando c'è bisogno di sincronizzarti con gli altri utenti. Non supportato su MPC-HC/BE.", - "dontslowdownwithme-tooltip" : u"Gli altri utenti non vengono rallentati se non sei sincronizzato. Utile per i gestori della stanza.", - "pauseonleave-tooltip" : u"Mette in pausa la riproduzione se vieni disconnesso o se qualcuno lascia la stanza.", - "readyatstart-tooltip" : u"Imposta il tuo stato su \"pronto\" all'avvio (in caso contrario, sarai su \"non pronto\" finché non cambierai il tuo stato)", - "forceguiprompt-tooltip" : u"La finestra di configurazione non viene mostrata quando apri Syncplay.", - "nostore-tooltip" : u"Avvia Syncplay con la configurazione scelta, ma non salva le impostazioni.", - "rewindondesync-tooltip" : u"Torna indietro quando necessario per ristabilire la sincronizzazione. Disabilitare quest'opzione può causare gravi problemi di sincronizzazione!", - "fastforwardondesync-tooltip" : u"Avanza rapidamente quando non sei sincronizzato col gestore della stanza (usa una posizione fittizia se 'Non rallentare o riavvolgere gli altri utenti' è abilitato).", - "showosd-tooltip" : u"Invia i messaggi di Syncplay al media player tramite OSD.", - "showosdwarnings-tooltip" : u"Mostra gli avvisi in caso di riproduzione di un file differente, se sei l'unico utente nella stanza, se ci sono utenti non pronti, ecc.", - "showsameroomosd-tooltip" : u"Mostra le notifiche OSD per gli eventi relativi alla stanza in cui si trova l'utente.", - "shownoncontrollerosd-tooltip" : u"Mostra le notifiche OSD per gli eventi relativi ai non operatori presenti nelle stanze gestite.", - "showdifferentroomosd-tooltip" : u"Mostra le notifiche OSD per gli eventi relativi alle stanze in cui l'utente non si trova.", - "showslowdownosd-tooltip" : u"Mostra le notifiche di rallentamento / riavvolgimento in caso di differenza temporale.", - "showdurationnotification-tooltip" : u"Utile quando manca un segmento di un file con più parti. Può causare dei falsi positivi.", - "language-tooltip" : u"Lingua da utilizzare in Syncplay.", - "unpause-always-tooltip" : u"Se riprendi la riproduzione, il tuo stato cambia in \"pronto\" e la riproduzione viene avviata, piuttosto che impostarti solo su pronto.", - "unpause-ifalreadyready-tooltip" : u"Se riprendi la riproduzione quando non sei \"pronto\", verrai impostato su pronto - ripeti il comando ancora una volta per avviare la riproduzione.", - "unpause-ifothersready-tooltip" : u"Se riprendi la riproduzione quando non sei \"pronto\" la riproduzione verrà avviata solo se gli altri sono pronti.", - "unpause-ifminusersready-tooltip" : u"Se riprendi la riproduzione quando non sei \"pronto\", la riproduzione verrà avviata solo se un numero minimo di utenti è \"pronto\".", - "trusteddomains-arguments-tooltip" : u"Domini verso cui è possibile collegarsi automaticamente quando le playlist condivise sono abilitate.", + "more-tooltip" : "Mostra le impostazioni usate meno frequentemente.", + "filename-privacy-tooltip" : "Modalità di invio al server del nome del file attualmente in riproduzione.", + "filesize-privacy-tooltip" : "Modalità di invio al server della dimensione del file attualmente in riproduzione.", + "privacy-sendraw-tooltip" : "Invia questa informazione in chiaro. Questa è l'impostazione predefinita per la maggior parte delle funzionalità.", + "privacy-sendhashed-tooltip" : "Invia una versione cifrata dell'informazione, rendendola meno visibile agli altri client.", + "privacy-dontsend-tooltip" : "Non inviare questa informazione al server. Questo garantisce massima privacy.", + "checkforupdatesautomatically-tooltip" : "Controlla regolarmente la presenza di nuove versioni di Syncplay.", + "slowondesync-tooltip" : "Riduce temporaneamente la velocità di riproduzione quando c'è bisogno di sincronizzarti con gli altri utenti. Non supportato su MPC-HC/BE.", + "dontslowdownwithme-tooltip" : "Gli altri utenti non vengono rallentati se non sei sincronizzato. Utile per i gestori della stanza.", + "pauseonleave-tooltip" : "Mette in pausa la riproduzione se vieni disconnesso o se qualcuno lascia la stanza.", + "readyatstart-tooltip" : "Imposta il tuo stato su \"pronto\" all'avvio (in caso contrario, sarai su \"non pronto\" finché non cambierai il tuo stato)", + "forceguiprompt-tooltip" : "La finestra di configurazione non viene mostrata quando apri Syncplay.", + "nostore-tooltip" : "Avvia Syncplay con la configurazione scelta, ma non salva le impostazioni.", + "rewindondesync-tooltip" : "Torna indietro quando necessario per ristabilire la sincronizzazione. Disabilitare quest'opzione può causare gravi problemi di sincronizzazione!", + "fastforwardondesync-tooltip" : "Avanza rapidamente quando non sei sincronizzato col gestore della stanza (usa una posizione fittizia se 'Non rallentare o riavvolgere gli altri utenti' è abilitato).", + "showosd-tooltip" : "Invia i messaggi di Syncplay al media player tramite OSD.", + "showosdwarnings-tooltip" : "Mostra gli avvisi in caso di riproduzione di un file differente, se sei l'unico utente nella stanza, se ci sono utenti non pronti, ecc.", + "showsameroomosd-tooltip" : "Mostra le notifiche OSD per gli eventi relativi alla stanza in cui si trova l'utente.", + "shownoncontrollerosd-tooltip" : "Mostra le notifiche OSD per gli eventi relativi ai non operatori presenti nelle stanze gestite.", + "showdifferentroomosd-tooltip" : "Mostra le notifiche OSD per gli eventi relativi alle stanze in cui l'utente non si trova.", + "showslowdownosd-tooltip" : "Mostra le notifiche di rallentamento / riavvolgimento in caso di differenza temporale.", + "showdurationnotification-tooltip" : "Utile quando manca un segmento di un file con più parti. Può causare dei falsi positivi.", + "language-tooltip" : "Lingua da utilizzare in Syncplay.", + "unpause-always-tooltip" : "Se riprendi la riproduzione, il tuo stato cambia in \"pronto\" e la riproduzione viene avviata, piuttosto che impostarti solo su pronto.", + "unpause-ifalreadyready-tooltip" : "Se riprendi la riproduzione quando non sei \"pronto\", verrai impostato su pronto - ripeti il comando ancora una volta per avviare la riproduzione.", + "unpause-ifothersready-tooltip" : "Se riprendi la riproduzione quando non sei \"pronto\" la riproduzione verrà avviata solo se gli altri sono pronti.", + "unpause-ifminusersready-tooltip" : "Se riprendi la riproduzione quando non sei \"pronto\", la riproduzione verrà avviata solo se un numero minimo di utenti è \"pronto\".", + "trusteddomains-arguments-tooltip" : "Domini verso cui è possibile collegarsi automaticamente quando le playlist condivise sono abilitate.", - "chatinputenabled-tooltip" : u"Abilita l'input della chat in mpv (premi Invio per chattare, per inviare ed Esc per cancellare)", - "chatdirectinput-tooltip" : u"Evita di dover premere Invio per aprire l'input della chat in mpv. Premi TAB in mpv per disabilitare temporaneamente questa funzione.", - "font-label-tooltip" : u"Font usato nell'input della chat in mpv. Non influenza cosa vedono gli altri, vale solo per te.", - "set-input-font-tooltip" : u"Font usato nell'input della chat in mpv. Non influenza cosa vedono gli altri, vale solo per te.", - "set-input-colour-tooltip" : u"Colore del font usato nell'input della chat in mpv. Non influenza cosa vedono gli altri, vale solo per te.", - "chatinputposition-tooltip" : u"Posizione dell'input della chat in mpv quando premi Invio.", - "chatinputposition-top-tooltip" : u"Posiziona l'input della chat in cima alla finestra di mpv.", - "chatinputposition-middle-tooltip" : u"Posizione l'input della chat al centro della finestra di mpv.", - "chatinputposition-bottom-tooltip" : u"Posiziona l'input della chat in basso alla finestra di mpv.", - "chatoutputenabled-tooltip": u"Mostra i messaggi di chat nell'OSD (se supportato dal media player).", - "font-output-label-tooltip": u"Font dell'output della chat.", - "set-output-font-tooltip": u"Font usato per mostrare i messaggi di chat.", - "chatoutputmode-tooltip": u"Come sono mostrati i messaggi di chat.", - "chatoutputmode-chatroom-tooltip": u"Mostra i nuovi messaggi di chat al di sotto di quelli precedenti.", - "chatoutputmode-scrolling-tooltip": u"Scorri il testo della chat da destra a sinistra.", + "chatinputenabled-tooltip" : "Abilita l'input della chat in mpv (premi Invio per chattare, per inviare ed Esc per cancellare)", + "chatdirectinput-tooltip" : "Evita di dover premere Invio per aprire l'input della chat in mpv. Premi TAB in mpv per disabilitare temporaneamente questa funzione.", + "font-label-tooltip" : "Font usato nell'input della chat in mpv. Non influenza cosa vedono gli altri, vale solo per te.", + "set-input-font-tooltip" : "Font usato nell'input della chat in mpv. Non influenza cosa vedono gli altri, vale solo per te.", + "set-input-colour-tooltip" : "Colore del font usato nell'input della chat in mpv. Non influenza cosa vedono gli altri, vale solo per te.", + "chatinputposition-tooltip" : "Posizione dell'input della chat in mpv quando premi Invio.", + "chatinputposition-top-tooltip" : "Posiziona l'input della chat in cima alla finestra di mpv.", + "chatinputposition-middle-tooltip" : "Posizione l'input della chat al centro della finestra di mpv.", + "chatinputposition-bottom-tooltip" : "Posiziona l'input della chat in basso alla finestra di mpv.", + "chatoutputenabled-tooltip": "Mostra i messaggi di chat nell'OSD (se supportato dal media player).", + "font-output-label-tooltip": "Font dell'output della chat.", + "set-output-font-tooltip": "Font usato per mostrare i messaggi di chat.", + "chatoutputmode-tooltip": "Come sono mostrati i messaggi di chat.", + "chatoutputmode-chatroom-tooltip": "Mostra i nuovi messaggi di chat al di sotto di quelli precedenti.", + "chatoutputmode-scrolling-tooltip": "Scorri il testo della chat da destra a sinistra.", - "help-tooltip" : u"Apri la guida utente su syncplay.pl.", - "reset-tooltip" : u"Ripristina le impostazioni iniziali di Syncplay.", - "update-server-list-tooltip" : u"Scarica la lista dei server pubblici da syncplay.pl.", + "help-tooltip" : "Apri la guida utente su syncplay.pl.", + "reset-tooltip" : "Ripristina le impostazioni iniziali di Syncplay.", + "update-server-list-tooltip" : "Scarica la lista dei server pubblici da syncplay.pl.", - "joinroom-tooltip" : u"Lascia la stanza attuale e entra in quella specificata.", - "seektime-msgbox-label" : u"Salta all'istante di tempo specificato (in secondi / min:sec). Usa +/- per una ricerca relativa.", - "ready-tooltip" : u"Indica quando sei pronto a guardare.", - "autoplay-tooltip" : u"Avvia la riproduzione automatica quando il numero minimo di utenti è pronto.", - "switch-to-file-tooltip" : u"Doppio click per passare a {}", # Filename - "sendmessage-tooltip" : u"Invia il messaggio alla stanza", + "joinroom-tooltip" : "Lascia la stanza attuale e entra in quella specificata.", + "seektime-msgbox-label" : "Salta all'istante di tempo specificato (in secondi / min:sec). Usa +/- per una ricerca relativa.", + "ready-tooltip" : "Indica quando sei pronto a guardare.", + "autoplay-tooltip" : "Avvia la riproduzione automatica quando il numero minimo di utenti è pronto.", + "switch-to-file-tooltip" : "Doppio click per passare a {}", # Filename + "sendmessage-tooltip" : "Invia il messaggio alla stanza", # In-userlist notes (GUI) - "differentsize-note" : u"Dimensione file diversa!", - "differentsizeandduration-note" : u"Durata e dimensione file diversi!", - "differentduration-note" : u"Durata diversa!", - "nofile-note" : u"(Nessun file in riproduzione)", + "differentsize-note" : "Dimensione file diversa!", + "differentsizeandduration-note" : "Durata e dimensione file diversi!", + "differentduration-note" : "Durata diversa!", + "nofile-note" : "(Nessun file in riproduzione)", # Server messages to client - "new-syncplay-available-motd-message" : u" Stai usando Syncplay {} ma una nuova versione è disponibile presso https://syncplay.pl ", # ClientVersion + "new-syncplay-available-motd-message" : " Stai usando Syncplay {} ma una nuova versione è disponibile presso https://syncplay.pl ", # ClientVersion # Server notifications - "welcome-server-notification" : u"Benvenuto nel server Syncplay, ver. {0}", # version - "client-connected-room-server-notification" : u"{0}({2}) connesso alla stanza '{1}'", # username, host, room - "client-left-server-notification" : u"{0} ha lasciato il server", # name - "no-salt-notification" : u"NOTA BENE: In futuro, per consentire il corretto funzionamento delle password generate da questo server (per le stanze gestite), aggiungi da linea di comando il seguente argomento prima di avviare il server Syncplay: --salt {}", #Salt + "welcome-server-notification" : "Benvenuto nel server Syncplay, ver. {0}", # version + "client-connected-room-server-notification" : "{0}({2}) connesso alla stanza '{1}'", # username, host, room + "client-left-server-notification" : "{0} ha lasciato il server", # name + "no-salt-notification" : "NOTA BENE: In futuro, per consentire il corretto funzionamento delle password generate da questo server (per le stanze gestite), aggiungi da linea di comando il seguente argomento prima di avviare il server Syncplay: --salt {}", #Salt # Server arguments - "server-argument-description" : u'Programma per sincronizzare la riproduzione di media player multipli attraverso la rete. Modulo server.', - "server-argument-epilog" : u'Se non è specificata alcuna opzione saranno utilizzati i valori _config', - "server-port-argument" : u'Porta TCP del server', - "server-password-argument" : u'password del server', - "server-isolate-room-argument" : u'Mantiene le stanze isolate', - "server-salt-argument" : u"usare stringhe casuali per generare le password delle stanze gestite", - "server-disable-ready-argument" : u"disabilita la funzionalità \"pronto\"", - "server-motd-argument": u"percorso del file da cui verrà letto il messaggio del giorno", - "server-chat-argument" : u"abilita o disabilita la chat", - "server-chat-maxchars-argument" : u"Numero massimo di caratteri in un messaggio di chat (default è {})", # Default number of characters - "server-maxusernamelength-argument": u"Maximum number of charactrs in a username (default is {})", # TODO: Translate - "server-messed-up-motd-unescaped-placeholders": u"Il messaggio del giorno ha dei caratteri non 'escaped'. Tutti i simboli $ devono essere doppi ($$).", - "server-messed-up-motd-too-long": u"Il messaggio del giorno è troppo lungo - numero massimo di caratteri è {}, {} trovati.", + "server-argument-description" : 'Programma per sincronizzare la riproduzione di media player multipli attraverso la rete. Modulo server.', + "server-argument-epilog" : 'Se non è specificata alcuna opzione saranno utilizzati i valori _config', + "server-port-argument" : 'Porta TCP del server', + "server-password-argument" : 'password del server', + "server-isolate-room-argument" : 'Mantiene le stanze isolate', + "server-salt-argument" : "usare stringhe casuali per generare le password delle stanze gestite", + "server-disable-ready-argument" : "disabilita la funzionalità \"pronto\"", + "server-motd-argument": "percorso del file da cui verrà letto il messaggio del giorno", + "server-chat-argument" : "abilita o disabilita la chat", + "server-chat-maxchars-argument" : "Numero massimo di caratteri in un messaggio di chat (default è {})", # Default number of characters + "server-maxusernamelength-argument": "Maximum number of charactrs in a username (default is {})", # TODO: Translate + "server-messed-up-motd-unescaped-placeholders": "Il messaggio del giorno ha dei caratteri non 'escaped'. Tutti i simboli $ devono essere doppi ($$).", + "server-messed-up-motd-too-long": "Il messaggio del giorno è troppo lungo - numero massimo di caratteri è {}, {} trovati.", # Server errors - "unknown-command-server-error" : u"Comando non riconosciuto {}", # message - "not-json-server-error" : u"Non è una stringa in codifica JSON {}", # message - "not-known-server-error" : u"Devi essere autenticato dal server prima di poter inviare questo comando", - "client-drop-server-error" : u"Il client è caduto: {} -- {}", # host, error - "password-required-server-error" : u"È richiesta una password", - "wrong-password-server-error" : u"La password inserita è errata", + "unknown-command-server-error" : "Comando non riconosciuto {}", # message + "not-json-server-error" : "Non è una stringa in codifica JSON {}", # message + "not-known-server-error" : "Devi essere autenticato dal server prima di poter inviare questo comando", + "client-drop-server-error" : "Il client è caduto: {} -- {}", # host, error + "password-required-server-error" : "È richiesta una password", + "wrong-password-server-error" : "La password inserita è errata", "hello-server-error" : "Not enough Hello arguments", #DO NOT TRANSLATE # Playlists - "playlist-selection-changed-notification" : u"{} ha cambiato il file selezionato nella playlist", # Username - "playlist-contents-changed-notification" : u"{} ha aggiornato la playlist", # Username - "cannot-find-file-for-playlist-switch-error" : u"Impossibile trovare il file {} nelle cartelle multimediali per permettere il cambio di file tramite la playlist!", # Filename - "cannot-add-duplicate-error" : u"Impossibile aggiungere una seconda voce per '{}' alla playlist. Non è possibile avere file duplicati.", #Filename - "cannot-add-unsafe-path-error" : u"Impossibile caricare automaticamente {} perché non è presente nei domini fidati. Puoi passare all'inserimento manuale facendo doppio click sull'indirizzo nella playlist, oppure aggiungerlo ai domini fidati tramite File->Avanzate->Imposta domini fidati. Cliccando col tasto destro del mouse su un indirizzo puoi impostare il suo dominio come fidato tramite il menù contestuale.", # Filename - "sharedplaylistenabled-label" : u"Abilita le playlist condivise", - "removefromplaylist-menu-label" : u"Rimuovi dalla playlist", - "shuffleremainingplaylist-menu-label" : u"Mescola i file non ancora riprodotti", - "shuffleentireplaylist-menu-label" : u"Mescola l'intera playlist", - "undoplaylist-menu-label" : u"Annulla l'ultima modifica alla playlist", - "addfilestoplaylist-menu-label" : u"Aggiungi un file alla fine della playlist", - "addurlstoplaylist-menu-label" : u"Aggiungi un indirizzo alla fine della playlist", - "editplaylist-menu-label": u"Modifica la playlist", + "playlist-selection-changed-notification" : "{} ha cambiato il file selezionato nella playlist", # Username + "playlist-contents-changed-notification" : "{} ha aggiornato la playlist", # Username + "cannot-find-file-for-playlist-switch-error" : "Impossibile trovare il file {} nelle cartelle multimediali per permettere il cambio di file tramite la playlist!", # Filename + "cannot-add-duplicate-error" : "Impossibile aggiungere una seconda voce per '{}' alla playlist. Non è possibile avere file duplicati.", #Filename + "cannot-add-unsafe-path-error" : "Impossibile caricare automaticamente {} perché non è presente nei domini fidati. Puoi passare all'inserimento manuale facendo doppio click sull'indirizzo nella playlist, oppure aggiungerlo ai domini fidati tramite File->Avanzate->Imposta domini fidati. Cliccando col tasto destro del mouse su un indirizzo puoi impostare il suo dominio come fidato tramite il menù contestuale.", # Filename + "sharedplaylistenabled-label" : "Abilita le playlist condivise", + "removefromplaylist-menu-label" : "Rimuovi dalla playlist", + "shuffleremainingplaylist-menu-label" : "Mescola i file non ancora riprodotti", + "shuffleentireplaylist-menu-label" : "Mescola l'intera playlist", + "undoplaylist-menu-label" : "Annulla l'ultima modifica alla playlist", + "addfilestoplaylist-menu-label" : "Aggiungi un file alla fine della playlist", + "addurlstoplaylist-menu-label" : "Aggiungi un indirizzo alla fine della playlist", + "editplaylist-menu-label": "Modifica la playlist", - "open-containing-folder": u"Apri la cartella contenente questo file", - "addusersfiletoplaylist-menu-label" : u"Aggiungi il file {} alla playlist", # item owner indicator # TODO needs testing - "addusersstreamstoplaylist-menu-label" : u"Aggiungi l'indirizzo {} alla playlist", # item owner indicator # TODO needs testing - "openusersstream-menu-label" : u"Apri l'indirizzo di {}", # [username]'s - "openusersfile-menu-label" : u"Apri il file di {}", # [username]'s - "item-is-yours-indicator" : u"tuo", # Goes with addusersfiletoplaylist/addusersstreamstoplaylist # TODO needs testing - "item-is-others-indicator" : u"di {}", # username - goes with addusersfiletoplaylist/addusersstreamstoplaylist # TODO needs testing + "open-containing-folder": "Apri la cartella contenente questo file", + "addusersfiletoplaylist-menu-label" : "Aggiungi il file {} alla playlist", # item owner indicator # TODO needs testing + "addusersstreamstoplaylist-menu-label" : "Aggiungi l'indirizzo {} alla playlist", # item owner indicator # TODO needs testing + "openusersstream-menu-label" : "Apri l'indirizzo di {}", # [username]'s + "openusersfile-menu-label" : "Apri il file di {}", # [username]'s + "item-is-yours-indicator" : "tuo", # Goes with addusersfiletoplaylist/addusersstreamstoplaylist # TODO needs testing + "item-is-others-indicator" : "di {}", # username - goes with addusersfiletoplaylist/addusersstreamstoplaylist # TODO needs testing - "playlist-instruction-item-message" : u"Trascina qui i file per aggiungerli alla playlist condivisa.", - "sharedplaylistenabled-tooltip" : u"Gli operatori della stanza possono aggiungere i file a una playlist sincronizzata per garantire che tutti i partecipanti stiano guardando la stessa cosa. Configura le cartelle multimediali alla voce 'Miscellanea'.", + "playlist-instruction-item-message" : "Trascina qui i file per aggiungerli alla playlist condivisa.", + "sharedplaylistenabled-tooltip" : "Gli operatori della stanza possono aggiungere i file a una playlist sincronizzata per garantire che tutti i partecipanti stiano guardando la stessa cosa. Configura le cartelle multimediali alla voce 'Miscellanea'.", } diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py old mode 100644 new mode 100755 index c235946..47174e6 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -3,477 +3,477 @@ """Russian dictionary""" ru = { - "LANGUAGE" : u"Русский", # (Russian) + "LANGUAGE" : "Русский", # (Russian) # Client notifications - "config-cleared-notification" : u"Настройки сброшены. Изменения вступят в силу при сохранении корректной конфигурации.", + "config-cleared-notification" : "Настройки сброшены. Изменения вступят в силу при сохранении корректной конфигурации.", - "relative-config-notification" : u"Загружены файлы относительной конфигурации: {}", + "relative-config-notification" : "Загружены файлы относительной конфигурации: {}", - "connection-attempt-notification" : u"Подключение к {}:{}", # Port, IP - "reconnection-attempt-notification" : u"Соединение с сервером потеряно, переподключение", - "disconnection-notification" : u"Отключились от сервера", - "connection-failed-notification" : u"Не удалось подключиться к серверу", - "connected-successful-notification" : u"Соединение с сервером установлено", - "retrying-notification" : u"%s, следующая попытка через %d секунд(ы)...", # Seconds + "connection-attempt-notification" : "Подключение к {}:{}", # Port, IP + "reconnection-attempt-notification" : "Соединение с сервером потеряно, переподключение", + "disconnection-notification" : "Отключились от сервера", + "connection-failed-notification" : "Не удалось подключиться к серверу", + "connected-successful-notification" : "Соединение с сервером установлено", + "retrying-notification" : "%s, следующая попытка через %d секунд(ы)...", # Seconds - "rewind-notification" : u"Перемотано из-за разницы во времени с {}", # User - "fastforward-notification" : u"Ускорено из-за разницы во времени с {}", # User - "slowdown-notification" : u"Воспроизведение замедлено из-за разницы во времени с {}", # User - "revert-notification" : u"Возвращаемся к нормальной скорости воспроизведения", + "rewind-notification" : "Перемотано из-за разницы во времени с {}", # User + "fastforward-notification" : "Ускорено из-за разницы во времени с {}", # User + "slowdown-notification" : "Воспроизведение замедлено из-за разницы во времени с {}", # User + "revert-notification" : "Возвращаемся к нормальной скорости воспроизведения", - "pause-notification" : u"{} приостановил воспроизведение", # User - "unpause-notification" : u"{} возобновил воспроизведение", # User - "seek-notification" : u"{} перемотал с {} на {}", # User, from time, to time + "pause-notification" : "{} приостановил воспроизведение", # User + "unpause-notification" : "{} возобновил воспроизведение", # User + "seek-notification" : "{} перемотал с {} на {}", # User, from time, to time - "current-offset-notification" : u"Текущее смещение: {} секунд(ы)", # Offset + "current-offset-notification" : "Текущее смещение: {} секунд(ы)", # Offset - "media-directory-list-updated-notification" : u"Папки воспроизведения обновлены.", + "media-directory-list-updated-notification" : "Папки воспроизведения обновлены.", - "room-join-notification" : u"{} зашел в комнату: '{}'", # User - "left-notification" : u"{} покинул комнату", # User - "left-paused-notification" : u"{} покинул комнату, {} приостановил воспроизведение", # User who left, User who paused - "playing-notification" : u"{} включил '{}' ({})", # User, file, duration - "playing-notification/room-addendum" : u" в комнате: '{}'", # Room + "room-join-notification" : "{} зашел в комнату: '{}'", # User + "left-notification" : "{} покинул комнату", # User + "left-paused-notification" : "{} покинул комнату, {} приостановил воспроизведение", # User who left, User who paused + "playing-notification" : "{} включил '{}' ({})", # User, file, duration + "playing-notification/room-addendum" : " в комнате: '{}'", # Room - "not-all-ready" : u"Не готовы: {}", # Usernames - "all-users-ready" : u"Все зрители готовы ({} чел.)", #Number of ready users - "ready-to-unpause-notification" : u"Вы помечены как готовый - нажмите еще раз, чтобы продолжить воспроизведение", - "set-as-ready-notification" : u"Вы помечены как готовый", - "set-as-not-ready-notification" : u"Вы помечены как неготовый", - "autoplaying-notification" : u"Автовоспроизведение через {}...", # Number of seconds until playback will start + "not-all-ready" : "Не готовы: {}", # Usernames + "all-users-ready" : "Все зрители готовы ({} чел.)", #Number of ready users + "ready-to-unpause-notification" : "Вы помечены как готовый - нажмите еще раз, чтобы продолжить воспроизведение", + "set-as-ready-notification" : "Вы помечены как готовый", + "set-as-not-ready-notification" : "Вы помечены как неготовый", + "autoplaying-notification" : "Автовоспроизведение через {}...", # Number of seconds until playback will start - "identifying-as-controller-notification" : u"Идентификация как оператора комнаты с паролем '{}'...", - "failed-to-identify-as-controller-notification" : u"{} не прошел идентификацию в качестве оператора комнаты.", - "authenticated-as-controller-notification" : u"{} вошел как оператор комнаты.", - "created-controlled-room-notification" : u"Создана управляемая комната '{}' с паролем '{}'. Сохраните эти данные!", # RoomName, operatorPassword + "identifying-as-controller-notification" : "Идентификация как оператора комнаты с паролем '{}'...", + "failed-to-identify-as-controller-notification" : "{} не прошел идентификацию в качестве оператора комнаты.", + "authenticated-as-controller-notification" : "{} вошел как оператор комнаты.", + "created-controlled-room-notification" : "Создана управляемая комната '{}' с паролем '{}'. Сохраните эти данные!", # RoomName, operatorPassword - "file-different-notification" : u"Вероятно, файл, который Вы смотрите, отличается от того, который смотрит {}.", # User - "file-differences-notification" : u"Ваш файл отличается: {}", # Differences - "room-file-differences" : u"Несовпадения файла: {}", # File differences (filename, size, and/or duration) - "file-difference-filename" : u"имя", - "file-difference-filesize" : u"размер", - "file-difference-duration" : u"длительность", - "alone-in-the-room" : u"В комнате кроме Вас никого нет.", + "file-different-notification" : "Вероятно, файл, который Вы смотрите, отличается от того, который смотрит {}.", # User + "file-differences-notification" : "Ваш файл отличается: {}", # Differences + "room-file-differences" : "Несовпадения файла: {}", # File differences (filename, size, and/or duration) + "file-difference-filename" : "имя", + "file-difference-filesize" : "размер", + "file-difference-duration" : "длительность", + "alone-in-the-room" : "В комнате кроме Вас никого нет.", - "different-filesize-notification" : u" (размер Вашего файла не совпадает с размером их файла!)", - "userlist-playing-notification" : u"{} смотрит:", #Username - "file-played-by-notification" : u"Файл: {} просматривают:", # File - "no-file-played-notification" : u"{} не смотрит ничего", # Username - "notplaying-notification" : u"Люди, которые не смотрят ничего:", - "userlist-room-notification" : u"В комнате '{}':", # Room - "userlist-file-notification" : u"Файл", - "controller-userlist-userflag" : u"Оператор", - "ready-userlist-userflag" : u"Готов", + "different-filesize-notification" : " (размер Вашего файла не совпадает с размером их файла!)", + "userlist-playing-notification" : "{} смотрит:", #Username + "file-played-by-notification" : "Файл: {} просматривают:", # File + "no-file-played-notification" : "{} не смотрит ничего", # Username + "notplaying-notification" : "Люди, которые не смотрят ничего:", + "userlist-room-notification" : "В комнате '{}':", # Room + "userlist-file-notification" : "Файл", + "controller-userlist-userflag" : "Оператор", + "ready-userlist-userflag" : "Готов", - "update-check-failed-notification" : u"Невозможно автоматически проверить, что версия Syncplay {} все еще актуальна. Хотите зайти на https://syncplay.pl/ и вручную проверить наличие обновлений?", - "syncplay-uptodate-notification" : u"У вас последняя версия Syncplay", - "syncplay-updateavailable-notification" : u"Доступна новая версия Syncplay. Хотите открыть страницу релиза?", + "update-check-failed-notification" : "Невозможно автоматически проверить, что версия Syncplay {} все еще актуальна. Хотите зайти на https://syncplay.pl/ и вручную проверить наличие обновлений?", + "syncplay-uptodate-notification" : "У вас последняя версия Syncplay", + "syncplay-updateavailable-notification" : "Доступна новая версия Syncplay. Хотите открыть страницу релиза?", - "mplayer-file-required-notification" : u"Для использования Syncplay с mplayer необходимо передать файл в качестве параметра", - "mplayer-file-required-notification/example" : u"Пример использования: syncplay [options] [url|path/]filename", - "mplayer2-required" : u"Syncplay не совместим с MPlayer 1.x, пожалуйста, используйте mplayer2 или mpv", + "mplayer-file-required-notification" : "Для использования Syncplay с mplayer необходимо передать файл в качестве параметра", + "mplayer-file-required-notification/example" : "Пример использования: syncplay [options] [url|path/]filename", + "mplayer2-required" : "Syncplay не совместим с MPlayer 1.x, пожалуйста, используйте mplayer2 или mpv", - "unrecognized-command-notification" : u"Неизвестная команда.", - "commandlist-notification" : u"Доступные команды:", - "commandlist-notification/room" : u"\tr [name] - сменить комнату", - "commandlist-notification/list" : u"\tl - показать список пользователей", - "commandlist-notification/undo" : u"\tu - отменить последнюю перемотку", - "commandlist-notification/pause" : u"\tp - вкл./выкл. паузу", - "commandlist-notification/seek" : u"\t[s][+-]time - перемотать к заданному моменту времени, если не указан + или -, то время считается абсолютным (от начала файла) в секундах или мин:сек", - "commandlist-notification/help" : u"\th - помощь", - "commandlist-notification/toggle" : u"\tt - переключить статус готов/не готов к просмотру", - "commandlist-notification/create" : u"\tc [name] - создать управляемую комнату с таким же именем, как у текущей", - "commandlist-notification/auth" : u"\ta [password] - авторизоваться как оператор комнаты с помощью пароля", + "unrecognized-command-notification" : "Неизвестная команда.", + "commandlist-notification" : "Доступные команды:", + "commandlist-notification/room" : "\tr [name] - сменить комнату", + "commandlist-notification/list" : "\tl - показать список пользователей", + "commandlist-notification/undo" : "\tu - отменить последнюю перемотку", + "commandlist-notification/pause" : "\tp - вкл./выкл. паузу", + "commandlist-notification/seek" : "\t[s][+-]time - перемотать к заданному моменту времени, если не указан + или -, то время считается абсолютным (от начала файла) в секундах или мин:сек", + "commandlist-notification/help" : "\th - помощь", + "commandlist-notification/toggle" : "\tt - переключить статус готов/не готов к просмотру", + "commandlist-notification/create" : "\tc [name] - создать управляемую комнату с таким же именем, как у текущей", + "commandlist-notification/auth" : "\ta [password] - авторизоваться как оператор комнаты с помощью пароля", "commandlist-notification/chat" : "\tch [message] - send a chat message in a room", # TODO: Translate - "syncplay-version-notification" : u"Версия Syncplay: {}", # syncplay.version - "more-info-notification" : u"Больше информации на {}", # projectURL + "syncplay-version-notification" : "Версия Syncplay: {}", # syncplay.version + "more-info-notification" : "Больше информации на {}", # projectURL - "gui-data-cleared-notification" : u"Syncplay очистил путь и информацию о состоянии окна, использованного GUI.", - "language-changed-msgbox-label" : u"Язык переключится при следующем запуске Syncplay.", - "promptforupdate-label" : u"Вы не против, если Syncplay будет автоматически изредка проверять наличие обновлений?", + "gui-data-cleared-notification" : "Syncplay очистил путь и информацию о состоянии окна, использованного GUI.", + "language-changed-msgbox-label" : "Язык переключится при следующем запуске Syncplay.", + "promptforupdate-label" : "Вы не против, если Syncplay будет автоматически изредка проверять наличие обновлений?", - "vlc-version-mismatch": u"Syncplay не поддерживает данную версию VLC. Syncplay поддерживает VLC {}+, но не VLC 3. Используйте другой проигрыватель.", # VLC min version - "vlc-interface-version-mismatch" : u"Вы используете модуль интерфейса Syncplay устаревшей версии {} для VLC. К сожалению, Syncplay способен работать с версией {} и выше. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (https://syncplay.pl/guide/) за инструкциями о том, как установить syncplay.lua.", # VLC interface version, VLC interface min version - "vlc-interface-oldversion-warning" : u"Внимание: Syncplay обнаружил, что старая версия модуля интерфейса Syncplay для VLC уже установлена в директорию VLC. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (https://syncplay.pl/guide/) за инструкциями о том, как установить syncplay.lua.", - "vlc-interface-not-installed" : u"Внимание: Модуль интерфейса Syncplay для VLC не обнаружен в директории VLC. По существу, если Вы используете VLC 2.0, то VLC будет использовать модуль syncplay.lua из директории Syncplay, но в таком случае другие пользовательские скрипты и расширения интерфейса не будут работать. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (https://syncplay.pl/guide/) за инструкциями о том, как установить syncplay.lua.", - "media-player-latency-warning": u"Внимание: У Вашего проигрывателя слишком большой отклик ({} секунд). Если Вы замечаете проблемы с синхронизацией, то закройте ресурсоемкие приложения. Если это не помогло - попробуйте другой проигрыватель.", # Seconds to respond - "mpv-unresponsive-error": u"mpv не отвечает {} секунд, по-видимому, произошел сбой. Пожалуйста, перезапустите Syncplay.", # Seconds to respond + "vlc-version-mismatch": "Syncplay не поддерживает данную версию VLC. Syncplay поддерживает VLC {}+, но не VLC 3. Используйте другой проигрыватель.", # VLC min version + "vlc-interface-version-mismatch" : "Вы используете модуль интерфейса Syncplay устаревшей версии {} для VLC. К сожалению, Syncplay способен работать с версией {} и выше. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (https://syncplay.pl/guide/) за инструкциями о том, как установить syncplay.lua.", # VLC interface version, VLC interface min version + "vlc-interface-oldversion-warning" : "Внимание: Syncplay обнаружил, что старая версия модуля интерфейса Syncplay для VLC уже установлена в директорию VLC. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (https://syncplay.pl/guide/) за инструкциями о том, как установить syncplay.lua.", + "vlc-interface-not-installed" : "Внимание: Модуль интерфейса Syncplay для VLC не обнаружен в директории VLC. По существу, если Вы используете VLC 2.0, то VLC будет использовать модуль syncplay.lua из директории Syncplay, но в таком случае другие пользовательские скрипты и расширения интерфейса не будут работать. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (https://syncplay.pl/guide/) за инструкциями о том, как установить syncplay.lua.", + "media-player-latency-warning": "Внимание: У Вашего проигрывателя слишком большой отклик ({} секунд). Если Вы замечаете проблемы с синхронизацией, то закройте ресурсоемкие приложения. Если это не помогло - попробуйте другой проигрыватель.", # Seconds to respond + "mpv-unresponsive-error": "mpv не отвечает {} секунд, по-видимому, произошел сбой. Пожалуйста, перезапустите Syncplay.", # Seconds to respond # Client prompts - "enter-to-exit-prompt" : u"Для выхода нажмите Enter\n", + "enter-to-exit-prompt" : "Для выхода нажмите Enter\n", # Client errors - "missing-arguments-error" : u"Некоторые необходимые аргументы отсутствуют, обратитесь к --help", - "server-timeout-error" : u"Подключение к серверу превысило лимит времени", - "mpc-slave-error" : u"Невозможно запустить MPC в slave режиме!", - "mpc-version-insufficient-error" : u"Версия MPC слишком старая, пожалуйста, используйте `mpc-hc` >= `{}`", - "mpc-be-version-insufficient-error" : u"Версия MPC слишком старая, пожалуйста, используйте `mpc-be` >= `{}`", - "mpv-version-error" : u"Syncplay не совместим с данной версией mpv. Пожалуйста, используйте другую версию mpv (лучше свежайшую).", - "player-file-open-error" : u"Проигрыватель не может открыть файл.", - "player-path-error" : u"Путь к проигрывателю задан неверно. Supported players are: mpv, VLC, MPC-HC, MPC-BE and mplayer2.", # TODO: Translate last sentence - "hostname-empty-error" : u"Имя пользователя не может быть пустым.", - "empty-error" : u"{} не может быть пустым.", # Configuration - "media-player-error" : u"Ошибка проигрывателя: \"{}\"", # Error line - "unable-import-gui-error" : u"Невозможно импортировать библиотеки GUI (графического интерфейса). Необходимо установить PySide, иначе графический интерфейс не будет работать.", + "missing-arguments-error" : "Некоторые необходимые аргументы отсутствуют, обратитесь к --help", + "server-timeout-error" : "Подключение к серверу превысило лимит времени", + "mpc-slave-error" : "Невозможно запустить MPC в slave режиме!", + "mpc-version-insufficient-error" : "Версия MPC слишком старая, пожалуйста, используйте `mpc-hc` >= `{}`", + "mpc-be-version-insufficient-error" : "Версия MPC слишком старая, пожалуйста, используйте `mpc-be` >= `{}`", + "mpv-version-error" : "Syncplay не совместим с данной версией mpv. Пожалуйста, используйте другую версию mpv (лучше свежайшую).", + "player-file-open-error" : "Проигрыватель не может открыть файл.", + "player-path-error" : "Путь к проигрывателю задан неверно. Supported players are: mpv, VLC, MPC-HC, MPC-BE and mplayer2.", # TODO: Translate last sentence + "hostname-empty-error" : "Имя пользователя не может быть пустым.", + "empty-error" : "{} не может быть пустым.", # Configuration + "media-player-error" : "Ошибка проигрывателя: \"{}\"", # Error line + "unable-import-gui-error" : "Невозможно импортировать библиотеки GUI (графического интерфейса). Необходимо установить PySide, иначе графический интерфейс не будет работать.", - "arguments-missing-error" : u"Некоторые необходимые аргументы отсутствуют, обратитесь к --help", + "arguments-missing-error" : "Некоторые необходимые аргументы отсутствуют, обратитесь к --help", - "unable-to-start-client-error" : u"Невозможно запустить клиент", + "unable-to-start-client-error" : "Невозможно запустить клиент", - "player-path-config-error": u"Путь к проигрывателю установлен неверно. Supported players are: mpv, VLC, MPC-HC, MPC-BE and mplayer2", # To do: Translate end - "no-file-path-config-error" : u"Файл должен быть указан до включения проигрывателя", - "no-hostname-config-error": u"Имя сервера не может быть пустым", - "invalid-port-config-error" : u"Неверный номер порта", - "empty-value-config-error" : u"Поле '{}' не может быть пустым", # Config option + "player-path-config-error": "Путь к проигрывателю установлен неверно. Supported players are: mpv, VLC, MPC-HC, MPC-BE and mplayer2", # To do: Translate end + "no-file-path-config-error" : "Файл должен быть указан до включения проигрывателя", + "no-hostname-config-error": "Имя сервера не может быть пустым", + "invalid-port-config-error" : "Неверный номер порта", + "empty-value-config-error" : "Поле '{}' не может быть пустым", # Config option - "not-json-error" : u"Не является закодированной json-строкой\n", - "hello-arguments-error" : u"Не хватает аргументов Hello\n", - "version-mismatch-error" : u"Конфликт версий между клиентом и сервером\n", - "vlc-failed-connection" : u"Ошибка подключения к VLC. Если у Вас не установлен syncplay.lua, то обратитесь к https://syncplay.pl/LUA/ за инструкциями.", - "vlc-failed-noscript" : u"VLC сообщает, что скрипт интерфейса syncplay.lua не установлен. Пожалуйста, обратитесь к https://syncplay.pl/LUA/ за инструкциями.", - "vlc-failed-versioncheck" : u"Данная версия VLC не поддерживается Syncplay. Пожалуйста, используйте VLC версии 2 или выше.", - "vlc-failed-other" : u"Во время загрузки скрипта интерфейса syncplay.lua в VLC произошла следующая ошибка: {}", # Syncplay Error + "not-json-error" : "Не является закодированной json-строкой\n", + "hello-arguments-error" : "Не хватает аргументов Hello\n", + "version-mismatch-error" : "Конфликт версий между клиентом и сервером\n", + "vlc-failed-connection" : "Ошибка подключения к VLC. Если у Вас не установлен syncplay.lua, то обратитесь к https://syncplay.pl/LUA/ за инструкциями.", + "vlc-failed-noscript" : "VLC сообщает, что скрипт интерфейса syncplay.lua не установлен. Пожалуйста, обратитесь к https://syncplay.pl/LUA/ за инструкциями.", + "vlc-failed-versioncheck" : "Данная версия VLC не поддерживается Syncplay. Пожалуйста, используйте VLC версии 2 или выше.", + "vlc-failed-other" : "Во время загрузки скрипта интерфейса syncplay.lua в VLC произошла следующая ошибка: {}", # Syncplay Error - "feature-sharedPlaylists": u"shared playlists", # used for not-supported-by-server-error # TODO: Translate - "feature-chat": u"chat", # used for not-supported-by-server-error # TODO: Translate - "feature-readiness": u"readiness", # used for not-supported-by-server-error # TODO: Translate - "feature-managedRooms": u"managed rooms", # used for not-supported-by-server-error # TODO: Translate + "feature-sharedPlaylists": "shared playlists", # used for not-supported-by-server-error # TODO: Translate + "feature-chat": "chat", # used for not-supported-by-server-error # TODO: Translate + "feature-readiness": "readiness", # used for not-supported-by-server-error # TODO: Translate + "feature-managedRooms": "managed rooms", # used for not-supported-by-server-error # TODO: Translate - "not-supported-by-server-error": u"The {} feature is not supported by this server..", # feature # TODO: Translate + "not-supported-by-server-error": "The {} feature is not supported by this server..", # feature # TODO: Translate #OLD TRANSLATION: "not-supported-by-server-error" : u"Эта возможность не поддерживается сервером. Требуется сервер Syncplay {}+, вы подключены к серверу Syncplay {}.", #minVersion, serverVersion - "shared-playlists-not-supported-by-server-error" : u"Общие списки воспроизведения могут не поддерживаться сервером. Для корректной работы требуется сервер Syncplay {}+, вы подключены к серверу Syncplay {}.", #minVersion, serverVersion + "shared-playlists-not-supported-by-server-error" : "Общие списки воспроизведения могут не поддерживаться сервером. Для корректной работы требуется сервер Syncplay {}+, вы подключены к серверу Syncplay {}.", #minVersion, serverVersion "shared-playlists-disabled-by-server-error" : "The shared playlist feature has been disabled in the server configuration. To use this feature you will need to connect to a different server.", # TODO: Translate - "invalid-seek-value" : u"Некорректное значение для перемотки", - "invalid-offset-value" : u"Некорректное смещение", + "invalid-seek-value" : "Некорректное значение для перемотки", + "invalid-offset-value" : "Некорректное смещение", - "switch-file-not-found-error" : u"Невозможно найти файл '{0}'. Проверьте папки воспроизведения.", # File not found - "folder-search-timeout-error" : u"Поиск файла был прерван в папке '{}'. Это может происходить из-за большого количества подпапок. Для корректной работы поиска файлов зайдите через выпадающее меню в Файл->Папки воспроизведения и удалите данную папку из списка, или замените её на нужную подпапку. If the folder is actually fine then you can re-enable it by selecting File->Set Media Directories and pressing 'OK'.", #Folder # TODO: Translate last sentence - "folder-search-first-file-timeout-error" : u"Поиск файла в '{}' был прерван, так как невозможно открыть каталог. Это может происходить, если это сетевой диск или диск перешел в режим экономии энергии. Для корректной работы поиска файлов зайдите через выпадающее меню в Файл->Папки воспроизведения и удалите данную папку, или решите проблему через изменение параметров энергосбережения.", #Folder - "added-file-not-in-media-directory-error" : u"Вы загрузили файл из '{}', который не числится в папках воспроизведения. Вы можете добавить его через выпадающее меню Файл->Папки воспроизведения.", #Folder - "no-media-directories-error" : u"Вы не указали папки воспроизведения. Для корректной работы зайдите через выпадающее меню в Файл->Папки воспроизведения и укажите нужные каталоги.", - "cannot-find-directory-error" : u"Не удалось найти папку воспроизведения '{}'. Для обновления списка папок, через выпадающее меню, перейдите в Файл->Папки воспроизведения и укажите нужные каталоги.", + "switch-file-not-found-error" : "Невозможно найти файл '{0}'. Проверьте папки воспроизведения.", # File not found + "folder-search-timeout-error" : "Поиск файла был прерван в папке '{}'. Это может происходить из-за большого количества подпапок. Для корректной работы поиска файлов зайдите через выпадающее меню в Файл->Папки воспроизведения и удалите данную папку из списка, или замените её на нужную подпапку. If the folder is actually fine then you can re-enable it by selecting File->Set Media Directories and pressing 'OK'.", #Folder # TODO: Translate last sentence + "folder-search-first-file-timeout-error" : "Поиск файла в '{}' был прерван, так как невозможно открыть каталог. Это может происходить, если это сетевой диск или диск перешел в режим экономии энергии. Для корректной работы поиска файлов зайдите через выпадающее меню в Файл->Папки воспроизведения и удалите данную папку, или решите проблему через изменение параметров энергосбережения.", #Folder + "added-file-not-in-media-directory-error" : "Вы загрузили файл из '{}', который не числится в папках воспроизведения. Вы можете добавить его через выпадающее меню Файл->Папки воспроизведения.", #Folder + "no-media-directories-error" : "Вы не указали папки воспроизведения. Для корректной работы зайдите через выпадающее меню в Файл->Папки воспроизведения и укажите нужные каталоги.", + "cannot-find-directory-error" : "Не удалось найти папку воспроизведения '{}'. Для обновления списка папок, через выпадающее меню, перейдите в Файл->Папки воспроизведения и укажите нужные каталоги.", - "failed-to-load-server-list-error" : u"Не удалось загрузить список публичных серверов. Откройте http://www.syncplay.pl/ через браузер.", + "failed-to-load-server-list-error" : "Не удалось загрузить список публичных серверов. Откройте http://www.syncplay.pl/ через браузер.", # Client arguments - "argument-description" : u'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC/BE через Интернет.', - "argument-epilog" : u'Если параметр не будет передан, то будет использоваться значение, указанное в _config.', - "nogui-argument" : u'не использовать GUI', - "host-argument" : u'адрес сервера', - "name-argument" : u'желательное имя пользователя', - "debug-argument" : u'режим отладки', - "force-gui-prompt-argument" : u'показать окно настройки', - "no-store-argument" : u'не сохранять данные в .syncplay', - "room-argument" : u'начальная комната', - "password-argument" : u'пароль для доступа к серверу', - "player-path-argument" : u'путь к исполняемому файлу Вашего проигрывателя', - "file-argument" : u'воспроизводимый файл', - "args-argument" : u'параметры проигрывателя; если нужно передать параметры, начинающиеся с - , то сначала пишите \'--\'', - "clear-gui-data-argument" : u'сбрасывает путь и данные о состоянии окна GUI, хранимые как QSettings', - "language-argument" : u'язык сообщений Syncplay (de/en/ru)', + "argument-description" : 'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC/BE через Интернет.', + "argument-epilog" : 'Если параметр не будет передан, то будет использоваться значение, указанное в _config.', + "nogui-argument" : 'не использовать GUI', + "host-argument" : 'адрес сервера', + "name-argument" : 'желательное имя пользователя', + "debug-argument" : 'режим отладки', + "force-gui-prompt-argument" : 'показать окно настройки', + "no-store-argument" : 'не сохранять данные в .syncplay', + "room-argument" : 'начальная комната', + "password-argument" : 'пароль для доступа к серверу', + "player-path-argument" : 'путь к исполняемому файлу Вашего проигрывателя', + "file-argument" : 'воспроизводимый файл', + "args-argument" : 'параметры проигрывателя; если нужно передать параметры, начинающиеся с - , то сначала пишите \'--\'', + "clear-gui-data-argument" : 'сбрасывает путь и данные о состоянии окна GUI, хранимые как QSettings', + "language-argument" : 'язык сообщений Syncplay (de/en/ru)', - "version-argument" : u'выводит номер версии', - "version-message" : u"Вы используете Syncplay версии {} ({})", + "version-argument" : 'выводит номер версии', + "version-message" : "Вы используете Syncplay версии {} ({})", # Client labels - "config-window-title" : u"Настройка Syncplay", + "config-window-title" : "Настройка Syncplay", - "connection-group-title" : u"Подключение", - "host-label" : u"Адрес сервера: ", - "name-label" : u"Имя пользователя (не обязательно):", - "password-label" : u"Пароль сервера (если требуется):", - "room-label" : u"Комната:", + "connection-group-title" : "Подключение", + "host-label" : "Адрес сервера: ", + "name-label" : "Имя пользователя (не обязательно):", + "password-label" : "Пароль сервера (если требуется):", + "room-label" : "Комната:", - "media-setting-title" : u"Воспроизведение", - "executable-path-label" : u"Путь к проигрывателю:", - "media-path-label" : u"Путь к видеофайлу:", # Todo: Translate to 'Path to video (optional)' - "player-arguments-label" : u"Аргументы запуска проигрывателя:", - "browse-label" : u"Выбрать", - "update-server-list-label" : u"Обновить список", + "media-setting-title" : "Воспроизведение", + "executable-path-label" : "Путь к проигрывателю:", + "media-path-label" : "Путь к видеофайлу:", # Todo: Translate to 'Path to video (optional)' + "player-arguments-label" : "Аргументы запуска проигрывателя:", + "browse-label" : "Выбрать", + "update-server-list-label" : "Обновить список", - "more-title" : u"Больше настроек", - "never-rewind-value" : u"Никогда", - "seconds-suffix" : u" секунд(ы)", - "privacy-sendraw-option" : u"отпр. как есть", - "privacy-sendhashed-option" : u"отпр. хэш", - "privacy-dontsend-option" : u"не отпр.", - "filename-privacy-label" : u"Имя файла:", - "filesize-privacy-label" : u"Размер файла:", - "checkforupdatesautomatically-label" : u"Проверять обновления автоматически", - "slowondesync-label" : u"Замедлять при небольших рассинхронизациях (не поддерживаетя в MPC-HC/BE)", - "rewindondesync-label" : u"Перемотка при больших рассинхронизациях (настоятельно рекомендуется)", - "dontslowdownwithme-label" : u"Никогда не замедлять и не перематывать видео другим (функция тестируется)", - "pausing-title" : u"Приостановка", - "pauseonleave-label" : u"Приостанавливать, когда кто-то уходит (например, отключился)", - "readiness-title" : u"Готовность", - "readyatstart-label" : u"Выставить статус 'Я готов' по умолчанию", - "fastforwardondesync-label" : u"Ускорять видео при отставании (рекомендуется)", - "forceguiprompt-label" : u"Не показывать больше этот диалог", # (Inverted) - "showosd-label" : u"Включить экранные сообщения (поверх видео)", + "more-title" : "Больше настроек", + "never-rewind-value" : "Никогда", + "seconds-suffix" : " секунд(ы)", + "privacy-sendraw-option" : "отпр. как есть", + "privacy-sendhashed-option" : "отпр. хэш", + "privacy-dontsend-option" : "не отпр.", + "filename-privacy-label" : "Имя файла:", + "filesize-privacy-label" : "Размер файла:", + "checkforupdatesautomatically-label" : "Проверять обновления автоматически", + "slowondesync-label" : "Замедлять при небольших рассинхронизациях (не поддерживаетя в MPC-HC/BE)", + "rewindondesync-label" : "Перемотка при больших рассинхронизациях (настоятельно рекомендуется)", + "dontslowdownwithme-label" : "Никогда не замедлять и не перематывать видео другим (функция тестируется)", + "pausing-title" : "Приостановка", + "pauseonleave-label" : "Приостанавливать, когда кто-то уходит (например, отключился)", + "readiness-title" : "Готовность", + "readyatstart-label" : "Выставить статус 'Я готов' по умолчанию", + "fastforwardondesync-label" : "Ускорять видео при отставании (рекомендуется)", + "forceguiprompt-label" : "Не показывать больше этот диалог", # (Inverted) + "showosd-label" : "Включить экранные сообщения (поверх видео)", - "showosdwarnings-label" : u"Показывать предупреждения (напр., когда файлы не совпадают)", - "showsameroomosd-label" : u"Показывать события Вашей комнаты", - "shownoncontrollerosd-label" : u"Включить события, связанные с не-операторами в управляемой комнате.", - "showdifferentroomosd-label" : u"Показывать события других комнат", - "showslowdownosd-label" : u"Показывать уведомления о замедлении/перемотке", - "language-label" : u"Язык:", - "automatic-language" : u"По умолчанию ({})", # Automatic language - "showdurationnotification-label" : u"Предупреждать о несовпадении продолжительности видео", - "basics-label" : u"Основное", - "readiness-label" : u"Поведение", - "misc-label" : u"Прочее", - "core-behaviour-title" : u"Информация о файлах", - "syncplay-internals-title" : u"Системные настройки", - "syncplay-mediasearchdirectories-title" : u"Папки воспроизведения (один путь на строку)", - "sync-label" : u"Синхронизация", - "sync-otherslagging-title" : u"Опережение", - "sync-youlaggging-title" : u"Отставание", - "messages-label" : u"Сообщения", - "messages-osd-title" : u"Настройки OSD", - "messages-other-title" : u"Другие настройки отображения", - "chat-label" : u"Chat", # TODO: Translate - "privacy-label" : u"Приватность", - "privacy-title" : u"Настройки приватности", - "unpause-title" : u"Если вы стартуете, то:", - "unpause-ifalreadyready-option" : u"Снять паузу, если уже готов", - "unpause-ifothersready-option" : u"Снять паузу, если Вы и остальные в комнате готовы (по-умолчанию)", - "unpause-ifminusersready-option" : u"Снять паузу, если все в комнате готовы и присутствует минимум зрителей", - "unpause-always" : u"Всегда снимать паузу", - "syncplay-trusteddomains-title": u"Доверенные сайты (стрим-сервисы, видеохостинги, файлы в сети)", -"addtrusteddomain-menu-label" : u"Добавить {} как доверенный сайт", # Domain + "showosdwarnings-label" : "Показывать предупреждения (напр., когда файлы не совпадают)", + "showsameroomosd-label" : "Показывать события Вашей комнаты", + "shownoncontrollerosd-label" : "Включить события, связанные с не-операторами в управляемой комнате.", + "showdifferentroomosd-label" : "Показывать события других комнат", + "showslowdownosd-label" : "Показывать уведомления о замедлении/перемотке", + "language-label" : "Язык:", + "automatic-language" : "По умолчанию ({})", # Automatic language + "showdurationnotification-label" : "Предупреждать о несовпадении продолжительности видео", + "basics-label" : "Основное", + "readiness-label" : "Поведение", + "misc-label" : "Прочее", + "core-behaviour-title" : "Информация о файлах", + "syncplay-internals-title" : "Системные настройки", + "syncplay-mediasearchdirectories-title" : "Папки воспроизведения (один путь на строку)", + "sync-label" : "Синхронизация", + "sync-otherslagging-title" : "Опережение", + "sync-youlaggging-title" : "Отставание", + "messages-label" : "Сообщения", + "messages-osd-title" : "Настройки OSD", + "messages-other-title" : "Другие настройки отображения", + "chat-label" : "Chat", # TODO: Translate + "privacy-label" : "Приватность", + "privacy-title" : "Настройки приватности", + "unpause-title" : "Если вы стартуете, то:", + "unpause-ifalreadyready-option" : "Снять паузу, если уже готов", + "unpause-ifothersready-option" : "Снять паузу, если Вы и остальные в комнате готовы (по-умолчанию)", + "unpause-ifminusersready-option" : "Снять паузу, если все в комнате готовы и присутствует минимум зрителей", + "unpause-always" : "Всегда снимать паузу", + "syncplay-trusteddomains-title": "Доверенные сайты (стрим-сервисы, видеохостинги, файлы в сети)", +"addtrusteddomain-menu-label" : "Добавить {} как доверенный сайт", # Domain - "chat-title": u"Chat message input", # TODO: Translate - "chatinputenabled-label": u"Enable chat input via mpv (using enter key)", # TODO: Translate - "chatdirectinput-label" : u"Allow instant chat input (bypass having to press enter key to chat)", # TODO: Translate - "chatinputfont-label": u"Chat input font", # TODO: Translate - "chatfont-label": u"Set font", # TODO: Translate - "chatcolour-label": u"Set colour", # TODO: Translate - "chatinputposition-label": u"Position of message input area in mpv", # TODO: Translate - "chat-top-option": u"Top", # TODO: Translate - "chat-middle-option": u"Middle", # TODO: Translate - "chat-bottom-option": u"Bottom", # TODO: Translate - "chatoutputheader-label" : u"Chat message output", # TODO: Traslate - "chatoutputfont-label": u"Chat output font", # TODO: Translate - "chatoutputenabled-label": u"Enable chat output in media player (mpv only for now)", # TODO: Translate - "chatoutputposition-label": u"Output mode", # TODO: Translate - "chat-chatroom-option": u"Chatroom style", # TODO: Translate - "chat-scrolling-option": u"Scrolling style", # TODO: Translate + "chat-title": "Chat message input", # TODO: Translate + "chatinputenabled-label": "Enable chat input via mpv (using enter key)", # TODO: Translate + "chatdirectinput-label" : "Allow instant chat input (bypass having to press enter key to chat)", # TODO: Translate + "chatinputfont-label": "Chat input font", # TODO: Translate + "chatfont-label": "Set font", # TODO: Translate + "chatcolour-label": "Set colour", # TODO: Translate + "chatinputposition-label": "Position of message input area in mpv", # TODO: Translate + "chat-top-option": "Top", # TODO: Translate + "chat-middle-option": "Middle", # TODO: Translate + "chat-bottom-option": "Bottom", # TODO: Translate + "chatoutputheader-label" : "Chat message output", # TODO: Traslate + "chatoutputfont-label": "Chat output font", # TODO: Translate + "chatoutputenabled-label": "Enable chat output in media player (mpv only for now)", # TODO: Translate + "chatoutputposition-label": "Output mode", # TODO: Translate + "chat-chatroom-option": "Chatroom style", # TODO: Translate + "chat-scrolling-option": "Scrolling style", # TODO: Translate - "mpv-key-tab-hint": u"[TAB] to toggle access to alphabet row key shortcuts.", # TODO: Translate - "mpv-key-hint": u"[ENTER] to send message. [ESC] to escape chat mode.", # TODO: Translate - "alphakey-mode-warning-first-line": u"You can temporarily use old mpv bindings with a-z keys.", # TODO: Translate - "alphakey-mode-warning-second-line": u"Press [TAB] to return to Syncplay chat mode.", # TODO: Translate + "mpv-key-tab-hint": "[TAB] to toggle access to alphabet row key shortcuts.", # TODO: Translate + "mpv-key-hint": "[ENTER] to send message. [ESC] to escape chat mode.", # TODO: Translate + "alphakey-mode-warning-first-line": "You can temporarily use old mpv bindings with a-z keys.", # TODO: Translate + "alphakey-mode-warning-second-line": "Press [TAB] to return to Syncplay chat mode.", # TODO: Translate - "help-label" : u"Помощь", - "reset-label" : u"Сброс настроек", - "run-label" : u"Запустить", - "storeandrun-label" : u"Сохранить и запустить", + "help-label" : "Помощь", + "reset-label" : "Сброс настроек", + "run-label" : "Запустить", + "storeandrun-label" : "Сохранить и запустить", - "contact-label" : u"Есть идея, нашли ошибку или хотите оставить отзыв? Пишите на dev@syncplay.pl, в IRC канал #Syncplay на irc.freenode.net или задавайте вопросы через GitHub. Кроме того, заходите на www.syncplay.pl за инорфмацией, помощью и обновлениями! NOTE: Chat messages are not encrypted so do not use Syncplay to send sensitive information.", # TODO: Translate last sentence + "contact-label" : "Есть идея, нашли ошибку или хотите оставить отзыв? Пишите на dev@syncplay.pl, в IRC канал #Syncplay на irc.freenode.net или задавайте вопросы через GitHub. Кроме того, заходите на www.syncplay.pl за инорфмацией, помощью и обновлениями! NOTE: Chat messages are not encrypted so do not use Syncplay to send sensitive information.", # TODO: Translate last sentence - "joinroom-label" : u"Зайти в комнату", - "joinroom-menu-label" : u"Зайти в комнату {}", - "seektime-menu-label" : u"Пере&мотать", - "undoseek-menu-label" : u"&Отменить перемотку", - "play-menu-label" : u"&Старт", - "pause-menu-label" : u"&Пауза", - "playbackbuttons-menu-label" : u"&Показывать кнопки управления", - "autoplay-menu-label" : u"Показывать кнопку &автовоспроизведения", - "autoplay-guipushbuttonlabel" : u"Стартовать, когда все будут готовы", - "autoplay-minimum-label" : u"Минимум зрителей:", - "sendmessage-label" : u"Send", # TODO: Translate + "joinroom-label" : "Зайти в комнату", + "joinroom-menu-label" : "Зайти в комнату {}", + "seektime-menu-label" : "Пере&мотать", + "undoseek-menu-label" : "&Отменить перемотку", + "play-menu-label" : "&Старт", + "pause-menu-label" : "&Пауза", + "playbackbuttons-menu-label" : "&Показывать кнопки управления", + "autoplay-menu-label" : "Показывать кнопку &автовоспроизведения", + "autoplay-guipushbuttonlabel" : "Стартовать, когда все будут готовы", + "autoplay-minimum-label" : "Минимум зрителей:", + "sendmessage-label" : "Send", # TODO: Translate - "ready-guipushbuttonlabel" : u"Я готов", + "ready-guipushbuttonlabel" : "Я готов", - "roomuser-heading-label" : u"Комната / Зритель", + "roomuser-heading-label" : "Комната / Зритель", - "size-heading-label" : u"Размер", - "duration-heading-label" : u"Время", - "filename-heading-label" : u"Имя файла", - "notifications-heading-label" : u"Уведомления", - "userlist-heading-label" : u"Кто что смотрит", + "size-heading-label" : "Размер", + "duration-heading-label" : "Время", + "filename-heading-label" : "Имя файла", + "notifications-heading-label" : "Уведомления", + "userlist-heading-label" : "Кто что смотрит", - "browseformedia-label" : u"Выбрать файл", + "browseformedia-label" : "Выбрать файл", - "file-menu-label" : u"&Файл", # & precedes shortcut key - "openmedia-menu-label" : u"&Открыть файл", - "openstreamurl-menu-label" : u"Открыть &ссылку", - "setmediadirectories-menu-label" : u"&Папки воспроизведения", - "exit-menu-label" : u"&Выход", - "advanced-menu-label" : u"&Дополнительно", - "window-menu-label" : u"&Вид", - "setoffset-menu-label" : u"&Установить смещение", - "createcontrolledroom-menu-label" : u"Создать управляемую &комнату", - "identifyascontroller-menu-label" : u"&Войти как оператор комнаты", - "settrusteddomains-menu-label" : u"Доверенные &сайты", + "file-menu-label" : "&Файл", # & precedes shortcut key + "openmedia-menu-label" : "&Открыть файл", + "openstreamurl-menu-label" : "Открыть &ссылку", + "setmediadirectories-menu-label" : "&Папки воспроизведения", + "exit-menu-label" : "&Выход", + "advanced-menu-label" : "&Дополнительно", + "window-menu-label" : "&Вид", + "setoffset-menu-label" : "&Установить смещение", + "createcontrolledroom-menu-label" : "Создать управляемую &комнату", + "identifyascontroller-menu-label" : "&Войти как оператор комнаты", + "settrusteddomains-menu-label" : "Доверенные &сайты", - "playback-menu-label" : u"&Управление", + "playback-menu-label" : "&Управление", - "help-menu-label" : u"&Помощь", - "userguide-menu-label" : u"&Руководство пользователя", - "update-menu-label" : u"Проверить &обновления", + "help-menu-label" : "&Помощь", + "userguide-menu-label" : "&Руководство пользователя", + "update-menu-label" : "Проверить &обновления", #About dialog - TODO: Translate - "about-menu-label": u"&About Syncplay", - "about-dialog-title": u"About Syncplay", - "about-dialog-release": u"Version {} release {} on {}", - "about-dialog-license-text" : u"Licensed under the Apache License, Version 2.0", - "about-dialog-license-button": u"License", - "about-dialog-dependencies": u"Dependencies", + "about-menu-label": "&About Syncplay", + "about-dialog-title": "About Syncplay", + "about-dialog-release": "Version {} release {} on {}", + "about-dialog-license-text" : "Licensed under the Apache License, Version 2.0", + "about-dialog-license-button": "License", + "about-dialog-dependencies": "Dependencies", - "setoffset-msgbox-label" : u"Установить смещение", - "offsetinfo-msgbox-label" : u"Смещение (см. инструкцию на странице www.syncplay.pl/guide):", + "setoffset-msgbox-label" : "Установить смещение", + "offsetinfo-msgbox-label" : "Смещение (см. инструкцию на странице www.syncplay.pl/guide):", - "promptforstreamurl-msgbox-label" : u"Открыть ссылку", - "promptforstreamurlinfo-msgbox-label" : u"Ссылка:", + "promptforstreamurl-msgbox-label" : "Открыть ссылку", + "promptforstreamurlinfo-msgbox-label" : "Ссылка:", - "addfolder-label" : u"Добавить папку", + "addfolder-label" : "Добавить папку", - "adduris-msgbox-label" : u"Список ссылок (одна на строку)", - "editplaylist-msgbox-label" : u"Список воспроизведения (один на строку)", - "trusteddomains-msgbox-label" : u"Список доверенных сайтов для автоматического воспроизведения (один на строку)", + "adduris-msgbox-label" : "Список ссылок (одна на строку)", + "editplaylist-msgbox-label" : "Список воспроизведения (один на строку)", + "trusteddomains-msgbox-label" : "Список доверенных сайтов для автоматического воспроизведения (один на строку)", - "createcontrolledroom-msgbox-label" : u"Создать управляемую комнату", - "controlledroominfo-msgbox-label" : u"Введите имя управляемой комнаты\r\n(см. инструкцию на странице www.syncplay.pl/guide):", + "createcontrolledroom-msgbox-label" : "Создать управляемую комнату", + "controlledroominfo-msgbox-label" : "Введите имя управляемой комнаты\r\n(см. инструкцию на странице www.syncplay.pl/guide):", - "identifyascontroller-msgbox-label" : u"Войти как оператор комнаты", - "identifyinfo-msgbox-label" : u"Введите пароль оператора комнаты\r\n(см. инструкцию на странице www.syncplay.pl/guide):", + "identifyascontroller-msgbox-label" : "Войти как оператор комнаты", + "identifyinfo-msgbox-label" : "Введите пароль оператора комнаты\r\n(см. инструкцию на странице www.syncplay.pl/guide):", - "public-server-msgbox-label" : u"Выбите публичный сервер для данной сессии", + "public-server-msgbox-label" : "Выбите публичный сервер для данной сессии", - "megabyte-suffix" : u" МБ", # Technically it is a mebibyte + "megabyte-suffix" : " МБ", # Technically it is a mebibyte # Tooltips - "host-tooltip" : u"Имя или IP-адрес, к которому будет произведено подключение, может содержать номер порта (напр., syncplay.pl:8999). Синхронизация возможна только в рамках одного сервера/порта.", - "name-tooltip" : u"Имя, под которым Вы будете известны. Регистриция не требуется, так что имя пользователя можно легко сменить в любой момент. Будет сгенерировано случайным образом, если не указать.", - "password-tooltip" : u"Пароли нужны для подключения к приватным серверам.", - "room-tooltip" : u"Комната, в которую Вы попадете сразу после подключения. Синхронизация возможна только между людьми в одной и той же комнате.", + "host-tooltip" : "Имя или IP-адрес, к которому будет произведено подключение, может содержать номер порта (напр., syncplay.pl:8999). Синхронизация возможна только в рамках одного сервера/порта.", + "name-tooltip" : "Имя, под которым Вы будете известны. Регистриция не требуется, так что имя пользователя можно легко сменить в любой момент. Будет сгенерировано случайным образом, если не указать.", + "password-tooltip" : "Пароли нужны для подключения к приватным серверам.", + "room-tooltip" : "Комната, в которую Вы попадете сразу после подключения. Синхронизация возможна только между людьми в одной и той же комнате.", - "executable-path-tooltip" : u"Расположение Вашего видеопроигрывателя (MPC-HC, MPC-BE, VLC, mplayer2 или mpv).", - "media-path-tooltip" : u"Расположение видеофайла или потока для просмотра. Обязательно для mplayer2.", # TODO: Confirm translation - "player-arguments-tooltip" : u"Передавать дополнительные аргументы командной строки этому проигрывателю.", - "mediasearcdirectories-arguments-tooltip" : u"Папки, где Syncplay будет искать медиа файлы, включая подпапки.", + "executable-path-tooltip" : "Расположение Вашего видеопроигрывателя (MPC-HC, MPC-BE, VLC, mplayer2 или mpv).", + "media-path-tooltip" : "Расположение видеофайла или потока для просмотра. Обязательно для mplayer2.", # TODO: Confirm translation + "player-arguments-tooltip" : "Передавать дополнительные аргументы командной строки этому проигрывателю.", + "mediasearcdirectories-arguments-tooltip" : "Папки, где Syncplay будет искать медиа файлы, включая подпапки.", - "more-tooltip" : u"Показать дополнительные настройки.", - "filename-privacy-tooltip" : u"Режим приватности для передачи имени воспроизводимого файла на сервер.", - "filesize-privacy-tooltip" : u"Режим приватности для передачи размера воспроизводимого файла на сервер.", - "privacy-sendraw-tooltip" : u"Отправляет эту информацию без шифрования. Рекомендуемая опция с наибольшей функциональностью.", - "privacy-sendhashed-tooltip" : u"Отправляет хэш-сумму этой информации, делая ее невидимой для других пользователей.", - "privacy-dontsend-tooltip" : u"Не отправлять эту информацию на сервер. Предоставляет наибольшую приватность.", - "checkforupdatesautomatically-tooltip" : u"Syncplay будет регулярно заходить на сайт и проверять наличие новых версий.", - "slowondesync-tooltip" : u"Временно уменьшить скорость воспроизведения в целях синхронизации с другими зрителями. Не поддерживается в MPC-HC/BE.", - "dontslowdownwithme-tooltip" : u"Ваши лаги не будут влиять на других зрителей.", - "pauseonleave-tooltip" : u"Приостановить воспроизведение, если Вы покинули комнату или кто-то из зрителей отключился от сервера.", - "readyatstart-tooltip" : u"Отметить Вас готовым к просмотру сразу же (по умолчанию Вы отмечены не готовым)", - "forceguiprompt-tooltip" : u"Окно настройки не будет отображаться при открытии файла в Syncplay.", # (Inverted) - "nostore-tooltip" : u"Запустить Syncplay с данной конфигурацией, но не сохранять изменения навсегда.", - "rewindondesync-tooltip" : u"Перематывать назад, когда это необходимо для синхронизации. Отключение этой опции может привести к большим рассинхронизациям!", - "fastforwardondesync-tooltip" : u"Перематывать вперед при рассинхронизации с оператором комнаты (или если включена опция 'Никогда не замедлять и не перематывать видео другим').", - "showosd-tooltip" : u"Отправлять сообщения Syncplay в видеопроигрыватель и отображать их поверх видео (OSD - On Screen Display).", - "showosdwarnings-tooltip" : u"Показывать OSC-предупреждения, если проигрываются разные файлы или если Вы в комнате больше никого нет.", - "showsameroomosd-tooltip" : u"Показывать OSD-уведомления о событиях, относящихся к комнате, в которой Вы находитесь.", - "shownoncontrollerosd-tooltip" : u"Показывать OSD-уведомления о событиях, относящихся к не-операторам в управляемой комнате.", - "showdifferentroomosd-tooltip" : u"Показывать OSD-уведомления о событиях, относящихся к любым другим комнатам.", - "showslowdownosd-tooltip" : u"Показывать уведомления о замедлении или перемотке в целях синхронизации.", - "showdurationnotification-tooltip" : u"Полезно, когда сегмент составного файла отсутствует. Возможны ложные срабатывания.", - "language-tooltip" : u"Язык, используемый Syncplay.", - "unpause-always-tooltip" : u"Когда вы стартуете, статус изменится на готов и начнется воспроизведение, а не просто смена статуса.", - "unpause-ifalreadyready-tooltip" : u"Когда вы стартуете не готовым, это меняет статус на готов - нажмите старт еще раз для начала воспроизведения.", - "unpause-ifothersready-tooltip" : u"Когда вы стартуете не готовым, воспроизведение начнется, если остальные готовы.", - "unpause-ifminusersready-tooltip" : u"Когда вы стартуете не готовым, воспроизведение начнется, если остальные готовы и присутствует достаточное число зрителей.", - "trusteddomains-arguments-tooltip" : u"Сайты, которые разрешены для автоматического воспроизведения из общего списка воспроизведения.", + "more-tooltip" : "Показать дополнительные настройки.", + "filename-privacy-tooltip" : "Режим приватности для передачи имени воспроизводимого файла на сервер.", + "filesize-privacy-tooltip" : "Режим приватности для передачи размера воспроизводимого файла на сервер.", + "privacy-sendraw-tooltip" : "Отправляет эту информацию без шифрования. Рекомендуемая опция с наибольшей функциональностью.", + "privacy-sendhashed-tooltip" : "Отправляет хэш-сумму этой информации, делая ее невидимой для других пользователей.", + "privacy-dontsend-tooltip" : "Не отправлять эту информацию на сервер. Предоставляет наибольшую приватность.", + "checkforupdatesautomatically-tooltip" : "Syncplay будет регулярно заходить на сайт и проверять наличие новых версий.", + "slowondesync-tooltip" : "Временно уменьшить скорость воспроизведения в целях синхронизации с другими зрителями. Не поддерживается в MPC-HC/BE.", + "dontslowdownwithme-tooltip" : "Ваши лаги не будут влиять на других зрителей.", + "pauseonleave-tooltip" : "Приостановить воспроизведение, если Вы покинули комнату или кто-то из зрителей отключился от сервера.", + "readyatstart-tooltip" : "Отметить Вас готовым к просмотру сразу же (по умолчанию Вы отмечены не готовым)", + "forceguiprompt-tooltip" : "Окно настройки не будет отображаться при открытии файла в Syncplay.", # (Inverted) + "nostore-tooltip" : "Запустить Syncplay с данной конфигурацией, но не сохранять изменения навсегда.", + "rewindondesync-tooltip" : "Перематывать назад, когда это необходимо для синхронизации. Отключение этой опции может привести к большим рассинхронизациям!", + "fastforwardondesync-tooltip" : "Перематывать вперед при рассинхронизации с оператором комнаты (или если включена опция 'Никогда не замедлять и не перематывать видео другим').", + "showosd-tooltip" : "Отправлять сообщения Syncplay в видеопроигрыватель и отображать их поверх видео (OSD - On Screen Display).", + "showosdwarnings-tooltip" : "Показывать OSC-предупреждения, если проигрываются разные файлы или если Вы в комнате больше никого нет.", + "showsameroomosd-tooltip" : "Показывать OSD-уведомления о событиях, относящихся к комнате, в которой Вы находитесь.", + "shownoncontrollerosd-tooltip" : "Показывать OSD-уведомления о событиях, относящихся к не-операторам в управляемой комнате.", + "showdifferentroomosd-tooltip" : "Показывать OSD-уведомления о событиях, относящихся к любым другим комнатам.", + "showslowdownosd-tooltip" : "Показывать уведомления о замедлении или перемотке в целях синхронизации.", + "showdurationnotification-tooltip" : "Полезно, когда сегмент составного файла отсутствует. Возможны ложные срабатывания.", + "language-tooltip" : "Язык, используемый Syncplay.", + "unpause-always-tooltip" : "Когда вы стартуете, статус изменится на готов и начнется воспроизведение, а не просто смена статуса.", + "unpause-ifalreadyready-tooltip" : "Когда вы стартуете не готовым, это меняет статус на готов - нажмите старт еще раз для начала воспроизведения.", + "unpause-ifothersready-tooltip" : "Когда вы стартуете не готовым, воспроизведение начнется, если остальные готовы.", + "unpause-ifminusersready-tooltip" : "Когда вы стартуете не готовым, воспроизведение начнется, если остальные готовы и присутствует достаточное число зрителей.", + "trusteddomains-arguments-tooltip" : "Сайты, которые разрешены для автоматического воспроизведения из общего списка воспроизведения.", - "chatinputenabled-tooltip": u"Enable chat input in mpv (press enter to chat, enter to send, escape to cancel)",# TODO: Translate - "chatdirectinput-tooltip" : u"Skip having to press 'enter' to go into chat input mode in mpv. Press TAB in mpv to temporarily disable this feature.", # TODO: Translate - "font-label-tooltip": u"Font used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.",# TODO: Translate - "set-input-font-tooltip": u"Font family used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.",# TODO: Translate - "set-input-colour-tooltip": u"Font colour used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.",# TODO: Translate - "chatinputposition-tooltip": u"Location in mpv where chat input text will appear when you press enter and type.",# TODO: Translate - "chatinputposition-top-tooltip": u"Place chat input at top of mpv window.", # TODO: Translate - "chatinputposition-middle-tooltip": u"Place chat input in dead centre of mpv window.", # TODO: Translate - "chatinputposition-bottom-tooltip": u"Place chat input at bottom of mpv window.", # TODO: Translate - "chatoutputenabled-tooltip": u"Show chat messages in OSD (if supported by media player).", # TODO: Translate - "font-output-label-tooltip": u"Chat output font.", # TODO: Translate - "set-output-font-tooltip": u"Font used for when displaying chat messages.", # TODO: Translate - "chatoutputmode-tooltip": u"How chat messages are displayed.", # TODO: Translate - "chatoutputmode-chatroom-tooltip": u"Display new lines of chat directly below previous line.", # TODO: Translate - "chatoutputmode-scrolling-tooltip": u"Scroll chat text from right to left.", # TODO: Translate + "chatinputenabled-tooltip": "Enable chat input in mpv (press enter to chat, enter to send, escape to cancel)",# TODO: Translate + "chatdirectinput-tooltip" : "Skip having to press 'enter' to go into chat input mode in mpv. Press TAB in mpv to temporarily disable this feature.", # TODO: Translate + "font-label-tooltip": "Font used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.",# TODO: Translate + "set-input-font-tooltip": "Font family used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.",# TODO: Translate + "set-input-colour-tooltip": "Font colour used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.",# TODO: Translate + "chatinputposition-tooltip": "Location in mpv where chat input text will appear when you press enter and type.",# TODO: Translate + "chatinputposition-top-tooltip": "Place chat input at top of mpv window.", # TODO: Translate + "chatinputposition-middle-tooltip": "Place chat input in dead centre of mpv window.", # TODO: Translate + "chatinputposition-bottom-tooltip": "Place chat input at bottom of mpv window.", # TODO: Translate + "chatoutputenabled-tooltip": "Show chat messages in OSD (if supported by media player).", # TODO: Translate + "font-output-label-tooltip": "Chat output font.", # TODO: Translate + "set-output-font-tooltip": "Font used for when displaying chat messages.", # TODO: Translate + "chatoutputmode-tooltip": "How chat messages are displayed.", # TODO: Translate + "chatoutputmode-chatroom-tooltip": "Display new lines of chat directly below previous line.", # TODO: Translate + "chatoutputmode-scrolling-tooltip": "Scroll chat text from right to left.", # TODO: Translate - "help-tooltip" : u"Открыть Руководство Пользователя на Syncplay.pl.", - "reset-tooltip" : u"Сбрасывает все настройки Syncplay в начальное состояние.", - "update-server-list-tooltip" : u"Обновить список публичных серверов от syncplay.pl.", + "help-tooltip" : "Открыть Руководство Пользователя на Syncplay.pl.", + "reset-tooltip" : "Сбрасывает все настройки Syncplay в начальное состояние.", + "update-server-list-tooltip" : "Обновить список публичных серверов от syncplay.pl.", - "joinroom-tooltip" : u"Покинуть комнату и зайти в другую, указанную комнату.", - "seektime-msgbox-label" : u"Перемотать к определенному моменту времени (указывать в секундах или мин:сек). Используйте +/-, чтобы перемотать вперед/назад относительно настоящего момента.", - "ready-tooltip" : u"Показывает, готовы ли Вы к просмотру или нет.", - "autoplay-tooltip" : u"Автоматическое воспроизведение, когда все пользователи с индикаторами готовности будут готовы и присутствует достаточное число зрителей.", - "switch-to-file-tooltip" : u"Кликните два раза для воспроизведения {}", # Filename - "sendmessage-tooltip" : u"Send message to room", # TODO: Translate + "joinroom-tooltip" : "Покинуть комнату и зайти в другую, указанную комнату.", + "seektime-msgbox-label" : "Перемотать к определенному моменту времени (указывать в секундах или мин:сек). Используйте +/-, чтобы перемотать вперед/назад относительно настоящего момента.", + "ready-tooltip" : "Показывает, готовы ли Вы к просмотру или нет.", + "autoplay-tooltip" : "Автоматическое воспроизведение, когда все пользователи с индикаторами готовности будут готовы и присутствует достаточное число зрителей.", + "switch-to-file-tooltip" : "Кликните два раза для воспроизведения {}", # Filename + "sendmessage-tooltip" : "Send message to room", # TODO: Translate # In-userlist notes (GUI) - "differentsize-note" : u"Размер файла не совпадает!", - "differentsizeandduration-note" : u"Размер и продолжительность файла не совпадают!", - "differentduration-note" : u"Продолжительность файла не совпадает!", - "nofile-note" : u"(ничего)", + "differentsize-note" : "Размер файла не совпадает!", + "differentsizeandduration-note" : "Размер и продолжительность файла не совпадают!", + "differentduration-note" : "Продолжительность файла не совпадает!", + "nofile-note" : "(ничего)", # Server messages to client - "new-syncplay-available-motd-message" : u" Вы используете Syncplay версии {}. Доступна более новая версия на https://syncplay.pl/ . ", # ClientVersion + "new-syncplay-available-motd-message" : " Вы используете Syncplay версии {}. Доступна более новая версия на https://syncplay.pl/ . ", # ClientVersion # Server notifications - "welcome-server-notification" : u"Добро пожаловать на сервер Syncplay версии {0}", # version - "client-connected-room-server-notification" : u"{0}({2}) подключился к комнате '{1}'", # username, host, room - "client-left-server-notification" : u"{0} покинул сервер", # name - "no-salt-notification" : u"ВНИМАНИЕ: Чтобы сгенерированные сервером пароли операторов комнат работали после перезагрузки сервера, необходимо указать следующий аргумент командной строки при запуске сервера Syncplay: --salt {}", #Salt + "welcome-server-notification" : "Добро пожаловать на сервер Syncplay версии {0}", # version + "client-connected-room-server-notification" : "{0}({2}) подключился к комнате '{1}'", # username, host, room + "client-left-server-notification" : "{0} покинул сервер", # name + "no-salt-notification" : "ВНИМАНИЕ: Чтобы сгенерированные сервером пароли операторов комнат работали после перезагрузки сервера, необходимо указать следующий аргумент командной строки при запуске сервера Syncplay: --salt {}", #Salt # Server arguments - "server-argument-description" : u'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC/BE через Интернет. Серверная часть', - "server-argument-epilog" : u'Если параметр не будет передан, то будет использоваться значение, указанное в _config.', - "server-port-argument" : u'номер TCP порта сервера', - "server-password-argument" : u'пароль к серверу', - "server-isolate-room-argument" : u'должны ли комнаты быть изолированными?', - "server-salt-argument" : u"генерировать пароли к управляемым комнатам на основании указанной строки (соли)", - "server-disable-ready-argument" : u"отключить статусы готов/не готов", - "server-motd-argument" : u"путь к файлу, из которого будет извлекаться MOTD-сообщение", + "server-argument-description" : 'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC/BE через Интернет. Серверная часть', + "server-argument-epilog" : 'Если параметр не будет передан, то будет использоваться значение, указанное в _config.', + "server-port-argument" : 'номер TCP порта сервера', + "server-password-argument" : 'пароль к серверу', + "server-isolate-room-argument" : 'должны ли комнаты быть изолированными?', + "server-salt-argument" : "генерировать пароли к управляемым комнатам на основании указанной строки (соли)", + "server-disable-ready-argument" : "отключить статусы готов/не готов", + "server-motd-argument" : "путь к файлу, из которого будет извлекаться MOTD-сообщение", "server-chat-argument" : "Should chat be disabled?", # TODO: Translate - "server-chat-maxchars-argument": u"Maximum number of characters in a chat message (default is {})", # TODO: Translate - "server-maxusernamelength-argument": u"Maximum number of charactrs in a username (default is {})", # TODO: Translate - "server-messed-up-motd-unescaped-placeholders" : u"MOTD-сообщение содержит неэкранированные спец.символы. Все знаки $ должны быть продублированы ($$).", - "server-messed-up-motd-too-long" : u"MOTD-сообщение слишком длинное: максимальная длина - {} символ(ов), текущая длина - {} символ(ов).", + "server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # TODO: Translate + "server-maxusernamelength-argument": "Maximum number of charactrs in a username (default is {})", # TODO: Translate + "server-messed-up-motd-unescaped-placeholders" : "MOTD-сообщение содержит неэкранированные спец.символы. Все знаки $ должны быть продублированы ($$).", + "server-messed-up-motd-too-long" : "MOTD-сообщение слишком длинное: максимальная длина - {} символ(ов), текущая длина - {} символ(ов).", # Server errors - "unknown-command-server-error" : u"Неизвестная команда: {}", # message - "not-json-server-error" : u"Не является закодированной json-строкой: {}", # message - "not-known-server-error" : u"Данную команду могут выполнять только авторизованные пользователи.", - "client-drop-server-error" : u"Клиент отключен с ошибкой: {} -- {}", # host, error - "password-required-server-error" : u"Необходимо указать пароль.", - "wrong-password-server-error" : u"Указан неверный пароль.", - "hello-server-error" : u"Не хватает аргументов Hello.", + "unknown-command-server-error" : "Неизвестная команда: {}", # message + "not-json-server-error" : "Не является закодированной json-строкой: {}", # message + "not-known-server-error" : "Данную команду могут выполнять только авторизованные пользователи.", + "client-drop-server-error" : "Клиент отключен с ошибкой: {} -- {}", # host, error + "password-required-server-error" : "Необходимо указать пароль.", + "wrong-password-server-error" : "Указан неверный пароль.", + "hello-server-error" : "Не хватает аргументов Hello.", - "playlist-selection-changed-notification" : u"{} изменил выбор в списке воспроизведения", # Username - "playlist-contents-changed-notification" : u"{} обновил список воспроизведения", # Username - "cannot-find-file-for-playlist-switch-error" : u"Не удалось найти файл {} в папках воспроизведения!", # Filename - "cannot-add-duplicate-error" : u"'{}' уже есть в списке воспроизведения.", #Filename - "cannot-add-unsafe-path-error" : u"Не удалось автоматически переключиться на {}, потому что ссылка не соответствует доверенным сайтам. Её можно включить вручную, дважны кливнув по ссылке в списке воспроизведения. Добавить доверенный сайт можно в выпадающем меню 'Дополнительно' или просто кликнув по ссылке правой кнопкой мыши.", # Filename - "sharedplaylistenabled-label" : u"Включить общий список воспроизведения", - "removefromplaylist-menu-label" : u"Удалить", - "shufflepremaininglaylist-menuu-label" : u"Shuffle remaining playlist", # Was: Перемешать список # TODO: Translate - "shuffleentireplaylist-menu-label" : u"Shuffle entire playlist", # TODO: Translate - "undoplaylist-menu-label" : u"Отменить последнее действие", - "addfilestoplaylist-menu-label" : u"Добавить файлы в очередь", - "addurlstoplaylist-menu-label" : u"Добавить ссылку в очередь", - "editplaylist-menu-label": u"Редактировать список", + "playlist-selection-changed-notification" : "{} изменил выбор в списке воспроизведения", # Username + "playlist-contents-changed-notification" : "{} обновил список воспроизведения", # Username + "cannot-find-file-for-playlist-switch-error" : "Не удалось найти файл {} в папках воспроизведения!", # Filename + "cannot-add-duplicate-error" : "'{}' уже есть в списке воспроизведения.", #Filename + "cannot-add-unsafe-path-error" : "Не удалось автоматически переключиться на {}, потому что ссылка не соответствует доверенным сайтам. Её можно включить вручную, дважны кливнув по ссылке в списке воспроизведения. Добавить доверенный сайт можно в выпадающем меню 'Дополнительно' или просто кликнув по ссылке правой кнопкой мыши.", # Filename + "sharedplaylistenabled-label" : "Включить общий список воспроизведения", + "removefromplaylist-menu-label" : "Удалить", + "shufflepremaininglaylist-menuu-label" : "Shuffle remaining playlist", # Was: Перемешать список # TODO: Translate + "shuffleentireplaylist-menu-label" : "Shuffle entire playlist", # TODO: Translate + "undoplaylist-menu-label" : "Отменить последнее действие", + "addfilestoplaylist-menu-label" : "Добавить файлы в очередь", + "addurlstoplaylist-menu-label" : "Добавить ссылку в очередь", + "editplaylist-menu-label": "Редактировать список", - "open-containing-folder": u"Open folder containing this file", # TODO: Traslate - "addusersfiletoplaylist-menu-label" : u"Добавить файл {} в список воспроизведения", # item owner indicator - "addusersstreamstoplaylist-menu-label" : u"Добавить поток {} в список воспроизведения", # item owner indicator - "openusersstream-menu-label" : u"Открыть поток от {}", # [username]'s - "openusersfile-menu-label" : u"Открыть файл от {}", # [username]'s - "item-is-yours-indicator" : u"от вас", # Goes with addusersfiletoplaylist/addusersstreamstoplaylist - "item-is-others-indicator" : u"{}", # username - goes with addusersfiletoplaylist/addusersstreamstoplaylist + "open-containing-folder": "Open folder containing this file", # TODO: Traslate + "addusersfiletoplaylist-menu-label" : "Добавить файл {} в список воспроизведения", # item owner indicator + "addusersstreamstoplaylist-menu-label" : "Добавить поток {} в список воспроизведения", # item owner indicator + "openusersstream-menu-label" : "Открыть поток от {}", # [username]'s + "openusersfile-menu-label" : "Открыть файл от {}", # [username]'s + "item-is-yours-indicator" : "от вас", # Goes with addusersfiletoplaylist/addusersstreamstoplaylist + "item-is-others-indicator" : "{}", # username - goes with addusersfiletoplaylist/addusersstreamstoplaylist - "playlist-instruction-item-message" : u"Перетащите сюда файлы, чтобы добавить их в общий список.", - "sharedplaylistenabled-tooltip" : u"Оператор комнаты может добавлять файлы в список общего воспроизведения для удобного совместного просмотра. Папки воспроизведения настраиваются во вкладке 'Файл'.", + "playlist-instruction-item-message" : "Перетащите сюда файлы, чтобы добавить их в общий список.", + "sharedplaylistenabled-tooltip" : "Оператор комнаты может добавлять файлы в список общего воспроизведения для удобного совместного просмотра. Папки воспроизведения настраиваются во вкладке 'Файл'.", } diff --git a/syncplay/players/__init__.py b/syncplay/players/__init__.py old mode 100644 new mode 100755 index 710bfe1..a2a888b --- a/syncplay/players/__init__.py +++ b/syncplay/players/__init__.py @@ -1,16 +1,16 @@ -from syncplay.players.mplayer import MplayerPlayer -from syncplay.players.mpv import MpvPlayer -from syncplay.players.vlc import VlcPlayer -try: - from syncplay.players.mpc import MPCHCAPIPlayer -except ImportError: - from syncplay.players.basePlayer import DummyPlayer - MPCHCAPIPlayer = DummyPlayer -try: - from syncplay.players.mpcbe import MpcBePlayer -except ImportError: - from syncplay.players.basePlayer import DummyPlayer - MpcBePlayer = DummyPlayer - -def getAvailablePlayers(): - return [MPCHCAPIPlayer, MplayerPlayer, MpvPlayer, VlcPlayer, MpcBePlayer] +from syncplay.players.mplayer import MplayerPlayer +from syncplay.players.mpv import MpvPlayer +from syncplay.players.vlc import VlcPlayer +try: + from syncplay.players.mpc import MPCHCAPIPlayer +except ImportError: + from syncplay.players.basePlayer import DummyPlayer + MPCHCAPIPlayer = DummyPlayer +try: + from syncplay.players.mpcbe import MpcBePlayer +except ImportError: + from syncplay.players.basePlayer import DummyPlayer + MpcBePlayer = DummyPlayer + +def getAvailablePlayers(): + return [MPCHCAPIPlayer, MplayerPlayer, MpvPlayer, VlcPlayer, MpcBePlayer] diff --git a/syncplay/players/basePlayer.py b/syncplay/players/basePlayer.py old mode 100644 new mode 100755 index ab93a4c..96069d9 --- a/syncplay/players/basePlayer.py +++ b/syncplay/players/basePlayer.py @@ -1,130 +1,130 @@ -from syncplay import constants -class BasePlayer(object): - - ''' - This method is supposed to - execute updatePlayerStatus(paused, position) on client - Given the arguments: boolean paused and float position in seconds - ''' - def askForStatus(self): - raise NotImplementedError() - - ''' - Display given message on player's OSD or similar means - ''' - def displayMessage(self, message, duration = (constants.OSD_DURATION*1000), secondaryOSD=False, mood=constants.MESSAGE_NEUTRAL): - raise NotImplementedError() - - ''' - Cleanup connection with player before syncplay will close down - ''' - def drop(self): - raise NotImplementedError() - - ''' - Start up the player, returns its instance - ''' - @staticmethod - def run(client, playerPath, filePath, args): - raise NotImplementedError() - - ''' - @type value: boolean - ''' - def setPaused(self, value): - raise NotImplementedError() - - ''' - @type value: list - ''' - def setFeatures(self, featureList): - raise NotImplementedError() - - ''' - @type value: float - ''' - def setPosition(self, value): - raise NotImplementedError() - - ''' - @type value: float - ''' - def setSpeed(self, value): - raise NotImplementedError() - - ''' - @type filePath: string - ''' - def openFile(self, filePath, resetPosition=False): - raise NotImplementedError() - - - ''' - @return: list of strings - ''' - @staticmethod - def getDefaultPlayerPathsList(): - raise NotImplementedError() - - ''' - @type path: string - ''' - @staticmethod - def isValidPlayerPath(path): - raise NotImplementedError() - - ''' - @type path: string - @return: string - ''' - @staticmethod - def getIconPath(path): - raise NotImplementedError() - - ''' - @type path: string - @return: string - ''' - @staticmethod - def getExpandedPath(path): - raise NotImplementedError() - - ''' - Opens a custom media browse dialog, and then changes to that media if appropriate - ''' - @staticmethod - def openCustomOpenDialog(self): - raise NotImplementedError() - - ''' - @type playerPath: string - @type filePath: string - @return errorMessage: string - - Checks if the player has any problems with the given player/file path - ''' - @staticmethod - def getPlayerPathErrors(playerPath, filePath): - raise NotImplementedError() - -class DummyPlayer(BasePlayer): - - @staticmethod - def getDefaultPlayerPathsList(): - return [] - - @staticmethod - def isValidPlayerPath(path): - return False - - @staticmethod - def getIconPath(path): - return None - - @staticmethod - def getExpandedPath(path): - return path - - @staticmethod - def getPlayerPathErrors(playerPath, filePath): +from syncplay import constants +class BasePlayer(object): + + ''' + This method is supposed to + execute updatePlayerStatus(paused, position) on client + Given the arguments: boolean paused and float position in seconds + ''' + def askForStatus(self): + raise NotImplementedError() + + ''' + Display given message on player's OSD or similar means + ''' + def displayMessage(self, message, duration = (constants.OSD_DURATION*1000), secondaryOSD=False, mood=constants.MESSAGE_NEUTRAL): + raise NotImplementedError() + + ''' + Cleanup connection with player before syncplay will close down + ''' + def drop(self): + raise NotImplementedError() + + ''' + Start up the player, returns its instance + ''' + @staticmethod + def run(client, playerPath, filePath, args): + raise NotImplementedError() + + ''' + @type value: boolean + ''' + def setPaused(self, value): + raise NotImplementedError() + + ''' + @type value: list + ''' + def setFeatures(self, featureList): + raise NotImplementedError() + + ''' + @type value: float + ''' + def setPosition(self, value): + raise NotImplementedError() + + ''' + @type value: float + ''' + def setSpeed(self, value): + raise NotImplementedError() + + ''' + @type filePath: string + ''' + def openFile(self, filePath, resetPosition=False): + raise NotImplementedError() + + + ''' + @return: list of strings + ''' + @staticmethod + def getDefaultPlayerPathsList(): + raise NotImplementedError() + + ''' + @type path: string + ''' + @staticmethod + def isValidPlayerPath(path): + raise NotImplementedError() + + ''' + @type path: string + @return: string + ''' + @staticmethod + def getIconPath(path): + raise NotImplementedError() + + ''' + @type path: string + @return: string + ''' + @staticmethod + def getExpandedPath(path): + raise NotImplementedError() + + ''' + Opens a custom media browse dialog, and then changes to that media if appropriate + ''' + @staticmethod + def openCustomOpenDialog(self): + raise NotImplementedError() + + ''' + @type playerPath: string + @type filePath: string + @return errorMessage: string + + Checks if the player has any problems with the given player/file path + ''' + @staticmethod + def getPlayerPathErrors(playerPath, filePath): + raise NotImplementedError() + +class DummyPlayer(BasePlayer): + + @staticmethod + def getDefaultPlayerPathsList(): + return [] + + @staticmethod + def isValidPlayerPath(path): + return False + + @staticmethod + def getIconPath(path): + return None + + @staticmethod + def getExpandedPath(path): + return path + + @staticmethod + def getPlayerPathErrors(playerPath, filePath): return None \ No newline at end of file diff --git a/syncplay/players/mpc.py b/syncplay/players/mpc.py old mode 100644 new mode 100755 index 3d40308..a407147 --- a/syncplay/players/mpc.py +++ b/syncplay/players/mpc.py @@ -1,7 +1,7 @@ #coding:utf8 import time import threading -import thread +import _thread import win32con, win32api, win32gui, ctypes, ctypes.wintypes #@UnresolvedImport @UnusedImport from functools import wraps from syncplay.players.basePlayer import BasePlayer @@ -49,7 +49,7 @@ class MpcHcApi: self.__listener.SendCommand(self.CMD_OPENFILE, filePath) def isPaused(self): - return self.playState <> self.__MPC_PLAYSTATE.PS_PLAY and self.playState <> None + return self.playState != self.__MPC_PLAYSTATE.PS_PLAY and self.playState != None def askForVersion(self): self.__listener.SendCommand(self.CMD_GETVERSION) @@ -72,11 +72,11 @@ class MpcHcApi: @waitForFileStateReady def seek(self, position): - self.__listener.SendCommand(self.CMD_SETPOSITION, unicode(position)) + self.__listener.SendCommand(self.CMD_SETPOSITION, str(position)) @waitForFileStateReady def setSpeed(self, rate): - self.__listener.SendCommand(self.CMD_SETSPEED, unicode(rate)) + self.__listener.SendCommand(self.CMD_SETSPEED, str(rate)) def sendOsd(self, message, MsgPos=constants.MPC_OSD_POSITION, DurationMs=(constants.OSD_DURATION*1000)): class __OSDDATASTRUCT(ctypes.Structure): @@ -99,7 +99,7 @@ class MpcHcApi: self.__listener.mpcHandle = int(value) self.__locks.mpcStart.set() if self.callbacks.onConnected: - thread.start_new_thread(self.callbacks.onConnected, ()) + _thread.start_new_thread(self.callbacks.onConnected, ()) elif cmd == self.CMD_STATE: self.loadState = int(value) @@ -110,12 +110,12 @@ class MpcHcApi: else: self.__locks.fileReady.set() if self.callbacks.onFileStateChange: - thread.start_new_thread(self.callbacks.onFileStateChange, (self.loadState,)) + _thread.start_new_thread(self.callbacks.onFileStateChange, (self.loadState,)) elif cmd == self.CMD_PLAYMODE: self.playState = int(value) if self.callbacks.onUpdatePlaystate: - thread.start_new_thread(self.callbacks.onUpdatePlaystate, (self.playState,)) + _thread.start_new_thread(self.callbacks.onUpdatePlaystate, (self.playState,)) elif cmd == self.CMD_NOWPLAYING: value = re.split(r'(? float(value): #Notify seek is sometimes sent twice + if self.lastFilePosition != float(value): #Notify seek is sometimes sent twice self.lastFilePosition = float(value) if self.callbacks.onSeek: - thread.start_new_thread(self.callbacks.onSeek, (self.lastFilePosition,)) + _thread.start_new_thread(self.callbacks.onSeek, (self.lastFilePosition,)) elif cmd == self.CMD_DISCONNECT: if self.callbacks.onMpcClosed: - thread.start_new_thread(self.callbacks.onMpcClosed, (None,)) + _thread.start_new_thread(self.callbacks.onMpcClosed, (None,)) elif cmd == self.CMD_VERSION: if self.callbacks.onVersion: self.version = value - thread.start_new_thread(self.callbacks.onVersion, (value,)) + _thread.start_new_thread(self.callbacks.onVersion, (value,)) class PlayerNotReadyException(Exception): pass @@ -278,7 +278,7 @@ class MpcHcApi: #print "API:\tin>\t 0x%X\t" % int(pCDS.contents.dwData), ctypes.wstring_at(pCDS.contents.lpData) self.__mpcApi.handleCommand(pCDS.contents.dwData, ctypes.wstring_at(pCDS.contents.lpData)) - def SendCommand(self, cmd, message=u''): + def SendCommand(self, cmd, message=''): #print "API:\t self.__client.getGlobalPaused(): + if self._mpcApi.isPaused() != self.__client.getGlobalPaused(): self.__refreshMpcPlayState() - if self._mpcApi.isPaused() <> self.__client.getGlobalPaused(): + if self._mpcApi.isPaused() != self.__client.getGlobalPaused(): self.__setUpStateForNewlyOpenedFile() @retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1) @@ -482,7 +482,7 @@ class MPCHCAPIPlayer(BasePlayer): @staticmethod def getIconPath(path): - if MPCHCAPIPlayer.getExpandedPath(path).lower().endswith(u'mpc-hc64.exe'.lower()) or MPCHCAPIPlayer.getExpandedPath(path).lower().endswith(u'mpc-hc64_nvo.exe'.lower()): + if MPCHCAPIPlayer.getExpandedPath(path).lower().endswith('mpc-hc64.exe'.lower()) or MPCHCAPIPlayer.getExpandedPath(path).lower().endswith('mpc-hc64_nvo.exe'.lower()): return constants.MPC64_ICONPATH else: return constants.MPC_ICONPATH @@ -496,30 +496,30 @@ class MPCHCAPIPlayer(BasePlayer): @staticmethod def getExpandedPath(path): if os.path.isfile(path): - if path.lower().endswith(u'mpc-hc.exe'.lower()) or path.lower().endswith(u'mpc-hc64.exe'.lower()) or path.lower().endswith(u'mpc-hc64_nvo.exe'.lower()) or path.lower().endswith(u'mpc-hc_nvo.exe'.lower()): + if path.lower().endswith('mpc-hc.exe'.lower()) or path.lower().endswith('mpc-hc64.exe'.lower()) or path.lower().endswith('mpc-hc64_nvo.exe'.lower()) or path.lower().endswith('mpc-hc_nvo.exe'.lower()): return path - if os.path.isfile(path + u"mpc-hc.exe"): - path += u"mpc-hc.exe" + if os.path.isfile(path + "mpc-hc.exe"): + path += "mpc-hc.exe" return path - if os.path.isfile(path + u"\\mpc-hc.exe"): - path += u"\\mpc-hc.exe" + if os.path.isfile(path + "\\mpc-hc.exe"): + path += "\\mpc-hc.exe" return path - if os.path.isfile(path + u"mpc-hc_nvo.exe"): - path += u"mpc-hc_nvo.exe" + if os.path.isfile(path + "mpc-hc_nvo.exe"): + path += "mpc-hc_nvo.exe" return path - if os.path.isfile(path + u"\\mpc-hc_nvo.exe"): - path += u"\\mpc-hc_nvo.exe" + if os.path.isfile(path + "\\mpc-hc_nvo.exe"): + path += "\\mpc-hc_nvo.exe" return path - if os.path.isfile(path + u"mpc-hc64.exe"): - path += u"mpc-hc64.exe" + if os.path.isfile(path + "mpc-hc64.exe"): + path += "mpc-hc64.exe" return path - if os.path.isfile(path + u"\\mpc-hc64.exe"): - path += u"\\mpc-hc64.exe" + if os.path.isfile(path + "\\mpc-hc64.exe"): + path += "\\mpc-hc64.exe" return path - if os.path.isfile(path + u"mpc-hc64_nvo.exe"): - path += u"mpc-hc64_nvo.exe" + if os.path.isfile(path + "mpc-hc64_nvo.exe"): + path += "mpc-hc64_nvo.exe" return path - if os.path.isfile(path + u"\\mpc-hc64_nvo.exe"): - path += u"\\mpc-hc64_nvo.exe" + if os.path.isfile(path + "\\mpc-hc64_nvo.exe"): + path += "\\mpc-hc64_nvo.exe" return path diff --git a/syncplay/players/mpcbe.py b/syncplay/players/mpcbe.py old mode 100644 new mode 100755 index 68b2074..fcda770 --- a/syncplay/players/mpcbe.py +++ b/syncplay/players/mpcbe.py @@ -30,19 +30,19 @@ class MpcBePlayer(MPCHCAPIPlayer): @staticmethod def getExpandedPath(path): if os.path.isfile(path): - if path.lower().endswith(u'mpc-be.exe'.lower()) or path.lower().endswith(u'mpc-be64.exe'.lower()): + if path.lower().endswith('mpc-be.exe'.lower()) or path.lower().endswith('mpc-be64.exe'.lower()): return path - if os.path.isfile(path + u"mpc-be.exe"): - path += u"mpc-be.exe" + if os.path.isfile(path + "mpc-be.exe"): + path += "mpc-be.exe" return path - if os.path.isfile(path + u"\\mpc-be.exe"): - path += u"\\mpc-be.exe" + if os.path.isfile(path + "\\mpc-be.exe"): + path += "\\mpc-be.exe" return path - if os.path.isfile(path + u"mpc-be64.exe"): - path += u"mpc-be64.exe" + if os.path.isfile(path + "mpc-be64.exe"): + path += "mpc-be64.exe" return path - if os.path.isfile(path + u"\\mpc-be64.exe"): - path += u"\\mpc-be64.exe" + if os.path.isfile(path + "\\mpc-be64.exe"): + path += "\\mpc-be64.exe" return path @staticmethod diff --git a/syncplay/players/mplayer.py b/syncplay/players/mplayer.py old mode 100644 new mode 100755 index 4d5b167..42802b4 --- a/syncplay/players/mplayer.py +++ b/syncplay/players/mplayer.py @@ -92,19 +92,19 @@ class MplayerPlayer(BasePlayer): def displayMessage(self, message, duration=(constants.OSD_DURATION * 1000), OSDType=constants.OSD_NOTIFICATION, mood=constants.MESSAGE_NEUTRAL): messageString = self._sanitizeText(message.replace("\\n", "")).replace("", "\\n") - self._listener.sendLine(u'{} "{!s}" {} {}'.format(self.OSD_QUERY, messageString, duration, constants.MPLAYER_OSD_LEVEL).encode('utf-8')) + self._listener.sendLine('{} "{!s}" {} {}'.format(self.OSD_QUERY, messageString, duration, constants.MPLAYER_OSD_LEVEL)) def displayChatMessage(self, username, message): - messageString = u"<{}> {}".format(username, message) + messageString = "<{}> {}".format(username, message) messageString = self._sanitizeText(messageString.replace("\\n", "")).replace("", "\\n") duration = int(constants.OSD_DURATION * 1000) - self._listener.sendLine(u'{} "{!s}" {} {}'.format(self.OSD_QUERY, messageString, duration, constants.MPLAYER_OSD_LEVEL).encode('utf-8')) + self._listener.sendLine('{} "{!s}" {} {}'.format(self.OSD_QUERY, messageString, duration, constants.MPLAYER_OSD_LEVEL)) def setSpeed(self, value): self._setProperty('speed', "{:.2f}".format(value)) def _loadFile(self, filePath): - self._listener.sendLine(u'loadfile {}'.format(self._quoteArg(filePath))) + self._listener.sendLine('loadfile {}'.format(self._quoteArg(filePath))) def openFile(self, filePath, resetPosition=False): self._filepath = filePath @@ -123,7 +123,7 @@ class MplayerPlayer(BasePlayer): time.sleep(0.03) def setPaused(self, value): - if self._paused <> value: + if self._paused != value: self._paused = not self._paused self._listener.sendLine('pause') @@ -160,7 +160,7 @@ class MplayerPlayer(BasePlayer): arg = arg.replace('"', '\\"') arg = arg.replace("\r", "") arg = arg.replace("\n", "") - return u'"{}"'.format(arg) + return '"{}"'.format(arg) def _fileIsLoaded(self): return True @@ -213,7 +213,7 @@ class MplayerPlayer(BasePlayer): self._filepath = value self._pathAsk.set() elif name == "filename": - self._filename = value.decode('utf-8') + self._filename = value self._filenameAsk.set() elif name == "exiting": if value != 'Quit': @@ -262,11 +262,11 @@ class MplayerPlayer(BasePlayer): @staticmethod def getExpandedPath(playerPath): if not os.path.isfile(playerPath): - if os.path.isfile(playerPath + u"mplayer.exe"): - playerPath += u"mplayer.exe" + if os.path.isfile(playerPath + "mplayer.exe"): + playerPath += "mplayer.exe" return playerPath - elif os.path.isfile(playerPath + u"\\mplayer.exe"): - playerPath += u"\\mplayer.exe" + elif os.path.isfile(playerPath + "\\mplayer.exe"): + playerPath += "\\mplayer.exe" return playerPath if os.access(playerPath, os.X_OK): return playerPath @@ -324,9 +324,9 @@ class MplayerPlayer(BasePlayer): if 'TERM' in env: del env['TERM'] if filePath: - self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.__getCwd(filePath, env), env=env) + self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.__getCwd(filePath, env), env=env, universal_newlines=True) else: - self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, env=env) + self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, universal_newlines=True) threading.Thread.__init__(self, name="MPlayer Listener") @@ -358,7 +358,7 @@ class MplayerPlayer(BasePlayer): def sendChat(self, message): if message: - if message[:1] == "/" and message <> "/": + if message[:1] == "/" and message != "/": command = message[1:] if command and command[:1] == "/": message = message[1:] @@ -399,11 +399,11 @@ class MplayerPlayer(BasePlayer): if line.startswith(command): for itemID, deletionCandidate in enumerate(self.sendQueue): if deletionCandidate.startswith(command): - self.__playerController._client.ui.showDebugMessage(u" Remove duplicate (supersede): {}".format(self.sendQueue[itemID])) + self.__playerController._client.ui.showDebugMessage(" Remove duplicate (supersede): {}".format(self.sendQueue[itemID])) try: self.sendQueue.remove(self.sendQueue[itemID]) except UnicodeWarning: - self.__playerController._client.ui.showDebugMessage(u" Unicode mismatch occured when trying to remove duplicate") + self.__playerController._client.ui.showDebugMessage(" Unicode mismatch occured when trying to remove duplicate") # TODO: Prevent this from being triggered pass break @@ -413,7 +413,7 @@ class MplayerPlayer(BasePlayer): if line == command: for itemID, deletionCandidate in enumerate(self.sendQueue): if deletionCandidate == command: - self.__playerController._client.ui.showDebugMessage(u" Remove duplicate (delete both): {}".format(self.sendQueue[itemID])) + self.__playerController._client.ui.showDebugMessage(" Remove duplicate (delete both): {}".format(self.sendQueue[itemID])) self.__playerController._client.ui.showDebugMessage(self.sendQueue[itemID]) return except: @@ -438,9 +438,9 @@ class MplayerPlayer(BasePlayer): def actuallySendLine(self, line): try: - if not isinstance(line, unicode): - line = line.decode('utf8') - line = (line + u"\n").encode('utf8') + #if not isinstance(line, str): + #line = line.decode('utf8') + line = line + "\n" self.__playerController._client.ui.showDebugMessage("player >> {}".format(line)) self.__process.stdin.write(line) except IOError: diff --git a/syncplay/players/mpv.py b/syncplay/players/mpv.py old mode 100644 new mode 100755 index 4872544..b1a3ed8 --- a/syncplay/players/mpv.py +++ b/syncplay/players/mpv.py @@ -21,7 +21,7 @@ class MpvPlayer(MplayerPlayer): constants.MPV_NEW_VERSION = ver is None or int(ver.group(1)) > 0 or int(ver.group(2)) >= 6 constants.MPV_OSC_VISIBILITY_CHANGE_VERSION = False if ver is None else int(ver.group(1)) > 0 or int(ver.group(2)) >= 28 if not constants.MPV_OSC_VISIBILITY_CHANGE_VERSION: - client.ui.showDebugMessage(u"This version of mpv is not known to be compatible with changing the OSC visibility. Please use mpv >=0.28.0.") + client.ui.showDebugMessage("This version of mpv is not known to be compatible with changing the OSC visibility. Please use mpv >=0.28.0.") if constants.MPV_NEW_VERSION: return NewMpvPlayer(client, MpvPlayer.getExpandedPath(playerPath), filePath, args) else: @@ -35,7 +35,7 @@ class MpvPlayer(MplayerPlayer): args.extend(constants.MPV_SLAVE_ARGS) if constants.MPV_NEW_VERSION: args.extend(constants.MPV_SLAVE_ARGS_NEW) - args.extend([u"--script={}".format(findResourcePath("syncplayintf.lua"))]) + args.extend(["--script={}".format(findResourcePath("syncplayintf.lua"))]) return args @staticmethod @@ -56,11 +56,11 @@ class MpvPlayer(MplayerPlayer): @staticmethod def getExpandedPath(playerPath): if not os.path.isfile(playerPath): - if os.path.isfile(playerPath + u"mpv.exe"): - playerPath += u"mpv.exe" + if os.path.isfile(playerPath + "mpv.exe"): + playerPath += "mpv.exe" return playerPath - elif os.path.isfile(playerPath + u"\\mpv.exe"): - playerPath += u"\\mpv.exe" + elif os.path.isfile(playerPath + "\\mpv.exe"): + playerPath += "\\mpv.exe" return playerPath if os.access(playerPath, os.X_OK): return playerPath @@ -82,10 +82,10 @@ class OldMpvPlayer(MpvPlayer): OSD_QUERY = 'show_text' def _setProperty(self, property_, value): - self._listener.sendLine(u"no-osd set {} {}".format(property_, value)) + self._listener.sendLine("no-osd set {} {}".format(property_, value)) def setPaused(self, value): - if self._paused <> value: + if self._paused != value: self._paused = not self._paused self._listener.sendLine('cycle pause') @@ -123,7 +123,7 @@ class NewMpvPlayer(OldMpvPlayer): super(self.__class__, self).displayMessage(message=message,duration=duration,OSDType=OSDType,mood=mood) return messageString = self._sanitizeText(message.replace("\\n", "")).replace("\\\\",constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER).replace("", "\\n") - self._listener.sendLine(u'script-message-to syncplayintf {}-osd-{} "{}"'.format(OSDType, mood, messageString)) + self._listener.sendLine('script-message-to syncplayintf {}-osd-{} "{}"'.format(OSDType, mood, messageString)) def displayChatMessage(self, username, message): if not self._client._config["chatOutputEnabled"]: @@ -131,8 +131,8 @@ class NewMpvPlayer(OldMpvPlayer): return username = self._sanitizeText(username.replace("\\",constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER)) message = self._sanitizeText(message.replace("\\",constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER)) - messageString = u"<{}> {}".format(username, message) - self._listener.sendLine(u'script-message-to syncplayintf chat "{}"'.format(messageString)) + messageString = "<{}> {}".format(username, message) + self._listener.sendLine('script-message-to syncplayintf chat "{}"'.format(messageString)) def setPaused(self, value): if self._paused == value: @@ -147,12 +147,12 @@ class NewMpvPlayer(OldMpvPlayer): def _getProperty(self, property_): floatProperties = ['time-pos'] if property_ in floatProperties: - propertyID = u"={}".format(property_) + propertyID = "={}".format(property_) elif property_ == 'length': - propertyID = u'=duration:${=length:0}' + propertyID = '=duration:${=length:0}' else: propertyID = property_ - self._listener.sendLine(u"print_text ""ANS_{}=${{{}}}""".format(property_, propertyID)) + self._listener.sendLine("print_text ""ANS_{}=${{{}}}""".format(property_, propertyID)) def getCalculatedPosition(self): if self.fileLoaded == False: @@ -208,7 +208,7 @@ class NewMpvPlayer(OldMpvPlayer): self._client.updatePlayerStatus(self._paused if self.fileLoaded else self._client.getGlobalPaused(), self.getCalculatedPosition()) def _getPausedAndPosition(self): - self._listener.sendLine(u"print_text ANS_pause=${pause}\r\nprint_text ANS_time-pos=${=time-pos}") + self._listener.sendLine("print_text ANS_pause=${pause}\r\nprint_text ANS_time-pos=${=time-pos}") def _preparePlayer(self): if self.delayedFilePath: @@ -222,7 +222,7 @@ class NewMpvPlayer(OldMpvPlayer): def _loadFile(self, filePath): self._clearFileLoaded() - self._listener.sendLine(u'loadfile {}'.format(self._quoteArg(filePath)), notReadyAfterThis=True) + self._listener.sendLine('loadfile {}'.format(self._quoteArg(filePath)), notReadyAfterThis=True) def setFeatures(self, featureList): self.sendMpvOptions() @@ -253,14 +253,14 @@ class NewMpvPlayer(OldMpvPlayer): def sendMpvOptions(self): options = [] for option in constants.MPV_SYNCPLAYINTF_OPTIONS_TO_SEND: - options.append(u"{}={}".format(option, self._client._config[option])) + options.append("{}={}".format(option, self._client._config[option])) for option in constants.MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND: options.append(option) for option in constants.MPV_SYNCPLAYINTF_LANGUAGE_TO_SEND: - options.append(u"{}={}".format(option, getMessage(option))) - options.append(u"OscVisibilityChangeCompatible={}".format(constants.MPV_OSC_VISIBILITY_CHANGE_VERSION)) + options.append("{}={}".format(option, getMessage(option))) + options.append("OscVisibilityChangeCompatible={}".format(constants.MPV_OSC_VISIBILITY_CHANGE_VERSION)) options_string = ", ".join(options) - self._listener.sendLine(u'script-message-to syncplayintf set_syncplayintf_options "{}"'.format(options_string)) + self._listener.sendLine('script-message-to syncplayintf set_syncplayintf_options "{}"'.format(options_string)) self._setOSDPosition() def _handleUnknownLine(self, line): diff --git a/syncplay/players/playerFactory.py b/syncplay/players/playerFactory.py old mode 100644 new mode 100755 diff --git a/syncplay/players/vlc.py b/syncplay/players/vlc.py index 316b3a9..18782bb 100755 --- a/syncplay/players/vlc.py +++ b/syncplay/players/vlc.py @@ -1,478 +1,482 @@ -import subprocess -import re -import threading -from syncplay.players.basePlayer import BasePlayer -from syncplay import constants, utils -import os -import sys -import random -import socket -import asynchat, asyncore -import urllib -import time -from syncplay.messages import getMessage -from syncplay.utils import isBSD, isLinux, isWindows, isMacOS - -class VlcPlayer(BasePlayer): - speedSupported = True - customOpenDialog = False - chatOSDSupported = False - alertOSDSupported = True - osdMessageSeparator = "; " - - RE_ANSWER = re.compile(constants.VLC_ANSWER_REGEX) - SLAVE_ARGS = constants.VLC_SLAVE_ARGS - if isMacOS(): - SLAVE_ARGS.extend(constants.VLC_SLAVE_MACOS_ARGS) - else: - SLAVE_ARGS.extend(constants.VLC_SLAVE_NONMACOS_ARGS) - vlcport = random.randrange(constants.VLC_MIN_PORT, constants.VLC_MAX_PORT) if (constants.VLC_MIN_PORT < constants.VLC_MAX_PORT) else constants.VLC_MIN_PORT - - def __init__(self, client, playerPath, filePath, args): - from twisted.internet import reactor - self.reactor = reactor - self._client = client - self._paused = None - self._duration = None - self._filename = None - self._filepath = None - self._filechanged = False - self._lastVLCPositionUpdate = None - self.shownVLCLatencyError = False - self._previousPreviousPosition = -2 - self._previousPosition = -1 - self._position = 0 - try: # Hack to fix locale issue without importing locale library - self.radixChar = "{:n}".format(1.5)[1:2] - if self.radixChar == "" or self.radixChar == "1" or self.radixChar == "5": - raise ValueError - except: - self._client.ui.showErrorMessage("Failed to determine locale. As a fallback Syncplay is using the following radix character: \".\".") - self.radixChar = "." - - self._durationAsk = threading.Event() - self._filenameAsk = threading.Event() - self._pathAsk = threading.Event() - self._positionAsk = threading.Event() - self._pausedAsk = threading.Event() - self._vlcready = threading.Event() - self._vlcclosed = threading.Event() - self._listener = None - try: - self._listener = self.__Listener(self, playerPath, filePath, args, self._vlcready, self._vlcclosed) - except ValueError: - self._client.ui.showErrorMessage(getMessage("vlc-failed-connection"), True) - self.reactor.callFromThread(self._client.stop, True,) - return - try: - self._listener.setDaemon(True) - self._listener.start() - if not self._vlcready.wait(constants.VLC_OPEN_MAX_WAIT_TIME): - self._vlcready.set() - self._client.ui.showErrorMessage(getMessage("vlc-failed-connection"), True) - self.reactor.callFromThread(self._client.stop, True,) - self.reactor.callFromThread(self._client.initPlayer, self,) - except: - pass - - def _fileUpdateClearEvents(self): - self._durationAsk.clear() - self._filenameAsk.clear() - self._pathAsk.clear() - - def _fileUpdateWaitEvents(self): - self._durationAsk.wait() - self._filenameAsk.wait() - self._pathAsk.wait() - - def _onFileUpdate(self): - self._fileUpdateClearEvents() - self._getFileInfo() - self._fileUpdateWaitEvents() - args = (self._filename, self._duration, self._filepath) - self.reactor.callFromThread(self._client.updateFile, *args) - self.setPaused(self._client.getGlobalPaused()) - self.setPosition(self._client.getGlobalPosition()) - - def askForStatus(self): - self._filechanged = False - self._positionAsk.clear() - self._pausedAsk.clear() - self._listener.sendLine(".") - if self._filename and not self._filechanged: - self._positionAsk.wait(constants.PLAYER_ASK_DELAY) - self._client.updatePlayerStatus(self._paused, self.getCalculatedPosition()) - else: - self._client.updatePlayerStatus(self._client.getGlobalPaused(), self._client.getGlobalPosition()) - - def getCalculatedPosition(self): - if self._lastVLCPositionUpdate is None: - return self._client.getGlobalPosition() - diff = time.time() - self._lastVLCPositionUpdate - if diff > constants.PLAYER_ASK_DELAY and not self._paused: - self._client.ui.showDebugMessage("VLC did not response in time, so assuming position is {} ({}+{})".format(self._position + diff, self._position, diff)) - if diff > constants.VLC_LATENCY_ERROR_THRESHOLD: - if not self.shownVLCLatencyError or constants.DEBUG_MODE: - self._client.ui.showErrorMessage(getMessage("media-player-latency-warning").format(int(diff))) - self.shownVLCLatencyError = True - return self._position + diff - else: - return self._position - - def displayMessage(self, message, duration=constants.OSD_DURATION * 1000, OSDType=constants.OSD_DURATION, mood=constants.MESSAGE_NEUTRAL): - duration /= 1000 - if OSDType != constants.OSD_ALERT: - self._listener.sendLine('display-osd: {}, {}, {}'.format('top-right', duration, message.encode('utf8'))) - else: - self._listener.sendLine('display-secondary-osd: {}, {}, {}'.format('center', duration, message.encode('utf8'))) - - def setSpeed(self, value): - self._listener.sendLine("set-rate: {:.2n}".format(value)) - - def setFeatures(self, featureList): - pass - - def setPosition(self, value): - self._lastVLCPositionUpdate = time.time() - self._listener.sendLine("set-position: {}".format(value).replace(".",self.radixChar)) - - def setPaused(self, value): - self._paused = value - if not value: - self._lastVLCPositionUpdate = time.time() - self._listener.sendLine('set-playstate: {}'.format("paused" if value else "playing")) - - def getMRL(self, fileURL): - if utils.isURL(fileURL): - fileURL = fileURL.encode('utf8') - fileURL = urllib.quote(fileURL, safe="%/:=&?~#+!$,;'@()*[]") - return fileURL - - fileURL = fileURL.replace(u'\\', u'/') - fileURL = fileURL.encode('utf8') - fileURL = urllib.quote_plus(fileURL) - if isWindows(): - fileURL = "file:///" + fileURL - else: - fileURL = "file://" + fileURL - fileURL = fileURL.replace("+", "%20") - return fileURL - - def openFile(self, filePath, resetPosition=False): - if not utils.isURL(filePath): - normedPath = os.path.normpath(filePath) - if os.path.isfile(normedPath): - filePath = normedPath - if utils.isASCII(filePath) and not utils.isURL(filePath): - self._listener.sendLine('load-file: {}'.format(filePath.encode('ascii', 'ignore'))) - else: - fileURL = self.getMRL(filePath) - self._listener.sendLine('load-file: {}'.format(fileURL)) - - def _getFileInfo(self): - self._listener.sendLine("get-duration") - self._listener.sendLine("get-filepath") - self._listener.sendLine("get-filename") - - def lineReceived(self, line): - try: - self._client.ui.showDebugMessage("player << {}".format(line)) - except: - pass - match, name, value = self.RE_ANSWER.match(line), "", "" - if match: - name, value = match.group('command'), match.group('argument') - - if line == "filepath-change-notification": - self._filechanged = True - t = threading.Thread(target=self._onFileUpdate) - t.setDaemon(True) - t.start() - elif name == "filepath": - self._filechanged = True - if value == "no-input": - self._filepath = None - else: - if "file://" in value: - value = value.replace("file://", "") - if not os.path.isfile(value): - value = value.lstrip("/") - elif utils.isURL(value): - value = urllib.unquote(value) - value = value.decode('utf-8') - self._filepath = value - self._pathAsk.set() - elif name == "duration": - if value == "no-input": - self._duration = 0 - elif value == "invalid-32-bit-value": - self._duration = 0 - self.drop(getMessage("vlc-failed-versioncheck")) - else: - self._duration = float(value.replace(",", ".")) - self._durationAsk.set() - elif name == "playstate": - self._paused = bool(value != 'playing') if(value != "no-input" and self._filechanged == False) else self._client.getGlobalPaused() - diff = time.time() - self._lastVLCPositionUpdate if self._lastVLCPositionUpdate else 0 - if self._paused == False \ - and self._position == self._previousPreviousPosition \ - and self._previousPosition == self._position \ - and self._duration > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH \ - and (self._duration - self._position) < constants.VLC_EOF_DURATION_THRESHOLD \ - and diff > constants.VLC_LATENCY_ERROR_THRESHOLD: - self._client.ui.showDebugMessage("Treating 'playing' response as 'paused' due to VLC EOF bug") - self.setPaused(True) - self._pausedAsk.set() - elif name == "position": - newPosition = float(value.replace(",", ".")) if (value != "no-input" and self._filechanged == False) else self._client.getGlobalPosition() - if newPosition == self._previousPosition and newPosition <> self._duration and not self._paused: - self._client.ui.showDebugMessage("Not considering position {} duplicate as new time because of VLC time precision bug".format(newPosition)) - self._previousPreviousPosition = self._previousPosition - self._previousPosition = self._position - self._positionAsk.set() - return - self._previousPreviousPosition = self._previousPosition - self._previousPosition = self._position - self._position = newPosition - if self._position < 0 and self._duration > 2147 and self._vlcVersion == "3.0.0": - self.drop(getMessage("vlc-failed-versioncheck")) - self._lastVLCPositionUpdate = time.time() - self._positionAsk.set() - elif name == "filename": - self._filechanged = True - self._filename = value.decode('utf-8') - self._filenameAsk.set() - elif line.startswith("vlc-version: "): - self._vlcVersion = line.split(': ')[1].replace(' ','-').split('-')[0] - if not utils.meetsMinVersion(self._vlcVersion, constants.VLC_MIN_VERSION): - self._client.ui.showErrorMessage(getMessage("vlc-version-mismatch").format(constants.VLC_MIN_VERSION)) - self._vlcready.set() - - @staticmethod - def run(client, playerPath, filePath, args): - vlc = VlcPlayer(client, VlcPlayer.getExpandedPath(playerPath), filePath, args) - return vlc - - @staticmethod - def getDefaultPlayerPathsList(): - l = [] - for path in constants.VLC_PATHS: - p = VlcPlayer.getExpandedPath(path) - if p: - l.append(p) - return l - - @staticmethod - def isValidPlayerPath(path): - if "vlc" in path.lower() and VlcPlayer.getExpandedPath(path): - return True - return False - - @staticmethod - def getPlayerPathErrors(playerPath, filePath): - return None - - @staticmethod - def getIconPath(path): - return constants.VLC_ICONPATH - - @staticmethod - def getExpandedPath(playerPath): - if not os.path.isfile(playerPath): - if os.path.isfile(playerPath + u"vlc.exe"): - playerPath += u"vlc.exe" - return playerPath - elif os.path.isfile(playerPath + u"\\vlc.exe"): - playerPath += u"\\vlc.exe" - return playerPath - if os.access(playerPath, os.X_OK): - return playerPath - for path in os.environ['PATH'].split(':'): - path = os.path.join(os.path.realpath(path), playerPath) - if os.access(path, os.X_OK): - return path - - def drop(self, dropErrorMessage=None): - if self._listener: - self._vlcclosed.clear() - self._listener.sendLine('close-vlc') - self._vlcclosed.wait() - self._durationAsk.set() - self._filenameAsk.set() - self._pathAsk.set() - self._positionAsk.set() - self._vlcready.set() - self._pausedAsk.set() - if dropErrorMessage: - self.reactor.callFromThread(self._client.ui.showErrorMessage, dropErrorMessage, True) - self.reactor.callFromThread(self._client.stop, False,) - - class __Listener(threading.Thread, asynchat.async_chat): - def __init__(self, playerController, playerPath, filePath, args, vlcReady, vlcClosed): - self.__playerController = playerController - self.requestedVLCVersion = False - self.vlcHasResponded = False - self.oldIntfVersion = None - self.timeVLCLaunched = None - call = [playerPath] - if filePath: - if utils.isASCII(filePath): - call.append(filePath) - else: - call.append(self.__playerController.getMRL(filePath)) - def _usevlcintf(vlcIntfPath, vlcIntfUserPath): - vlcSyncplayInterfacePath = vlcIntfPath + "syncplay.lua" - if not os.path.isfile(vlcSyncplayInterfacePath): - vlcSyncplayInterfacePath = vlcIntfUserPath + "syncplay.lua" - if os.path.isfile(vlcSyncplayInterfacePath): - with open(vlcSyncplayInterfacePath, 'rU') as interfacefile: - for line in interfacefile: - if "local connectorversion" in line: - interface_version = line[26:31] - if utils.meetsMinVersion(interface_version, constants.VLC_INTERFACE_MIN_VERSION): - return True - else: - self.oldIntfVersion = line[26:31] - return False - playerController._client.ui.showErrorMessage(getMessage("vlc-interface-not-installed")) - return False - if isLinux(): - playerController.vlcIntfPath = "/usr/lib/vlc/lua/intf/" - playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") - elif isMacOS(): - playerController.vlcIntfPath = "/Applications/VLC.app/Contents/MacOS/share/lua/intf/" - playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), "Library/Application Support/org.videolan.vlc/lua/intf/") - elif isBSD(): - # *BSD ports/pkgs install to /usr/local by default. - # This should also work for all the other BSDs, such as OpenBSD or DragonFly. - playerController.vlcIntfPath = "/usr/local/lib/vlc/lua/intf/" - playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") - else: - playerController.vlcIntfPath = os.path.dirname(playerPath).replace("\\", "/") + "/lua/intf/" - playerController.vlcIntfUserPath = os.path.join(os.getenv('APPDATA', '.'), "VLC\\lua\\intf\\") - playerController.vlcModulePath = playerController.vlcIntfPath + "modules/?.luac" - if _usevlcintf(playerController.vlcIntfPath, playerController.vlcIntfUserPath): - playerController.SLAVE_ARGS.append('--lua-config=syncplay={{port=\"{}\"}}'.format(str(playerController.vlcport))) - else: - if isLinux(): - playerController.vlcDataPath = "/usr/lib/syncplay/resources" - else: - playerController.vlcDataPath = utils.findWorkingDir() + "\\resources" - playerController.SLAVE_ARGS.append('--data-path={}'.format(playerController.vlcDataPath)) - playerController.SLAVE_ARGS.append('--lua-config=syncplay={{modulepath=\"{}\",port=\"{}\"}}'.format(playerController.vlcModulePath, str(playerController.vlcport))) - - call.extend(playerController.SLAVE_ARGS) - if args: - call.extend(args) - - self._vlcready = vlcReady - self._vlcclosed = vlcClosed - self._vlcVersion = None - - if self.oldIntfVersion: - self.__playerController.drop(getMessage("vlc-interface-version-mismatch").format(self.oldIntfVersion,constants.VLC_INTERFACE_MIN_VERSION)) - - else: - self.__process = subprocess.Popen(call, stderr=subprocess.PIPE, stdout=subprocess.PIPE) - self.timeVLCLaunched = time.time() - if self._shouldListenForSTDOUT(): - for line in iter(self.__process.stderr.readline, ''): - self.vlcHasResponded = True - self.timeVLCLaunched = None - if "[syncplay]" in line: - if "Listening on host" in line: - break - if "Hosting Syncplay" in line: - break - elif "Couldn't find lua interface" in line: - playerController._client.ui.showErrorMessage( - getMessage("vlc-failed-noscript").format(line), True) - break - elif "lua interface error" in line: - playerController._client.ui.showErrorMessage( - getMessage("media-player-error").format(line), True) - break - if not isMacOS(): - self.__process.stderr = None - else: - vlcoutputthread = threading.Thread(target = self.handle_vlcoutput, args=()) - vlcoutputthread.setDaemon(True) - vlcoutputthread.start() - threading.Thread.__init__(self, name="VLC Listener") - asynchat.async_chat.__init__(self) - self.set_terminator("\n") - self._ibuffer = [] - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self._sendingData = threading.Lock() - - def _shouldListenForSTDOUT(self): - if isWindows(): - return False # Due to VLC3 not using STDOUT/STDERR - else: - return True - - def initiate_send(self): - with self._sendingData: - asynchat.async_chat.initiate_send(self) - - def run(self): - self._vlcready.clear() - self.connect(('localhost', self.__playerController.vlcport)) - asyncore.loop() - - def handle_connect(self): - asynchat.async_chat.handle_connect(self) - self._vlcready.set() - self.timeVLCLaunched = None - - def collect_incoming_data(self, data): - self._ibuffer.append(data) - - def handle_close(self): - if self.timeVLCLaunched and time.time() - self.timeVLCLaunched < constants.VLC_OPEN_MAX_WAIT_TIME: - try: - self.__playerController._client.ui.showDebugMessage("Failed to connect to VLC, but reconnecting as within max wait time") - except: - pass - self.run() - elif self.vlcHasResponded: - asynchat.async_chat.handle_close(self) - self.__playerController.drop() - else: - self.vlcHasResponded = True - asynchat.async_chat.handle_close(self) - self.__playerController.drop(getMessage("vlc-failed-connection").format(constants.VLC_MIN_VERSION)) - - def handle_vlcoutput(self): - out = self.__process.stderr - for line in iter(out.readline, ''): - if '[syncplay] core interface debug: removing module' in line: - self.__playerController.drop() - break - out.close() - - - def found_terminator(self): - self.vlcHasResponded = True - self.__playerController.lineReceived("".join(self._ibuffer)) - self._ibuffer = [] - - def sendLine(self, line): - if self.connected: - if not self.requestedVLCVersion: - self.requestedVLCVersion = True - self.sendLine("get-vlc-version") - try: - self.push(line + "\n") - if self.__playerController._client and self.__playerController._client.ui: - self.__playerController._client.ui.showDebugMessage("player >> {}".format(line)) - except: - pass - if line == "close-vlc": - self._vlcclosed.set() - if not self.connected and not self.timeVLCLaunched: - # For circumstances where Syncplay is not connected to VLC and is not reconnecting - try: - self.__process.terminate() - except: # When VLC is already closed +import subprocess +import re +import threading +from syncplay.players.basePlayer import BasePlayer +from syncplay import constants, utils +import os +import sys +import random +import socket +import asynchat, asyncore +import urllib.request, urllib.parse, urllib.error +import time +from syncplay.messages import getMessage +from syncplay.utils import isBSD, isLinux, isWindows, isMacOS + +class VlcPlayer(BasePlayer): + speedSupported = True + customOpenDialog = False + chatOSDSupported = False + alertOSDSupported = True + osdMessageSeparator = "; " + + RE_ANSWER = re.compile(constants.VLC_ANSWER_REGEX) + SLAVE_ARGS = constants.VLC_SLAVE_ARGS + if isMacOS(): + SLAVE_ARGS.extend(constants.VLC_SLAVE_MACOS_ARGS) + else: + SLAVE_ARGS.extend(constants.VLC_SLAVE_NONMACOS_ARGS) + vlcport = random.randrange(constants.VLC_MIN_PORT, constants.VLC_MAX_PORT) if (constants.VLC_MIN_PORT < constants.VLC_MAX_PORT) else constants.VLC_MIN_PORT + + def __init__(self, client, playerPath, filePath, args): + from twisted.internet import reactor + self.reactor = reactor + self._client = client + self._paused = None + self._duration = None + self._filename = None + self._filepath = None + self._filechanged = False + self._lastVLCPositionUpdate = None + self.shownVLCLatencyError = False + self._previousPreviousPosition = -2 + self._previousPosition = -1 + self._position = 0 + try: # Hack to fix locale issue without importing locale library + self.radixChar = "{:n}".format(1.5)[1:2] + if self.radixChar == "" or self.radixChar == "1" or self.radixChar == "5": + raise ValueError + except: + self._client.ui.showErrorMessage("Failed to determine locale. As a fallback Syncplay is using the following radix character: \".\".") + self.radixChar = "." + + self._durationAsk = threading.Event() + self._filenameAsk = threading.Event() + self._pathAsk = threading.Event() + self._positionAsk = threading.Event() + self._pausedAsk = threading.Event() + self._vlcready = threading.Event() + self._vlcclosed = threading.Event() + self._listener = None + try: + self._listener = self.__Listener(self, playerPath, filePath, args, self._vlcready, self._vlcclosed) + except ValueError: + self._client.ui.showErrorMessage(getMessage("vlc-failed-connection"), True) + self.reactor.callFromThread(self._client.stop, True,) + return + try: + self._listener.setDaemon(True) + self._listener.start() + if not self._vlcready.wait(constants.VLC_OPEN_MAX_WAIT_TIME): + self._vlcready.set() + self._client.ui.showErrorMessage(getMessage("vlc-failed-connection"), True) + self.reactor.callFromThread(self._client.stop, True,) + self.reactor.callFromThread(self._client.initPlayer, self,) + except: + pass + + def _fileUpdateClearEvents(self): + self._durationAsk.clear() + self._filenameAsk.clear() + self._pathAsk.clear() + + def _fileUpdateWaitEvents(self): + self._durationAsk.wait() + self._filenameAsk.wait() + self._pathAsk.wait() + + def _onFileUpdate(self): + self._fileUpdateClearEvents() + self._getFileInfo() + self._fileUpdateWaitEvents() + args = (self._filename, self._duration, self._filepath) + self.reactor.callFromThread(self._client.updateFile, *args) + self.setPaused(self._client.getGlobalPaused()) + self.setPosition(self._client.getGlobalPosition()) + + def askForStatus(self): + self._filechanged = False + self._positionAsk.clear() + self._pausedAsk.clear() + self._listener.sendLine(".") + if self._filename and not self._filechanged: + self._positionAsk.wait(constants.PLAYER_ASK_DELAY) + self._client.updatePlayerStatus(self._paused, self.getCalculatedPosition()) + else: + self._client.updatePlayerStatus(self._client.getGlobalPaused(), self._client.getGlobalPosition()) + + def getCalculatedPosition(self): + if self._lastVLCPositionUpdate is None: + return self._client.getGlobalPosition() + diff = time.time() - self._lastVLCPositionUpdate + if diff > constants.PLAYER_ASK_DELAY and not self._paused: + self._client.ui.showDebugMessage("VLC did not response in time, so assuming position is {} ({}+{})".format(self._position + diff, self._position, diff)) + if diff > constants.VLC_LATENCY_ERROR_THRESHOLD: + if not self.shownVLCLatencyError or constants.DEBUG_MODE: + self._client.ui.showErrorMessage(getMessage("media-player-latency-warning").format(int(diff))) + self.shownVLCLatencyError = True + return self._position + diff + else: + return self._position + + def displayMessage(self, message, duration=constants.OSD_DURATION * 1000, OSDType=constants.OSD_DURATION, mood=constants.MESSAGE_NEUTRAL): + duration /= 1000 + if OSDType != constants.OSD_ALERT: + self._listener.sendLine('display-osd: {}, {}, {}'.format('top-right', duration, message)) + else: + self._listener.sendLine('display-secondary-osd: {}, {}, {}'.format('center', duration, message)) + + def setSpeed(self, value): + self._listener.sendLine("set-rate: {:.2n}".format(value)) + + def setFeatures(self, featureList): + pass + + def setPosition(self, value): + self._lastVLCPositionUpdate = time.time() + self._listener.sendLine("set-position: {}".format(value).replace(".",self.radixChar)) + + def setPaused(self, value): + self._paused = value + if not value: + self._lastVLCPositionUpdate = time.time() + self._listener.sendLine('set-playstate: {}'.format("paused" if value else "playing")) + + def getMRL(self, fileURL): + if utils.isURL(fileURL): + fileURL = fileURL.encode('utf8') + fileURL = urllib.parse.quote(fileURL, safe="%/:=&?~#+!$,;'@()*[]") + return fileURL + + fileURL = fileURL.replace('\\', '/') + fileURL = fileURL.encode('utf8') + fileURL = urllib.parse.quote_plus(fileURL) + if isWindows(): + fileURL = "file:///" + fileURL + else: + fileURL = "file://" + fileURL + fileURL = fileURL.replace("+", "%20") + return fileURL + + def openFile(self, filePath, resetPosition=False): + if not utils.isURL(filePath): + normedPath = os.path.normpath(filePath) + if os.path.isfile(normedPath): + filePath = normedPath + if utils.isASCII(filePath) and not utils.isURL(filePath): + self._listener.sendLine('load-file: {}'.format(filePath.encode('ascii', 'ignore'))) + else: + fileURL = self.getMRL(filePath) + self._listener.sendLine('load-file: {}'.format(fileURL)) + + def _getFileInfo(self): + self._listener.sendLine("get-duration") + self._listener.sendLine("get-filepath") + self._listener.sendLine("get-filename") + + def lineReceived(self, line): + #try: + line = line.decode('utf-8') + self._client.ui.showDebugMessage("player << {}".format(line)) + #except: + #pass + match, name, value = self.RE_ANSWER.match(line), "", "" + if match: + name, value = match.group('command'), match.group('argument') + + if line == "filepath-change-notification": + self._filechanged = True + t = threading.Thread(target=self._onFileUpdate) + t.setDaemon(True) + t.start() + elif name == "filepath": + self._filechanged = True + if value == "no-input": + self._filepath = None + else: + if "file://" in value: + value = value.replace("file://", "") + if not os.path.isfile(value): + value = value.lstrip("/") + elif utils.isURL(value): + value = urllib.parse.unquote(value) + #value = value.decode('utf-8') + self._filepath = value + self._pathAsk.set() + elif name == "duration": + if value == "no-input": + self._duration = 0 + elif value == "invalid-32-bit-value": + self._duration = 0 + self.drop(getMessage("vlc-failed-versioncheck")) + else: + self._duration = float(value.replace(",", ".")) + self._durationAsk.set() + elif name == "playstate": + self._paused = bool(value != 'playing') if(value != "no-input" and self._filechanged == False) else self._client.getGlobalPaused() + diff = time.time() - self._lastVLCPositionUpdate if self._lastVLCPositionUpdate else 0 + if self._paused == False \ + and self._position == self._previousPreviousPosition \ + and self._previousPosition == self._position \ + and self._duration > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH \ + and (self._duration - self._position) < constants.VLC_EOF_DURATION_THRESHOLD \ + and diff > constants.VLC_LATENCY_ERROR_THRESHOLD: + self._client.ui.showDebugMessage("Treating 'playing' response as 'paused' due to VLC EOF bug") + self.setPaused(True) + self._pausedAsk.set() + elif name == "position": + newPosition = float(value.replace(",", ".")) if (value != "no-input" and self._filechanged == False) else self._client.getGlobalPosition() + if newPosition == self._previousPosition and newPosition != self._duration and not self._paused: + self._client.ui.showDebugMessage("Not considering position {} duplicate as new time because of VLC time precision bug".format(newPosition)) + self._previousPreviousPosition = self._previousPosition + self._previousPosition = self._position + self._positionAsk.set() + return + self._previousPreviousPosition = self._previousPosition + self._previousPosition = self._position + self._position = newPosition + if self._position < 0 and self._duration > 2147 and self._vlcVersion == "3.0.0": + self.drop(getMessage("vlc-failed-versioncheck")) + self._lastVLCPositionUpdate = time.time() + self._positionAsk.set() + elif name == "filename": + self._filechanged = True + self._filename = value + self._filenameAsk.set() + elif line.startswith("vlc-version: "): + self._vlcVersion = line.split(': ')[1].replace(' ','-').split('-')[0] + if not utils.meetsMinVersion(self._vlcVersion, constants.VLC_MIN_VERSION): + self._client.ui.showErrorMessage(getMessage("vlc-version-mismatch").format(constants.VLC_MIN_VERSION)) + self._vlcready.set() + + @staticmethod + def run(client, playerPath, filePath, args): + vlc = VlcPlayer(client, VlcPlayer.getExpandedPath(playerPath), filePath, args) + return vlc + + @staticmethod + def getDefaultPlayerPathsList(): + l = [] + for path in constants.VLC_PATHS: + p = VlcPlayer.getExpandedPath(path) + if p: + l.append(p) + return l + + @staticmethod + def isValidPlayerPath(path): + if "vlc" in path.lower() and VlcPlayer.getExpandedPath(path): + return True + return False + + @staticmethod + def getPlayerPathErrors(playerPath, filePath): + return None + + @staticmethod + def getIconPath(path): + return constants.VLC_ICONPATH + + @staticmethod + def getExpandedPath(playerPath): + if not os.path.isfile(playerPath): + if os.path.isfile(playerPath + "vlc.exe"): + playerPath += "vlc.exe" + return playerPath + elif os.path.isfile(playerPath + "\\vlc.exe"): + playerPath += "\\vlc.exe" + return playerPath + if os.access(playerPath, os.X_OK): + return playerPath + for path in os.environ['PATH'].split(':'): + path = os.path.join(os.path.realpath(path), playerPath) + if os.access(path, os.X_OK): + return path + + def drop(self, dropErrorMessage=None): + if self._listener: + self._vlcclosed.clear() + self._listener.sendLine('close-vlc') + self._vlcclosed.wait() + self._durationAsk.set() + self._filenameAsk.set() + self._pathAsk.set() + self._positionAsk.set() + self._vlcready.set() + self._pausedAsk.set() + if dropErrorMessage: + self.reactor.callFromThread(self._client.ui.showErrorMessage, dropErrorMessage, True) + self.reactor.callFromThread(self._client.stop, False,) + + class __Listener(threading.Thread, asynchat.async_chat): + def __init__(self, playerController, playerPath, filePath, args, vlcReady, vlcClosed): + self.__playerController = playerController + self.requestedVLCVersion = False + self.vlcHasResponded = False + self.oldIntfVersion = None + self.timeVLCLaunched = None + call = [playerPath] + if filePath: + if utils.isASCII(filePath): + call.append(filePath) + else: + call.append(self.__playerController.getMRL(filePath)) + def _usevlcintf(vlcIntfPath, vlcIntfUserPath): + vlcSyncplayInterfacePath = vlcIntfPath + "syncplay.lua" + if not os.path.isfile(vlcSyncplayInterfacePath): + vlcSyncplayInterfacePath = vlcIntfUserPath + "syncplay.lua" + if os.path.isfile(vlcSyncplayInterfacePath): + with open(vlcSyncplayInterfacePath, 'rU') as interfacefile: + for line in interfacefile: + if "local connectorversion" in line: + interface_version = line[26:31] + if utils.meetsMinVersion(interface_version, constants.VLC_INTERFACE_MIN_VERSION): + return True + else: + self.oldIntfVersion = line[26:31] + return False + playerController._client.ui.showErrorMessage(getMessage("vlc-interface-not-installed")) + return False + if isLinux(): + playerController.vlcIntfPath = "/usr/lib/vlc/lua/intf/" + playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") + elif isMacOS(): + playerController.vlcIntfPath = "/Applications/VLC.app/Contents/MacOS/share/lua/intf/" + playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), "Library/Application Support/org.videolan.vlc/lua/intf/") + elif isBSD(): + # *BSD ports/pkgs install to /usr/local by default. + # This should also work for all the other BSDs, such as OpenBSD or DragonFly. + playerController.vlcIntfPath = "/usr/local/lib/vlc/lua/intf/" + playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") + else: + playerController.vlcIntfPath = os.path.dirname(playerPath).replace("\\", "/") + "/lua/intf/" + playerController.vlcIntfUserPath = os.path.join(os.getenv('APPDATA', '.'), "VLC\\lua\\intf\\") + playerController.vlcModulePath = playerController.vlcIntfPath + "modules/?.luac" + if _usevlcintf(playerController.vlcIntfPath, playerController.vlcIntfUserPath): + playerController.SLAVE_ARGS.append('--lua-config=syncplay={{port=\"{}\"}}'.format(str(playerController.vlcport))) + else: + if isLinux(): + playerController.vlcDataPath = "/usr/lib/syncplay/resources" + else: + playerController.vlcDataPath = utils.findWorkingDir() + "\\resources" + playerController.SLAVE_ARGS.append('--data-path={}'.format(playerController.vlcDataPath)) + playerController.SLAVE_ARGS.append('--lua-config=syncplay={{modulepath=\"{}\",port=\"{}\"}}'.format(playerController.vlcModulePath, str(playerController.vlcport))) + + call.extend(playerController.SLAVE_ARGS) + if args: + call.extend(args) + + self._vlcready = vlcReady + self._vlcclosed = vlcClosed + self._vlcVersion = None + + if self.oldIntfVersion: + self.__playerController.drop(getMessage("vlc-interface-version-mismatch").format(self.oldIntfVersion,constants.VLC_INTERFACE_MIN_VERSION)) + + else: + self.__process = subprocess.Popen(call, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + self.timeVLCLaunched = time.time() + if self._shouldListenForSTDOUT(): + for line in iter(self.__process.stderr.readline, ''): + line = line.decode('utf-8') + self.vlcHasResponded = True + self.timeVLCLaunched = None + if "[syncplay]" in line: + if "Listening on host" in line: + break + if "Hosting Syncplay" in line: + break + elif "Couldn't find lua interface" in line: + playerController._client.ui.showErrorMessage( + getMessage("vlc-failed-noscript").format(line), True) + break + elif "lua interface error" in line: + playerController._client.ui.showErrorMessage( + getMessage("media-player-error").format(line), True) + break + if not isMacOS(): + self.__process.stderr = None + else: + vlcoutputthread = threading.Thread(target = self.handle_vlcoutput, args=()) + vlcoutputthread.setDaemon(True) + vlcoutputthread.start() + threading.Thread.__init__(self, name="VLC Listener") + asynchat.async_chat.__init__(self) + self.set_terminator(b'\n') + self._ibuffer = [] + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self._sendingData = threading.Lock() + + def _shouldListenForSTDOUT(self): + if isWindows(): + return False # Due to VLC3 not using STDOUT/STDERR + else: + return True + + def initiate_send(self): + with self._sendingData: + asynchat.async_chat.initiate_send(self) + + def run(self): + self._vlcready.clear() + self.connect(('localhost', self.__playerController.vlcport)) + asyncore.loop() + + def handle_connect(self): + asynchat.async_chat.handle_connect(self) + self._vlcready.set() + self.timeVLCLaunched = None + + def collect_incoming_data(self, data): + self._ibuffer.append(data) + + def handle_close(self): + if self.timeVLCLaunched and time.time() - self.timeVLCLaunched < constants.VLC_OPEN_MAX_WAIT_TIME: + try: + self.__playerController._client.ui.showDebugMessage("Failed to connect to VLC, but reconnecting as within max wait time") + except: + pass + self.run() + elif self.vlcHasResponded: + asynchat.async_chat.handle_close(self) + self.__playerController.drop() + else: + self.vlcHasResponded = True + asynchat.async_chat.handle_close(self) + self.__playerController.drop(getMessage("vlc-failed-connection").format(constants.VLC_MIN_VERSION)) + + def handle_vlcoutput(self): + out = self.__process.stderr + for line in iter(out.readline, ''): + line = line.decode('utf-8') + if '[syncplay] core interface debug: removing module' in line: + self.__playerController.drop() + break + out.close() + + + def found_terminator(self): + self.vlcHasResponded = True + self.__playerController.lineReceived(b"".join(self._ibuffer)) + self._ibuffer = [] + + def sendLine(self, line): + if self.connected: + if not self.requestedVLCVersion: + self.requestedVLCVersion = True + self.sendLine("get-vlc-version") + #try: + lineToSend = line + "\n" + self.push(lineToSend.encode('utf-8')) + if self.__playerController._client and self.__playerController._client.ui: + self.__playerController._client.ui.showDebugMessage("player >> {}".format(line)) + #except: + #pass + if line == "close-vlc": + self._vlcclosed.set() + if not self.connected and not self.timeVLCLaunched: + # For circumstances where Syncplay is not connected to VLC and is not reconnecting + try: + self.__process.terminate() + except: # When VLC is already closed pass \ No newline at end of file diff --git a/syncplay/protocols.py b/syncplay/protocols.py old mode 100644 new mode 100755 index ba0e187..6e9de1e --- a/syncplay/protocols.py +++ b/syncplay/protocols.py @@ -10,7 +10,7 @@ from syncplay.utils import meetsMinVersion class JSONCommandProtocol(LineReceiver): def handleMessages(self, messages): - for message in messages.iteritems(): + for message in messages.items(): command = message[0] if command == "Hello": self.handleHello(message[1]) @@ -28,7 +28,7 @@ class JSONCommandProtocol(LineReceiver): self.dropWithError(getMessage("unknown-command-server-error").format(message[1])) # TODO: log, not drop def lineReceived(self, line): - line = line.strip() + line = line.decode('utf-8').strip() if not line: return try: @@ -41,7 +41,7 @@ class JSONCommandProtocol(LineReceiver): def sendMessage(self, dict_): line = json.dumps(dict_) - self.sendLine(line) + self.sendLine(line.encode('utf-8')) self.showDebugMessage("client/server >> {}".format(line)) def drop(self): @@ -75,12 +75,12 @@ class SyncClientProtocol(JSONCommandProtocol): self.drop() def _extractHelloArguments(self, hello): - username = hello["username"] if hello.has_key("username") else None - roomName = hello["room"]["name"] if hello.has_key("room") else None - version = hello["version"] if hello.has_key("version") else None - version = hello["realversion"] if hello.has_key("realversion") else version # Used for 1.2.X compatibility - motd = hello["motd"] if hello.has_key("motd") else None - features = hello["features"] if hello.has_key("features") else None + username = hello["username"] if "username" in hello else None + roomName = hello["room"]["name"] if "room" in hello else None + version = hello["version"] if "version" in hello else None + version = hello["realversion"] if "realversion" in hello else version # Used for 1.2.X compatibility + motd = hello["motd"] if "motd" in hello else None + features = hello["features"] if "features" in hello else None return username, roomName, version, motd, features def handleHello(self, hello): @@ -111,23 +111,23 @@ class SyncClientProtocol(JSONCommandProtocol): self.sendMessage({"Hello": hello}) def _SetUser(self, users): - for user in users.iteritems(): + for user in users.items(): username = user[0] settings = user[1] - room = settings["room"]["name"] if settings.has_key("room") else None - file_ = settings["file"] if settings.has_key("file") else None - if settings.has_key("event"): - if settings["event"].has_key("joined"): + room = settings["room"]["name"] if "room" in settings else None + file_ = settings["file"] if "file" in settings else None + if "event" in settings: + if "joined" in settings["event"]: self._client.userlist.addUser(username, room, file_) - elif settings["event"].has_key("left"): + elif "left" in settings["event"]: self._client.removeUser(username) else: self._client.userlist.modUser(username, room, file_) def handleSet(self, settings): - for (command, values) in settings.iteritems(): + for (command, values) in settings.items(): if command == "room": - roomName = values["name"] if values.has_key("name") else None + roomName = values["name"] if "name" in values else None self._client.setRoom(roomName) elif command == "user": self._SetUser(values) @@ -142,7 +142,7 @@ class SyncClientProtocol(JSONCommandProtocol): self._client.controlledRoomCreated(roomName, controlPassword) elif command == "ready": user, isReady = values["username"], values["isReady"] - manuallyInitiated = values["manuallyInitiated"] if values.has_key("manuallyInitiated") else True + manuallyInitiated = values["manuallyInitiated"] if "manuallyInitiated" in values else True self._client.setReady(user, isReady, manuallyInitiated) elif command == "playlistIndex": self._client.playlist.changeToPlaylistIndex(values['index'], values['user']) @@ -172,11 +172,11 @@ class SyncClientProtocol(JSONCommandProtocol): def handleList(self, userList): self._client.userlist.clearList() - for room in userList.iteritems(): + for room in userList.items(): roomName = room[0] - for user in room[1].iteritems(): + for user in room[1].items(): userName = user[0] - file_ = user[1]['file'] if user[1]['file'] <> {} else None + file_ = user[1]['file'] if user[1]['file'] != {} else None isController = user[1]['controller'] if 'controller' in user[1] else False isReady = user[1]['isReady'] if 'isReady' in user[1] else None features = user[1]['features'] if 'features' in user[1] else None @@ -187,14 +187,14 @@ class SyncClientProtocol(JSONCommandProtocol): self.sendMessage({"List": None}) def _extractStatePlaystateArguments(self, state): - position = state["playstate"]["position"] if state["playstate"].has_key("position") else 0 - paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None - doSeek = state["playstate"]["doSeek"] if state["playstate"].has_key("doSeek") else None - setBy = state["playstate"]["setBy"] if state["playstate"].has_key("setBy") else None + position = state["playstate"]["position"] if "position" in state["playstate"] else 0 + paused = state["playstate"]["paused"] if "paused" in state["playstate"] else None + doSeek = state["playstate"]["doSeek"] if "doSeek" in state["playstate"] else None + setBy = state["playstate"]["setBy"] if "setBy" in state["playstate"] else None return position, paused, doSeek, setBy def _handleStatePing(self, state): - if state["ping"].has_key("latencyCalculation"): + if "latencyCalculation" in state["ping"]: latencyCalculation = state["ping"]["latencyCalculation"] if "clientLatencyCalculation" in state["ping"]: timestamp = state["ping"]["clientLatencyCalculation"] @@ -206,17 +206,17 @@ class SyncClientProtocol(JSONCommandProtocol): def handleState(self, state): position, paused, doSeek, setBy = None, None, None, None messageAge = 0 - if state.has_key("ignoringOnTheFly"): + if "ignoringOnTheFly" in state: ignore = state["ignoringOnTheFly"] - if ignore.has_key("server"): + if "server" in ignore: self.serverIgnoringOnTheFly = ignore["server"] self.clientIgnoringOnTheFly = 0 - elif ignore.has_key("client"): + elif "client" in ignore: if(ignore['client']) == self.clientIgnoringOnTheFly: self.clientIgnoringOnTheFly = 0 - if state.has_key("playstate"): + if "playstate" in state: position, paused, doSeek, setBy = self._extractStatePlaystateArguments(state) - if state.has_key("ping"): + if "ping" in state: messageAge, latencyCalculation = self._handleStatePing(state) if position is not None and paused is not None and not self.clientIgnoringOnTheFly: self._client.updateGlobalState(position, paused, doSeek, setBy, messageAge) @@ -320,7 +320,7 @@ class SyncServerProtocol(JSONCommandProtocol): pass def dropWithError(self, error): - print getMessage("client-drop-server-error").format(self.transport.getPeer().host, error) + print(getMessage("client-drop-server-error").format(self.transport.getPeer().host, error)) self.sendError(error) self.drop() @@ -348,22 +348,22 @@ class SyncServerProtocol(JSONCommandProtocol): def _extractHelloArguments(self, hello): roomName = None - if hello.has_key("username"): + if "username" in hello: username = hello["username"] username = username.strip() else: username = None - serverPassword = hello["password"] if hello.has_key("password") else None - room = hello["room"] if hello.has_key("room") else None + serverPassword = hello["password"] if "password" in hello else None + room = hello["room"] if "room" in hello else None if room: - if room.has_key("name"): + if "name" in room: roomName = room["name"] roomName = roomName.strip() else: roomName = None - version = hello["version"] if hello.has_key("version") else None - version = hello["realversion"] if hello.has_key("realversion") else version - features = hello["features"] if hello.has_key("features") else None + version = hello["version"] if "version" in hello else None + version = hello["realversion"] if "realversion" in hello else version + features = hello["features"] if "features" in hello else None return username, serverPassword, roomName, version, features def _checkPassword(self, serverPassword): @@ -419,19 +419,19 @@ class SyncServerProtocol(JSONCommandProtocol): @requireLogged def handleSet(self, settings): - for set_ in settings.iteritems(): + for set_ in settings.items(): command = set_[0] if command == "room": - roomName = set_[1]["name"] if set_[1].has_key("name") else None + roomName = set_[1]["name"] if "name" in set_[1] else None self._factory.setWatcherRoom(self._watcher, roomName) elif command == "file": self._watcher.setFile(set_[1]) elif command == "controllerAuth": - password = set_[1]["password"] if set_[1].has_key("password") else None - room = set_[1]["room"] if set_[1].has_key("room") else None + password = set_[1]["password"] if "password" in set_[1] else None + room = set_[1]["room"] if "room" in set_[1] else None self._factory.authRoomController(self._watcher, password, room) elif command == "ready": - manuallyInitiated = set_[1]['manuallyInitiated'] if set_[1].has_key("manuallyInitiated") else False + manuallyInitiated = set_[1]['manuallyInitiated'] if "manuallyInitiated" in set_[1] else False self._factory.setReady(self._watcher, set_[1]['isReady'], manuallyInitiated=manuallyInitiated) elif command == "playlistChange": self._factory.setPlaylist(self._watcher, set_[1]['files']) @@ -558,27 +558,27 @@ class SyncServerProtocol(JSONCommandProtocol): def _extractStatePlaystateArguments(self, state): - position = state["playstate"]["position"] if state["playstate"].has_key("position") else 0 - paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None - doSeek = state["playstate"]["doSeek"] if state["playstate"].has_key("doSeek") else None + position = state["playstate"]["position"] if "position" in state["playstate"] else 0 + paused = state["playstate"]["paused"] if "paused" in state["playstate"] else None + doSeek = state["playstate"]["doSeek"] if "doSeek" in state["playstate"] else None return position, paused, doSeek @requireLogged def handleState(self, state): position, paused, doSeek, latencyCalculation = None, None, None, None - if state.has_key("ignoringOnTheFly"): + if "ignoringOnTheFly" in state: ignore = state["ignoringOnTheFly"] - if ignore.has_key("server"): + if "server" in ignore: if self.serverIgnoringOnTheFly == ignore["server"]: self.serverIgnoringOnTheFly = 0 - if ignore.has_key("client"): + if "client" in ignore: self.clientIgnoringOnTheFly = ignore["client"] - if state.has_key("playstate"): + if "playstate" in state: position, paused, doSeek = self._extractStatePlaystateArguments(state) - if state.has_key("ping"): - latencyCalculation = state["ping"]["latencyCalculation"] if state["ping"].has_key("latencyCalculation") else 0 - clientRtt = state["ping"]["clientRtt"] if state["ping"].has_key("clientRtt") else 0 - self._clientLatencyCalculation = state["ping"]["clientLatencyCalculation"] if state["ping"].has_key("clientLatencyCalculation") else 0 + if "ping" in state: + latencyCalculation = state["ping"]["latencyCalculation"] if "latencyCalculation" in state["ping"] else 0 + clientRtt = state["ping"]["clientRtt"] if "clientRtt" in state["ping"] else 0 + self._clientLatencyCalculation = state["ping"]["clientLatencyCalculation"] if "clientLatencyCalculation" in state["ping"] else 0 self._clientLatencyCalculationArrivalTime = time.time() self._pingService.receiveMessage(latencyCalculation, clientRtt) if self.serverIgnoringOnTheFly == 0: diff --git a/syncplay/server.py b/syncplay/server.py old mode 100644 new mode 100755 index 8e1c625..3c32af5 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -16,13 +16,13 @@ from syncplay.utils import RoomPasswordProvider, NotControlledRoom, RandomString class SyncFactory(Factory): def __init__(self, password='', motdFilePath=None, isolateRooms=False, salt=None, disableReady=False,disableChat=False, maxChatMessageLength=constants.MAX_CHAT_MESSAGE_LENGTH, maxUsernameLength=constants.MAX_USERNAME_LENGTH): self.isolateRooms = isolateRooms - print getMessage("welcome-server-notification").format(syncplay.version) + print(getMessage("welcome-server-notification").format(syncplay.version)) if password: password = hashlib.md5(password).hexdigest() self.password = password if salt is None: salt = RandomStringGenerator.generate_server_salt() - print getMessage("no-salt-notification").format(salt) + print(getMessage("no-salt-notification").format(salt)) self._salt = salt self._motdFilePath = motdFilePath self.disableReady = disableReady @@ -190,13 +190,13 @@ class RoomManager(object): whatLambda(receiver) def broadcast(self, sender, whatLambda): - for room in self._rooms.itervalues(): + for room in self._rooms.values(): for receiver in room.getWatchers(): whatLambda(receiver) def getAllWatchersForUser(self, sender): watchers = [] - for room in self._rooms.itervalues(): + for room in self._rooms.values(): for watcher in room.getWatchers(): watchers.append(watcher) return watchers @@ -231,7 +231,7 @@ class RoomManager(object): def findFreeUsername(self, username): username = truncateText(username,constants.MAX_USERNAME_LENGTH) allnames = [] - for room in self._rooms.itervalues(): + for room in self._rooms.values(): for watcher in room.getWatchers(): allnames.append(watcher.getName().lower()) while username.lower() in allnames: @@ -293,7 +293,7 @@ class Room(object): def setPosition(self, position, setBy=None): self._position = position - for watcher in self._watchers.itervalues(): + for watcher in self._watchers.values(): watcher.setPosition(position) self._setBy = setBy @@ -304,7 +304,7 @@ class Room(object): return self._playState == self.STATE_PAUSED def getWatchers(self): - return self._watchers.values() + return list(self._watchers.values()) def addWatcher(self, watcher): if self._watchers: @@ -404,7 +404,7 @@ class Watcher(object): reactor.callLater(0.1, self._scheduleSendState) def setFile(self, file_): - if file_ and file_.has_key("name"): + if file_ and "name" in file_: file_["name"] = truncateText(file_["name"],constants.MAX_FILENAME_LENGTH) self._file = file_ self._server.sendFileUpdate(self) diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index 72cd879..aacf750 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -1,4 +1,4 @@ -from ConfigParser import SafeConfigParser, DEFAULTSECT +from configparser import SafeConfigParser, DEFAULTSECT import argparse import os import sys @@ -241,7 +241,7 @@ class ConfigurationGetter(object): for key in self._serialised: if self._config[key] is None or self._config[key] == "": self._config[key] = {} - elif isinstance(self._config[key], (str, unicode)): + elif isinstance(self._config[key], str): self._config[key] = ast.literal_eval(self._config[key]) for key in self._tristate: @@ -258,7 +258,7 @@ class ConfigurationGetter(object): for key in self._hexadecimal: match = re.search(r'^#(?:[0-9a-fA-F]){6}$', self._config[key]) if not match: - self._config[key] = u"#FFFFFF" + self._config[key] = "#FFFFFF" for key in self._required: if key == "playerPath": @@ -284,7 +284,7 @@ class ConfigurationGetter(object): raise InvalidConfigValue(getMessage("empty-value-config-error").format(key.capitalize())) def _overrideConfigWithArgs(self, args): - for key, val in vars(args).items(): + for key, val in list(vars(args).items()): if val: if key == "force_gui_prompt": key = "forceGuiPrompt" @@ -326,12 +326,12 @@ class ConfigurationGetter(object): for name in constants.CONFIG_NAMES: if configFile and os.path.isfile(configFile): break - if os.name <> 'nt': + if os.name != 'nt': configFile = os.path.join(os.getenv('HOME', '.'), name) else: configFile = os.path.join(os.getenv('APPDATA', '.'), name) if configFile and not os.path.isfile(configFile): - if os.name <> 'nt': + if os.name != 'nt': configFile = os.path.join(os.getenv('HOME', '.'), constants.DEFAULT_CONFIG_NAME_LINUX) else: configFile = os.path.join(os.getenv('APPDATA', '.'), constants.DEFAULT_CONFIG_NAME_WINDOWS) @@ -346,7 +346,7 @@ class ConfigurationGetter(object): else: return parser.readfp(codecs.open(iniPath, "r", "utf_8_sig")) - for section, options in self._iniStructure.items(): + for section, options in list(self._iniStructure.items()): if parser.has_section(section): for option in options: if parser.has_option(section, option): @@ -357,7 +357,7 @@ class ConfigurationGetter(object): self._validateArguments() except InvalidConfigValue as e: try: - for key, value in self._promptForMissingArguments(e.message).items(): + for key, value in list(self._promptForMissingArguments(e.message).items()): self._config[key] = value self._checkConfig() except: @@ -366,8 +366,8 @@ class ConfigurationGetter(object): def _promptForMissingArguments(self, error=None): if self._config['noGui']: if error: - print "{}!".format(error) - print getMessage("missing-arguments-error") + print("{}!".format(error)) + print(getMessage("missing-arguments-error")) sys.exit() else: from syncplay.ui.GuiConfiguration import GuiConfiguration @@ -378,7 +378,7 @@ class ConfigurationGetter(object): def __wasOptionChanged(self, parser, section, option): if parser.has_option(section, option): - if parser.get(section, option) != unicode(self._config[option]): + if parser.get(section, option) != str(self._config[option]): return True else: return True @@ -389,14 +389,14 @@ class ConfigurationGetter(object): return parser = SafeConfigParserUnicode() parser.readfp(codecs.open(iniPath, "r", "utf_8_sig")) - for section, options in self._iniStructure.items(): + for section, options in list(self._iniStructure.items()): if not parser.has_section(section): parser.add_section(section) changed = True for option in options: if self.__wasOptionChanged(parser, section, option): changed = True - parser.set(section, option, unicode(self._config[option]).replace('%', '%%')) + parser.set(section, option, str(self._config[option]).replace('%', '%%')) if changed: parser.write(codecs.open(iniPath, "wb", "utf_8_sig")) @@ -409,7 +409,7 @@ class ConfigurationGetter(object): pass try: - for key, value in self._promptForMissingArguments().items(): + for key, value in list(self._promptForMissingArguments().items()): self._config[key] = value except GuiConfiguration.WindowClosed: sys.exit() @@ -431,7 +431,7 @@ class ConfigurationGetter(object): for name in constants.CONFIG_NAMES: path = location + os.path.sep + name if os.path.isfile(path) and (os.name == 'nt' or path != os.path.join(os.getenv('HOME', '.'), constants.DEFAULT_CONFIG_NAME_LINUX)): - loadedPaths.append(u"'{}'".format(os.path.normpath(path))) + loadedPaths.append("'{}'".format(os.path.normpath(path))) self._parseConfigFile(path, createConfig=False) self._checkConfig() return loadedPaths @@ -456,13 +456,13 @@ class ConfigurationGetter(object): self._argparser.add_argument('-p', '--password', metavar='password', type=str, nargs='?', help=getMessage("password-argument")) self._argparser.add_argument('--player-path', metavar='path', type=str, help=getMessage("player-path-argument")) self._argparser.add_argument('--language', metavar='language', type=str, help=getMessage("language-argument")) - self._argparser.add_argument('file', metavar='file', type=lambda s: unicode(s, 'utf8'), nargs='?', help=getMessage("file-argument")) + self._argparser.add_argument('file', metavar='file', type=lambda s: str(s, 'utf8'), nargs='?', help=getMessage("file-argument")) self._argparser.add_argument('--clear-gui-data', action='store_true', help=getMessage("clear-gui-data-argument")) self._argparser.add_argument('-v', '--version', action='store_true', help=getMessage("version-argument")) self._argparser.add_argument('_args', metavar='options', type=str, nargs='*', help=getMessage("args-argument")) args = self._argparser.parse_args() if args.version: - print getMessage("version-message").format(version, milestone) + print(getMessage("version-message").format(version, milestone)) sys.exit() self._overrideConfigWithArgs(args) if not self._config['noGui']: @@ -479,7 +479,7 @@ class ConfigurationGetter(object): import appnope appnope.nope() except ImportError: - print getMessage("unable-import-gui-error") + print(getMessage("unable-import-gui-error")) self._config['noGui'] = True if self._config['file'] and self._config['file'][:2] == "--": self._config['playerArgs'].insert(0, self._config['file']) @@ -511,15 +511,15 @@ class SafeConfigParserUnicode(SafeConfigParser): """Write an .ini-format representation of the configuration state.""" if self._defaults: fp.write("[%s]\n" % DEFAULTSECT) - for (key, value) in self._defaults.items(): + for (key, value) in list(self._defaults.items()): fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t'))) fp.write("\n") for section in self._sections: fp.write("[%s]\n" % section) - for (key, value) in self._sections[section].items(): + for (key, value) in list(self._sections[section].items()): if key == "__name__": continue if (value is not None) or (self._optcre == self.OPTCRE): - key = " = ".join((key, unicode(value).replace('\n', '\n\t'))) + key = " = ".join((key, str(value).replace('\n', '\n\t'))) fp.write("%s\n" % key) fp.write("\n") diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 5a092f0..7af5bfc 100755 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -211,7 +211,7 @@ class ConfigDialog(QtWidgets.QDialog): take a long time to perform their checks and hang the GUI while doing so. """ - currentplayerpath = unicode(self.executablepathCombobox.currentText()) + currentplayerpath = str(self.executablepathCombobox.currentText()) self._playerProbeThread.setPlayerPath(currentplayerpath) def updatePlayerArguments(self, currentplayerpath): @@ -224,11 +224,11 @@ class ConfigDialog(QtWidgets.QDialog): currentplayerpath = self.executablepathCombobox.currentText() if currentplayerpath: - NewPlayerArgs = self.playerargsTextbox.text().split(u" ") if self.playerargsTextbox.text() else "" + NewPlayerArgs = self.playerargsTextbox.text().split(" ") if self.playerargsTextbox.text() else "" self.perPlayerArgs[self.executablepathCombobox.currentText()]=NewPlayerArgs def languageChanged(self): - setLanguage(unicode(self.languageCombobox.itemData(self.languageCombobox.currentIndex()))) + setLanguage(str(self.languageCombobox.itemData(self.languageCombobox.currentIndex()))) QtWidgets.QMessageBox.information(self, "Syncplay", getMessage("language-changed-msgbox-label")) def browsePlayerpath(self): @@ -325,7 +325,7 @@ class ConfigDialog(QtWidgets.QDialog): def getMoreState(self): settings = QSettings("Syncplay", "MoreSettings") settings.beginGroup("MoreSettings") - morestate = unicode.lower(unicode(settings.value("ShowMoreSettings", "false"))) + morestate = str.lower(str(settings.value("ShowMoreSettings", "false"))) settings.endGroup() if morestate == "true": return True @@ -342,7 +342,7 @@ class ConfigDialog(QtWidgets.QDialog): try: servers = utils.getListOfPublicServers() except IOError as e: - self.showErrorMessage(unicode(e)) + self.showErrorMessage(str(e)) return currentServer = self.hostCombobox.currentText() self.hostCombobox.clear() @@ -410,18 +410,18 @@ class ConfigDialog(QtWidgets.QDialog): self.config['password'] = self.serverpassTextbox.text() self.processWidget(self, lambda w: self.saveValues(w)) if self.hostCombobox.currentText(): - self.config['host'] = self.hostCombobox.currentText() if ":" in self.hostCombobox.currentText() else self.hostCombobox.currentText() + ":" + unicode(constants.DEFAULT_PORT) + self.config['host'] = self.hostCombobox.currentText() if ":" in self.hostCombobox.currentText() else self.hostCombobox.currentText() + ":" + str(constants.DEFAULT_PORT) self.config['host'] = self.config['host'].replace(" ","").replace("\t", "").replace("\n","").replace("\r","") else: self.config['host'] = None - self.config['playerPath'] = unicode(self.safenormcaseandpath(self.executablepathCombobox.currentText())) - self.config['language'] = unicode(self.languageCombobox.itemData(self.languageCombobox.currentIndex())) + self.config['playerPath'] = str(self.safenormcaseandpath(self.executablepathCombobox.currentText())) + self.config['language'] = str(self.languageCombobox.itemData(self.languageCombobox.currentIndex())) if self.mediapathTextbox.text() == "": self.config['file'] = None elif os.path.isfile(os.path.abspath(self.mediapathTextbox.text())): self.config['file'] = os.path.abspath(self.mediapathTextbox.text()) else: - self.config['file'] = unicode(self.mediapathTextbox.text()) + self.config['file'] = str(self.mediapathTextbox.text()) self.config['publicServers'] = self.publicServerAddresses self.pressedclosebutton = False @@ -448,7 +448,7 @@ class ConfigDialog(QtWidgets.QDialog): data = event.mimeData() urls = data.urls() if urls and urls[0].scheme() == 'file': - dropfilepath = os.path.abspath(unicode(event.mimeData().urls()[0].toLocalFile())) + dropfilepath = os.path.abspath(str(event.mimeData().urls()[0].toLocalFile())) if dropfilepath[-4:].lower() == ".exe": self.executablepathCombobox.setEditText(dropfilepath) else: @@ -524,7 +524,7 @@ class ConfigDialog(QtWidgets.QDialog): def connectChildren(self, widget): widgetName = str(widget.objectName()) - if self.subitems.has_key(widgetName): + if widgetName in self.subitems: widget.stateChanged.connect(lambda: self.updateSubwidgets(self, widget)) self.updateSubwidgets(self, widget) @@ -894,7 +894,7 @@ class ConfigDialog(QtWidgets.QDialog): self.chatDirectInputCheckbox = QCheckBox(getMessage("chatdirectinput-label")) self.chatDirectInputCheckbox.setObjectName("chatDirectInput") self.chatDirectInputCheckbox.setStyleSheet( - constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + u"chevrons_right.png")) + constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + "chevrons_right.png")) self.chatInputLayout.addWidget(self.chatDirectInputCheckbox, 2, 0, 1,1, Qt.AlignLeft) self.inputFontLayout = QtWidgets.QHBoxLayout() @@ -903,7 +903,7 @@ class ConfigDialog(QtWidgets.QDialog): self.inputFontFrame.setLayout(self.inputFontLayout) self.inputFontFrame.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.chatFontLabel = QLabel(getMessage("chatinputfont-label"), self) - self.chatFontLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + u"chevrons_right.png")) + self.chatFontLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + "chevrons_right.png")) self.chatFontLabel.setObjectName("font-label") self.chatInputFontButton = QtWidgets.QPushButton(getMessage("chatfont-label")) self.chatInputFontButton.setObjectName("set-input-font") @@ -926,7 +926,7 @@ class ConfigDialog(QtWidgets.QDialog): self.chatInputPositionFrame.setLayout(self.chatInputPositionLayout) self.chatInputPositionFrame.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.chatInputPositionLabel = QLabel(getMessage("chatinputposition-label"), self) - self.chatInputPositionLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + u"chevrons_right.png")) + self.chatInputPositionLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + "chevrons_right.png")) self.chatInputPositionGroup = QButtonGroup() self.chatInputTopOption = QRadioButton(getMessage("chat-top-option")) self.chatInputMiddleOption = QRadioButton(getMessage("chat-middle-option")) @@ -965,7 +965,7 @@ class ConfigDialog(QtWidgets.QDialog): self.outputFontFrame.setLayout(self.outputFontLayout) self.outputFontFrame.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.chatOutputFontLabel = QLabel(getMessage("chatoutputfont-label"), self) - self.chatOutputFontLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + u"chevrons_right.png")) + self.chatOutputFontLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + "chevrons_right.png")) self.chatOutputFontLabel.setObjectName("font-output-label") self.chatOutputFontButton = QtWidgets.QPushButton(getMessage("chatfont-label")) self.chatOutputFontButton.setObjectName("set-output-font") @@ -978,7 +978,7 @@ class ConfigDialog(QtWidgets.QDialog): self.chatOutputLayout.addWidget(self.outputFontFrame, 2, 0, 1, 3, Qt.AlignLeft) self.chatOutputModeLabel = QLabel(getMessage("chatoutputposition-label"), self) - self.chatOutputModeLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + u"chevrons_right.png")) + self.chatOutputModeLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + "chevrons_right.png")) self.chatOutputModeGroup = QButtonGroup() self.chatOutputChatroomOption = QRadioButton(getMessage("chat-chatroom-option")) self.chatOutputScrollingOption = QRadioButton(getMessage("chat-scrolling-option")) @@ -1008,23 +1008,23 @@ class ConfigDialog(QtWidgets.QDialog): def fontDialog(self, configName): font = QtGui.QFont() - font.setFamily(self.config[configName+ u"FontFamily"]) - font.setPointSize(self.config[configName + u"RelativeFontSize"]) - font.setWeight(self.config[configName + u"FontWeight"]) - font.setUnderline(self.config[configName + u"FontUnderline"]) + font.setFamily(self.config[configName+ "FontFamily"]) + font.setPointSize(self.config[configName + "RelativeFontSize"]) + font.setWeight(self.config[configName + "FontWeight"]) + font.setUnderline(self.config[configName + "FontUnderline"]) value, ok = QtWidgets.QFontDialog.getFont(font) if ok: - self.config[configName + u"FontFamily"] = value.family() - self.config[configName + u"RelativeFontSize"] = value.pointSize() - self.config[configName + u"FontWeight"] = value.weight() - self.config[configName + u"FontUnderline"] = value.underline() + self.config[configName + "FontFamily"] = value.family() + self.config[configName + "RelativeFontSize"] = value.pointSize() + self.config[configName + "FontWeight"] = value.weight() + self.config[configName + "FontUnderline"] = value.underline() def colourDialog(self, configName): oldColour = QtGui.QColor() - oldColour.setNamedColor(self.config[configName+ u"FontColor"]) + oldColour.setNamedColor(self.config[configName+ "FontColor"]) colour = QtWidgets.QColorDialog.getColor(oldColour, self) if colour.isValid(): - self.config[configName + u"FontColor"] = colour.name() + self.config[configName + "FontColor"] = colour.name() def addMessageTab(self): self.messageFrame = QtWidgets.QFrame() @@ -1042,27 +1042,27 @@ class ConfigDialog(QtWidgets.QDialog): self.showSameRoomOSDCheckbox = QCheckBox(getMessage("showsameroomosd-label")) self.showSameRoomOSDCheckbox.setObjectName("showSameRoomOSD") - self.showSameRoomOSDCheckbox.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(posixresourcespath + u"chevrons_right.png")) + self.showSameRoomOSDCheckbox.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(posixresourcespath + "chevrons_right.png")) self.osdSettingsLayout.addWidget(self.showSameRoomOSDCheckbox) self.showNonControllerOSDCheckbox = QCheckBox(getMessage("shownoncontrollerosd-label")) self.showNonControllerOSDCheckbox.setObjectName("showNonControllerOSD") - self.showNonControllerOSDCheckbox.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(posixresourcespath + u"chevrons_right.png")) + self.showNonControllerOSDCheckbox.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(posixresourcespath + "chevrons_right.png")) self.osdSettingsLayout.addWidget(self.showNonControllerOSDCheckbox) self.showDifferentRoomOSDCheckbox = QCheckBox(getMessage("showdifferentroomosd-label")) self.showDifferentRoomOSDCheckbox.setObjectName("showDifferentRoomOSD") - self.showDifferentRoomOSDCheckbox.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(posixresourcespath + u"chevrons_right.png")) + self.showDifferentRoomOSDCheckbox.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(posixresourcespath + "chevrons_right.png")) self.osdSettingsLayout.addWidget(self.showDifferentRoomOSDCheckbox) self.slowdownOSDCheckbox = QCheckBox(getMessage("showslowdownosd-label")) self.slowdownOSDCheckbox.setObjectName("showSlowdownOSD") - self.slowdownOSDCheckbox.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(posixresourcespath + u"chevrons_right.png")) + self.slowdownOSDCheckbox.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(posixresourcespath + "chevrons_right.png")) self.osdSettingsLayout.addWidget(self.slowdownOSDCheckbox) self.showOSDWarningsCheckbox = QCheckBox(getMessage("showosdwarnings-label")) self.showOSDWarningsCheckbox.setObjectName("showOSDWarnings") - self.showOSDWarningsCheckbox.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(posixresourcespath + u"chevrons_right.png")) + self.showOSDWarningsCheckbox.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(posixresourcespath + "chevrons_right.png")) self.osdSettingsLayout.addWidget(self.showOSDWarningsCheckbox) self.subitems['showOSD'] = ["showSameRoomOSD", "showDifferentRoomOSD", "showSlowdownOSD", "showOSDWarnings", "showNonControllerOSD"] @@ -1121,22 +1121,22 @@ class ConfigDialog(QtWidgets.QDialog): self.bottomButtonFrame = QtWidgets.QFrame() self.bottomButtonLayout = QtWidgets.QHBoxLayout() - self.helpButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'help.png'), getMessage("help-label")) + self.helpButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + 'help.png'), getMessage("help-label")) self.helpButton.setObjectName("help") self.helpButton.setMaximumSize(self.helpButton.sizeHint()) self.helpButton.released.connect(self.openHelp) - self.resetButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'cog_delete.png'),getMessage("reset-label")) + self.resetButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + 'cog_delete.png'),getMessage("reset-label")) self.resetButton.setMaximumSize(self.resetButton.sizeHint()) self.resetButton.setObjectName("reset") self.resetButton.released.connect(self.resetSettings) - self.runButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("run-label")) + self.runButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + 'accept.png'), getMessage("run-label")) self.runButton.released.connect(self._runWithoutStoringConfig) - self.runButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("run-label")) + self.runButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + 'accept.png'), getMessage("run-label")) self.runButton.pressed.connect(self._runWithoutStoringConfig) self.runButton.setToolTip(getMessage("nostore-tooltip")) - self.storeAndRunButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("storeandrun-label")) + self.storeAndRunButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + 'accept.png'), getMessage("storeandrun-label")) self.storeAndRunButton.released.connect(self._saveDataAndLeave) self.bottomButtonLayout.addWidget(self.helpButton) self.bottomButtonLayout.addWidget(self.resetButton) @@ -1164,12 +1164,12 @@ class ConfigDialog(QtWidgets.QDialog): self.tabListLayout = QtWidgets.QHBoxLayout() self.tabListFrame = QtWidgets.QFrame() self.tabListWidget = QtWidgets.QListWidget() - self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"house.png"),getMessage("basics-label"))) - self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"control_pause_blue.png"),getMessage("readiness-label"))) - self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"film_link.png"),getMessage("sync-label"))) - self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"user_comment.png"), getMessage("chat-label"))) - self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"error.png"),getMessage("messages-label"))) - self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"cog.png"),getMessage("misc-label"))) + self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + "house.png"),getMessage("basics-label"))) + self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + "control_pause_blue.png"),getMessage("readiness-label"))) + self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + "film_link.png"),getMessage("sync-label"))) + self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + "user_comment.png"), getMessage("chat-label"))) + self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + "error.png"),getMessage("messages-label"))) + self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + "cog.png"),getMessage("misc-label"))) self.tabListLayout.addWidget(self.tabListWidget) self.tabListFrame.setLayout(self.tabListLayout) self.tabListFrame.setFixedWidth(self.tabListFrame.minimumSizeHint().width() + constants.TAB_PADDING) @@ -1237,7 +1237,7 @@ class ConfigDialog(QtWidgets.QDialog): self.hostCombobox.setEditText(currentServer) def updatePasswordVisibilty(self): - if (self.hostCombobox.currentText() == "" and self.serverpassTextbox.text() == "") or unicode(self.hostCombobox.currentText()) in self.publicServerAddresses: + if (self.hostCombobox.currentText() == "" and self.serverpassTextbox.text() == "") or str(self.hostCombobox.currentText()) in self.publicServerAddresses: self.serverpassTextbox.setDisabled(True) self.serverpassTextbox.setReadOnly(True) if self.serverpassTextbox.text() != "": @@ -1272,15 +1272,15 @@ class ConfigDialog(QtWidgets.QDialog): if isWindows(): resourcespath = utils.findWorkingDir() + "\\resources\\" else: - resourcespath = utils.findWorkingDir() + u"/resources/" - self.posixresourcespath = utils.findWorkingDir().replace(u"\\","/") + u"/resources/" + resourcespath = utils.findWorkingDir() + "/resources/" + self.posixresourcespath = utils.findWorkingDir().replace("\\","/") + "/resources/" self.resourcespath = resourcespath super(ConfigDialog, self).__init__() self.setWindowTitle(getMessage("config-window-title")) self.setWindowFlags(self.windowFlags() & Qt.WindowCloseButtonHint & ~Qt.WindowContextHelpButtonHint) - self.setWindowIcon(QtGui.QIcon(resourcespath + u"syncplay.png")) + self.setWindowIcon(QtGui.QIcon(resourcespath + "syncplay.png")) self.stackedLayout = QtWidgets.QStackedLayout() self.stackedFrame = QtWidgets.QFrame() diff --git a/syncplay/ui/__init__.py b/syncplay/ui/__init__.py old mode 100644 new mode 100755 diff --git a/syncplay/ui/consoleUI.py b/syncplay/ui/consoleUI.py old mode 100644 new mode 100755 index 3f7433d..7314f2b --- a/syncplay/ui/consoleUI.py +++ b/syncplay/ui/consoleUI.py @@ -1,4 +1,4 @@ -from __future__ import print_function + import threading import time import syncplay @@ -32,7 +32,7 @@ class ConsoleUI(threading.Thread): def run(self): try: while True: - data = raw_input().decode(sys.stdin.encoding) + data = input() data = data.rstrip('\n\r') if not self.promptMode.isSet(): self.PromptResult = data @@ -49,7 +49,7 @@ class ConsoleUI(threading.Thread): pass def promptFor(self, prompt=">", message=""): - if message <> "": + if message != "": print(message) self.promptMode.clear() print(prompt, end='') @@ -58,20 +58,20 @@ class ConsoleUI(threading.Thread): def showUserList(self, currentUser, rooms): for room in rooms: - message = u"In room '{}':".format(room) + message = "In room '{}':".format(room) self.showMessage(message, True) for user in rooms[room]: - userflags = u"" + userflags = "" if user.isController(): - userflags += u"({}) ".format(getMessage("controller-userlist-userflag")) + userflags += "({}) ".format(getMessage("controller-userlist-userflag")) if user.isReady(): - userflags += u"({}) ".format(getMessage("ready-userlist-userflag")) + userflags += "({}) ".format(getMessage("ready-userlist-userflag")) - username = userflags + u"*<{}>*".format(user.username) if user == currentUser else userflags + u"<{}>".format(user.username) + username = userflags + "*<{}>*".format(user.username) if user == currentUser else userflags + "<{}>".format(user.username) if user.file: message = getMessage("userlist-playing-notification").format(username) self.showMessage(message, True) - message = u" {}: '{}' ({})".format(getMessage("userlist-file-notification"),user.file['name'], formatTime(user.file['duration'])) + message = " {}: '{}' ({})".format(getMessage("userlist-file-notification"),user.file['name'], formatTime(user.file['duration'])) if currentUser.file: if user.file['name'] == currentUser.file['name'] and user.file['size'] != currentUser.file['size']: message += getMessage("different-filesize-notification") @@ -98,7 +98,7 @@ class ConsoleUI(threading.Thread): if noTimestamp: print(message) else: - print(time.strftime(constants.UI_TIME_FORMAT, time.localtime()).decode('utf-8') + message) + print(time.strftime(constants.UI_TIME_FORMAT, time.localtime()) + message) def showDebugMessage(self, message): print(message) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 770a732..36233f5 100755 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -8,7 +8,7 @@ from syncplay.messages import getMessage from syncplay.utils import resourcespath import sys import time -import urllib +import urllib.request, urllib.parse, urllib.error from datetime import datetime from syncplay.utils import isLinux, isWindows, isMacOS import re @@ -54,9 +54,9 @@ class UserlistItemDelegate(QtWidgets.QStyledItemDelegate): if column == constants.USERLIST_GUI_USERNAME_COLUMN: currentQAbstractItemModel = indexQModelIndex.model() itemQModelIndex = currentQAbstractItemModel.index(indexQModelIndex.row(), constants.USERLIST_GUI_USERNAME_COLUMN, indexQModelIndex.parent()) - controlIconQPixmap = QtGui.QPixmap(resourcespath + u"user_key.png") - tickIconQPixmap = QtGui.QPixmap(resourcespath + u"tick.png") - crossIconQPixmap = QtGui.QPixmap(resourcespath + u"cross.png") + controlIconQPixmap = QtGui.QPixmap(resourcespath + "user_key.png") + tickIconQPixmap = QtGui.QPixmap(resourcespath + "tick.png") + crossIconQPixmap = QtGui.QPixmap(resourcespath + "cross.png") roomController = currentQAbstractItemModel.data(itemQModelIndex, Qt.UserRole + constants.USERITEM_CONTROLLER_ROLE) userReady = currentQAbstractItemModel.data(itemQModelIndex, Qt.UserRole + constants.USERITEM_READY_ROLE) @@ -85,7 +85,7 @@ class UserlistItemDelegate(QtWidgets.QStyledItemDelegate): itemQModelIndex = currentQAbstractItemModel.index(indexQModelIndex.row(), constants.USERLIST_GUI_FILENAME_COLUMN, indexQModelIndex.parent()) fileSwitchRole = currentQAbstractItemModel.data(itemQModelIndex, Qt.UserRole + constants.FILEITEM_SWITCH_ROLE) if fileSwitchRole == constants.FILEITEM_SWITCH_FILE_SWITCH: - fileSwitchIconQPixmap = QtGui.QPixmap(resourcespath + u"film_go.png") + fileSwitchIconQPixmap = QtGui.QPixmap(resourcespath + "film_go.png") itemQPainter.drawPixmap ( (optionQStyleOptionViewItem.rect.x()), midY - 8, @@ -93,7 +93,7 @@ class UserlistItemDelegate(QtWidgets.QStyledItemDelegate): optionQStyleOptionViewItem.rect.setX(optionQStyleOptionViewItem.rect.x()+16) elif fileSwitchRole == constants.FILEITEM_SWITCH_STREAM_SWITCH: - streamSwitchIconQPixmap = QtGui.QPixmap(resourcespath + u"world_go.png") + streamSwitchIconQPixmap = QtGui.QPixmap(resourcespath + "world_go.png") itemQPainter.drawPixmap ( (optionQStyleOptionViewItem.rect.x()), midY - 8, @@ -116,7 +116,7 @@ class AboutDialog(QtWidgets.QDialog): linkLabel.setOpenExternalLinks(True) versionLabel = QtWidgets.QLabel("
" + getMessage("about-dialog-release").format(version, release_number, __binding__) + "
") licenseLabel = QtWidgets.QLabel("

Copyright © 2017 Syncplay

" + getMessage("about-dialog-license-text") + "

") - aboutIconPixmap = QtGui.QPixmap(resourcespath + u"syncplay.png") + aboutIconPixmap = QtGui.QPixmap(resourcespath + "syncplay.png") aboutIconLabel = QtWidgets.QLabel() aboutIconLabel.setPixmap(aboutIconPixmap.scaled(120, 120, Qt.KeepAspectRatio)) aboutLayout = QtWidgets.QGridLayout() @@ -139,15 +139,15 @@ class AboutDialog(QtWidgets.QDialog): def openLicense(self): if isWindows(): - QtGui.QDesktopServices.openUrl(QUrl("file:///" + resourcespath + u"license.rtf")) + QtGui.QDesktopServices.openUrl(QUrl("file:///" + resourcespath + "license.rtf")) else: - QtGui.QDesktopServices.openUrl(QUrl("file://" + resourcespath + u"license.rtf")) + QtGui.QDesktopServices.openUrl(QUrl("file://" + resourcespath + "license.rtf")) def openDependencies(self): if isWindows(): - QtGui.QDesktopServices.openUrl(QUrl("file:///" + resourcespath + u"third-party-notices.rtf")) + QtGui.QDesktopServices.openUrl(QUrl("file:///" + resourcespath + "third-party-notices.rtf")) else: - QtGui.QDesktopServices.openUrl(QUrl("file://" + resourcespath + u"third-party-notices.rtf")) + QtGui.QDesktopServices.openUrl(QUrl("file://" + resourcespath + "third-party-notices.rtf")) class MainWindow(QtWidgets.QMainWindow): insertPosition = None @@ -158,7 +158,7 @@ class MainWindow(QtWidgets.QMainWindow): def setPlaylistInsertPosition(self, newPosition): if not self.playlist.isEnabled(): return - if MainWindow.insertPosition <> newPosition: + if MainWindow.insertPosition != newPosition: MainWindow.insertPosition = newPosition self.playlist.forceUpdate() @@ -168,7 +168,7 @@ class MainWindow(QtWidgets.QMainWindow): currentQAbstractItemModel = indexQModelIndex.model() currentlyPlayingFile = currentQAbstractItemModel.data(indexQModelIndex, Qt.UserRole + constants.PLAYLISTITEM_CURRENTLYPLAYING_ROLE) if currentlyPlayingFile: - currentlyplayingIconQPixmap = QtGui.QPixmap(resourcespath + u"bullet_right_grey.png") + currentlyplayingIconQPixmap = QtGui.QPixmap(resourcespath + "bullet_right_grey.png") midY = int((optionQStyleOptionViewItem.rect.y() + optionQStyleOptionViewItem.rect.bottomLeft().y()) / 2) itemQPainter.drawPixmap ( (optionQStyleOptionViewItem.rect.x()+4), @@ -223,11 +223,11 @@ class MainWindow(QtWidgets.QMainWindow): for url in urls[::-1]: if isMacOS() and IsPySide: - macURL = NSString.alloc().initWithString_(unicode(url.toString())) + macURL = NSString.alloc().initWithString_(str(url.toString())) pathString = macURL.stringByAddingPercentEscapesUsingEncoding_(NSUTF8StringEncoding) dropfilepath = os.path.abspath(NSURL.URLWithString_(pathString).filePathURL().path()) else: - dropfilepath = os.path.abspath(unicode(url.toLocalFile())) + dropfilepath = os.path.abspath(str(url.toLocalFile())) if os.path.isfile(dropfilepath): window.addFileToPlaylist(dropfilepath, indexRow) elif os.path.isdir(dropfilepath): @@ -240,12 +240,12 @@ class MainWindow(QtWidgets.QMainWindow): playlistIndexFilename = None def setPlaylistIndexFilename(self, filename): - if filename <> self.playlistIndexFilename: + if filename != self.playlistIndexFilename: self.playlistIndexFilename = filename self.updatePlaylistIndexIcon() def updatePlaylistIndexIcon(self): - for item in xrange(self.count()): + for item in range(self.count()): itemFilename = self.item(item).text() isPlayingFilename = itemFilename == self.playlistIndexFilename self.item(item).setData(Qt.UserRole + constants.PLAYLISTITEM_CURRENTLYPLAYING_ROLE, isPlayingFilename) @@ -278,7 +278,7 @@ class MainWindow(QtWidgets.QMainWindow): super(MainWindow.PlaylistWidget, self).keyPressEvent(event) def updatePlaylist(self, newPlaylist): - for index in xrange(self.count()): + for index in range(self.count()): self.takeItem(0) uniquePlaylist = [] for item in newPlaylist: @@ -330,11 +330,11 @@ class MainWindow(QtWidgets.QMainWindow): indexRow = window.playlist.count() for url in urls[::-1]: if isMacOS() and IsPySide: - macURL = NSString.alloc().initWithString_(unicode(url.toString())) + macURL = NSString.alloc().initWithString_(str(url.toString())) pathString = macURL.stringByAddingPercentEscapesUsingEncoding_(NSUTF8StringEncoding) dropfilepath = os.path.abspath(NSURL.URLWithString_(pathString).filePathURL().path()) else: - dropfilepath = os.path.abspath(unicode(url.toLocalFile())) + dropfilepath = os.path.abspath(str(url.toLocalFile())) if os.path.isfile(dropfilepath): window.addFileToPlaylist(dropfilepath, indexRow) elif os.path.isdir(dropfilepath): @@ -412,20 +412,20 @@ class MainWindow(QtWidgets.QMainWindow): self.roomInput.setMaxLength(constants.MAX_ROOM_NAME_LENGTH) def showMessage(self, message, noTimestamp=False): - message = unicode(message) + message = str(message) username = None messageWithUsername = re.match(constants.MESSAGE_WITH_USERNAME_REGEX, message, re.UNICODE) if messageWithUsername: username = messageWithUsername.group("username") message = messageWithUsername.group("message") - message = message.replace(u"&", u"&").replace(u'"', u""").replace(u"<", u"<").replace(u">", u">") + message = message.replace("&", "&").replace('"', """).replace("<", "<").replace(">", ">") if username: message = constants.STYLE_USER_MESSAGE.format(constants.STYLE_USERNAME, username, message) - message = message.replace(u"\n", u"
") + message = message.replace("\n", "
") if noTimestamp: - self.newMessage(u"{}
".format(message)) + self.newMessage("{}
".format(message)) else: - self.newMessage(time.strftime(constants.UI_TIME_FORMAT, time.localtime()).decode('utf-8') + message + u"
") + self.newMessage(time.strftime(constants.UI_TIME_FORMAT, time.localtime()) + message + "
") @needsClient def getFileSwitchState(self, filename): @@ -502,10 +502,10 @@ class MainWindow(QtWidgets.QMainWindow): useritem.setData(isReadyWithFile, Qt.UserRole + constants.USERITEM_READY_ROLE) if user.file: filesizeitem = QtGui.QStandardItem(formatSize(user.file['size'])) - filedurationitem = QtGui.QStandardItem(u"({})".format(formatTime(user.file['duration']))) + filedurationitem = QtGui.QStandardItem("({})".format(formatTime(user.file['duration']))) filename = user.file['name'] if isURL(filename): - filename = urllib.unquote(filename) + filename = urllib.parse.unquote(filename) try: filename = filename.decode('utf-8') except UnicodeEncodeError: @@ -587,29 +587,29 @@ class MainWindow(QtWidgets.QMainWindow): if item: firstFile = item.sibling(item.row(), 0).data() pathFound = self._syncplayClient.fileSwitch.findFilepath(firstFile) if not isURL(firstFile) else None - if self._syncplayClient.userlist.currentUser.file is None or firstFile <> self._syncplayClient.userlist.currentUser.file["name"]: + if self._syncplayClient.userlist.currentUser.file is None or firstFile != self._syncplayClient.userlist.currentUser.file["name"]: if isURL(firstFile): - menu.addAction(QtGui.QPixmap(resourcespath + u"world_go.png"), getMessage("openstreamurl-menu-label"), lambda: self.openFile(firstFile,resetPosition=True)) + menu.addAction(QtGui.QPixmap(resourcespath + "world_go.png"), getMessage("openstreamurl-menu-label"), lambda: self.openFile(firstFile,resetPosition=True)) elif pathFound: - menu.addAction(QtGui.QPixmap(resourcespath + u"film_go.png"), getMessage("openmedia-menu-label"), lambda: self.openFile(pathFound,resetPosition=True)) + menu.addAction(QtGui.QPixmap(resourcespath + "film_go.png"), getMessage("openmedia-menu-label"), lambda: self.openFile(pathFound,resetPosition=True)) if pathFound: - menu.addAction(QtGui.QPixmap(resourcespath + u"folder_film.png"), + menu.addAction(QtGui.QPixmap(resourcespath + "folder_film.png"), getMessage('open-containing-folder'), lambda: utils.open_system_file_browser(pathFound)) if self._syncplayClient.isUntrustedTrustableURI(firstFile): domain = utils.getDomainFromURL(firstFile) - menu.addAction(QtGui.QPixmap(resourcespath + u"shield_add.png"),getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain)) - menu.addAction(QtGui.QPixmap(resourcespath + u"delete.png"), getMessage("removefromplaylist-menu-label"), lambda: self.deleteSelectedPlaylistItems()) + menu.addAction(QtGui.QPixmap(resourcespath + "shield_add.png"),getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain)) + menu.addAction(QtGui.QPixmap(resourcespath + "delete.png"), getMessage("removefromplaylist-menu-label"), lambda: self.deleteSelectedPlaylistItems()) menu.addSeparator() - menu.addAction(QtGui.QPixmap(resourcespath + u"arrow_switch.png"), getMessage("shuffleremainingplaylist-menu-label"), lambda: self.shuffleRemainingPlaylist()) - menu.addAction(QtGui.QPixmap(resourcespath + u"arrow_switch.png"), getMessage("shuffleentireplaylist-menu-label"), lambda: self.shuffleEntirePlaylist()) - menu.addAction(QtGui.QPixmap(resourcespath + u"arrow_undo.png"), getMessage("undoplaylist-menu-label"), lambda: self.undoPlaylistChange()) - menu.addAction(QtGui.QPixmap(resourcespath + u"film_edit.png"), getMessage("editplaylist-menu-label"), lambda: self.openEditPlaylistDialog()) - menu.addAction(QtGui.QPixmap(resourcespath + u"film_add.png"),getMessage("addfilestoplaylist-menu-label"), lambda: self.OpenAddFilesToPlaylistDialog()) - menu.addAction(QtGui.QPixmap(resourcespath + u"world_add.png"), getMessage("addurlstoplaylist-menu-label"), lambda: self.OpenAddURIsToPlaylistDialog()) + menu.addAction(QtGui.QPixmap(resourcespath + "arrow_switch.png"), getMessage("shuffleremainingplaylist-menu-label"), lambda: self.shuffleRemainingPlaylist()) + menu.addAction(QtGui.QPixmap(resourcespath + "arrow_switch.png"), getMessage("shuffleentireplaylist-menu-label"), lambda: self.shuffleEntirePlaylist()) + menu.addAction(QtGui.QPixmap(resourcespath + "arrow_undo.png"), getMessage("undoplaylist-menu-label"), lambda: self.undoPlaylistChange()) + menu.addAction(QtGui.QPixmap(resourcespath + "film_edit.png"), getMessage("editplaylist-menu-label"), lambda: self.openEditPlaylistDialog()) + menu.addAction(QtGui.QPixmap(resourcespath + "film_add.png"),getMessage("addfilestoplaylist-menu-label"), lambda: self.OpenAddFilesToPlaylistDialog()) + menu.addAction(QtGui.QPixmap(resourcespath + "world_add.png"), getMessage("addurlstoplaylist-menu-label"), lambda: self.OpenAddURIsToPlaylistDialog()) menu.addSeparator() - menu.addAction(QtGui.QPixmap(resourcespath + u"film_folder_edit.png"), getMessage("setmediadirectories-menu-label"), lambda: self.openSetMediaDirectoriesDialog()) - menu.addAction(QtGui.QPixmap(resourcespath + u"shield_edit.png"), getMessage("settrusteddomains-menu-label"), lambda: self.openSetTrustedDomainsDialog()) + menu.addAction(QtGui.QPixmap(resourcespath + "film_folder_edit.png"), getMessage("setmediadirectories-menu-label"), lambda: self.openSetMediaDirectoriesDialog()) + menu.addAction(QtGui.QPixmap(resourcespath + "shield_edit.png"), getMessage("settrusteddomains-menu-label"), lambda: self.openSetTrustedDomainsDialog()) menu.exec_(self.playlist.viewport().mapToGlobal(position)) @@ -628,36 +628,36 @@ class MainWindow(QtWidgets.QMainWindow): elif len(username) < 15: shortUsername = getMessage("item-is-others-indicator").format(username) else: - shortUsername = u"{}...".format(getMessage("item-is-others-indicator").format(username[0:12])) # TODO: Enforce username limits in client and server + shortUsername = "{}...".format(getMessage("item-is-others-indicator").format(username[0:12])) # TODO: Enforce username limits in client and server filename = item.sibling(item.row(), 3).data() while item.parent().row() != -1: item = item.parent() roomToJoin = item.sibling(item.row(), 0).data() - if roomToJoin <> self._syncplayClient.getRoom(): + if roomToJoin != self._syncplayClient.getRoom(): menu.addAction(getMessage("joinroom-menu-label").format(roomToJoin), lambda: self.joinRoom(roomToJoin)) - elif username and filename and filename <> getMessage("nofile-note"): + elif username and filename and filename != getMessage("nofile-note"): if self.config['sharedPlaylistEnabled'] and not self.isItemInPlaylist(filename): if isURL(filename): - menu.addAction(QtGui.QPixmap(resourcespath + u"world_add.png"),getMessage("addusersstreamstoplaylist-menu-label").format(shortUsername), lambda: self.addStreamToPlaylist(filename)) + menu.addAction(QtGui.QPixmap(resourcespath + "world_add.png"),getMessage("addusersstreamstoplaylist-menu-label").format(shortUsername), lambda: self.addStreamToPlaylist(filename)) else: - menu.addAction(QtGui.QPixmap(resourcespath + u"film_add.png"), getMessage("addusersfiletoplaylist-menu-label").format(shortUsername), lambda: self.addStreamToPlaylist(filename)) + menu.addAction(QtGui.QPixmap(resourcespath + "film_add.png"), getMessage("addusersfiletoplaylist-menu-label").format(shortUsername), lambda: self.addStreamToPlaylist(filename)) - if self._syncplayClient.userlist.currentUser.file is None or filename <> self._syncplayClient.userlist.currentUser.file["name"]: + if self._syncplayClient.userlist.currentUser.file is None or filename != self._syncplayClient.userlist.currentUser.file["name"]: if isURL(filename): - menu.addAction(QtGui.QPixmap(resourcespath + u"world_go.png"), getMessage("openusersstream-menu-label").format(shortUsername), lambda: self.openFile(filename)) + menu.addAction(QtGui.QPixmap(resourcespath + "world_go.png"), getMessage("openusersstream-menu-label").format(shortUsername), lambda: self.openFile(filename)) else: pathFound = self._syncplayClient.fileSwitch.findFilepath(filename) if pathFound: - menu.addAction(QtGui.QPixmap(resourcespath + u"film_go.png"), getMessage("openusersfile-menu-label").format(shortUsername), lambda: self.openFile(pathFound)) + menu.addAction(QtGui.QPixmap(resourcespath + "film_go.png"), getMessage("openusersfile-menu-label").format(shortUsername), lambda: self.openFile(pathFound)) if self._syncplayClient.isUntrustedTrustableURI(filename): domain = utils.getDomainFromURL(filename) - menu.addAction(QtGui.QPixmap(resourcespath + u"shield_add.png"),getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain)) + menu.addAction(QtGui.QPixmap(resourcespath + "shield_add.png"),getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain)) - if not isURL(filename) and filename <> getMessage("nofile-note"): + if not isURL(filename) and filename != getMessage("nofile-note"): path = self._syncplayClient.fileSwitch.findFilepath(filename) if path: - menu.addAction(QtGui.QPixmap(resourcespath + u"folder_film.png"), getMessage('open-containing-folder'), lambda: utils.open_system_file_browser(path)) + menu.addAction(QtGui.QPixmap(resourcespath + "folder_film.png"), getMessage('open-containing-folder'), lambda: utils.open_system_file_browser(path)) else: return menu.exec_(self.listTreeView.viewport().mapToGlobal(position)) @@ -727,9 +727,9 @@ class MainWindow(QtWidgets.QMainWindow): while item.parent().row() != -1: item = item.parent() roomToJoin = item.sibling(item.row(), 0).data() - if roomToJoin <> self._syncplayClient.getRoom(): + if roomToJoin != self._syncplayClient.getRoom(): self.joinRoom(item.sibling(item.row(), 0).data()) - elif username and filename and username <> self._syncplayClient.userlist.currentUser.username: + elif username and filename and username != self._syncplayClient.userlist.currentUser.username: if self._isTryingToChangeToCurrentFile(filename): return if isURL(filename): @@ -757,13 +757,13 @@ class MainWindow(QtWidgets.QMainWindow): print(message) def showErrorMessage(self, message, criticalerror=False): - message = unicode(message) + message = str(message) if criticalerror: QtWidgets.QMessageBox.critical(self, "Syncplay", message) - message = message.replace(u"&", u"&").replace(u'"', u""").replace(u"<", u"<").replace(u">", u">") - message = message.replace(u"\n", u"
") - message = u"".format(constants.STYLE_ERRORNOTIFICATION) + message + u"" - self.newMessage(time.strftime(constants.UI_TIME_FORMAT, time.localtime()).decode('utf-8') + message + u"
") + message = message.replace("&", "&").replace('"', """).replace("<", "<").replace(">", ">") + message = message.replace("\n", "
") + message = "".format(constants.STYLE_ERRORNOTIFICATION) + message + "" + self.newMessage(time.strftime(constants.UI_TIME_FORMAT, time.localtime()) + message + "
") @needsClient def joinRoom(self, room=None): @@ -782,7 +782,7 @@ class MainWindow(QtWidgets.QMainWindow): def seekPositionDialog(self): seekTime, ok = QtWidgets.QInputDialog.getText(self, getMessage("seektime-menu-label"), getMessage("seektime-msgbox-label"), QtWidgets.QLineEdit.Normal, - u"0:00") + "0:00") if ok and seekTime != '': self.seekPosition(seekTime) @@ -957,9 +957,9 @@ class MainWindow(QtWidgets.QMainWindow): URI = URI.encode('utf-8') except UnicodeDecodeError: pass - URI = urllib.unquote(URI) + URI = urllib.parse.unquote(URI) URI = URI.decode('utf-8') - if URI <> "": + if URI != "": self.addStreamToPlaylist(URI) self.updatingPlaylist = False @@ -988,7 +988,7 @@ class MainWindow(QtWidgets.QMainWindow): result = editPlaylistDialog.exec_() if result == QtWidgets.QDialog.Accepted: newPlaylist = utils.convertMultilineStringToList(editPlaylistTextbox.toPlainText()) - if newPlaylist <> self.playlistState and self._syncplayClient and not self.updatingPlaylist: + if newPlaylist != self.playlistState and self._syncplayClient and not self.updatingPlaylist: self.setPlaylist(newPlaylist) self._syncplayClient.playlist.changePlaylist(newPlaylist) self._syncplayClient.fileSwitch.updateInfo() @@ -1058,14 +1058,14 @@ class MainWindow(QtWidgets.QMainWindow): options = QtWidgets.QFileDialog.Options(QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) else: options = QtWidgets.QFileDialog.Options(QtWidgets.QFileDialog.ShowDirsOnly) - folderName = unicode(QtWidgets.QFileDialog.getExistingDirectory(self,None,self.getInitialMediaDirectory(includeUserSpecifiedDirectories=False),options)) + folderName = str(QtWidgets.QFileDialog.getExistingDirectory(self,None,self.getInitialMediaDirectory(includeUserSpecifiedDirectories=False),options)) if folderName: existingMediaDirs = MediaDirectoriesTextbox.toPlainText() if existingMediaDirs == "": newMediaDirList = folderName else: - newMediaDirList = existingMediaDirs + u"\n" + folderName + newMediaDirList = existingMediaDirs + "\n" + folderName MediaDirectoriesTextbox.setPlainText(newMediaDirList) MediaDirectoriesDialog.raise_() MediaDirectoriesDialog.activateWindow() @@ -1136,9 +1136,9 @@ class MainWindow(QtWidgets.QMainWindow): def getPlaylistState(self): playlistItems = [] - for playlistItem in xrange(self.playlist.count()): + for playlistItem in range(self.playlist.count()): playlistItemText = self.playlist.item(playlistItem).text() - if playlistItemText <> getMessage("playlist-instruction-item-message"): + if playlistItemText != getMessage("playlist-instruction-item-message"): playlistItems.append(playlistItemText) return playlistItems @@ -1146,20 +1146,20 @@ class MainWindow(QtWidgets.QMainWindow): if self.updatingPlaylist: return newPlaylist = self.getPlaylistState() - if newPlaylist <> self.playlistState and self._syncplayClient and not self.updatingPlaylist: + if newPlaylist != self.playlistState and self._syncplayClient and not self.updatingPlaylist: self.playlistState = newPlaylist self._syncplayClient.playlist.changePlaylist(newPlaylist) self._syncplayClient.fileSwitch.updateInfo() def executeCommand(self, command): - self.showMessage(u"/{}".format(command)) + self.showMessage("/{}".format(command)) self.console.executeCommand(command) def sendChatMessage(self): chatText = self.chatInput.text() self.chatInput.setText("") - if chatText <> "": - if chatText[:1] == "/" and chatText <> "/": + if chatText != "": + if chatText[:1] == "/" and chatText != "/": command = chatText[1:] if command and command[:1] == "/": chatText = chatText[1:] @@ -1363,20 +1363,20 @@ class MainWindow(QtWidgets.QMainWindow): window.playbackFrame.setLayout(window.playbackLayout) window.seekInput = QtWidgets.QLineEdit() window.seekInput.returnPressed.connect(self.seekFromButton) - window.seekButton = QtWidgets.QPushButton(QtGui.QPixmap(resourcespath + u'clock_go.png'), "") + window.seekButton = QtWidgets.QPushButton(QtGui.QPixmap(resourcespath + 'clock_go.png'), "") window.seekButton.setToolTip(getMessage("seektime-menu-label")) window.seekButton.pressed.connect(self.seekFromButton) window.seekInput.setText("0:00") window.seekInput.setFixedWidth(60) window.playbackLayout.addWidget(window.seekInput) window.playbackLayout.addWidget(window.seekButton) - window.unseekButton = QtWidgets.QPushButton(QtGui.QPixmap(resourcespath + u'arrow_undo.png'), "") + window.unseekButton = QtWidgets.QPushButton(QtGui.QPixmap(resourcespath + 'arrow_undo.png'), "") window.unseekButton.setToolTip(getMessage("undoseek-menu-label")) window.unseekButton.pressed.connect(self.undoSeek) window.miscLayout = QtWidgets.QHBoxLayout() window.playbackLayout.addWidget(window.unseekButton) - window.playButton = QtWidgets.QPushButton(QtGui.QPixmap(resourcespath + u'control_play_blue.png'), "") + window.playButton = QtWidgets.QPushButton(QtGui.QPixmap(resourcespath + 'control_play_blue.png'), "") window.playButton.setToolTip(getMessage("play-menu-label")) window.playButton.pressed.connect(self.play) window.playbackLayout.addWidget(window.playButton) @@ -1466,12 +1466,12 @@ class MainWindow(QtWidgets.QMainWindow): window.updateAction = window.helpMenu.addAction(QtGui.QPixmap(resourcespath + 'application_get.png'), getMessage("update-menu-label")) window.updateAction.triggered.connect(self.userCheckForUpdates) - + if not isMacOS(): - window.helpMenu.addSeparator() + window.helpMenu.addSeparator() window.about = window.helpMenu.addAction(QtGui.QPixmap(resourcespath + 'syncplay.png'), getMessage("about-menu-label")) - else: + else: window.about = window.helpMenu.addAction("&About") window.about.triggered.connect(self.openAbout) @@ -1482,7 +1482,7 @@ class MainWindow(QtWidgets.QMainWindow): def openAbout(self): aboutMsgBox = AboutDialog() aboutMsgBox.exec_() - + def addMainFrame(self, window): window.mainFrame = QtWidgets.QFrame() window.mainFrame.setLineWidth(0) @@ -1614,11 +1614,11 @@ class MainWindow(QtWidgets.QMainWindow): if urls and urls[0].scheme() == 'file': url = event.mimeData().urls()[0] if isMacOS() and IsPySide: - macURL = NSString.alloc().initWithString_(unicode(url.toString())) + macURL = NSString.alloc().initWithString_(str(url.toString())) pathString = macURL.stringByAddingPercentEscapesUsingEncoding_(NSUTF8StringEncoding) dropfilepath = os.path.abspath(NSURL.URLWithString_(pathString).filePathURL().path()) else: - dropfilepath = os.path.abspath(unicode(url.toLocalFile())) + dropfilepath = os.path.abspath(str(url.toLocalFile())) if rewindFile == False: self._syncplayClient._player.openFile(dropfilepath) else: @@ -1628,7 +1628,7 @@ class MainWindow(QtWidgets.QMainWindow): def setPlaylist(self, newPlaylist, newIndexFilename=None): if self.updatingPlaylist: - self.ui.showDebugMessage(u"Trying to set playlist while it is already being updated") + self.ui.showDebugMessage("Trying to set playlist while it is already being updated") if newPlaylist == self.playlistState: if newIndexFilename: self.playlist.setPlaylistIndexFilename(newIndexFilename) @@ -1676,7 +1676,7 @@ class MainWindow(QtWidgets.QMainWindow): return True def isItemInPlaylist(self, filename): - for playlistindex in xrange(self.playlist.count()): + for playlistindex in range(self.playlist.count()): if self.playlist.item(playlistindex).text() == filename: return True return False @@ -1688,12 +1688,12 @@ class MainWindow(QtWidgets.QMainWindow): def removePlaylistNote(self): if not self.clearedPlaylistNote: - for index in xrange(self.playlist.count()): + for index in range(self.playlist.count()): self.playlist.takeItem(0) self.clearedPlaylistNote = True def addFolderToPlaylist(self, folderPath): - self.showErrorMessage(u"You tried to add the folder '{}' to the playlist. Syncplay only currently supports adding files to the playlist.".format(folderPath)) # TODO: Implement "add folder to playlist" + self.showErrorMessage("You tried to add the folder '{}' to the playlist. Syncplay only currently supports adding files to the playlist.".format(folderPath)) # TODO: Implement "add folder to playlist" def deleteSelectedPlaylistItems(self): self.playlist.remove_selected_items() @@ -1763,7 +1763,7 @@ class MainWindow(QtWidgets.QMainWindow): self.addMenubar(self) self.addMainFrame(self) self.loadSettings() - self.setWindowIcon(QtGui.QPixmap(resourcespath + u"syncplay.png")) + self.setWindowIcon(QtGui.QPixmap(resourcespath + "syncplay.png")) self.setWindowFlags(self.windowFlags() & Qt.WindowCloseButtonHint & Qt.AA_DontUseNativeMenuBar & Qt.WindowMinimizeButtonHint & ~Qt.WindowContextHelpButtonHint) self.show() self.setAcceptDrops(True) diff --git a/syncplay/utils.py b/syncplay/utils.py old mode 100644 new mode 100755 index 72fa947..b04b6ca --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -9,7 +9,7 @@ import itertools import hashlib import random import string -import urllib +import urllib.request, urllib.parse, urllib.error import ast import unicodedata import platform @@ -57,7 +57,7 @@ def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None): #try_one_last_time = False return f(*args, **kwargs) break - except ExceptionToCheck, e: + except ExceptionToCheck as e: if logger: msg = getMessage("retrying-notification").format(str(e), mdelay) logger.warning(msg) @@ -77,7 +77,7 @@ def parseTime(timeStr): return parts = parts.groupdict() time_params = {} - for (name, param) in parts.iteritems(): + for (name, param) in parts.items(): if param: if name == "miliseconds": time_params["microseconds"] = int(param) * 1000 @@ -152,12 +152,12 @@ def findWorkingDir(): def getResourcesPath(): if isWindows(): - return findWorkingDir() + u"\\resources\\" + return findWorkingDir() + "\\resources\\" else: - return findWorkingDir() + u"/resources/" + return findWorkingDir() + "/resources/" resourcespath = getResourcesPath() -posixresourcespath = findWorkingDir().replace(u"\\","/") + u"/resources/" +posixresourcespath = findWorkingDir().replace("\\","/") + "/resources/" def getDefaultMonospaceFont(): if platform.system() == "Windows": @@ -168,7 +168,7 @@ def getDefaultMonospaceFont(): return constants.FALLBACK_MONOSPACE_FONT def limitedPowerset(s, minLength): - return itertools.chain.from_iterable(itertools.combinations(s, r) for r in xrange(len(s), minLength, -1)) + return itertools.chain.from_iterable(itertools.combinations(s, r) for r in range(len(s), minLength, -1)) def blackholeStdoutForFrozenWindow(): if getattr(sys, 'frozen', '') == "windows_exe": @@ -178,7 +178,7 @@ def blackholeStdoutForFrozenWindow(): _error = None def write(self, text, fname='.syncplay.log'): if self._file is None and self._error is None: - if os.name <> 'nt': + if os.name != 'nt': path = os.path.join(os.getenv('HOME', '.'), fname) else: path = os.path.join(os.getenv('APPDATA', '.'), fname) @@ -208,7 +208,7 @@ def truncateText(unicodeText, maxLength): except: pass try: - return(unicode(unicodeText.encode("utf-8"), "utf-8", errors="ignore")[:maxLength]) + return(str(unicodeText.encode("utf-8"), "utf-8", errors="ignore")[:maxLength]) except: pass return "" @@ -219,7 +219,7 @@ def splitText(unicodeText, maxLength): except: pass try: - unicodeText = unicode(unicodeText.encode("utf-8"), "utf-8", errors="ignore") + unicodeText = str(unicodeText.encode("utf-8"), "utf-8", errors="ignore") unicodeArray = [unicodeText[i:i + maxLength] for i in range(0, len(unicodeText), maxLength)] return(unicodeArray) except: @@ -231,15 +231,15 @@ def splitText(unicodeText, maxLength): def stripfilename(filename, stripURL): if filename: try: - filename = filename.encode('utf-8') + filename = filename except UnicodeDecodeError: pass - filename = urllib.unquote(filename) + filename = urllib.parse.unquote(filename) if stripURL: try: - filename = urllib.unquote(filename.split(u"/")[-1]) + filename = urllib.parse.unquote(filename.split("/")[-1]) except UnicodeDecodeError: - filename = urllib.unquote(filename.split("/")[-1]) + filename = urllib.parse.unquote(filename.split("/")[-1]) return re.sub(constants.FILENAME_STRIP_REGEX, "", filename) else: return "" @@ -265,7 +265,7 @@ def hashFilename(filename, stripURL = False): return filenameHash def hashFilesize(size): - return hashlib.sha256(str(size)).hexdigest()[:12] + return hashlib.sha256(str(size).encode('utf-8')).hexdigest()[:12] def sameHashed(string1raw, string1hashed, string2raw, string2hashed): try: @@ -284,11 +284,11 @@ def sameHashed(string1raw, string1hashed, string2raw, string2hashed): def sameFilename (filename1, filename2): try: - filename1 = filename1.encode('utf-8') + filename1 = filename1 except UnicodeDecodeError: pass try: - filename2 = filename2.encode('utf-8') + filename2 = filename2 except UnicodeDecodeError: pass stripURL = True if isURL(filename1) ^ isURL(filename2) else False @@ -329,7 +329,7 @@ def isURL(path): return False def getPlayerArgumentsByPathAsArray(arguments, path): - if arguments and not isinstance(arguments, (str, unicode)) and arguments.has_key(path): + if arguments and not isinstance(arguments, str) and path in arguments: return arguments[path] else: return None @@ -339,10 +339,10 @@ def getPlayerArgumentsByPathAsText(arguments, path): return " ".join(argsToReturn) if argsToReturn else "" def getListAsMultilineString(pathArray): - return u"\n".join(pathArray) if pathArray else "" + return "\n".join(pathArray) if pathArray else "" def convertMultilineStringToList(multilineString): - return unicode.split(multilineString,u"\n") if multilineString else "" + return str.split(multilineString,"\n") if multilineString else "" def playlistIsValid(files): if len(files) > constants.PLAYLIST_MAX_ITEMS: @@ -373,10 +373,10 @@ def open_system_file_browser(path): def getListOfPublicServers(): try: - import urllib, syncplay, sys, messages, json - params = urllib.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, + import urllib.request, urllib.parse, urllib.error, syncplay, sys, messages, json + params = urllib.parse.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, 'language': messages.messages["CURRENT"]}) - f = urllib.urlopen(constants.SYNCPLAY_PUBLIC_SERVER_LIST_URL.format(params)) + f = urllib.request.urlopen(constants.SYNCPLAY_PUBLIC_SERVER_LIST_URL.format(params)) response = f.read() response = response.replace("

","").replace("

","").replace("
","").replace("“","'").replace("”","'").replace(":’","'").replace("’","'").replace("′","'").replace("\n","").replace("\r","") # Fix Wordpress response = ast.literal_eval(response) @@ -440,11 +440,11 @@ class RandomStringGenerator(object): @staticmethod def _get_random_letters(quantity): - return ''.join(random.choice(string.ascii_uppercase) for _ in xrange(quantity)) + return ''.join(random.choice(string.ascii_uppercase) for _ in range(quantity)) @staticmethod def _get_random_numbers(quantity): - return ''.join(random.choice(string.digits) for _ in xrange(quantity)) + return ''.join(random.choice(string.digits) for _ in range(quantity)) class NotControlledRoom(Exception): pass diff --git a/syncplay/vendor/Qt.py b/syncplay/vendor/Qt.py index 5115221..2bc6693 100755 --- a/syncplay/vendor/Qt.py +++ b/syncplay/vendor/Qt.py @@ -58,7 +58,7 @@ Qt = sys.modules[__name__] Qt.QtCompat = types.ModuleType("QtCompat") try: - long + int except NameError: # Python 3 compatibility long = int @@ -869,12 +869,12 @@ def _wrapinstance(func, ptr, base=None): """ - assert isinstance(ptr, long), "Argument 'ptr' must be of type " + assert isinstance(ptr, int), "Argument 'ptr' must be of type " assert (base is None) or issubclass(base, Qt.QtCore.QObject), ( "Argument 'base' must be of type ") if base is None: - q_object = func(long(ptr), Qt.QtCore.QObject) + q_object = func(int(ptr), Qt.QtCore.QObject) meta_object = q_object.metaObject() class_name = meta_object.className() super_class_name = meta_object.superClass().className() @@ -888,7 +888,7 @@ def _wrapinstance(func, ptr, base=None): else: base = Qt.QtCore.QObject - return func(long(ptr), base) + return func(int(ptr), base) def _reassign_misplaced_members(binding): @@ -899,7 +899,7 @@ def _reassign_misplaced_members(binding): """ - for src, dst in _misplaced_members[binding].items(): + for src, dst in list(_misplaced_members[binding].items()): src_module, src_member = src.split(".") dst_module, dst_member = dst.split(".") @@ -948,9 +948,9 @@ def _build_compatibility_members(binding, decorators=None): _QtCompat = type("QtCompat", (object,), {}) - for classname, bindings in _compatibility_members[binding].items(): + for classname, bindings in list(_compatibility_members[binding].items()): attrs = {} - for target, binding in bindings.items(): + for target, binding in list(bindings.items()): namespaces = binding.split('.') try: src_object = getattr(Qt, "_" + namespaces[0]) @@ -1233,7 +1233,7 @@ def _none(): Qt.QtCompat.loadUi = lambda uifile, baseinstance=None: None Qt.QtCompat.setSectionResizeMode = lambda *args, **kwargs: None - for submodule in _common_members.keys(): + for submodule in list(_common_members.keys()): setattr(Qt, submodule, Mock()) setattr(Qt, "_" + submodule, Mock()) @@ -1472,7 +1472,7 @@ def _install(): raise ImportError("No Qt binding were found.") # Install individual members - for name, members in _common_members.items(): + for name, members in list(_common_members.items()): try: their_submodule = getattr(Qt, "_%s" % name) except AttributeError: diff --git a/syncplay/vendor/__init__.py b/syncplay/vendor/__init__.py old mode 100644 new mode 100755 diff --git a/syncplay/vendor/qt5reactor.py b/syncplay/vendor/qt5reactor.py old mode 100644 new mode 100755 index cde2b5e..7cc5eb7 --- a/syncplay/vendor/qt5reactor.py +++ b/syncplay/vendor/qt5reactor.py @@ -251,10 +251,10 @@ class QtReactor(posixbase.PosixReactorBase): return self._removeAll(self._reads, self._writes) def getReaders(self): - return self._reads.keys() + return list(self._reads.keys()) def getWriters(self): - return self._writes.keys() + return list(self._writes.keys()) def callLater(self, howlong, *args, **kargs): rval = super(QtReactor, self).callLater(howlong, *args, **kargs) @@ -337,7 +337,7 @@ class QtEventReactor(QtReactor): del self._events[event] def doEvents(self): - handles = self._events.keys() + handles = list(self._events.keys()) if len(handles) > 0: val = None while val != WAIT_TIMEOUT: diff --git a/syncplayClient.py b/syncplayClient.py index 107d942..a414f89 100755 --- a/syncplayClient.py +++ b/syncplayClient.py @@ -4,12 +4,12 @@ import site, sys # libpath -try: - if (sys.version_info.major != 2) or (sys.version_info.minor < 7): - raise Exception("You must run Syncplay with Python 2.7!") -except AttributeError: - import warnings - warnings.warn("You must run Syncplay with Python 2.7!") +# try: +# if (sys.version_info.major != 2) or (sys.version_info.minor < 7): +# raise Exception("You must run Syncplay with Python 2.7!") +# except AttributeError: +# import warnings +# warnings.warn("You must run Syncplay with Python 2.7!") from syncplay.clientManager import SyncplayClientManager from syncplay.utils import blackholeStdoutForFrozenWindow diff --git a/syncplayServer.py b/syncplayServer.py index f1cac7e..ef9ae95 100755 --- a/syncplayServer.py +++ b/syncplayServer.py @@ -5,12 +5,12 @@ import site, sys # libpath -try: - if (sys.version_info.major != 2) or (sys.version_info.minor < 7): - raise Exception("You must run Syncplay with Python 2.7!") -except AttributeError: - import warnings - warnings.warn("You must run Syncplay with Python 2.7!") +# try: +# if (sys.version_info.major != 2) or (sys.version_info.minor < 7): +# raise Exception("You must run Syncplay with Python 2.7!") +# except AttributeError: +# import warnings +# warnings.warn("You must run Syncplay with Python 2.7!") from twisted.internet import reactor From 99e5e1ac0f537b231ff282ea27b1590553d4295b Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 24 Apr 2018 15:59:54 +0200 Subject: [PATCH 02/95] Fixes open file from Syncplay to VLC --- syncplay/players/vlc.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/syncplay/players/vlc.py b/syncplay/players/vlc.py index 18782bb..f4ec57b 100755 --- a/syncplay/players/vlc.py +++ b/syncplay/players/vlc.py @@ -144,7 +144,6 @@ class VlcPlayer(BasePlayer): def getMRL(self, fileURL): if utils.isURL(fileURL): - fileURL = fileURL.encode('utf8') fileURL = urllib.parse.quote(fileURL, safe="%/:=&?~#+!$,;'@()*[]") return fileURL @@ -164,7 +163,7 @@ class VlcPlayer(BasePlayer): if os.path.isfile(normedPath): filePath = normedPath if utils.isASCII(filePath) and not utils.isURL(filePath): - self._listener.sendLine('load-file: {}'.format(filePath.encode('ascii', 'ignore'))) + self._listener.sendLine('load-file: {}'.format(filePath)) else: fileURL = self.getMRL(filePath) self._listener.sendLine('load-file: {}'.format(fileURL)) From d0bfe5e06d08f96531dd7bc364786121a79cdcd9 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 24 Apr 2018 16:02:14 +0200 Subject: [PATCH 03/95] Preliminary AppVeyor support --- .appveyor.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index bbce8d2..72a9005 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,10 +9,10 @@ init: - set PATH=C:\Miniconda;C:\Miniconda\Scripts;C:\Program Files (x86)\NSIS;%PATH% - conda create -n syncplay -y - activate syncplay - - conda install python=2.7.13 -y + - conda install python=3.6 -y - conda install pywin32 -y - conda install -c conda-forge pyside2 -y - - pip install twisted py2exe_py2 zope.interface + - pip install twisted py2exe zope.interface - type nul > C:\Miniconda\envs\syncplay\lib\site-packages\zope\__init__.py - pip freeze - conda list @@ -27,7 +27,7 @@ install: - del syncplay_v%ver%\lib\MPR.dll - mkdir syncplay_v%ver%\platforms - copy C:\Miniconda\envs\syncplay\library\plugins\platforms\qwindows.dll syncplay_v%ver%\platforms\ - - copy Syncplay-%ver%-Setup.exe Syncplay-%ver%-Setup-PySide2.exe + - copy Syncplay-%ver%-Setup.exe Syncplay-%ver%-Setup-Py3-PySide2.exe # Not a project with an msbuild file, build done at install. build: off @@ -35,10 +35,10 @@ build: off artifacts: - path: 'syncplay_v$(ver)' type: zip - name: Syncplay-$(ver)-win-pyside2 + name: Syncplay-$(ver)-win-py3-pyside2 - - path: Syncplay-$(ver)-Setup-PySide2.exe - name: Syncplay-$(ver)-win-setup-pyside2 + - path: Syncplay-$(ver)-Setup-Py3-PySide2.exe + name: Syncplay-$(ver)-win-setup-py3-pyside2 # Push artefact to S3 bucket and list all before_deploy: From bcb7e7104fb16f3e4c94665f70152168e63abb67 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 24 Apr 2018 16:08:35 +0200 Subject: [PATCH 04/95] Preliminary AppVeyor support --- buildPy2exe.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/buildPy2exe.py b/buildPy2exe.py index c96b22c..cc76c66 100755 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -1,13 +1,10 @@ #!/usr/bin/env python #coding:utf8 -''' *** TROUBLESHOOTING *** -) If you get the error "ImportError: No module named zope.interface" then add an empty __init__.py file to the PYTHONDIR/Lib/site-packages/zope directory - -2) It is expected that you will have NSIS 3 NSIS from http://nsis.sourceforge.net installed. - -''' +#*** TROUBLESHOOTING *** +#1) If you get the error "ImportError: No module named zope.interface" then add an empty __init__.py file to the PYTHONDIR/Lib/site-packages/zope directory +#2) It is expected that you will have NSIS 3 NSIS from http://nsis.sourceforge.net installed. import sys, codecs try: From 0d47140ccf285f3ae2e1f851e3921a2128b6d2a7 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 24 Apr 2018 16:12:59 +0200 Subject: [PATCH 05/95] Preliminary AppVeyor support --- buildPy2exe.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/buildPy2exe.py b/buildPy2exe.py index cc76c66..b438c52 100755 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -7,12 +7,12 @@ #2) It is expected that you will have NSIS 3 NSIS from http://nsis.sourceforge.net installed. import sys, codecs -try: - if (sys.version_info.major != 2) or (sys.version_info.minor < 7): - raise Exception("You must build Syncplay with Python 2.7!") -except AttributeError: - import warnings - warnings.warn("You must build Syncplay with Python 2.7!") +# try: +# if (sys.version_info.major != 2) or (sys.version_info.minor < 7): +# raise Exception("You must build Syncplay with Python 2.7!") +# except AttributeError: +# import warnings +# warnings.warn("You must build Syncplay with Python 2.7!") from distutils.core import setup from py2exe.build_exe import py2exe From dd0051e32e5969e9d476e359195f0e2b52b01487 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 24 Apr 2018 16:19:57 +0200 Subject: [PATCH 06/95] Preliminary AppVeyor support --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 72a9005..6f66438 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,7 +9,7 @@ init: - set PATH=C:\Miniconda;C:\Miniconda\Scripts;C:\Program Files (x86)\NSIS;%PATH% - conda create -n syncplay -y - activate syncplay - - conda install python=3.6 -y + - conda install python=3.5 -y - conda install pywin32 -y - conda install -c conda-forge pyside2 -y - pip install twisted py2exe zope.interface From b76c50f9cc48332db9ec640061919b887f9f6ae8 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 24 Apr 2018 16:30:03 +0200 Subject: [PATCH 07/95] Preliminary AppVeyor support --- buildPy2exe.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/buildPy2exe.py b/buildPy2exe.py index b438c52..fe451d6 100755 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -15,7 +15,10 @@ import sys, codecs # warnings.warn("You must build Syncplay with Python 2.7!") from distutils.core import setup -from py2exe.build_exe import py2exe +try: + from py2exe.build_exe import py2exe +except ImportError: + from py2exe.distutils_build_exe import py2exe from string import Template import syncplay From 4fecdbab834cb6ee509ecec485ddedfbeaeb798d Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 24 Apr 2018 16:37:36 +0200 Subject: [PATCH 08/95] Preliminary AppVeyor support --- .appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 6f66438..aa7d463 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,9 +9,9 @@ init: - set PATH=C:\Miniconda;C:\Miniconda\Scripts;C:\Program Files (x86)\NSIS;%PATH% - conda create -n syncplay -y - activate syncplay - - conda install python=3.5 -y + - conda install python=3.4 -y - conda install pywin32 -y - - conda install -c conda-forge pyside2 -y + - conda install -c conda-forge pyside -y - pip install twisted py2exe zope.interface - type nul > C:\Miniconda\envs\syncplay\lib\site-packages\zope\__init__.py - pip freeze From 2093ca14c1a90fa86d6b6171e88d6f988dabf825 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 24 Apr 2018 16:41:51 +0200 Subject: [PATCH 09/95] Preliminary AppVeyor support --- .appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index aa7d463..6f66438 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,9 +9,9 @@ init: - set PATH=C:\Miniconda;C:\Miniconda\Scripts;C:\Program Files (x86)\NSIS;%PATH% - conda create -n syncplay -y - activate syncplay - - conda install python=3.4 -y + - conda install python=3.5 -y - conda install pywin32 -y - - conda install -c conda-forge pyside -y + - conda install -c conda-forge pyside2 -y - pip install twisted py2exe zope.interface - type nul > C:\Miniconda\envs\syncplay\lib\site-packages\zope\__init__.py - pip freeze From 777e046ae19203aafef301c20c10556267060031 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 24 Apr 2018 17:57:33 +0200 Subject: [PATCH 10/95] Correct resourcesPath for frozen macOS app --- syncplay/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/syncplay/utils.py b/syncplay/utils.py index b04b6ca..1198b08 100755 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -141,10 +141,8 @@ def findWorkingDir(): frozen = getattr(sys, 'frozen', '') if not frozen: path = os.path.dirname(os.path.dirname(__file__)) - elif frozen in ('dll', 'console_exe', 'windows_exe'): + elif frozen in ('dll', 'console_exe', 'windows_exe', 'macosx_app'): path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) - elif frozen in ('macosx_app'): - path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))) else: path = "" return path From 2a1a291f3fcb6c220cc1812d9e0c6103813e443d Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 24 Apr 2018 19:12:42 +0200 Subject: [PATCH 11/95] Add Travis support --- .travis.yml | 34 +++++++++++++++++----------------- syncplay/players/mplayer.py | 4 ++-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index f7f88e3..8111954 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,48 +3,48 @@ osx_image: xcode7.3 branches: only: - - qtpy-pyside2 + - py3-qtpy-pyside2 script: -- python buildPy2app.py py2app +- python3 buildPy2app.py py2app before_install: +- hg clone https://alby128@bitbucket.org/alby128/py2app - brew update +- brew upgrade python +- which python3 +- python3 --version +- which pip3 +- pip3 --version - curl -O http://syncplay.s3.amazonaws.com/qt595.rb - mv qt595.rb qt.rb - brew install ./qt.rb -- which python -- which pip -- curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=PySide2-5.9.0a1-5.9.5-cp27-cp27m-macosx_10_11_x86_64.whl -o PySide2-5.9.0a1-5.9.5-cp27-cp27m-macosx_10_11_x86_64.whl -- pip install PySide2-5.9.0a1-5.9.5-cp27-cp27m-macosx_10_11_x86_64.whl +- curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=PySide2-5.9.0a1-5.9.5-cp36-cp36m-macosx_10_11_x86_64.whl -o PySide2-5.9.0a1-5.9.5-cp36-cp36m-macosx_10_11_x86_64.whl +- pip3 install PySide2-5.9.0a1-5.9.5-cp36-cp36m-macosx_10_11_x86_64.whl - ln -s /usr/local/lib/python2.7/site-packages/PySide2/libshiboken2-python2.7v.5.9.dylib /usr/local/lib/ - ln -s /usr/local/lib/python2.7/site-packages/PySide2/libpyside2-python2.7v.5.9.dylib /usr/local/lib/ #- python -c "from PySide2 import QtCore" -- python -c "from PySide2.QtCore import __version__; print __version__" -- hg clone https://alby128@bitbucket.org/alby128/py2app +- python3 -c "from PySide2.QtCore import __version__; print(__version__)" - cd py2app -- python setup.py install +- python3 setup.py install - cd .. -- python -c "from py2app.recipes import pyside2" +- python3 -c "from py2app.recipes import pyside2" install: -- pip install twisted appnope pyobjc -#- git clone -b qtpy-pyside2 https://github.com/alby128/syncplay.git syncplay-qtpy-PySide2 -#- cd syncplay-qtpy-PySide2 -#- git checkout qtpy-pyside2 +- pip3 install twisted appnope before_deploy: -- pip install dmgbuild +- pip3 install dmgbuild - mkdir dist_dmg - mv resources/macos_vlc_install.command resources/.macos_vlc_install.command - mv resources/lua/intf/syncplay.lua resources/lua/intf/.syncplay.lua - mv resources/macOS_readme.pdf resources/.macOS_readme.pdf - export VER="$(cat syncplay/__init__.py | awk '/version/ {gsub("\047", "", $3); print $NF}')" -- dmgbuild -s appdmg.py "Syncplay" dist_dmg/Syncplay_${VER}_macOS_pyside2.dmg +- dmgbuild -s appdmg.py "Syncplay" dist_dmg/Syncplay_${VER}_macOS_py3_pyside2.dmg deploy: skip_cleanup: true - on: qtpy-pyside2 + on: py3-qtpy-pyside2 provider: bintray file: "bintray.json" user: alby128 diff --git a/syncplay/players/mplayer.py b/syncplay/players/mplayer.py index 42802b4..f3a4f9e 100755 --- a/syncplay/players/mplayer.py +++ b/syncplay/players/mplayer.py @@ -324,9 +324,9 @@ class MplayerPlayer(BasePlayer): if 'TERM' in env: del env['TERM'] if filePath: - self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.__getCwd(filePath, env), env=env, universal_newlines=True) + self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.__getCwd(filePath, env), env=env) else: - self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, universal_newlines=True) + self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, env=env) threading.Thread.__init__(self, name="MPlayer Listener") From dd2b0dfc74539a5557fd2c4b5962bc1e10cc2566 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 24 Apr 2018 20:45:13 +0200 Subject: [PATCH 12/95] Correct symbolic links to PySide2 dylibs --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8111954..00d55b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,8 +21,8 @@ before_install: - brew install ./qt.rb - curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=PySide2-5.9.0a1-5.9.5-cp36-cp36m-macosx_10_11_x86_64.whl -o PySide2-5.9.0a1-5.9.5-cp36-cp36m-macosx_10_11_x86_64.whl - pip3 install PySide2-5.9.0a1-5.9.5-cp36-cp36m-macosx_10_11_x86_64.whl -- ln -s /usr/local/lib/python2.7/site-packages/PySide2/libshiboken2-python2.7v.5.9.dylib /usr/local/lib/ -- ln -s /usr/local/lib/python2.7/site-packages/PySide2/libpyside2-python2.7v.5.9.dylib /usr/local/lib/ +- ln -s /usr/local/lib/python3.6/site-packages/PySide2/libpyside2.cpython-36m-darwin.5.9.dylib /usr/local/lib/ +- ln -s /usr/local/lib/python3.6/site-packages/PySide2/libshiboken2.cpython-36m-darwin.5.9.dylib /usr/local/lib/ #- python -c "from PySide2 import QtCore" - python3 -c "from PySide2.QtCore import __version__; print(__version__)" - cd py2app @@ -40,7 +40,7 @@ before_deploy: - mv resources/lua/intf/syncplay.lua resources/lua/intf/.syncplay.lua - mv resources/macOS_readme.pdf resources/.macOS_readme.pdf - export VER="$(cat syncplay/__init__.py | awk '/version/ {gsub("\047", "", $3); print $NF}')" -- dmgbuild -s appdmg.py "Syncplay" dist_dmg/Syncplay_${VER}_macOS_py3_pyside2.dmg +- dmgbuild -s appdmg.py "Syncplay" dist_dmg/Syncplay_${VER}_macOS_py36_pyside2.dmg deploy: skip_cleanup: true From 111976faf966b62bb74f35cbec0ffcb18e36e978 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Wed, 25 Apr 2018 15:42:19 +0200 Subject: [PATCH 13/95] Adapt mplayer/mpv protocols to Python 3.x --- syncplay/players/mplayer.py | 3 +++ syncplay/players/mpv.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/syncplay/players/mplayer.py b/syncplay/players/mplayer.py index f3a4f9e..f0b0404 100755 --- a/syncplay/players/mplayer.py +++ b/syncplay/players/mplayer.py @@ -345,6 +345,7 @@ class MplayerPlayer(BasePlayer): def run(self): line = self.__process.stdout.readline() + line = line.decode('utf-8') if "MPlayer 1" in line: self.__playerController.notMplayer2() else: @@ -352,6 +353,7 @@ class MplayerPlayer(BasePlayer): self.__playerController.lineReceived(line) while self.__process.poll() is None: line = self.__process.stdout.readline() + line = line.decode('utf-8') line = line.rstrip("\r\n") self.__playerController.lineReceived(line) self.__playerController.drop() @@ -442,6 +444,7 @@ class MplayerPlayer(BasePlayer): #line = line.decode('utf8') line = line + "\n" self.__playerController._client.ui.showDebugMessage("player >> {}".format(line)) + line = line.encode('utf-8') self.__process.stdin.write(line) except IOError: pass diff --git a/syncplay/players/mpv.py b/syncplay/players/mpv.py index b1a3ed8..dbe01e9 100755 --- a/syncplay/players/mpv.py +++ b/syncplay/players/mpv.py @@ -15,7 +15,7 @@ class MpvPlayer(MplayerPlayer): @staticmethod def run(client, playerPath, filePath, args): try: - ver = MpvPlayer.RE_VERSION.search(subprocess.check_output([playerPath, '--version'])) + ver = MpvPlayer.RE_VERSION.search(subprocess.check_output([playerPath, '--version']).decode('utf-8')) except: ver = None constants.MPV_NEW_VERSION = ver is None or int(ver.group(1)) > 0 or int(ver.group(2)) >= 6 From ff08eecf02386294fba8fffaa9ab04d04cdd5617 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Wed, 25 Apr 2018 15:43:48 +0200 Subject: [PATCH 14/95] Fixes server list retrieval --- syncplay/utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/syncplay/utils.py b/syncplay/utils.py index 1198b08..2d76c46 100755 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -371,14 +371,15 @@ def open_system_file_browser(path): def getListOfPublicServers(): try: - import urllib.request, urllib.parse, urllib.error, syncplay, sys, messages, json + import urllib.request, urllib.parse, urllib.error, syncplay, sys, json params = urllib.parse.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, - 'language': messages.messages["CURRENT"]}) + 'language': syncplay.messages.messages["CURRENT"]}) f = urllib.request.urlopen(constants.SYNCPLAY_PUBLIC_SERVER_LIST_URL.format(params)) response = f.read() + response = response.decode('utf-8') response = response.replace("

","").replace("

","").replace("
","").replace("“","'").replace("”","'").replace(":’","'").replace("’","'").replace("′","'").replace("\n","").replace("\r","") # Fix Wordpress response = ast.literal_eval(response) - + if response: return response else: From bac3cf9a7f5dc4ef15f732d325f720b0be9ed3a1 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Wed, 25 Apr 2018 18:19:01 +0200 Subject: [PATCH 15/95] Fixes client updates checker --- syncplay/client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/syncplay/client.py b/syncplay/client.py index cdce3e9..3b4386d 100755 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -910,12 +910,13 @@ class SyncplayClient(object): def checkForUpdate(self, userInitiated): try: - import urllib.request, urllib.parse, urllib.error, syncplay, sys, messages, json + import urllib.request, urllib.parse, urllib.error, syncplay, sys, json params = urllib.parse.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, - 'language': messages.messages["CURRENT"], 'platform': sys.platform, 'userInitiated': userInitiated}) + 'language': syncplay.messages.messages["CURRENT"], 'platform': sys.platform, 'userInitiated': userInitiated}) f = urllib.request.urlopen(constants.SYNCPLAY_UPDATE_URL.format(params)) response = f.read() + response = response.decode('utf-8') response = response.replace("

","").replace("

","").replace("
","").replace("“","\"").replace("”","\"") # Fix Wordpress response = json.loads(response) publicServers = None From 4fb771a4509acb59f464431e6caa2c046ebc19d0 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Thu, 26 Apr 2018 23:34:09 +0200 Subject: [PATCH 16/95] Add preliminary support of PyInstaller on AppVeyor --- .appveyor.yml | 21 +++++++++------------ .gitignore | 1 + pyinstaller-onedir.spec | 34 ++++++++++++++++++++++++++++++++++ pyinstaller-onefile.spec | 30 ++++++++++++++++++++++++++++++ syncplay/players/vlc.py | 5 ++++- syncplay/utils.py | 6 +++++- 6 files changed, 83 insertions(+), 14 deletions(-) create mode 100755 pyinstaller-onedir.spec create mode 100755 pyinstaller-onefile.spec diff --git a/.appveyor.yml b/.appveyor.yml index 6f66438..f796de9 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -12,7 +12,7 @@ init: - conda install python=3.5 -y - conda install pywin32 -y - conda install -c conda-forge pyside2 -y - - pip install twisted py2exe zope.interface + - pip install twisted pyinstaller zope.interface - type nul > C:\Miniconda\envs\syncplay\lib\site-packages\zope\__init__.py - pip freeze - conda list @@ -20,25 +20,22 @@ init: install: - cd %APPVEYOR_BUILD_FOLDER% - for /F "tokens=2 delims='" %%a in ('findstr version syncplay\__init__.py') do @set ver=%%a - - python buildPy2exe.py - - del syncplay_v%ver%\lib\api-* - - del syncplay_v%ver%\lib\DNSAPI.dll - - del syncplay_v%ver%\lib\IPHLPAPI.dll - - del syncplay_v%ver%\lib\MPR.dll - - mkdir syncplay_v%ver%\platforms - - copy C:\Miniconda\envs\syncplay\library\plugins\platforms\qwindows.dll syncplay_v%ver%\platforms\ - - copy Syncplay-%ver%-Setup.exe Syncplay-%ver%-Setup-Py3-PySide2.exe + - pyinstaller pyinstaller-onefile.spec + - move dist\Syncplay.exe Syncplay-%ver%-win-Py3-PySide2.exe + - del build /s /Q + - del dist /s /Q + - pyinstaller pyinstaller-onedir.spec # Not a project with an msbuild file, build done at install. build: off artifacts: - - path: 'syncplay_v$(ver)' + - path: 'dist' type: zip name: Syncplay-$(ver)-win-py3-pyside2 - - path: Syncplay-$(ver)-Setup-Py3-PySide2.exe - name: Syncplay-$(ver)-win-setup-py3-pyside2 + - path: Syncplay-$(ver)-win-Py3-PySide2.exe + name: Syncplay-$(ver)-win-py3-pyside2 # Push artefact to S3 bucket and list all before_deploy: diff --git a/.gitignore b/.gitignore index 9b9e6b4..5b13d1c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ dist.7z .* !.travis.yml !.appveyor.yml +__pycache__ \ No newline at end of file diff --git a/pyinstaller-onedir.spec b/pyinstaller-onedir.spec new file mode 100755 index 0000000..0569b89 --- /dev/null +++ b/pyinstaller-onedir.spec @@ -0,0 +1,34 @@ +# -*- mode: python -*- + +block_cipher = None + + +a = Analysis(['syncplayClient.py'], + pathex=['C:\\Users\\Alberto\\Documents\\syncplay-py3-qtpy-pyside2'], + binaries=[], + datas=[('resources/*', 'resources')], + hiddenimports=['PySide2', 'PySide2.QtCore', 'PySide2.QtWidgets'], + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name='Syncplay', + debug=False, + strip=False, + upx=True, + console=False, + icon='resources/icon.ico') +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + name='Syncplay') diff --git a/pyinstaller-onefile.spec b/pyinstaller-onefile.spec new file mode 100755 index 0000000..0d26e97 --- /dev/null +++ b/pyinstaller-onefile.spec @@ -0,0 +1,30 @@ +# -*- mode: python -*- + +block_cipher = None + + +a = Analysis(['syncplayClient.py'], + pathex=['C:\\Users\\Alberto\\Documents\\syncplay-py3-qtpy-pyside2'], + binaries=[], + datas=[('resources/*', 'resources')], + hiddenimports=['PySide2', 'PySide2.QtCore', 'PySide2.QtWidgets'], + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + name='Syncplay', + debug=False, + strip=False, + upx=False, + runtime_tmpdir=None, + console=False, + icon='resources/icon.ico') diff --git a/syncplay/players/vlc.py b/syncplay/players/vlc.py index f4ec57b..f8eefe3 100755 --- a/syncplay/players/vlc.py +++ b/syncplay/players/vlc.py @@ -373,7 +373,10 @@ class VlcPlayer(BasePlayer): self.__playerController.drop(getMessage("vlc-interface-version-mismatch").format(self.oldIntfVersion,constants.VLC_INTERFACE_MIN_VERSION)) else: - self.__process = subprocess.Popen(call, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + if isWindows() and getattr(sys, 'frozen', '') and getattr(sys, '_MEIPASS', '') is not None: #Needed for pyinstaller --onefile bundle + self.__process = subprocess.Popen(call, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False, creationflags=0x08000000) + else: + self.__process = subprocess.Popen(call, stderr=subprocess.PIPE, stdout=subprocess.PIPE) self.timeVLCLaunched = time.time() if self._shouldListenForSTDOUT(): for line in iter(self.__process.stderr.readline, ''): diff --git a/syncplay/utils.py b/syncplay/utils.py index 2d76c46..d0ce940 100755 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -143,12 +143,16 @@ def findWorkingDir(): path = os.path.dirname(os.path.dirname(__file__)) elif frozen in ('dll', 'console_exe', 'windows_exe', 'macosx_app'): path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) + elif frozen: #needed for PyInstaller + if getattr(sys, '_MEIPASS', '') is not None: + path = getattr(sys, '_MEIPASS', '') #--onefile + else: + path = os.path.dirname(sys.executable) #--onedir else: path = "" return path def getResourcesPath(): - if isWindows(): return findWorkingDir() + "\\resources\\" else: From fe224c031600934d1719a99fc8140d9169ed8835 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Fri, 27 Apr 2018 00:19:05 +0200 Subject: [PATCH 17/95] Fixes mpv support --- syncplay/players/mplayer.py | 4 ++-- syncplay/players/mpv.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/syncplay/players/mplayer.py b/syncplay/players/mplayer.py index f0b0404..5e85b67 100755 --- a/syncplay/players/mplayer.py +++ b/syncplay/players/mplayer.py @@ -324,9 +324,9 @@ class MplayerPlayer(BasePlayer): if 'TERM' in env: del env['TERM'] if filePath: - self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.__getCwd(filePath, env), env=env) + self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.__getCwd(filePath, env), env=env, bufsize=0) else: - self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, env=env) + self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, bufsize=0) threading.Thread.__init__(self, name="MPlayer Listener") diff --git a/syncplay/players/mpv.py b/syncplay/players/mpv.py index dbe01e9..26da661 100755 --- a/syncplay/players/mpv.py +++ b/syncplay/players/mpv.py @@ -267,7 +267,7 @@ class NewMpvPlayer(OldMpvPlayer): self.mpvErrorCheck(line) if "" in line: - line = line.decode("utf-8").replace(constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER, "\\").encode("utf-8") + line = line.replace(constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER, "\\") self._listener.sendChat(line[6:-7]) if "" in line: From 53dcb17eaf27d81b19d9ca8cd975eec5b37d4bf8 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Fri, 27 Apr 2018 00:54:05 +0200 Subject: [PATCH 18/95] Remove specific path from pyinstaller script --- pyinstaller-onedir.spec | 5 ++++- pyinstaller-onefile.spec | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pyinstaller-onedir.spec b/pyinstaller-onedir.spec index 0569b89..cfc0c1c 100755 --- a/pyinstaller-onedir.spec +++ b/pyinstaller-onedir.spec @@ -1,10 +1,13 @@ # -*- mode: python -*- +import os +workdir = os.getcwd() + block_cipher = None a = Analysis(['syncplayClient.py'], - pathex=['C:\\Users\\Alberto\\Documents\\syncplay-py3-qtpy-pyside2'], + pathex=[workdir], binaries=[], datas=[('resources/*', 'resources')], hiddenimports=['PySide2', 'PySide2.QtCore', 'PySide2.QtWidgets'], diff --git a/pyinstaller-onefile.spec b/pyinstaller-onefile.spec index 0d26e97..8e1f105 100755 --- a/pyinstaller-onefile.spec +++ b/pyinstaller-onefile.spec @@ -1,10 +1,13 @@ # -*- mode: python -*- +import os +workdir = os.getcwd() + block_cipher = None a = Analysis(['syncplayClient.py'], - pathex=['C:\\Users\\Alberto\\Documents\\syncplay-py3-qtpy-pyside2'], + pathex=[workdir], binaries=[], datas=[('resources/*', 'resources')], hiddenimports=['PySide2', 'PySide2.QtCore', 'PySide2.QtWidgets'], From 6972b4d5eaaa8eee228dcd06f7dd21c012059fb3 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Fri, 27 Apr 2018 09:39:54 +0200 Subject: [PATCH 19/95] Disable twisted runtime hook in PyInstaller --- .appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.appveyor.yml b/.appveyor.yml index f796de9..aaffc53 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -14,6 +14,7 @@ init: - conda install -c conda-forge pyside2 -y - pip install twisted pyinstaller zope.interface - type nul > C:\Miniconda\envs\syncplay\lib\site-packages\zope\__init__.py + - type nul > C:\Miniconda\envs\syncplay\lib\site-packages\pyinstaller\loader\rthooks\pyi_rth_twisted.py - pip freeze - conda list From 7df01104bfbaa637e71fc6245526613da9250704 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Fri, 27 Apr 2018 21:10:26 +0200 Subject: [PATCH 20/95] Build installer only with NSIS on AppVeyor --- .appveyor.yml | 14 +- buildInstaller.py | 705 ++++++++++++++++++++++++++++++++++++++++ pyinstaller-onedir.spec | 4 +- 3 files changed, 718 insertions(+), 5 deletions(-) create mode 100644 buildInstaller.py diff --git a/.appveyor.yml b/.appveyor.yml index aaffc53..0ae9d30 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -22,10 +22,13 @@ install: - cd %APPVEYOR_BUILD_FOLDER% - for /F "tokens=2 delims='" %%a in ('findstr version syncplay\__init__.py') do @set ver=%%a - pyinstaller pyinstaller-onefile.spec - - move dist\Syncplay.exe Syncplay-%ver%-win-Py3-PySide2.exe + - move dist\Syncplay.exe Syncplay-%ver%-win-Py35-PySide2.exe - del build /s /Q - del dist /s /Q - pyinstaller pyinstaller-onedir.spec + - cd dist + - python buildInstaller.py + - cd .. # Not a project with an msbuild file, build done at install. build: off @@ -33,14 +36,17 @@ build: off artifacts: - path: 'dist' type: zip - name: Syncplay-$(ver)-win-py3-pyside2 + name: Syncplay-$(ver)-win-py35-pyside2 - - path: Syncplay-$(ver)-win-Py3-PySide2.exe - name: Syncplay-$(ver)-win-py3-pyside2 + - path: Syncplay-$(ver)-win-Py35-PySide2.exe + name: Syncplay-$(ver)-win-py35-pyside2 # Push artefact to S3 bucket and list all before_deploy: - dir + - cd dist + - dir + - cd .. #- python -c "from PySide2 import QtCore; print QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.PluginsPath)" diff --git a/buildInstaller.py b/buildInstaller.py new file mode 100644 index 0000000..bd9215f --- /dev/null +++ b/buildInstaller.py @@ -0,0 +1,705 @@ +#!/usr/bin/env python +#coding:utf8 + + +#*** TROUBLESHOOTING *** +#1) If you get the error "ImportError: No module named zope.interface" then add an empty __init__.py file to the PYTHONDIR/Lib/site-packages/zope directory +#2) It is expected that you will have NSIS 3 NSIS from http://nsis.sourceforge.net installed. + +import sys, codecs +# try: +# if (sys.version_info.major != 2) or (sys.version_info.minor < 7): +# raise Exception("You must build Syncplay with Python 2.7!") +# except AttributeError: +# import warnings +# warnings.warn("You must build Syncplay with Python 2.7!") + +from string import Template + +import syncplay +import os +import subprocess + +from syncplay.messages import getMissingStrings +missingStrings = getMissingStrings() +if missingStrings is not None and missingStrings is not "": + import warnings + warnings.warn("MISSING/UNUSED STRINGS DETECTED:\n{}".format(missingStrings)) + +def get_nsis_path(): + bin_name = "makensis.exe" + from winreg import HKEY_LOCAL_MACHINE as HKLM + from winreg import KEY_READ, KEY_WOW64_32KEY, OpenKey, QueryValueEx + + try: + nsisreg = OpenKey(HKLM, "Software\\NSIS", 0, KEY_READ | KEY_WOW64_32KEY) + if QueryValueEx(nsisreg, "VersionMajor")[0] >= 3: + return "{}\\{}".format(QueryValueEx(nsisreg, "")[0], bin_name) + else: + raise Exception("You must install NSIS 3 or later.") + except WindowsError: + return bin_name +NSIS_COMPILE = get_nsis_path() + +OUT_DIR = "syncplay_v{}".format(syncplay.version) +SETUP_SCRIPT_PATH = "syncplay_setup.nsi" +NSIS_SCRIPT_TEMPLATE = r""" + !include LogicLib.nsh + !include nsDialogs.nsh + !include FileFunc.nsh + + LoadLanguageFile "$${NSISDIR}\Contrib\Language files\English.nlf" + LoadLanguageFile "$${NSISDIR}\Contrib\Language files\Polish.nlf" + LoadLanguageFile "$${NSISDIR}\Contrib\Language files\Russian.nlf" + LoadLanguageFile "$${NSISDIR}\Contrib\Language files\German.nlf" + LoadLanguageFile "$${NSISDIR}\Contrib\Language files\Italian.nlf" + + Unicode true + + Name "Syncplay $version" + OutFile "Syncplay-$version-Setup.exe" + InstallDir $$PROGRAMFILES\Syncplay + RequestExecutionLevel admin + XPStyle on + Icon resources\icon.ico ;Change DIR + SetCompressor /SOLID lzma + + VIProductVersion "$version.0" + VIAddVersionKey /LANG=$${LANG_ENGLISH} "ProductName" "Syncplay" + VIAddVersionKey /LANG=$${LANG_ENGLISH} "FileVersion" "$version.0" + VIAddVersionKey /LANG=$${LANG_ENGLISH} "LegalCopyright" "Syncplay" + VIAddVersionKey /LANG=$${LANG_ENGLISH} "FileDescription" "Syncplay" + + VIAddVersionKey /LANG=$${LANG_POLISH} "ProductName" "Syncplay" + VIAddVersionKey /LANG=$${LANG_POLISH} "FileVersion" "$version.0" + VIAddVersionKey /LANG=$${LANG_POLISH} "LegalCopyright" "Syncplay" + VIAddVersionKey /LANG=$${LANG_POLISH} "FileDescription" "Syncplay" + + VIAddVersionKey /LANG=$${LANG_RUSSIAN} "ProductName" "Syncplay" + VIAddVersionKey /LANG=$${LANG_RUSSIAN} "FileVersion" "$version.0" + VIAddVersionKey /LANG=$${LANG_RUSSIAN} "LegalCopyright" "Syncplay" + VIAddVersionKey /LANG=$${LANG_RUSSIAN} "FileDescription" "Syncplay" + + VIAddVersionKey /LANG=$${LANG_ITALIAN} "ProductName" "Syncplay" + VIAddVersionKey /LANG=$${LANG_ITALIAN} "FileVersion" "$version.0" + VIAddVersionKey /LANG=$${LANG_ITALIAN} "LegalCopyright" "Syncplay" + VIAddVersionKey /LANG=$${LANG_ITALIAN} "FileDescription" "Syncplay" + + LangString ^SyncplayLanguage $${LANG_ENGLISH} "en" + LangString ^Associate $${LANG_ENGLISH} "Associate Syncplay with multimedia files." + LangString ^VLC $${LANG_ENGLISH} "Install Syncplay interface for VLC 2 and above" + LangString ^BrowseVLCBtn $${LANG_ENGLISH} "Select VLC folder" + LangString ^Shortcut $${LANG_ENGLISH} "Create Shortcuts in following locations:" + LangString ^StartMenu $${LANG_ENGLISH} "Start Menu" + LangString ^Desktop $${LANG_ENGLISH} "Desktop" + LangString ^QuickLaunchBar $${LANG_ENGLISH} "Quick Launch Bar" + LangString ^AutomaticUpdates $${LANG_ENGLISH} "Check for updates automatically" + LangString ^UninstConfig $${LANG_ENGLISH} "Delete configuration file." + + LangString ^SyncplayLanguage $${LANG_POLISH} "pl" + LangString ^Associate $${LANG_POLISH} "Skojarz Syncplaya z multimediami" + LangString ^VLC $${LANG_POLISH} "Zainstaluj interface Syncplaya dla VLC 2+" + LangString ^BrowseVLCBtn $${LANG_POLISH} "Określ folder VLC" + LangString ^Shortcut $${LANG_POLISH} "Utworz skroty w nastepujacych miejscach:" + LangString ^StartMenu $${LANG_POLISH} "Menu Start" + LangString ^Desktop $${LANG_POLISH} "Pulpit" + LangString ^QuickLaunchBar $${LANG_POLISH} "Pasek szybkiego uruchamiania" + LangString ^UninstConfig $${LANG_POLISH} "Usun plik konfiguracyjny." + + LangString ^SyncplayLanguage $${LANG_RUSSIAN} "ru" + LangString ^Associate $${LANG_RUSSIAN} "Ассоциировать Syncplay с видеофайлами" + LangString ^VLC $${LANG_RUSSIAN} "Установить интерфейс Syncplay для VLC 2+" + LangString ^BrowseVLCBtn $${LANG_RUSSIAN} "Укажите папку VLC" + LangString ^Shortcut $${LANG_RUSSIAN} "Создать ярлыки:" + LangString ^StartMenu $${LANG_RUSSIAN} "в меню Пуск" + LangString ^Desktop $${LANG_RUSSIAN} "на рабочем столе" + LangString ^QuickLaunchBar $${LANG_RUSSIAN} "в меню быстрого запуска" + LangString ^AutomaticUpdates $${LANG_RUSSIAN} "Проверять обновления автоматически"; TODO: Confirm Russian translation ("Check for updates automatically") + LangString ^UninstConfig $${LANG_RUSSIAN} "Удалить файл настроек." + + LangString ^SyncplayLanguage $${LANG_GERMAN} "de" + LangString ^Associate $${LANG_GERMAN} "Syncplay als Standardprogramm für Multimedia-Dateien verwenden." + LangString ^VLC $${LANG_GERMAN} "Syncplay-Interface für VLC installieren (ab VLC 2+)" + LangString ^Shortcut $${LANG_GERMAN} "Erstelle Verknüpfungen an folgenden Orten:" + LangString ^BrowseVLCBtn $${LANG_GERMAN} "VLC-Ordner wählen" + LangString ^StartMenu $${LANG_GERMAN} "Startmenü" + LangString ^Desktop $${LANG_GERMAN} "Desktop" + LangString ^QuickLaunchBar $${LANG_GERMAN} "Schnellstartleiste" + LangString ^AutomaticUpdates $${LANG_GERMAN} "Automatisch nach Updates suchen"; + LangString ^UninstConfig $${LANG_GERMAN} "Konfigurationsdatei löschen." + + LangString ^SyncplayLanguage $${LANG_ITALIAN} "it" + LangString ^Associate $${LANG_ITALIAN} "Associa Syncplay con i file multimediali." + LangString ^VLC $${LANG_ITALIAN} "Installa l'interfaccia di Syncplay per VLC 2+" + LangString ^BrowseVLCBtn $${LANG_ITALIAN} "Cartella di VLC" + LangString ^Shortcut $${LANG_ITALIAN} "Crea i collegamenti nei percorsi seguenti:" + LangString ^StartMenu $${LANG_ITALIAN} "Menu Start" + LangString ^Desktop $${LANG_ITALIAN} "Desktop" + LangString ^QuickLaunchBar $${LANG_ITALIAN} "Barra di avvio rapido" + LangString ^AutomaticUpdates $${LANG_ITALIAN} "Controllo automatico degli aggiornamenti" + LangString ^UninstConfig $${LANG_ITALIAN} "Cancella i file di configurazione." + + ; Remove text to save space + LangString ^ClickInstall $${LANG_GERMAN} " " + + PageEx license + LicenseData resources\license.rtf + PageExEnd + Page custom DirectoryCustom DirectoryCustomLeave + Page instFiles + + UninstPage custom un.installConfirm un.installConfirmLeave + UninstPage instFiles + + Var Dialog + Var Icon_Syncplay + Var Icon_Syncplay_Handle + ;Var CheckBox_Associate + Var CheckBox_VLC + Var CheckBox_AutomaticUpdates + Var CheckBox_StartMenuShortcut + Var CheckBox_DesktopShortcut + Var CheckBox_QuickLaunchShortcut + ;Var CheckBox_Associate_State + Var CheckBox_VLC_State + Var CheckBox_AutomaticUpdates_State + Var CheckBox_StartMenuShortcut_State + Var CheckBox_DesktopShortcut_State + Var CheckBox_QuickLaunchShortcut_State + Var Button_Browse + Var Button_Browse_VLC + Var Directory + Var GroupBox_DirSub + Var Label_Text + Var Label_Shortcut + Var Label_Size + Var Label_Space + Var Text_Directory + + Var Uninst_Dialog + Var Uninst_Icon + Var Uninst_Icon_Handle + Var Uninst_Label_Directory + Var Uninst_Label_Text + Var Uninst_Text_Directory + Var Uninst_CheckBox_Config + Var Uninst_CheckBox_Config_State + + Var Size + Var SizeHex + Var AvailibleSpace + Var AvailibleSpaceGiB + Var Drive + Var VLC_Directory + + ;!macro APP_ASSOCIATE EXT FileCLASS DESCRIPTION COMMANDTEXT COMMAND + ; WriteRegStr HKCR ".$${EXT}" "" "$${FileCLASS}" + ; WriteRegStr HKCR "$${FileCLASS}" "" `$${DESCRIPTION}` + ; WriteRegStr HKCR "$${FileCLASS}\shell" "" "open" + ; WriteRegStr HKCR "$${FileCLASS}\shell\open" "" `$${COMMANDTEXT}` + ; WriteRegStr HKCR "$${FileCLASS}\shell\open\command" "" `$${COMMAND}` + ;!macroend + + !macro APP_UNASSOCIATE EXT FileCLASS + ; Backup the previously associated File class + ReadRegStr $$R0 HKCR ".$${EXT}" `$${FileCLASS}_backup` + WriteRegStr HKCR ".$${EXT}" "" "$$R0" + DeleteRegKey HKCR `$${FileCLASS}` + !macroend + + ;!macro ASSOCIATE EXT + ; !insertmacro APP_ASSOCIATE "$${EXT}" "Syncplay.$${EXT}" "$$INSTDIR\Syncplay.exe,%1%" \ + ; "Open with Syncplay" "$$INSTDIR\Syncplay.exe $$\"%1$$\"" + ;!macroend + + !macro UNASSOCIATE EXT + !insertmacro APP_UNASSOCIATE "$${EXT}" "Syncplay.$${EXT}" + !macroend + + ;Prevents from running more than one instance of installer and sets default state of checkboxes + Function .onInit + System::Call 'kernel32::CreateMutexA(i 0, i 0, t "SyncplayMutex") i .r1 ?e' + Pop $$R0 + StrCmp $$R0 0 +3 + MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running." + Abort + + ;StrCpy $$CheckBox_Associate_State $${BST_CHECKED} + StrCpy $$CheckBox_StartMenuShortcut_State $${BST_CHECKED} + Call GetVLCDir + Call UpdateVLCCheckbox + + Call GetSize + Call DriveSpace + Call Language + FunctionEnd + + ;Language selection dialog + Function Language + Push "" + Push $${LANG_ENGLISH} + Push English + Push $${LANG_POLISH} + Push Polski + Push $${LANG_RUSSIAN} + Push Русский + Push $${LANG_GERMAN} + Push Deutsch + Push $${LANG_ITALIAN} + Push Italiano + Push A ; A means auto count languages + LangDLL::LangDialog "Language Selection" "Please select the language of Syncplay and the installer" + Pop $$LANGUAGE + StrCmp $$LANGUAGE "cancel" 0 +2 + Abort + FunctionEnd + + Function DirectoryCustom + + nsDialogs::Create 1018 + Pop $$Dialog + + GetFunctionAddress $$R8 DirectoryCustomLeave + nsDialogs::OnBack $$R8 + + $${NSD_CreateIcon} 0u 0u 22u 20u "" + Pop $$Icon_Syncplay + $${NSD_SetIconFromInstaller} $$Icon_Syncplay $$Icon_Syncplay_Handle + + $${NSD_CreateLabel} 25u 0u 241u 34u "$$(^DirText)" + Pop $$Label_Text + + $${NSD_CreateText} 8u 38u 187u 12u "$$INSTDIR" + Pop $$Text_Directory + $${NSD_SetFocus} $$Text_Directory + + $${NSD_CreateBrowseButton} 202u 37u 55u 14u "$$(^BrowseBtn)" + Pop $$Button_Browse + $${NSD_OnClick} $$Button_Browse DirectoryBrowseDialog + + $${NSD_CreateGroupBox} 1u 27u 264u 30u "$$(^DirSubText)" + Pop $$GroupBox_DirSub + + $${NSD_CreateLabel} 0u 122u 132 8u "$$(^SpaceRequired)$$SizeMB" + Pop $$Label_Size + + $${NSD_CreateLabel} 321u 122u 132 8u "$$(^SpaceAvailable)$$AvailibleSpaceGiB.$$AvailibleSpaceGB" + Pop $$Label_Space + + ;$${NSD_CreateCheckBox} 8u 59u 187u 10u "$$(^Associate)" + ;Pop $$CheckBox_Associate + + $${NSD_CreateBrowseButton} 185u 70u 70u 14u "$$(^BrowseVLCBtn)" + Pop $$Button_Browse_VLC + $${NSD_OnClick} $$Button_Browse_VLC DirectoryBrowseDialogVLC + + $${NSD_CreateCheckBox} 8u 72u 250u 10u "$$(^VLC)" + Pop $$CheckBox_VLC + + $${NSD_CreateCheckBox} 8u 85u 250u 10u "$$(^AutomaticUpdates)" + Pop $$CheckBox_AutomaticUpdates + $${NSD_Check} $$CheckBox_AutomaticUpdates + + $${NSD_CreateLabel} 8u 98u 187u 10u "$$(^Shortcut)" + Pop $$Label_Shortcut + + $${NSD_CreateCheckbox} 8u 111u 60u 10u "$$(^StartMenu)" + Pop $$CheckBox_StartMenuShortcut + + $${NSD_CreateCheckbox} 78u 111u 70u 10u "$$(^Desktop)" + Pop $$CheckBox_DesktopShortcut + + $${NSD_CreateCheckbox} 158u 111u 130u 10u "$$(^QuickLaunchBar)" + Pop $$CheckBox_QuickLaunchShortcut + + ;$${If} $$CheckBox_Associate_State == $${BST_CHECKED} + ; $${NSD_Check} $$CheckBox_Associate + ;$${EndIf} + + $${If} $$CheckBox_VLC_State == $${BST_CHECKED} + $${NSD_Check} $$CheckBox_VLC + $${EndIf} + + Call UpdateVLCCheckbox + + $${If} $$CheckBox_StartMenuShortcut_State == $${BST_CHECKED} + $${NSD_Check} $$CheckBox_StartMenuShortcut + $${EndIf} + + $${If} $$CheckBox_DesktopShortcut_State == $${BST_CHECKED} + $${NSD_Check} $$CheckBox_DesktopShortcut + $${EndIf} + + $${If} $$CheckBox_QuickLaunchShortcut_State == $${BST_CHECKED} + $${NSD_Check} $$CheckBox_QuickLaunchShortcut + $${EndIf} + + $${If} $$CheckBox_AutomaticUpdates_State == $${BST_CHECKED} + $${NSD_Check} $$CheckBox_AutomaticUpdates + $${EndIf} + + nsDialogs::Show + + $${NSD_FreeIcon} $$Icon_Syncplay_Handle + + FunctionEnd + + Function DirectoryCustomLeave + $${NSD_GetText} $$Text_Directory $$INSTDIR + ;$${NSD_GetState} $$CheckBox_Associate $$CheckBox_Associate_State + $${NSD_GetState} $$CheckBox_VLC $$CheckBox_VLC_State + $${NSD_GetState} $$CheckBox_AutomaticUpdates $$CheckBox_AutomaticUpdates_State + $${NSD_GetState} $$CheckBox_StartMenuShortcut $$CheckBox_StartMenuShortcut_State + $${NSD_GetState} $$CheckBox_DesktopShortcut $$CheckBox_DesktopShortcut_State + $${NSD_GetState} $$CheckBox_QuickLaunchShortcut $$CheckBox_QuickLaunchShortcut_State + FunctionEnd + + Function DirectoryBrowseDialog + nsDialogs::SelectFolderDialog $$(^DirBrowseText) + Pop $$Directory + $${If} $$Directory != error + StrCpy $$INSTDIR $$Directory + $${NSD_SetText} $$Text_Directory $$INSTDIR + Call DriveSpace + $${NSD_SetText} $$Label_Space "$$(^SpaceAvailable)$$AvailibleSpaceGiB.$$AvailibleSpaceGB" + $${EndIf} + Abort + FunctionEnd + + Function GetVLCDir + IfFileExists "$$VLC_Directory\vlc.exe" VLCFound 0 + ReadRegStr $$VLC_Directory HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "VLCInstallLocation" + IfFileExists "$$VLC_Directory\vlc.exe" VLCFound 0 + ReadRegStr $$VLC_Directory HKLM "Software\VideoLAN\VLC" "InstallDir" + IfFileExists "$$VLC_Directory\vlc.exe" VLCFound 0 + StrCpy $$VLC_Directory "c:\program files (x86)\videolan\vlc" + IfFileExists "$$VLC_Directory\vlc.exe" VLCFound 0 + StrCpy $$VLC_Directory "c:\program files\videolan\vlc" + IfFileExists "$$VLC_Directory\vlc.exe" VLCFound 0 + StrCpy $$VLC_Directory "" + VLCFound: + FunctionEnd + + Function UpdateVLCCheckbox + IfFileExists "$$VLC_Directory\vlc.exe" VLC_Enabled VLC_Disabled + + VLC_Enabled: + EnableWindow $$CheckBox_VLC 1 + StrCpy $$CheckBox_VLC_State $${BST_CHECKED} + $${NSD_SetState} $$CheckBox_VLC $$CheckBox_VLC_State + goto CheckboxUpdated + + VLC_Disabled: + EnableWindow $$CheckBox_VLC 0 + StrCpy $$CheckBox_VLC_State $${BST_UNCHECKED} + $${NSD_SetState} $$CheckBox_VLC $$CheckBox_VLC_State + + CheckboxUpdated: + FunctionEnd + + Function DirectoryBrowseDialogVLC + nsDialogs::SelectFolderDialog $$(^BrowseVLCBtn) $$VLC_Directory + Pop $$Directory + $${If} $$Directory != error + StrCpy $$VLC_Directory $$Directory + Call UpdateVLCCheckbox + $${EndIf} + Abort + FunctionEnd + + Function GetSize + StrCpy $$Size "$totalSize" + IntOp $$Size $$Size / 1024 + IntFmt $$SizeHex "0x%08X" $$Size + IntOp $$Size $$Size / 1024 + FunctionEnd + + ;Calculates Free Space on HDD + Function DriveSpace + StrCpy $$Drive $$INSTDIR 1 + $${DriveSpace} "$$Drive:\" "/D=F /S=M" $$AvailibleSpace + IntOp $$AvailibleSpaceGiB $$AvailibleSpace / 1024 + IntOp $$AvailibleSpace $$AvailibleSpace % 1024 + IntOp $$AvailibleSpace $$AvailibleSpace / 102 + FunctionEnd + + Function InstallOptions + ;$${If} $$CheckBox_Associate_State == $${BST_CHECKED} + ; Call Associate + ; DetailPrint "Associated Syncplay with multimedia files" + ;$${EndIf} + + $${If} $$CheckBox_StartMenuShortcut_State == $${BST_CHECKED} + CreateDirectory $$SMPROGRAMS\Syncplay + CreateShortCut "$$SMPROGRAMS\Syncplay\Syncplay.lnk" "$$INSTDIR\Syncplay.exe" "" + CreateShortCut "$$SMPROGRAMS\Syncplay\Syncplay Server.lnk" "$$INSTDIR\syncplayServer.exe" "" + CreateShortCut "$$SMPROGRAMS\Syncplay\Uninstall.lnk" "$$INSTDIR\Uninstall.exe" "" + WriteINIStr "$$SMPROGRAMS\Syncplay\SyncplayWebsite.url" "InternetShortcut" "URL" "http://syncplay.pl" + $${EndIf} + + $${If} $$CheckBox_DesktopShortcut_State == $${BST_CHECKED} + CreateShortCut "$$DESKTOP\Syncplay.lnk" "$$INSTDIR\Syncplay.exe" "" + $${EndIf} + + $${If} $$CheckBox_QuickLaunchShortcut_State == $${BST_CHECKED} + CreateShortCut "$$QUICKLAUNCH\Syncplay.lnk" "$$INSTDIR\Syncplay.exe" "" + $${EndIf} + + $${If} $$CheckBox_VLC_State == $${BST_CHECKED} + IfFileExists "$$VLC_Directory\vlc.exe" 0 EndOfVLC + SetOutPath $$VLC_Directory\lua\intf + File resources\lua\intf\syncplay.lua + EndOfVLC: + $${EndIf} + FunctionEnd + + ;Associates extensions with Syncplay + ;Function Associate + ; !insertmacro ASSOCIATE avi + ; !insertmacro ASSOCIATE mpg + ; !insertmacro ASSOCIATE mpeg + ; !insertmacro ASSOCIATE mpe + ; !insertmacro ASSOCIATE m1v + ; !insertmacro ASSOCIATE m2v + ; !insertmacro ASSOCIATE mpv2 + ; !insertmacro ASSOCIATE mp2v + ; !insertmacro ASSOCIATE mkv + ; !insertmacro ASSOCIATE mp4 + ; !insertmacro ASSOCIATE m4v + ; !insertmacro ASSOCIATE mp4v + ; !insertmacro ASSOCIATE 3gp + ; !insertmacro ASSOCIATE 3gpp + ; !insertmacro ASSOCIATE 3g2 + ; !insertmacro ASSOCIATE 3pg2 + ; !insertmacro ASSOCIATE flv + ; !insertmacro ASSOCIATE f4v + ; !insertmacro ASSOCIATE rm + ; !insertmacro ASSOCIATE wmv + ; !insertmacro ASSOCIATE swf + ; !insertmacro ASSOCIATE rmvb + ; !insertmacro ASSOCIATE divx + ; !insertmacro ASSOCIATE amv + ;FunctionEnd + + Function WriteRegistry + Call GetSize + WriteRegStr HKLM SOFTWARE\Syncplay "Install_Dir" "$$INSTDIR" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "DisplayName" "Syncplay" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "InstallLocation" "$$INSTDIR" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "VLCInstallLocation" "$$VLC_Directory" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "UninstallString" '"$$INSTDIR\uninstall.exe"' + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "DisplayIcon" "$$INSTDIR\resources\icon.ico" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "Publisher" "Syncplay" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "DisplayVersion" "$version" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "URLInfoAbout" "http://syncplay.pl/" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "NoRepair" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "EstimatedSize" "$$SizeHex" + WriteINIStr $$APPDATA\syncplay.ini general language $$(^SyncplayLanguage) + $${If} $$CheckBox_AutomaticUpdates_State == $${BST_CHECKED} + WriteINIStr $$APPDATA\syncplay.ini general CheckForUpdatesAutomatically "True" + $${Else} + WriteINIStr $$APPDATA\syncplay.ini general CheckForUpdatesAutomatically "False" + $${EndIf} + FunctionEnd + + Function un.installConfirm + nsDialogs::Create 1018 + Pop $$Uninst_Dialog + + $${NSD_CreateIcon} 0u 1u 22u 20u "" + Pop $$Uninst_Icon + $${NSD_SetIconFromInstaller} $$Uninst_Icon $$Uninst_Icon_Handle + + $${NSD_CreateLabel} 0u 45u 55u 8u "$$(^UninstallingSubText)" + Pop $$Uninst_Label_Directory + + $${NSD_CreateLabel} 25u 0u 241u 34u "$$(^UninstallingText)" + Pop $$Uninst_Label_Text + + ReadRegStr $$INSTDIR HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "InstallLocation" + $${NSD_CreateText} 56u 43u 209u 12u "$$INSTDIR" + Pop $$Uninst_Text_Directory + EnableWindow $$Uninst_Text_Directory 0 + + $${NSD_CreateCheckBox} 0u 60u 250u 10u "$$(^UninstConfig)" + Pop $$Uninst_CheckBox_Config + + + nsDialogs::Show + $${NSD_FreeIcon} $$Uninst_Icon_Handle + FunctionEnd + + Function un.installConfirmLeave + $${NSD_GetState} $$Uninst_CheckBox_Config $$Uninst_CheckBox_Config_State + FunctionEnd + + Function un.AssociateDel + !insertmacro UNASSOCIATE avi + !insertmacro UNASSOCIATE mpg + !insertmacro UNASSOCIATE mpeg + !insertmacro UNASSOCIATE mpe + !insertmacro UNASSOCIATE m1v + !insertmacro UNASSOCIATE m2v + !insertmacro UNASSOCIATE mpv2 + !insertmacro UNASSOCIATE mp2v + !insertmacro UNASSOCIATE mkv + !insertmacro UNASSOCIATE mp4 + !insertmacro UNASSOCIATE m4v + !insertmacro UNASSOCIATE mp4v + !insertmacro UNASSOCIATE 3gp + !insertmacro UNASSOCIATE 3gpp + !insertmacro UNASSOCIATE 3g2 + !insertmacro UNASSOCIATE 3pg2 + !insertmacro UNASSOCIATE flv + !insertmacro UNASSOCIATE f4v + !insertmacro UNASSOCIATE rm + !insertmacro UNASSOCIATE wmv + !insertmacro UNASSOCIATE swf + !insertmacro UNASSOCIATE rmvb + !insertmacro UNASSOCIATE divx + !insertmacro UNASSOCIATE amv + FunctionEnd + + Function un.InstallOptions + Delete $$SMPROGRAMS\Syncplay\Syncplay.lnk + Delete "$$SMPROGRAMS\Syncplay\Syncplay Server.lnk" + Delete $$SMPROGRAMS\Syncplay\Uninstall.lnk + Delete $$SMPROGRAMS\Syncplay\SyncplayWebsite.url + RMDir $$SMPROGRAMS\Syncplay + Delete $$DESKTOP\Syncplay.lnk + Delete $$QUICKLAUNCH\Syncplay.lnk + ReadRegStr $$VLC_Directory HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "VLCInstallLocation" + IfFileExists "$$VLC_Directory\lua\intf\syncplay.lua" 0 +2 + Delete $$VLC_Directory\lua\intf\syncplay.lua + FunctionEnd + + Section "Install" + SetOverwrite on + SetOutPath $$INSTDIR + WriteUninstaller uninstall.exe + + $installFiles + + Call InstallOptions + Call WriteRegistry + SectionEnd + + Section "Uninstall" + Call un.AssociateDel + Call un.InstallOptions + $uninstallFiles + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" + DeleteRegKey HKLM SOFTWARE\Syncplay + Delete $$INSTDIR\uninstall.exe + RMDir $$INSTDIR\Syncplay\\resources\lua\intf + RMDir $$INSTDIR\Syncplay\\resources\lua + RMDir $$INSTDIR\Syncplay\\resources + RMDir $$INSTDIR\resources + RMDir $$INSTDIR\lib + RMDir $$INSTDIR + + $${If} $$Uninst_CheckBox_Config_State == $${BST_CHECKED} + IfFileExists "$$APPDATA\.syncplay" 0 +2 + Delete $$APPDATA\.syncplay + IfFileExists "$$APPDATA\syncplay.ini" 0 +2 + Delete $$APPDATA\syncplay.ini + $${EndIf} + SectionEnd +""" + +class NSISScript(object): + def create(self): + fileList, totalSize = self.getBuildDirContents(OUT_DIR) + print("Total size eq: {}".format(totalSize)) + installFiles = self.prepareInstallListTemplate(fileList) + uninstallFiles = self.prepareDeleteListTemplate(fileList) + + if os.path.isfile(SETUP_SCRIPT_PATH): + raise RuntimeError("Cannot create setup script, file exists at {}".format(SETUP_SCRIPT_PATH)) + contents = Template(NSIS_SCRIPT_TEMPLATE).substitute( + version = syncplay.version, + uninstallFiles = uninstallFiles, + installFiles = installFiles, + totalSize = totalSize, + ) + with codecs.open(SETUP_SCRIPT_PATH, "w", "utf-8-sig") as outfile: + outfile.write(contents.decode('utf-8')) + + def compile(self): + if not os.path.isfile(NSIS_COMPILE): + return "makensis.exe not found, won't create the installer" + subproc = subprocess.Popen([NSIS_COMPILE, SETUP_SCRIPT_PATH], env=os.environ) + subproc.communicate() + retcode = subproc.returncode + os.remove(SETUP_SCRIPT_PATH) + if retcode: + raise RuntimeError("NSIS compilation return code: %d" % retcode) + + def getBuildDirContents(self, path): + fileList = {} + totalSize = 0 + for root, _, files in os.walk(path): + totalSize += sum(os.path.getsize(os.path.join(root, file_)) for file_ in files) + for file_ in files: + new_root = root.replace(OUT_DIR, "").strip("\\") + if new_root not in fileList: + fileList[new_root] = [] + fileList[new_root].append(file_) + return fileList, totalSize + + def prepareInstallListTemplate(self, fileList): + create = [] + for dir_ in fileList.keys(): + create.append('SetOutPath "$INSTDIR\\{}"'.format(dir_)) + for file_ in fileList[dir_]: + create.append('FILE "{}\\{}\\{}"'.format(OUT_DIR, dir_, file_)) + return "\n".join(create) + + def prepareDeleteListTemplate(self, fileList): + delete = [] + for dir_ in fileList.keys(): + for file_ in fileList[dir_]: + delete.append('DELETE "$INSTDIR\\{}\\{}"'.format(dir_, file_)) + delete.append('RMdir "$INSTDIR\\{}"'.format(file_)) + return "\n".join(delete) + +guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock_go.png', + 'resources/control_pause_blue.png', 'resources/cross.png', 'resources/door_in.png', + 'resources/folder_explore.png', 'resources/help.png', 'resources/table_refresh.png', + 'resources/timeline_marker.png','resources/control_play_blue.png', + 'resources/mpc-hc.png','resources/mpc-hc64.png','resources/mplayer.png', + 'resources/mpc-be.png', + 'resources/mpv.png','resources/vlc.png', 'resources/house.png', 'resources/film_link.png', + 'resources/eye.png', 'resources/comments.png', 'resources/cog_delete.png', 'resources/chevrons_right.png', + 'resources/user_key.png', 'resources/lock.png', 'resources/key_go.png', 'resources/page_white_key.png', + 'resources/tick.png', 'resources/lock_open.png', 'resources/empty_checkbox.png', 'resources/tick_checkbox.png', + 'resources/world_explore.png', 'resources/application_get.png', 'resources/cog.png', 'resources/arrow_switch.png', + 'resources/film_go.png', 'resources/world_go.png', 'resources/arrow_refresh.png', 'resources/bullet_right_grey.png', + 'resources/user_comment.png', + 'resources/error.png', + 'resources/film_folder_edit.png', + 'resources/film_edit.png', + 'resources/folder_film.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", "resources/syncplayintf.lua", "resources/license.rtf", "resources/third-party-notices.rtf"] +resources.extend(guiIcons) +intf_resources = ["resources/lua/intf/syncplay.lua"] + +common_info = dict( + name='Syncplay', + version=syncplay.version, + author='Uriziel', + author_email='dev@syncplay.pl', + description='Syncplay', +) + +script = NSISScript() +script.create() +print("*** compiling the NSIS setup script***") +script.compile() +print("*** DONE ***") diff --git a/pyinstaller-onedir.spec b/pyinstaller-onedir.spec index cfc0c1c..5fced31 100755 --- a/pyinstaller-onedir.spec +++ b/pyinstaller-onedir.spec @@ -3,6 +3,8 @@ import os workdir = os.getcwd() +import syncplay + block_cipher = None @@ -34,4 +36,4 @@ coll = COLLECT(exe, a.datas, strip=False, upx=True, - name='Syncplay') + name="syncplay_v{}".format(syncplay.version)) From c4e912a2d5bebb834e4bc99f8959d9bb93cd5243 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Fri, 27 Apr 2018 21:20:57 +0200 Subject: [PATCH 21/95] Disable --onefile and change packed dir name --- .appveyor.yml | 8 ++++---- buildInstaller.py | 2 +- pyinstaller-onedir.spec | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 0ae9d30..df31a8f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -21,10 +21,10 @@ init: install: - cd %APPVEYOR_BUILD_FOLDER% - for /F "tokens=2 delims='" %%a in ('findstr version syncplay\__init__.py') do @set ver=%%a - - pyinstaller pyinstaller-onefile.spec - - move dist\Syncplay.exe Syncplay-%ver%-win-Py35-PySide2.exe - - del build /s /Q - - del dist /s /Q + #- pyinstaller pyinstaller-onefile.spec + #- move dist\Syncplay.exe Syncplay-%ver%-win-Py35-PySide2.exe + #- del build /s /Q + #- del dist /s /Q - pyinstaller pyinstaller-onedir.spec - cd dist - python buildInstaller.py diff --git a/buildInstaller.py b/buildInstaller.py index bd9215f..0f43d04 100644 --- a/buildInstaller.py +++ b/buildInstaller.py @@ -41,7 +41,7 @@ def get_nsis_path(): return bin_name NSIS_COMPILE = get_nsis_path() -OUT_DIR = "syncplay_v{}".format(syncplay.version) +OUT_DIR = "Syncplay" SETUP_SCRIPT_PATH = "syncplay_setup.nsi" NSIS_SCRIPT_TEMPLATE = r""" !include LogicLib.nsh diff --git a/pyinstaller-onedir.spec b/pyinstaller-onedir.spec index 5fced31..19b0956 100755 --- a/pyinstaller-onedir.spec +++ b/pyinstaller-onedir.spec @@ -3,8 +3,6 @@ import os workdir = os.getcwd() -import syncplay - block_cipher = None @@ -36,4 +34,4 @@ coll = COLLECT(exe, a.datas, strip=False, upx=True, - name="syncplay_v{}".format(syncplay.version)) + name="Syncplay") From 8cd1d35958a002aa4e78387e2de272a7b3603526 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Fri, 27 Apr 2018 21:32:21 +0200 Subject: [PATCH 22/95] Removed decode from NSIS script --- buildInstaller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildInstaller.py b/buildInstaller.py index 0f43d04..0b91b9c 100644 --- a/buildInstaller.py +++ b/buildInstaller.py @@ -624,7 +624,7 @@ class NSISScript(object): totalSize = totalSize, ) with codecs.open(SETUP_SCRIPT_PATH, "w", "utf-8-sig") as outfile: - outfile.write(contents.decode('utf-8')) + outfile.write(contents) def compile(self): if not os.path.isfile(NSIS_COMPILE): From 62b7b33fddc5e4238f7ed65e05690a87e19f885e Mon Sep 17 00:00:00 2001 From: albertosottile Date: Fri, 27 Apr 2018 21:27:06 +0200 Subject: [PATCH 23/95] Fixed typo in AppVeyor --- .appveyor.yml | 2 -- buildInstaller.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index df31a8f..d4b9b93 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -26,9 +26,7 @@ install: #- del build /s /Q #- del dist /s /Q - pyinstaller pyinstaller-onedir.spec - - cd dist - python buildInstaller.py - - cd .. # Not a project with an msbuild file, build done at install. build: off diff --git a/buildInstaller.py b/buildInstaller.py index 0b91b9c..73560ce 100644 --- a/buildInstaller.py +++ b/buildInstaller.py @@ -41,7 +41,7 @@ def get_nsis_path(): return bin_name NSIS_COMPILE = get_nsis_path() -OUT_DIR = "Syncplay" +OUT_DIR = "dist\Syncplay" SETUP_SCRIPT_PATH = "syncplay_setup.nsi" NSIS_SCRIPT_TEMPLATE = r""" !include LogicLib.nsh From f85d40a44bc2b6101ed79b255871ac555807e940 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Fri, 27 Apr 2018 22:21:58 +0200 Subject: [PATCH 24/95] Deploy Windows installer from AppVeyor to Bintray --- .appveyor.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index d4b9b93..ba50d9d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -27,6 +27,7 @@ install: #- del dist /s /Q - pyinstaller pyinstaller-onedir.spec - python buildInstaller.py + - copy Syncplay-%ver%-Setup.exe Syncplay-%ver%-Setup-Py35-PySide2.exe # Not a project with an msbuild file, build done at install. build: off @@ -36,15 +37,12 @@ artifacts: type: zip name: Syncplay-$(ver)-win-py35-pyside2 - - path: Syncplay-$(ver)-win-Py35-PySide2.exe - name: Syncplay-$(ver)-win-py35-pyside2 + - path: Syncplay-$(ver)-Setup-Py35-PySide2.exe + name: Syncplay-$(ver)-Setup-Py35-PySide2 # Push artefact to S3 bucket and list all before_deploy: - dir - - cd dist - - dir - - cd .. #- python -c "from PySide2 import QtCore; print QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.PluginsPath)" From e9a194b36e5eba69ffaf06c0da3f1159066812a5 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Sat, 28 Apr 2018 01:01:53 +0200 Subject: [PATCH 25/95] Missing fields errors were not notified --- syncplay/ui/ConfigurationGetter.py | 2 +- syncplay/ui/GuiConfiguration.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index aacf750..7d1e33b 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -357,7 +357,7 @@ class ConfigurationGetter(object): self._validateArguments() except InvalidConfigValue as e: try: - for key, value in list(self._promptForMissingArguments(e.message).items()): + for key, value in list(self._promptForMissingArguments(e).items()): self._config[key] = value self._checkConfig() except: diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 7af5bfc..f7c167c 100755 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -660,6 +660,7 @@ class ConfigDialog(QtWidgets.QDialog): self.basicOptionsFrame = QtWidgets.QFrame() self.basicOptionsLayout = QtWidgets.QVBoxLayout() if error: + error = str(error) self.errorLabel = QLabel(self) if error[:1] != constants.ERROR_MESSAGE_MARKER: self.errorLabel.setStyleSheet(constants.STYLE_ERRORLABEL) From 31392f5e1deb76d7b8d78ffd4edb6b091227c955 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Mon, 7 May 2018 16:58:41 +0200 Subject: [PATCH 26/95] Remove dependency from pyobjc on Travis --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 00d55b6..d8d38d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ before_install: install: - pip3 install twisted appnope + before_deploy: - pip3 install dmgbuild - mkdir dist_dmg @@ -40,7 +41,7 @@ before_deploy: - mv resources/lua/intf/syncplay.lua resources/lua/intf/.syncplay.lua - mv resources/macOS_readme.pdf resources/.macOS_readme.pdf - export VER="$(cat syncplay/__init__.py | awk '/version/ {gsub("\047", "", $3); print $NF}')" -- dmgbuild -s appdmg.py "Syncplay" dist_dmg/Syncplay_${VER}_macOS_py36_pyside2.dmg +- dmgbuild -s appdmg.py "Syncplay" dist_dmg/Syncplay_${VER}_macOS_py36.dmg deploy: skip_cleanup: true From ef0574bccf870896af9098ce34154d3dc239da4a Mon Sep 17 00:00:00 2001 From: albertosottile Date: Mon, 7 May 2018 18:31:06 +0200 Subject: [PATCH 27/95] Update third-party-notices for PySide2 --- resources/third-party-notices.rtf | 61 +++++++------------------------ 1 file changed, 14 insertions(+), 47 deletions(-) diff --git a/resources/third-party-notices.rtf b/resources/third-party-notices.rtf index 492a24a..90bddfc 100644 --- a/resources/third-party-notices.rtf +++ b/resources/third-party-notices.rtf @@ -24,26 +24,25 @@ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ -\b PySide\ +\b Qt for Python\ \b0 \ -Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).\ -Contact: PySide team \ +Copyright (C) 2018 The Qt Company Ltd.\ +Contact: https://www.qt.io/licensing/\ \ -This library is free software; you can redistribute it and/or\ -modify it under the terms of the GNU Lesser General Public\ -License as published by the Free Software Foundation; either\ -version 2.1 of the License, or (at your option) any later version.\ -This library is distributed in the hope that it will be useful,\ +This program is free software: you can redistribute it and/or modify\ +it under the terms of the GNU Lesser General Public License as published\ +by the Free Software Foundation, either version 3 of the License, or\ +(at your option) any later version.\ +\ +This program is distributed in the hope that it will be useful,\ but WITHOUT ANY WARRANTY; without even the implied warranty of\ -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\ -Lesser General Public License for more details.\ +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\ +GNU Lesser General Public License for more details.\ +\ +You should have received a copy of the GNU Lesser General Public License\ +along with this program. If not, see .\ \ -\pard\pardeftab720\partightenfactor0 -\cf0 You should have received a copy of the GNU Lesser General Public License\ -along with this program. If not, see \ -\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 -\cf0 \ \b Twisted\ \ @@ -113,27 +112,6 @@ The above copyright notice and this permission notice shall be\ included in all copies or substantial portions of the Software.\ \b \ -Qt 4\ -\ - -\b0 Copyright (C) 2015 The Qt Company Ltd.\ -Contact: http://www.qt.io/licensing/\ -\ -This library is free software; you can redistribute it and/or\ -modify it under the terms of the GNU Lesser General Public\ -License as published by the Free Software Foundation; either\ -version 2.1 of the License, or (at your option) any later version.\ -This library is distributed in the hope that it will be useful,\ -but WITHOUT ANY WARRANTY; without even the implied warranty of\ -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\ -Lesser General Public License for more details.\ -\ -\pard\pardeftab720\partightenfactor0 -\cf0 You should have received a copy of the GNU Lesser General Public License\ -along with this program. If not, see \ -\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 - -\b \cf0 \ appnope\ \b0 \ @@ -157,17 +135,6 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\ - -\b \ -PyObjC\ - -\b0 \ -Copyright 2002, 2003 - Bill Bumgarner, Ronald Oussoren, Steve Majewski, Lele Gaifax, et.al.\ -Copyright 2003-2016 - Ronald Oussoren\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\ \ \b py2exe\ From 67c40f9618f3aac1c8484611e55070afb0741856 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Mon, 7 May 2018 16:18:46 +0200 Subject: [PATCH 28/95] Add extra files to Windows portable release --- .appveyor.yml | 14 ++++++-------- resources/win_lua_note.txt | 6 ++++++ 2 files changed, 12 insertions(+), 8 deletions(-) create mode 100755 resources/win_lua_note.txt diff --git a/.appveyor.yml b/.appveyor.yml index ba50d9d..dba3741 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -21,13 +21,11 @@ init: install: - cd %APPVEYOR_BUILD_FOLDER% - for /F "tokens=2 delims='" %%a in ('findstr version syncplay\__init__.py') do @set ver=%%a - #- pyinstaller pyinstaller-onefile.spec - #- move dist\Syncplay.exe Syncplay-%ver%-win-Py35-PySide2.exe - #- del build /s /Q - #- del dist /s /Q - pyinstaller pyinstaller-onedir.spec - python buildInstaller.py - - copy Syncplay-%ver%-Setup.exe Syncplay-%ver%-Setup-Py35-PySide2.exe + - copy Syncplay-%ver%-Setup.exe Syncplay-%ver%-Setup-Py35.exe + - type nul > dist\syncplay.ini + - copy resources\win_lua_note.txt dist\"VLC LUA Script installation.txt" # Not a project with an msbuild file, build done at install. build: off @@ -35,10 +33,10 @@ build: off artifacts: - path: 'dist' type: zip - name: Syncplay-$(ver)-win-py35-pyside2 + name: Syncplay_$(ver)_Portable_py35 - - path: Syncplay-$(ver)-Setup-Py35-PySide2.exe - name: Syncplay-$(ver)-Setup-Py35-PySide2 + - path: Syncplay-$(ver)-Setup-Py35.exe + name: Syncplay-$(ver)-Setup-Py35 # Push artefact to S3 bucket and list all before_deploy: diff --git a/resources/win_lua_note.txt b/resources/win_lua_note.txt new file mode 100755 index 0000000..66835d8 --- /dev/null +++ b/resources/win_lua_note.txt @@ -0,0 +1,6 @@ +You must follow the following instructions to use Syncplay with VLC: + +Place the syncplay.lua file from /resources/lua/intf/ into the main (all user) VLC /lua/intf/ sub-directory: +* Window: %ProgramFiles%\VideoLAN\VLC\lua\intf\ + +Note: A version of these instructions is also available from http://syncplay.pl/LUA \ No newline at end of file From 545247c144071b231e9078040544e703deb8fb5b Mon Sep 17 00:00:00 2001 From: albertosottile Date: Mon, 7 May 2018 19:27:22 +0200 Subject: [PATCH 29/95] Upver to 1.5.4 release 62 --- syncplay/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index d601a37..874d028 100755 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ -version = '1.5.3' +version = '1.5.4' milestone = 'Yoitsu' -release_number = '61' +release_number = '62' projectURL = 'https://syncplay.pl/' From 9e524704bd7f8b8478d4e8801c54f45fbca91c4a Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 8 May 2018 11:35:03 +0200 Subject: [PATCH 30/95] Fixes part of issue #184 --- syncplay/ui/GuiConfiguration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 5a092f0..f46af65 100755 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -342,7 +342,7 @@ class ConfigDialog(QtWidgets.QDialog): try: servers = utils.getListOfPublicServers() except IOError as e: - self.showErrorMessage(unicode(e)) + self.showErrorMessage(e.args[0]) return currentServer = self.hostCombobox.currentText() self.hostCombobox.clear() From 481f27569aeee6a00603a23b9f7ae6536606e18a Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 8 May 2018 17:58:11 +0200 Subject: [PATCH 31/95] Add Qt 5 license notice in the dependencies text file --- resources/third-party-notices.rtf | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/resources/third-party-notices.rtf b/resources/third-party-notices.rtf index 90bddfc..5ef095d 100644 --- a/resources/third-party-notices.rtf +++ b/resources/third-party-notices.rtf @@ -44,6 +44,32 @@ You should have received a copy of the GNU Lesser General Public License\ along with this program. If not, see .\ \ +\b Qt +\b0 \ +\ +This program uses Qt versions 5.6 and 5.9 under the GNU LGPL version 3.\ +\ +Qt is a C++ toolkit for cross-platform application development.\ +\ +Qt provides single-source portability across all major desktop operating systems. It is also available for embedded Linux and other embedded and mobile operating systems.\ +\ +Qt is available under three different licensing options designed to accommodate the needs of our various users.\ +\ +Qt licensed under our commercial license agreement is appropriate for development of proprietary/commercial software where you do not want to share any source code with third parties or otherwise cannot comply with the terms of the GNU LGPL version 3 or GNU LGPL version 2.1.\ +\ +Qt licensed under the GNU LGPL version 3 is appropriate for the development of Qt applications provided you can comply with the terms and conditions of the GNU LGPL version 3.\ +\ +Qt licensed under the GNU LGPL version 2.1 is appropriate for the development of Qt applications provided you can comply with the terms and conditions of the GNU LGPL version 2.1.\ +\ +Please see qt.io/licensing for an overview of Qt licensing.\ +\ +Copyright (C) 2017 The Qt Company Ltd and other contributors.\ +\ +Qt and the Qt logo are trademarks of The Qt Company Ltd.\ +\ +Qt is The Qt Company Ltd product developed as an open source project. See qt.io for more information.\ +\ + \b Twisted\ \ From 09629f91969e71a3c8b08fa7bf974ed0a9cf867a Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Tue, 8 May 2018 21:44:40 +0200 Subject: [PATCH 32/95] Add https url in 'failed-to-load-server-list-error' message --- syncplay/messages_de.py | 2 +- syncplay/messages_en.py | 2 +- syncplay/messages_it.py | 2 +- syncplay/messages_ru.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index eac5b70..3f4905e 100644 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -149,7 +149,7 @@ de = { "no-media-directories-error" : u"No media directories have been set. For shared playlist and file switching features to work properly please select File->Set Media Directories and specify where Syncplay should look to find media files.", # TODO: Translate "cannot-find-directory-error" : u"Could not find media directory '{}'. To update your list of media directories please select File->Set Media Directories from the menu bar and specify where Syncplay should look to find media files.", # TODO: Translate - "failed-to-load-server-list-error" : u"Konnte die Liste der öffentlichen Server nicht laden. Bitte besuche http://www.syncplay.pl/ [Englisch] mit deinem Browser.", + "failed-to-load-server-list-error" : u"Konnte die Liste der öffentlichen Server nicht laden. Bitte besuche https://www.syncplay.pl/ [Englisch] mit deinem Browser.", # Client arguments "argument-description" : u'Syncplay ist eine Anwendung um mehrere MPlayer, MPC-HC, MPC-BE und VLC-Instanzen über das Internet zu synchronisieren.', diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 2dc14a5..94f858e 100644 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -152,7 +152,7 @@ en = { "no-media-directories-error" : u"No media directories have been set. For shared playlist and file switching features to work properly please select File->Set Media Directories and specify where Syncplay should look to find media files.", "cannot-find-directory-error" : u"Could not find media directory '{}'. To update your list of media directories please select File->Set Media Directories from the menu bar and specify where Syncplay should look to find media files.", - "failed-to-load-server-list-error" : u"Failed to load public server list. Please visit http://www.syncplay.pl/ in your browser.", + "failed-to-load-server-list-error" : u"Failed to load public server list. Please visit https://www.syncplay.pl/ in your browser.", # Client arguments "argument-description" : 'Solution to synchronize playback of multiple media player instances over the network.', diff --git a/syncplay/messages_it.py b/syncplay/messages_it.py index 2de7b79..f73e6d0 100644 --- a/syncplay/messages_it.py +++ b/syncplay/messages_it.py @@ -152,7 +152,7 @@ it = { "no-media-directories-error" : u"Nessuna cartella multimediale è stata configurata. Per permettere il corretto funzionamento delle playlist condivise e la selezione automatica dei file, naviga in File->Imposta cartelle multimediali e specifica dove Syncplay deve ricercare i file multimediali.", "cannot-find-directory-error" : u"Impossibile trovare la cartella multimediale '{}'. Per aggiornare la lista delle cartelle multimediali seleziona File->Imposta cartelle multimediali dalla barra dei menù e specifica dove Syncplay deve ricercare i file multimediali.", - "failed-to-load-server-list-error" : u"Impossibile caricare la lista dei server pubblici. Per favore, visita http://www.syncplay.pl/ con il tuo browser.", + "failed-to-load-server-list-error" : u"Impossibile caricare la lista dei server pubblici. Per favore, visita https://www.syncplay.pl/ con il tuo browser.", # Client arguments "argument-description" : u'Programma per sincronizzare la riproduzione di media player multipli attraverso la rete.', diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index c235946..4070105 100644 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -155,7 +155,7 @@ ru = { "no-media-directories-error" : u"Вы не указали папки воспроизведения. Для корректной работы зайдите через выпадающее меню в Файл->Папки воспроизведения и укажите нужные каталоги.", "cannot-find-directory-error" : u"Не удалось найти папку воспроизведения '{}'. Для обновления списка папок, через выпадающее меню, перейдите в Файл->Папки воспроизведения и укажите нужные каталоги.", - "failed-to-load-server-list-error" : u"Не удалось загрузить список публичных серверов. Откройте http://www.syncplay.pl/ через браузер.", + "failed-to-load-server-list-error" : u"Не удалось загрузить список публичных серверов. Откройте https://www.syncplay.pl/ через браузер.", # Client arguments "argument-description" : u'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC/BE через Интернет.', From 44967b120305e3f85e59234f7bd4c9db9abaf6e3 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Fri, 11 May 2018 22:56:30 +0200 Subject: [PATCH 33/95] Shows server list update error when in debug mode --- syncplay/ui/GuiConfiguration.py | 1 + syncplay/utils.py | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index f46af65..fdf8724 100755 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -20,6 +20,7 @@ class GuiConfiguration: self.config = config self._availablePlayerPaths = [] self.error = error + constants.DEBUG_MODE = config['debug'] def run(self): if QCoreApplication.instance() is None: diff --git a/syncplay/utils.py b/syncplay/utils.py index 72fa947..17d7fcc 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -14,6 +14,7 @@ import ast import unicodedata import platform import subprocess +import traceback folderSearchEnabled = True @@ -386,7 +387,11 @@ def getListOfPublicServers(): else: raise IOError except: - raise IOError(getMessage("failed-to-load-server-list-error")) + if constants.DEBUG_MODE == True: + traceback.print_exc() + raise + else: + raise IOError(getMessage("failed-to-load-server-list-error")) class RoomPasswordProvider(object): CONTROLLED_ROOM_REGEX = re.compile("^\+(.*):(\w{12})$") From e712f39f3fdd4457001e525ddae9dcd36096142f Mon Sep 17 00:00:00 2001 From: albertosottile Date: Mon, 28 May 2018 15:00:17 +0200 Subject: [PATCH 34/95] Use requests instead of urllib in getListOfPublicServers and checkForUpdate --- .travis.yml | 1 + buildPy2app.py | 2 +- buildPy2exe.py | 4 ++-- resources/third-party-notices.rtf | 16 +++++++++++++++- syncplay/client.py | 20 ++++++++++++-------- syncplay/utils.py | 14 +++++++++----- 6 files changed, 40 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index fc84525..018a443 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,7 @@ before_install: install: - pip install twisted appnope +- pip install -U https://github.com/requests/requests/zipball/master #- git clone -b qtpy-pyside2 https://github.com/alby128/syncplay.git syncplay-qtpy-PySide2 #- cd syncplay-qtpy-PySide2 #- git checkout qtpy-pyside2 diff --git a/buildPy2app.py b/buildPy2app.py index f889c3d..74cb424 100644 --- a/buildPy2app.py +++ b/buildPy2app.py @@ -15,7 +15,7 @@ DATA_FILES = [ ] OPTIONS = { 'iconfile':'resources/icon.icns', - 'includes': {'PySide2.QtCore', 'PySide2.QtUiTools', 'PySide2.QtGui','PySide2.QtWidgets'}, + 'includes': {'PySide2.QtCore', 'PySide2.QtUiTools', 'PySide2.QtGui','PySide2.QtWidgets', 'certifi'}, 'excludes': {'PySide', 'PySide.QtCore', 'PySide.QtUiTools', 'PySide.QtGui'}, 'qt_plugins': ['platforms/libqcocoa.dylib', 'platforms/libqminimal.dylib','platforms/libqoffscreen.dylib'], 'plist': { diff --git a/buildPy2exe.py b/buildPy2exe.py index bff96a4..881667c 100755 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -721,8 +721,8 @@ info = dict( options={'py2exe': { 'dist_dir': OUT_DIR, 'packages': 'PySide2.QtUiTools', - 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide2, liburl, ast, unicodedata', - 'excludes': 'venv, _ssl, doctest, pdb, unittest, win32clipboard, win32file, win32pdh, win32security, win32trace, win32ui, winxpgui, win32pipe, win32process, Tkinter', + 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide2, liburl, ast, unicodedata, _ssl', + 'excludes': 'venv, doctest, pdb, unittest, win32clipboard, win32file, win32pdh, win32security, win32trace, win32ui, winxpgui, win32pipe, win32process, Tkinter', 'dll_excludes': 'msvcr71.dll, MSVCP90.dll, POWRPROF.dll', 'optimize': 2, 'compressed': 1 diff --git a/resources/third-party-notices.rtf b/resources/third-party-notices.rtf index 5ef095d..e67066e 100644 --- a/resources/third-party-notices.rtf +++ b/resources/third-party-notices.rtf @@ -208,4 +208,18 @@ furnished to do so, subject to the following conditions:\ The above copyright notice and this permission notice shall be included in\ all copies or substantial portions of the Software.\ \ -} \ No newline at end of file + +\b Requests\ +\ + +\b0 Copyright 2018 Kenneth Reitz\ +\ +Licensed under the Apache License, Version 2.0 (the \'93License\'94); you may not use this file\ +except in compliance with the License. You may obtain a copy of the License at\ +\ +http://www.apache.org/licenses/LICENSE-2.0\ +\ +Unless required by applicable law or agreed to in writing, software distributed under the \ +License is distributed on an \'93AS IS\'94 BASIS, WITHOUT WARRANTIES OR CONDI-\ +TIONS OF ANY KIND, either express or implied. See the License for the specific lang-\ +uage governing permissions and limitations under the License.} \ No newline at end of file diff --git a/syncplay/client.py b/syncplay/client.py index e8cb8b5..665da98 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -11,7 +11,8 @@ from twisted.internet import reactor, task, defer, threads from functools import wraps from copy import deepcopy from syncplay.protocols import SyncClientProtocol -from syncplay import utils, constants +from syncplay import utils, constants, version +from syncplay.utils import isMacOS from syncplay.messages import getMissingStrings, getMessage from syncplay.constants import PRIVACY_SENDHASHED_MODE, PRIVACY_DONTSEND_MODE, \ PRIVACY_HIDDENFILENAME @@ -910,12 +911,15 @@ class SyncplayClient(object): def checkForUpdate(self, userInitiated): try: - import urllib, syncplay, sys, messages, json - params = urllib.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, - 'language': messages.messages["CURRENT"], 'platform': sys.platform, 'userInitiated': userInitiated}) - - f = urllib.urlopen(constants.SYNCPLAY_UPDATE_URL.format(params)) - response = f.read() + import syncplay, sys, messages, urllib, json + params = urllib.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, 'language': messages.messages["CURRENT"], 'platform': sys.platform, 'userInitiated': userInitiated}) + if isMacOS(): + import requests + response = requests.get(constants.SYNCPLAY_UPDATE_URL.format(params)) + response = response.text + else: + f = urllib.urlopen(constants.SYNCPLAY_UPDATE_URL.format(params)) + response = f.read() response = response.replace("

","").replace("

","").replace("
","").replace("“","\"").replace("”","\"") # Fix Wordpress response = json.loads(response) publicServers = None @@ -926,7 +930,7 @@ class SyncplayClient(object): return response["version-status"], response["version-message"] if response.has_key("version-message")\ else None, response["version-url"] if response.has_key("version-url") else None, publicServers except: - return "failed", getMessage("update-check-failed-notification").format(syncplay.version), constants.SYNCPLAY_DOWNLOAD_URL, None + return "failed", getMessage("update-check-failed-notification").format(version), constants.SYNCPLAY_DOWNLOAD_URL, None class _WarningManager(object): def __init__(self, player, userlist, ui, client): diff --git a/syncplay/utils.py b/syncplay/utils.py index 17d7fcc..5ad8b6c 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -374,11 +374,15 @@ def open_system_file_browser(path): def getListOfPublicServers(): try: - import urllib, syncplay, sys, messages, json - params = urllib.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, - 'language': messages.messages["CURRENT"]}) - f = urllib.urlopen(constants.SYNCPLAY_PUBLIC_SERVER_LIST_URL.format(params)) - response = f.read() + import syncplay, sys, messages, urllib + params = urllib.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, 'language': messages.messages["CURRENT"]}) + if isMacOS(): + import requests + response = requests.get(constants.SYNCPLAY_PUBLIC_SERVER_LIST_URL.format(params)) + response = response.text + else: + f = urllib.urlopen(constants.SYNCPLAY_PUBLIC_SERVER_LIST_URL.format(params)) + response = f.read() response = response.replace("

","").replace("

","").replace("
","").replace("“","'").replace("”","'").replace(":’","'").replace("’","'").replace("′","'").replace("\n","").replace("\r","") # Fix Wordpress response = ast.literal_eval(response) From e49ea0de12dff53d7b48fd599fa416a82ba57ff7 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 29 May 2018 14:28:47 +0200 Subject: [PATCH 35/95] Fix show URL in GUI when loaded from Syncplay --- syncplay/client.py | 5 +---- syncplay/ui/gui.py | 4 ---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/syncplay/client.py b/syncplay/client.py index 3b4386d..e56a5c1 100755 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -447,10 +447,7 @@ class SyncplayClient(object): def updateFile(self, filename, duration, path): newPath = "" if utils.isURL(path): - try: - filename = path.encode('utf-8') - except UnicodeDecodeError: - filename = path + filename = path if not path: return try: diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 36233f5..e5e8037 100755 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -506,10 +506,6 @@ class MainWindow(QtWidgets.QMainWindow): filename = user.file['name'] if isURL(filename): filename = urllib.parse.unquote(filename) - try: - filename = filename.decode('utf-8') - except UnicodeEncodeError: - pass filenameitem = QtGui.QStandardItem(filename) fileSwitchState = self.getFileSwitchState(user.file['name']) if room == currentUser.room else None if fileSwitchState != constants.FILEITEM_SWITCH_NO_SWITCH: From f385019fa4bbb769dd4005c5c5955f07dada23ed Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Wed, 30 May 2018 22:36:56 +0200 Subject: [PATCH 36/95] Update license notices with Qt and Requests --- resources/third-party-notices.rtf | 42 ++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/resources/third-party-notices.rtf b/resources/third-party-notices.rtf index 90bddfc..e67066e 100644 --- a/resources/third-party-notices.rtf +++ b/resources/third-party-notices.rtf @@ -44,6 +44,32 @@ You should have received a copy of the GNU Lesser General Public License\ along with this program. If not, see .\ \ +\b Qt +\b0 \ +\ +This program uses Qt versions 5.6 and 5.9 under the GNU LGPL version 3.\ +\ +Qt is a C++ toolkit for cross-platform application development.\ +\ +Qt provides single-source portability across all major desktop operating systems. It is also available for embedded Linux and other embedded and mobile operating systems.\ +\ +Qt is available under three different licensing options designed to accommodate the needs of our various users.\ +\ +Qt licensed under our commercial license agreement is appropriate for development of proprietary/commercial software where you do not want to share any source code with third parties or otherwise cannot comply with the terms of the GNU LGPL version 3 or GNU LGPL version 2.1.\ +\ +Qt licensed under the GNU LGPL version 3 is appropriate for the development of Qt applications provided you can comply with the terms and conditions of the GNU LGPL version 3.\ +\ +Qt licensed under the GNU LGPL version 2.1 is appropriate for the development of Qt applications provided you can comply with the terms and conditions of the GNU LGPL version 2.1.\ +\ +Please see qt.io/licensing for an overview of Qt licensing.\ +\ +Copyright (C) 2017 The Qt Company Ltd and other contributors.\ +\ +Qt and the Qt logo are trademarks of The Qt Company Ltd.\ +\ +Qt is The Qt Company Ltd product developed as an open source project. See qt.io for more information.\ +\ + \b Twisted\ \ @@ -182,4 +208,18 @@ furnished to do so, subject to the following conditions:\ The above copyright notice and this permission notice shall be included in\ all copies or substantial portions of the Software.\ \ -} \ No newline at end of file + +\b Requests\ +\ + +\b0 Copyright 2018 Kenneth Reitz\ +\ +Licensed under the Apache License, Version 2.0 (the \'93License\'94); you may not use this file\ +except in compliance with the License. You may obtain a copy of the License at\ +\ +http://www.apache.org/licenses/LICENSE-2.0\ +\ +Unless required by applicable law or agreed to in writing, software distributed under the \ +License is distributed on an \'93AS IS\'94 BASIS, WITHOUT WARRANTIES OR CONDI-\ +TIONS OF ANY KIND, either express or implied. See the License for the specific lang-\ +uage governing permissions and limitations under the License.} \ No newline at end of file From a58aa6de54a2c3242819255c05a15830390d06e5 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Wed, 30 May 2018 23:03:02 +0200 Subject: [PATCH 37/95] Fix issue #184 --- .travis.yml | 1 + buildPy2app.py | 2 +- buildPy2exe.py | 4 ++-- syncplay/client.py | 18 +++++++++++------- syncplay/ui/GuiConfiguration.py | 3 ++- syncplay/utils.py | 23 ++++++++++++++++------- 6 files changed, 33 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index d8d38d9..199140f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,7 @@ before_install: install: - pip3 install twisted appnope +- pip3 install -U https://github.com/requests/requests/zipball/master before_deploy: diff --git a/buildPy2app.py b/buildPy2app.py index f889c3d..74cb424 100755 --- a/buildPy2app.py +++ b/buildPy2app.py @@ -15,7 +15,7 @@ DATA_FILES = [ ] OPTIONS = { 'iconfile':'resources/icon.icns', - 'includes': {'PySide2.QtCore', 'PySide2.QtUiTools', 'PySide2.QtGui','PySide2.QtWidgets'}, + 'includes': {'PySide2.QtCore', 'PySide2.QtUiTools', 'PySide2.QtGui','PySide2.QtWidgets', 'certifi'}, 'excludes': {'PySide', 'PySide.QtCore', 'PySide.QtUiTools', 'PySide.QtGui'}, 'qt_plugins': ['platforms/libqcocoa.dylib', 'platforms/libqminimal.dylib','platforms/libqoffscreen.dylib'], 'plist': { diff --git a/buildPy2exe.py b/buildPy2exe.py index fe451d6..721a442 100755 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -721,8 +721,8 @@ info = dict( options={'py2exe': { 'dist_dir': OUT_DIR, 'packages': 'PySide2.QtUiTools', - 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide2, liburl, ast, unicodedata', - 'excludes': 'venv, _ssl, doctest, pdb, unittest, win32clipboard, win32file, win32pdh, win32security, win32trace, win32ui, winxpgui, win32pipe, win32process, Tkinter', + 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide2, liburl, ast, unicodedata, _ssl', + 'excludes': 'venv, doctest, pdb, unittest, win32clipboard, win32file, win32pdh, win32security, win32trace, win32ui, winxpgui, win32pipe, win32process, Tkinter', 'dll_excludes': 'msvcr71.dll, MSVCP90.dll, POWRPROF.dll', 'optimize': 2, 'compressed': 1 diff --git a/syncplay/client.py b/syncplay/client.py index e56a5c1..1e7d965 100755 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -11,7 +11,8 @@ from twisted.internet import reactor, task, defer, threads from functools import wraps from copy import deepcopy from syncplay.protocols import SyncClientProtocol -from syncplay import utils, constants +from syncplay import utils, constants, version +from syncplay.utils import isMacOS from syncplay.messages import getMissingStrings, getMessage from syncplay.constants import PRIVACY_SENDHASHED_MODE, PRIVACY_DONTSEND_MODE, \ PRIVACY_HIDDENFILENAME @@ -908,12 +909,15 @@ class SyncplayClient(object): def checkForUpdate(self, userInitiated): try: import urllib.request, urllib.parse, urllib.error, syncplay, sys, json - params = urllib.parse.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, - 'language': syncplay.messages.messages["CURRENT"], 'platform': sys.platform, 'userInitiated': userInitiated}) - - f = urllib.request.urlopen(constants.SYNCPLAY_UPDATE_URL.format(params)) - response = f.read() - response = response.decode('utf-8') + params = urllib.parse.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, 'language': syncplay.messages.messages["CURRENT"], 'platform': sys.platform, 'userInitiated': userInitiated}) + if isMacOS(): + import requests + response = requests.get(constants.SYNCPLAY_UPDATE_URL.format(params)) + response = response.text + else: + f = urllib.request.urlopen(constants.SYNCPLAY_UPDATE_URL.format(params)) + response = f.read() + response = response.decode('utf-8') response = response.replace("

","").replace("

","").replace("
","").replace("“","\"").replace("”","\"") # Fix Wordpress response = json.loads(response) publicServers = None diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index f7c167c..ab24c50 100755 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -20,6 +20,7 @@ class GuiConfiguration: self.config = config self._availablePlayerPaths = [] self.error = error + constants.DEBUG_MODE = config['debug'] def run(self): if QCoreApplication.instance() is None: @@ -342,7 +343,7 @@ class ConfigDialog(QtWidgets.QDialog): try: servers = utils.getListOfPublicServers() except IOError as e: - self.showErrorMessage(str(e)) + self.showErrorMessage(e.args[0]) return currentServer = self.hostCombobox.currentText() self.hostCombobox.clear() diff --git a/syncplay/utils.py b/syncplay/utils.py index d0ce940..6f8beae 100755 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -14,6 +14,7 @@ import ast import unicodedata import platform import subprocess +import traceback folderSearchEnabled = True @@ -375,12 +376,16 @@ def open_system_file_browser(path): def getListOfPublicServers(): try: - import urllib.request, urllib.parse, urllib.error, syncplay, sys, json - params = urllib.parse.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, - 'language': syncplay.messages.messages["CURRENT"]}) - f = urllib.request.urlopen(constants.SYNCPLAY_PUBLIC_SERVER_LIST_URL.format(params)) - response = f.read() - response = response.decode('utf-8') + import urllib.request, urllib.parse, urllib.error, syncplay, sys + params = urllib.parse.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number, 'language': syncplay.messages.messages["CURRENT"]}) + if isMacOS(): + import requests + response = requests.get(constants.SYNCPLAY_PUBLIC_SERVER_LIST_URL.format(params)) + response = response.text + else: + f = urllib.request.urlopen(constants.SYNCPLAY_PUBLIC_SERVER_LIST_URL.format(params)) + response = f.read() + response = response.decode('utf-8') response = response.replace("

","").replace("

","").replace("
","").replace("“","'").replace("”","'").replace(":’","'").replace("’","'").replace("′","'").replace("\n","").replace("\r","") # Fix Wordpress response = ast.literal_eval(response) @@ -389,7 +394,11 @@ def getListOfPublicServers(): else: raise IOError except: - raise IOError(getMessage("failed-to-load-server-list-error")) + if constants.DEBUG_MODE == True: + traceback.print_exc() + raise + else: + raise IOError(getMessage("failed-to-load-server-list-error")) class RoomPasswordProvider(object): CONTROLLED_ROOM_REGEX = re.compile("^\+(.*):(\w{12})$") From 7fc3a0bb77cbebe51d4b420edca3cc805159b76d Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Wed, 30 May 2018 23:04:18 +0200 Subject: [PATCH 38/95] Add https url in 'failed-to-load-server-list-error' message --- syncplay/messages_de.py | 2 +- syncplay/messages_en.py | 2 +- syncplay/messages_it.py | 2 +- syncplay/messages_ru.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index 08bf649..6981dfb 100755 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -149,7 +149,7 @@ de = { "no-media-directories-error" : "No media directories have been set. For shared playlist and file switching features to work properly please select File->Set Media Directories and specify where Syncplay should look to find media files.", # TODO: Translate "cannot-find-directory-error" : "Could not find media directory '{}'. To update your list of media directories please select File->Set Media Directories from the menu bar and specify where Syncplay should look to find media files.", # TODO: Translate - "failed-to-load-server-list-error" : "Konnte die Liste der öffentlichen Server nicht laden. Bitte besuche http://www.syncplay.pl/ [Englisch] mit deinem Browser.", + "failed-to-load-server-list-error" : "Konnte die Liste der öffentlichen Server nicht laden. Bitte besuche https://www.syncplay.pl/ [Englisch] mit deinem Browser.", # Client arguments "argument-description" : 'Syncplay ist eine Anwendung um mehrere MPlayer, MPC-HC, MPC-BE und VLC-Instanzen über das Internet zu synchronisieren.', diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 41343e6..26c7a2d 100755 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -152,7 +152,7 @@ en = { "no-media-directories-error" : "No media directories have been set. For shared playlist and file switching features to work properly please select File->Set Media Directories and specify where Syncplay should look to find media files.", "cannot-find-directory-error" : "Could not find media directory '{}'. To update your list of media directories please select File->Set Media Directories from the menu bar and specify where Syncplay should look to find media files.", - "failed-to-load-server-list-error" : "Failed to load public server list. Please visit http://www.syncplay.pl/ in your browser.", + "failed-to-load-server-list-error" : "Failed to load public server list. Please visit https://www.syncplay.pl/ in your browser.", # Client arguments "argument-description" : 'Solution to synchronize playback of multiple media player instances over the network.', diff --git a/syncplay/messages_it.py b/syncplay/messages_it.py index fac2bb2..5c51810 100755 --- a/syncplay/messages_it.py +++ b/syncplay/messages_it.py @@ -152,7 +152,7 @@ it = { "no-media-directories-error" : "Nessuna cartella multimediale è stata configurata. Per permettere il corretto funzionamento delle playlist condivise e la selezione automatica dei file, naviga in File->Imposta cartelle multimediali e specifica dove Syncplay deve ricercare i file multimediali.", "cannot-find-directory-error" : "Impossibile trovare la cartella multimediale '{}'. Per aggiornare la lista delle cartelle multimediali seleziona File->Imposta cartelle multimediali dalla barra dei menù e specifica dove Syncplay deve ricercare i file multimediali.", - "failed-to-load-server-list-error" : "Impossibile caricare la lista dei server pubblici. Per favore, visita http://www.syncplay.pl/ con il tuo browser.", + "failed-to-load-server-list-error" : "Impossibile caricare la lista dei server pubblici. Per favore, visita https://www.syncplay.pl/ con il tuo browser.", # Client arguments "argument-description" : 'Programma per sincronizzare la riproduzione di media player multipli attraverso la rete.', diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index 47174e6..e8671fc 100755 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -155,7 +155,7 @@ ru = { "no-media-directories-error" : "Вы не указали папки воспроизведения. Для корректной работы зайдите через выпадающее меню в Файл->Папки воспроизведения и укажите нужные каталоги.", "cannot-find-directory-error" : "Не удалось найти папку воспроизведения '{}'. Для обновления списка папок, через выпадающее меню, перейдите в Файл->Папки воспроизведения и укажите нужные каталоги.", - "failed-to-load-server-list-error" : "Не удалось загрузить список публичных серверов. Откройте http://www.syncplay.pl/ через браузер.", + "failed-to-load-server-list-error" : "Не удалось загрузить список публичных серверов. Откройте https://www.syncplay.pl/ через браузер.", # Client arguments "argument-description" : 'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC/BE через Интернет.', From 3f910ebcba4633d7f7de5d7de18f738db117b850 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 31 May 2018 16:53:09 +0200 Subject: [PATCH 39/95] Check for Python version newer than 3.5 --- syncplayClient.py | 12 ++++++------ syncplayServer.py | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/syncplayClient.py b/syncplayClient.py index a414f89..6ad0b89 100755 --- a/syncplayClient.py +++ b/syncplayClient.py @@ -4,12 +4,12 @@ import site, sys # libpath -# try: -# if (sys.version_info.major != 2) or (sys.version_info.minor < 7): -# raise Exception("You must run Syncplay with Python 2.7!") -# except AttributeError: -# import warnings -# warnings.warn("You must run Syncplay with Python 2.7!") +try: + if (sys.version_info.major != 3) or (sys.version_info.minor < 5): + raise Exception("You must run Syncplay with Python 3.5 or newer!") +except AttributeError: + import warnings + warnings.warn("You must run Syncplay with Python 3.5 or newer!") from syncplay.clientManager import SyncplayClientManager from syncplay.utils import blackholeStdoutForFrozenWindow diff --git a/syncplayServer.py b/syncplayServer.py index ef9ae95..7cc3ac6 100755 --- a/syncplayServer.py +++ b/syncplayServer.py @@ -5,12 +5,12 @@ import site, sys # libpath -# try: -# if (sys.version_info.major != 2) or (sys.version_info.minor < 7): -# raise Exception("You must run Syncplay with Python 2.7!") -# except AttributeError: -# import warnings -# warnings.warn("You must run Syncplay with Python 2.7!") +try: + if (sys.version_info.major != 3) or (sys.version_info.minor < 5): + raise Exception("You must run Syncplay with Python 3.5 or newer!") +except AttributeError: + import warnings + warnings.warn("You must run Syncplay with Python 3.5 or newer!") from twisted.internet import reactor From 60ad43406655f29bbb789fd4f1a34e2764599161 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 31 May 2018 16:59:56 +0200 Subject: [PATCH 40/95] Add pyinstaller script for server --- .appveyor.yml | 3 ++- pyinstaller-onedir.spec => pyinstaller-client.spec | 0 pyinstaller-onefile.spec => pyinstaller-server.spec | 7 +++---- 3 files changed, 5 insertions(+), 5 deletions(-) rename pyinstaller-onedir.spec => pyinstaller-client.spec (100%) rename pyinstaller-onefile.spec => pyinstaller-server.spec (76%) diff --git a/.appveyor.yml b/.appveyor.yml index dba3741..b977262 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -21,7 +21,8 @@ init: install: - cd %APPVEYOR_BUILD_FOLDER% - for /F "tokens=2 delims='" %%a in ('findstr version syncplay\__init__.py') do @set ver=%%a - - pyinstaller pyinstaller-onedir.spec + - pyinstaller pyinstaller-client.spec + - pyinstaller pyinstaller-server.spec - python buildInstaller.py - copy Syncplay-%ver%-Setup.exe Syncplay-%ver%-Setup-Py35.exe - type nul > dist\syncplay.ini diff --git a/pyinstaller-onedir.spec b/pyinstaller-client.spec similarity index 100% rename from pyinstaller-onedir.spec rename to pyinstaller-client.spec diff --git a/pyinstaller-onefile.spec b/pyinstaller-server.spec similarity index 76% rename from pyinstaller-onefile.spec rename to pyinstaller-server.spec index 8e1f105..eb1dd32 100755 --- a/pyinstaller-onefile.spec +++ b/pyinstaller-server.spec @@ -6,11 +6,11 @@ workdir = os.getcwd() block_cipher = None -a = Analysis(['syncplayClient.py'], +a = Analysis(['syncplayServer.py'], pathex=[workdir], binaries=[], datas=[('resources/*', 'resources')], - hiddenimports=['PySide2', 'PySide2.QtCore', 'PySide2.QtWidgets'], + hiddenimports=[], hookspath=[], runtime_hooks=[], excludes=[], @@ -29,5 +29,4 @@ exe = EXE(pyz, strip=False, upx=False, runtime_tmpdir=None, - console=False, - icon='resources/icon.ico') + console=True) From ce8684bbd8a140e21de4d3896e7e1c49500ede5b Mon Sep 17 00:00:00 2001 From: albertosottile Date: Sat, 2 Jun 2018 15:08:08 +0200 Subject: [PATCH 41/95] Patch QSocketNotifier call syntax --- syncplay/vendor/qt5reactor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/vendor/qt5reactor.py b/syncplay/vendor/qt5reactor.py index cde2b5e..47cd220 100644 --- a/syncplay/vendor/qt5reactor.py +++ b/syncplay/vendor/qt5reactor.py @@ -123,8 +123,8 @@ class TwistedSocketNotifier(QObject): QObject.__init__(self, parent) self.reactor = reactor self.watcher = watcher - fd = self.watcher.fileno() - self.notifier = QSocketNotifier(watcher, socketType, parent) + fd = watcher.fileno() + self.notifier = QSocketNotifier(fd, socketType, parent) self.notifier.setEnabled(True) if socketType == QSocketNotifier.Read: self.fn = self.read From 89c1f2089a83397933a1e0c69b65a1db16a1e58b Mon Sep 17 00:00:00 2001 From: albertosottile Date: Sat, 2 Jun 2018 16:57:09 +0200 Subject: [PATCH 42/95] Revert "Patch QSocketNotifier", keep using former PySide2 wheels This reverts commit ce8684bbd8a140e21de4d3896e7e1c49500ede5b. --- syncplay/vendor/qt5reactor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/vendor/qt5reactor.py b/syncplay/vendor/qt5reactor.py index 47cd220..cde2b5e 100644 --- a/syncplay/vendor/qt5reactor.py +++ b/syncplay/vendor/qt5reactor.py @@ -123,8 +123,8 @@ class TwistedSocketNotifier(QObject): QObject.__init__(self, parent) self.reactor = reactor self.watcher = watcher - fd = watcher.fileno() - self.notifier = QSocketNotifier(fd, socketType, parent) + fd = self.watcher.fileno() + self.notifier = QSocketNotifier(watcher, socketType, parent) self.notifier.setEnabled(True) if socketType == QSocketNotifier.Read: self.fn = self.read From 62d49f53eb3dccc4cbcd94a17f2ddca8d6f089f5 Mon Sep 17 00:00:00 2001 From: Flisk Date: Tue, 5 Jun 2018 17:31:39 +0200 Subject: [PATCH 43/95] Refactor config path logic --- syncplay/ui/ConfigurationGetter.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index 72cd879..1e06360 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -322,21 +322,22 @@ class ConfigurationGetter(object): def _getConfigurationFilePath(self): configFile = self._checkForPortableFile() - if not configFile: - for name in constants.CONFIG_NAMES: - if configFile and os.path.isfile(configFile): - break - if os.name <> 'nt': - configFile = os.path.join(os.getenv('HOME', '.'), name) - else: - configFile = os.path.join(os.getenv('APPDATA', '.'), name) - if configFile and not os.path.isfile(configFile): - if os.name <> 'nt': - configFile = os.path.join(os.getenv('HOME', '.'), constants.DEFAULT_CONFIG_NAME_LINUX) - else: - configFile = os.path.join(os.getenv('APPDATA', '.'), constants.DEFAULT_CONFIG_NAME_WINDOWS) + if configFile: + return configFile + for name in constants.CONFIG_NAMES: + configFile = self._expandConfigPath(name) + if os.path.isfile(configFile): + return configFile + return self._expandConfigPath() - return configFile + def _expandConfigPath(self, name = None): + if os.name != 'nt': + prefix = os.getenv('HOME', '.') + default_name = constants.DEFAULT_CONFIG_NAME_LINUX + else: + prefix = os.getenv('APPDATA', '.') + default_name = constants.DEFAULT_CONFIG_NAME_WINDOWS + return os.path.join(prefix, name or default_name) def _parseConfigFile(self, iniPath, createConfig=True): parser = SafeConfigParserUnicode() From ff3df0ae05757f26320f155c911d9fbec5208fb7 Mon Sep 17 00:00:00 2001 From: Flisk Date: Tue, 5 Jun 2018 17:52:27 +0200 Subject: [PATCH 44/95] Default to XDG_CONFIG_HOME on non-'nt' platforms The default config path on Linux becomes `~/.config/syncplay.ini`, provided the user hasn't changed her $XDG_CONFIG_HOME. Existing installations continue to read their config from `~/.syncplay`, should that file exist. --- syncplay/constants.py | 3 +-- syncplay/ui/ConfigurationGetter.py | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index 122b491..f999ed0 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -8,8 +8,7 @@ MPC_OSD_POSITION = 1 #Right corner, 1 for left MPLAYER_OSD_LEVEL = 1 UI_TIME_FORMAT = "[%X] " CONFIG_NAMES = [".syncplay", "syncplay.ini"] #Syncplay searches first to last -DEFAULT_CONFIG_NAME_WINDOWS = "syncplay.ini" -DEFAULT_CONFIG_NAME_LINUX = ".syncplay" +DEFAULT_CONFIG_NAME = "syncplay.ini" RECENT_CLIENT_THRESHOLD = "1.5.3" #This and higher considered 'recent' clients (no warnings) WARN_OLD_CLIENTS = True #Use MOTD to inform old clients to upgrade LIST_RELATIVE_CONFIGS = True # Print list of relative configs loaded diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index 1e06360..068d321 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -325,19 +325,26 @@ class ConfigurationGetter(object): if configFile: return configFile for name in constants.CONFIG_NAMES: - configFile = self._expandConfigPath(name) + configFile = self._expandConfigPath(name, xdg = False) if os.path.isfile(configFile): return configFile return self._expandConfigPath() - def _expandConfigPath(self, name = None): + def _expandConfigPath(self, name = None, xdg = True): if os.name != 'nt': - prefix = os.getenv('HOME', '.') - default_name = constants.DEFAULT_CONFIG_NAME_LINUX + if xdg: + prefix = self._getXdgConfigHome() + else: + prefix = os.getenv('HOME', '.') else: prefix = os.getenv('APPDATA', '.') - default_name = constants.DEFAULT_CONFIG_NAME_WINDOWS - return os.path.join(prefix, name or default_name) + return os.path.join(prefix, name or constants.DEFAULT_CONFIG_NAME) + + def _getXdgConfigHome(self): + path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) + if not os.path.isdir(path): + os.mkdir(path, 0o755) + return path def _parseConfigFile(self, iniPath, createConfig=True): parser = SafeConfigParserUnicode() From d2e965fe54a15b78aed86bd228927e0502c39767 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Sat, 9 Jun 2018 15:29:08 +0200 Subject: [PATCH 45/95] Change config path logic to support XDG --- syncplay/constants.py | 3 +-- syncplay/ui/ConfigurationGetter.py | 36 ++++++++++++++++++------------ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index 00ec2cf..4a60c5e 100755 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -8,8 +8,7 @@ MPC_OSD_POSITION = 1 #Right corner, 1 for left MPLAYER_OSD_LEVEL = 1 UI_TIME_FORMAT = "[%X] " CONFIG_NAMES = [".syncplay", "syncplay.ini"] #Syncplay searches first to last -DEFAULT_CONFIG_NAME_WINDOWS = "syncplay.ini" -DEFAULT_CONFIG_NAME_LINUX = ".syncplay" +DEFAULT_CONFIG_NAME = "syncplay.ini" RECENT_CLIENT_THRESHOLD = "1.5.3" #This and higher considered 'recent' clients (no warnings) WARN_OLD_CLIENTS = True #Use MOTD to inform old clients to upgrade LIST_RELATIVE_CONFIGS = True # Print list of relative configs loaded diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index 7d1e33b..a4fa113 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -322,21 +322,29 @@ class ConfigurationGetter(object): def _getConfigurationFilePath(self): configFile = self._checkForPortableFile() - if not configFile: - for name in constants.CONFIG_NAMES: - if configFile and os.path.isfile(configFile): - break - if os.name != 'nt': - configFile = os.path.join(os.getenv('HOME', '.'), name) - else: - configFile = os.path.join(os.getenv('APPDATA', '.'), name) - if configFile and not os.path.isfile(configFile): - if os.name != 'nt': - configFile = os.path.join(os.getenv('HOME', '.'), constants.DEFAULT_CONFIG_NAME_LINUX) - else: - configFile = os.path.join(os.getenv('APPDATA', '.'), constants.DEFAULT_CONFIG_NAME_WINDOWS) + if configFile: + return configFile + for name in constants.CONFIG_NAMES: + configFile = self._expandConfigPath(name, xdg = False) + if os.path.isfile(configFile): + return configFile + return self._expandConfigPath() - return configFile + def _expandConfigPath(self, name = None, xdg = True): + if os.name != 'nt': + if xdg: + prefix = self._getXdgConfigHome() + else: + prefix = os.getenv('HOME', '.') + else: + prefix = os.getenv('APPDATA', '.') + return os.path.join(prefix, name or constants.DEFAULT_CONFIG_NAME) + + def _getXdgConfigHome(self): + path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) + if not os.path.isdir(path): + os.mkdir(path, 0o755) + return path def _parseConfigFile(self, iniPath, createConfig=True): parser = SafeConfigParserUnicode() From 4bf7db5a5d50327947abad658b1a8b4c5111e2e8 Mon Sep 17 00:00:00 2001 From: Etoh Date: Sat, 9 Jun 2018 15:29:19 +0100 Subject: [PATCH 46/95] Fix VLC error when HTTPS URL contains square brackets (#187) --- syncplay/players/vlc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/players/vlc.py b/syncplay/players/vlc.py index 316b3a9..f7392ec 100755 --- a/syncplay/players/vlc.py +++ b/syncplay/players/vlc.py @@ -145,7 +145,7 @@ class VlcPlayer(BasePlayer): def getMRL(self, fileURL): if utils.isURL(fileURL): fileURL = fileURL.encode('utf8') - fileURL = urllib.quote(fileURL, safe="%/:=&?~#+!$,;'@()*[]") + fileURL = urllib.quote(fileURL, safe="%/:=&?~#+!$,;'@()*") return fileURL fileURL = fileURL.replace(u'\\', u'/') From 5cb490f0899a35d671ba5c6927c33a6ba50a9be0 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Sat, 9 Jun 2018 17:08:11 +0200 Subject: [PATCH 47/95] Fix VLC error when HTTPS URL contains square brackets (#187) --- syncplay/players/vlc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/players/vlc.py b/syncplay/players/vlc.py index f8eefe3..7651267 100755 --- a/syncplay/players/vlc.py +++ b/syncplay/players/vlc.py @@ -144,7 +144,7 @@ class VlcPlayer(BasePlayer): def getMRL(self, fileURL): if utils.isURL(fileURL): - fileURL = urllib.parse.quote(fileURL, safe="%/:=&?~#+!$,;'@()*[]") + fileURL = urllib.parse.quote(fileURL, safe="%/:=&?~#+!$,;'@()*") return fileURL fileURL = fileURL.replace('\\', '/') From 615283b84da55ee1c7375a7e23e61df3be338830 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 13 Jun 2018 14:25:45 +0200 Subject: [PATCH 48/95] Travis: use PySide2 TP with Qt 5.11.0 --- .travis.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 018a443..a726f16 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,15 +10,13 @@ script: before_install: - brew update -- curl -O http://syncplay.s3.amazonaws.com/qt595.rb -- mv qt595.rb qt.rb -- brew install ./qt.rb +- brew install qt - which python - which pip -- curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=PySide2-5.9.0a1-5.9.5-cp27-cp27m-macosx_10_11_x86_64.whl -o PySide2-5.9.0a1-5.9.5-cp27-cp27m-macosx_10_11_x86_64.whl -- pip install PySide2-5.9.0a1-5.9.5-cp27-cp27m-macosx_10_11_x86_64.whl -- ln -s /usr/local/lib/python2.7/site-packages/PySide2/libshiboken2-python2.7v.5.9.dylib /usr/local/lib/ -- ln -s /usr/local/lib/python2.7/site-packages/PySide2/libpyside2-python2.7v.5.9.dylib /usr/local/lib/ +- curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=PySide2-5.11.0-5.11.0-cp27-cp27m-macosx_10_11_x86_64.whl -o PySide2-5.11.0-5.11.0-cp27-cp27m-macosx_10_11_x86_64.whl +- pip install PySide2-5.11.0-5.11.0-cp27-cp27m-macosx_10_11_x86_64.whl +- ln -s /usr/local/lib/python2.7/site-packages/PySide2/libshiboken2-python2.7v.5.11.dylib /usr/local/lib/ +- ln -s /usr/local/lib/python2.7/site-packages/PySide2/libpyside2-python2.7v.5.11.dylib /usr/local/lib/ #- python -c "from PySide2 import QtCore" - python -c "from PySide2.QtCore import __version__; print __version__" - hg clone https://alby128@bitbucket.org/alby128/py2app From 4c85d49c9c498376fa8370e47c202b210dde475a Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 13 Jun 2018 15:10:35 +0200 Subject: [PATCH 49/95] Fix syncplay-mediasearchdirectories-label in all languages --- syncplay/messages_de.py | 3 ++- syncplay/messages_en.py | 3 ++- syncplay/messages_ru.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index 3f4905e..ea987db 100644 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -218,7 +218,8 @@ de = { "misc-label" : u"Diverse", "core-behaviour-title" : u"Verhalten des Raumes", "syncplay-internals-title" : u"Syncplay intern", - "syncplay-mediasearchdirectories-title" : u"In diesen Verzeichnissen nach Medien suchen (ein Pfad pro Zeile)", + "syncplay-mediasearchdirectories-title" : u"In diesen Verzeichnissen nach Medien suchen", #needs to be checked + "syncplay-mediasearchdirectories-label" : u"In diesen Verzeichnissen nach Medien suchen (ein Pfad pro Zeile)", "sync-label" : u"Synchronisation", "sync-otherslagging-title" : u"Wenn andere laggen...", "sync-youlaggging-title" : u"Wenn du laggst...", diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 94f858e..6f5d085 100644 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -223,7 +223,8 @@ en = { "misc-label" : u"Misc", "core-behaviour-title" : "Core room behaviour", "syncplay-internals-title" : u"Syncplay internals", - "syncplay-mediasearchdirectories-title" : u"Directories to search for media (one path per line)", + "syncplay-mediasearchdirectories-title" : u"Directories to search for media", + "syncplay-mediasearchdirectories-label" : u"Directories to search for media (one path per line)", "sync-label" : "Sync", "sync-otherslagging-title" : "If others are lagging behind...", "sync-youlaggging-title" : "If you are lagging behind...", diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index 4070105..ca790d6 100644 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -226,7 +226,8 @@ ru = { "misc-label" : u"Прочее", "core-behaviour-title" : u"Информация о файлах", "syncplay-internals-title" : u"Системные настройки", - "syncplay-mediasearchdirectories-title" : u"Папки воспроизведения (один путь на строку)", + "syncplay-mediasearchdirectories-title" : u"Папки воспроизведения", #needs to be checked + "syncplay-mediasearchdirectories-label" : u"Папки воспроизведения (один путь на строку)", "sync-label" : u"Синхронизация", "sync-otherslagging-title" : u"Опережение", "sync-youlaggging-title" : u"Отставание", From 23f59f95ea3e7d6504e03949a1400be452166d17 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 13 Jun 2018 16:14:08 +0200 Subject: [PATCH 50/95] Update py2app script for Qt 5.11 --- buildPy2app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildPy2app.py b/buildPy2app.py index 74cb424..0821adf 100644 --- a/buildPy2app.py +++ b/buildPy2app.py @@ -17,7 +17,7 @@ OPTIONS = { 'iconfile':'resources/icon.icns', 'includes': {'PySide2.QtCore', 'PySide2.QtUiTools', 'PySide2.QtGui','PySide2.QtWidgets', 'certifi'}, 'excludes': {'PySide', 'PySide.QtCore', 'PySide.QtUiTools', 'PySide.QtGui'}, - 'qt_plugins': ['platforms/libqcocoa.dylib', 'platforms/libqminimal.dylib','platforms/libqoffscreen.dylib'], + 'qt_plugins': ['platforms/libqcocoa.dylib', 'platforms/libqminimal.dylib','platforms/libqoffscreen.dylib', 'styles/libqmacstyle.dylib'], 'plist': { 'CFBundleName':'Syncplay', 'CFBundleShortVersionString':syncplay.version, From 6c306137587ebb3729ad56f589db64ce78249b1c Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 13 Jun 2018 17:01:38 +0200 Subject: [PATCH 51/95] Edit third-party-notices to include Qt 5.11 --- resources/third-party-notices.rtf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/third-party-notices.rtf b/resources/third-party-notices.rtf index e67066e..436ba5f 100644 --- a/resources/third-party-notices.rtf +++ b/resources/third-party-notices.rtf @@ -47,7 +47,7 @@ along with this program. If not, see .\ \b Qt \b0 \ \ -This program uses Qt versions 5.6 and 5.9 under the GNU LGPL version 3.\ +This program uses Qt versions 5.6 and 5.11 under the GNU LGPL version 3.\ \ Qt is a C++ toolkit for cross-platform application development.\ \ From 038e7e149dcda587fd157d222df46cea00f28bbe Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 13 Jun 2018 17:01:38 +0200 Subject: [PATCH 52/95] Edit third-party-notices to include Qt 5.11 --- resources/third-party-notices.rtf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/third-party-notices.rtf b/resources/third-party-notices.rtf index e67066e..436ba5f 100644 --- a/resources/third-party-notices.rtf +++ b/resources/third-party-notices.rtf @@ -47,7 +47,7 @@ along with this program. If not, see .\ \b Qt \b0 \ \ -This program uses Qt versions 5.6 and 5.9 under the GNU LGPL version 3.\ +This program uses Qt versions 5.6 and 5.11 under the GNU LGPL version 3.\ \ Qt is a C++ toolkit for cross-platform application development.\ \ From 839003f09afaf00f6d7f40edc0f892400e974e8a Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 14 Jun 2018 11:35:37 +0200 Subject: [PATCH 53/95] Fix syncplay-mediasearchdirectories-label in all languages --- syncplay/messages_de.py | 3 ++- syncplay/messages_en.py | 3 ++- syncplay/messages_ru.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index 6981dfb..a001aa9 100755 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -218,7 +218,8 @@ de = { "misc-label" : "Diverse", "core-behaviour-title" : "Verhalten des Raumes", "syncplay-internals-title" : "Syncplay intern", - "syncplay-mediasearchdirectories-title" : "In diesen Verzeichnissen nach Medien suchen (ein Pfad pro Zeile)", + "syncplay-mediasearchdirectories-title" : "In diesen Verzeichnissen nach Medien suchen", #needs to be checked + "syncplay-mediasearchdirectories-label" : "In diesen Verzeichnissen nach Medien suchen (ein Pfad pro Zeile)", "sync-label" : "Synchronisation", "sync-otherslagging-title" : "Wenn andere laggen...", "sync-youlaggging-title" : "Wenn du laggst...", diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 26c7a2d..91bd135 100755 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -223,7 +223,8 @@ en = { "misc-label" : "Misc", "core-behaviour-title" : "Core room behaviour", "syncplay-internals-title" : "Syncplay internals", - "syncplay-mediasearchdirectories-title" : "Directories to search for media (one path per line)", + "syncplay-mediasearchdirectories-title" : "Directories to search for media", + "syncplay-mediasearchdirectories-label" : "Directories to search for media (one path per line)", "sync-label" : "Sync", "sync-otherslagging-title" : "If others are lagging behind...", "sync-youlaggging-title" : "If you are lagging behind...", diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index e8671fc..459a0e6 100755 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -226,7 +226,8 @@ ru = { "misc-label" : "Прочее", "core-behaviour-title" : "Информация о файлах", "syncplay-internals-title" : "Системные настройки", - "syncplay-mediasearchdirectories-title" : "Папки воспроизведения (один путь на строку)", + "syncplay-mediasearchdirectories-title" : "Папки воспроизведения", #needs to be checked + "syncplay-mediasearchdirectories-label" : "Папки воспроизведения (один путь на строку)", "sync-label" : "Синхронизация", "sync-otherslagging-title" : "Опережение", "sync-youlaggging-title" : "Отставание", From 92af4aa4d33a29fe3a6afbd32bfdb2f21dac7fec Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 14 Jun 2018 11:38:00 +0200 Subject: [PATCH 54/95] Travis: use PySide2 TP with Qt 5.11.0 --- .travis.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 199140f..31ff06e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,14 +16,12 @@ before_install: - python3 --version - which pip3 - pip3 --version -- curl -O http://syncplay.s3.amazonaws.com/qt595.rb -- mv qt595.rb qt.rb -- brew install ./qt.rb -- curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=PySide2-5.9.0a1-5.9.5-cp36-cp36m-macosx_10_11_x86_64.whl -o PySide2-5.9.0a1-5.9.5-cp36-cp36m-macosx_10_11_x86_64.whl -- pip3 install PySide2-5.9.0a1-5.9.5-cp36-cp36m-macosx_10_11_x86_64.whl -- ln -s /usr/local/lib/python3.6/site-packages/PySide2/libpyside2.cpython-36m-darwin.5.9.dylib /usr/local/lib/ -- ln -s /usr/local/lib/python3.6/site-packages/PySide2/libshiboken2.cpython-36m-darwin.5.9.dylib /usr/local/lib/ -#- python -c "from PySide2 import QtCore" +- brew install qt +- curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=PySide2-5.11.0-5.11.0-cp36-cp36m-macosx_10_11_x86_64.whl -o PySide2-5.11.0-5.11.0-cp36-cp36m-macosx_10_11_x86_64.whl +- pip3 install PySide2-5.11.0-5.11.0-cp36-cp36m-macosx_10_11_x86_64.whl +- ln -s /usr/local/lib/python3.6/site-packages/PySide2/libpyside2.cpython-36m-darwin.5.11.dylib /usr/local/lib/ +- ln -s /usr/local/lib/python3.6/site-packages/PySide2/libshiboken2.cpython-36m-darwin.5.11.dylib /usr/local/lib/ +- python3 -c "from PySide2 import __version__; print(__version__)" - python3 -c "from PySide2.QtCore import __version__; print(__version__)" - cd py2app - python3 setup.py install From 9f6b664c4b0f45828ef8d8a77cdae30bba6ee3a8 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 14 Jun 2018 11:38:50 +0200 Subject: [PATCH 55/95] Update py2app script for Qt 5.11 --- buildPy2app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildPy2app.py b/buildPy2app.py index 74cb424..0821adf 100755 --- a/buildPy2app.py +++ b/buildPy2app.py @@ -17,7 +17,7 @@ OPTIONS = { 'iconfile':'resources/icon.icns', 'includes': {'PySide2.QtCore', 'PySide2.QtUiTools', 'PySide2.QtGui','PySide2.QtWidgets', 'certifi'}, 'excludes': {'PySide', 'PySide.QtCore', 'PySide.QtUiTools', 'PySide.QtGui'}, - 'qt_plugins': ['platforms/libqcocoa.dylib', 'platforms/libqminimal.dylib','platforms/libqoffscreen.dylib'], + 'qt_plugins': ['platforms/libqcocoa.dylib', 'platforms/libqminimal.dylib','platforms/libqoffscreen.dylib', 'styles/libqmacstyle.dylib'], 'plist': { 'CFBundleName':'Syncplay', 'CFBundleShortVersionString':syncplay.version, From 33c816fc568a3d9ccca946ffffbf1392a7487b86 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 14 Jun 2018 11:49:27 +0200 Subject: [PATCH 56/95] Patch QSocketNotifier call syntax --- syncplay/vendor/qt5reactor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/vendor/qt5reactor.py b/syncplay/vendor/qt5reactor.py index 7cc5eb7..da82ccc 100755 --- a/syncplay/vendor/qt5reactor.py +++ b/syncplay/vendor/qt5reactor.py @@ -124,7 +124,7 @@ class TwistedSocketNotifier(QObject): self.reactor = reactor self.watcher = watcher fd = self.watcher.fileno() - self.notifier = QSocketNotifier(watcher, socketType, parent) + self.notifier = QSocketNotifier(fd, socketType, parent) self.notifier.setEnabled(True) if socketType == QSocketNotifier.Read: self.fn = self.read From fc809574f456193cb9cd5d7bbcba8118bd9248d5 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 14 Jun 2018 17:08:34 +0200 Subject: [PATCH 57/95] Patch QSocketNotifier syntax excluding macOS and Windows on PySide2 --- syncplay/vendor/qt5reactor.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/syncplay/vendor/qt5reactor.py b/syncplay/vendor/qt5reactor.py index cde2b5e..f70d16b 100644 --- a/syncplay/vendor/qt5reactor.py +++ b/syncplay/vendor/qt5reactor.py @@ -106,6 +106,8 @@ Subsequent port by therve import sys +from syncplay.utils import isMacOS, isWindows +from syncplay.vendor.Qt import IsPySide2 from syncplay.vendor.Qt.QtCore import ( QCoreApplication, QEventLoop, QObject, QSocketNotifier, QTimer, Signal) from twisted.internet import posixbase @@ -124,7 +126,12 @@ class TwistedSocketNotifier(QObject): self.reactor = reactor self.watcher = watcher fd = self.watcher.fileno() - self.notifier = QSocketNotifier(watcher, socketType, parent) + if (isMacOS() and IsPySide2): + self.notifier = QSocketNotifier(watcher, socketType, parent) + elif (isWindows() and IsPySide2): + self.notifier = QSocketNotifier(watcher, socketType, parent) + else: + self.notifier = QSocketNotifier(fd, socketType, parent) self.notifier.setEnabled(True) if socketType == QSocketNotifier.Read: self.fn = self.read From ad699ac1c5cc72f517c372d6ba9c07e25e4a3e3f Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 14 Jun 2018 18:14:29 +0200 Subject: [PATCH 58/95] Manually check for updates if automaticUpdateCheck fails --- syncplay/ui/gui.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 770a732..5d2c5e4 100755 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -1559,16 +1559,20 @@ class MainWindow(QtWidgets.QMainWindow): currentDateTimeValue = QDateTime.currentDateTime() if not self.config['checkForUpdatesAutomatically']: return - if self.config['lastCheckedForUpdates']: - configLastChecked = datetime.strptime(self.config["lastCheckedForUpdates"], "%Y-%m-%d %H:%M:%S.%f") - if self.lastCheckedForUpdates is None or configLastChecked > self.lastCheckedForUpdates.toPython(): - self.lastCheckedForUpdates = QDateTime.fromString(self.config["lastCheckedForUpdates"],'yyyy-MM-dd HH-mm-ss') - if self.lastCheckedForUpdates is None: - self.checkForUpdates() - else: - timeDelta = currentDateTimeValue.toPython() - self.lastCheckedForUpdates.toPython() - if timeDelta.total_seconds() > constants.AUTOMATIC_UPDATE_CHECK_FREQUENCY: + try: + if self.config['lastCheckedForUpdates']: + configLastChecked = datetime.strptime(self.config["lastCheckedForUpdates"], "%Y-%m-%d %H:%M:%S.%f") + if self.lastCheckedForUpdates is None or configLastChecked > self.lastCheckedForUpdates.toPython(): + self.lastCheckedForUpdates = QDateTime.fromString(self.config["lastCheckedForUpdates"],'yyyy-MM-dd HH-mm-ss') + if self.lastCheckedForUpdates is None: self.checkForUpdates() + else: + timeDelta = currentDateTimeValue.toPython() - self.lastCheckedForUpdates.toPython() + if timeDelta.total_seconds() > constants.AUTOMATIC_UPDATE_CHECK_FREQUENCY: + self.checkForUpdates() + except: + self.showDebugMessage(u"Automatic check for updates failed. An update check was manually trigggered.") + self.checkForUpdates() def userCheckForUpdates(self): self.checkForUpdates(userInitiated=True) From d510bf080e163b4095f06fbb60ab3b89dd57abde Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 14 Jun 2018 23:57:36 +0200 Subject: [PATCH 59/95] AppVeyor: revert to PySide 1.x --- .appveyor.yml | 9 +-------- buildPy2exe.py | 6 +++--- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 6c9fcf3..dd7ea18 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,9 +9,7 @@ init: - set PATH=C:\Miniconda;C:\Miniconda\Scripts;C:\Program Files (x86)\NSIS;%PATH% - conda create -n syncplay -y - activate syncplay - - conda install python=2.7.13 -y - - conda install pywin32 -y - - conda install -c conda-forge pyside2 -y + - conda install python pywin32 pyside -y - pip install twisted py2exe_py2 zope.interface - type nul > C:\Miniconda\envs\syncplay\lib\site-packages\zope\__init__.py - pip freeze @@ -21,12 +19,7 @@ install: - cd %APPVEYOR_BUILD_FOLDER% - for /F "tokens=2 delims='" %%a in ('findstr version syncplay\__init__.py') do @set ver=%%a - python buildPy2exe.py - - del syncplay_v%ver%\lib\api-* - - del syncplay_v%ver%\lib\DNSAPI.dll - - del syncplay_v%ver%\lib\IPHLPAPI.dll - del syncplay_v%ver%\lib\MPR.dll - - mkdir syncplay_v%ver%\platforms - - copy C:\Miniconda\envs\syncplay\library\plugins\platforms\qwindows.dll syncplay_v%ver%\platforms\ - type nul > syncplay_v%ver%\syncplay.ini - copy resources\win_lua_note.txt syncplay_v%ver%\"VLC LUA Script installation.txt" diff --git a/buildPy2exe.py b/buildPy2exe.py index 881667c..310f765 100755 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -720,8 +720,8 @@ info = dict( # console=['syncplayServer.py', {"script":"syncplayClient.py", "icon_resources":[(1, "resources\\icon.ico")], 'dest_base': "Syncplay"}], options={'py2exe': { 'dist_dir': OUT_DIR, - 'packages': 'PySide2.QtUiTools', - 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide2, liburl, ast, unicodedata, _ssl', + 'packages': 'PySide.QtUiTools', + 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide, liburl, ast, unicodedata', 'excludes': 'venv, doctest, pdb, unittest, win32clipboard, win32file, win32pdh, win32security, win32trace, win32ui, winxpgui, win32pipe, win32process, Tkinter', 'dll_excludes': 'msvcr71.dll, MSVCP90.dll, POWRPROF.dll', 'optimize': 2, @@ -733,5 +733,5 @@ info = dict( cmdclass = {"py2exe": build_installer}, ) -sys.argv.extend(['py2exe', '-p win32com ', '-i twisted.web.resource', '-i PySide2.QtCore', '-p PySide2.QtGui', '-i PySide2.QtWidgets']) +sys.argv.extend(['py2exe', '-p win32com ', '-i twisted.web.resource', '-p PySide.QtGui']) setup(**info) From a4503329b6bcf5a348066b657aa98af245c6cc2e Mon Sep 17 00:00:00 2001 From: albertosottile Date: Fri, 15 Jun 2018 00:13:18 +0200 Subject: [PATCH 60/95] Restore PySide 1.x third party notice for Windows --- resources/third-party-notices.rtf | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/resources/third-party-notices.rtf b/resources/third-party-notices.rtf index 436ba5f..12e2102 100644 --- a/resources/third-party-notices.rtf +++ b/resources/third-party-notices.rtf @@ -24,6 +24,27 @@ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ +\b PySide 1.2.1\ + +\b0 \ +Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).\ +Contact: PySide team \ +\ +This library is free software; you can redistribute it and/or\ +modify it under the terms of the GNU Lesser General Public\ +License as published by the Free Software Foundation; either\ +version 2.1 of the License, or (at your option) any later version.\ +This library is distributed in the hope that it will be useful,\ +but WITHOUT ANY WARRANTY; without even the implied warranty of\ +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\ +Lesser General Public License for more details.\ +\ +\pard\pardeftab720\partightenfactor0 +\cf0 You should have received a copy of the GNU Lesser General Public License\ +along with this program. If not, see \ +\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 +\cf0 \ + \b Qt for Python\ \b0 \ @@ -47,7 +68,7 @@ along with this program. If not, see .\ \b Qt \b0 \ \ -This program uses Qt versions 5.6 and 5.11 under the GNU LGPL version 3.\ +This program uses Qt versions 4.8.7 and 5.11 under the GNU LGPL version 3.\ \ Qt is a C++ toolkit for cross-platform application development.\ \ From 245b9973939da33e9ddc8fade7c2e0b41dab5b3d Mon Sep 17 00:00:00 2001 From: Etoh Date: Thu, 14 Jun 2018 22:04:52 +0100 Subject: [PATCH 61/95] Detect portable versions of MPC-HC, MPC-BE and VLC --- syncplay/players/mpc.py | 8 +++++++- syncplay/players/mpcbe.py | 8 +++++++- syncplay/players/vlc.py | 5 +++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/syncplay/players/mpc.py b/syncplay/players/mpc.py index 3d40308..313fbf4 100644 --- a/syncplay/players/mpc.py +++ b/syncplay/players/mpc.py @@ -496,7 +496,7 @@ class MPCHCAPIPlayer(BasePlayer): @staticmethod def getExpandedPath(path): if os.path.isfile(path): - if path.lower().endswith(u'mpc-hc.exe'.lower()) or path.lower().endswith(u'mpc-hc64.exe'.lower()) or path.lower().endswith(u'mpc-hc64_nvo.exe'.lower()) or path.lower().endswith(u'mpc-hc_nvo.exe'.lower()): + if path.lower().endswith(u'mpc-hc.exe'.lower()) or path.lower().endswith(u'mpc-hcportable.exe'.lower()) or path.lower().endswith(u'mpc-hc64.exe'.lower()) or path.lower().endswith(u'mpc-hc64_nvo.exe'.lower()) or path.lower().endswith(u'mpc-hc_nvo.exe'.lower()): return path if os.path.isfile(path + u"mpc-hc.exe"): path += u"mpc-hc.exe" @@ -504,6 +504,12 @@ class MPCHCAPIPlayer(BasePlayer): if os.path.isfile(path + u"\\mpc-hc.exe"): path += u"\\mpc-hc.exe" return path + if os.path.isfile(path + u"mpc-hcportable.exe"): + path += u"mpc-hcportable.exe" + return path + if os.path.isfile(path + u"\\mpc-hcportable.exe"): + path += u"\\mpc-hcportable.exe" + return path if os.path.isfile(path + u"mpc-hc_nvo.exe"): path += u"mpc-hc_nvo.exe" return path diff --git a/syncplay/players/mpcbe.py b/syncplay/players/mpcbe.py index 68b2074..900ace4 100644 --- a/syncplay/players/mpcbe.py +++ b/syncplay/players/mpcbe.py @@ -30,7 +30,7 @@ class MpcBePlayer(MPCHCAPIPlayer): @staticmethod def getExpandedPath(path): if os.path.isfile(path): - if path.lower().endswith(u'mpc-be.exe'.lower()) or path.lower().endswith(u'mpc-be64.exe'.lower()): + if path.lower().endswith(u'mpc-be.exe'.lower()) or path.lower().endswith(u'mpc-be64.exe'.lower() or path.lower().endswith(u'mpc-beportable.exe'.lower())): return path if os.path.isfile(path + u"mpc-be.exe"): path += u"mpc-be.exe" @@ -38,6 +38,12 @@ class MpcBePlayer(MPCHCAPIPlayer): if os.path.isfile(path + u"\\mpc-be.exe"): path += u"\\mpc-be.exe" return path + if os.path.isfile(path + u"mpc-beportable.exe"): + path += u"mpc-beportable.exe" + return path + if os.path.isfile(path + u"\\mpc-beportable.exe"): + path += u"\\mpc-beportable.exe" + return path if os.path.isfile(path + u"mpc-be64.exe"): path += u"mpc-be64.exe" return path diff --git a/syncplay/players/vlc.py b/syncplay/players/vlc.py index f7392ec..9a0ebbb 100755 --- a/syncplay/players/vlc.py +++ b/syncplay/players/vlc.py @@ -285,6 +285,11 @@ class VlcPlayer(BasePlayer): elif os.path.isfile(playerPath + u"\\vlc.exe"): playerPath += u"\\vlc.exe" return playerPath + elif os.path.isfile(playerPath + u"VLCPortable.exe"): + playerPath += u"VLCPortable.exe" + elif os.path.isfile(playerPath + u"\\VLCPortable.exe"): + playerPath += u"\\VLCPortable.exe" + return playerPath if os.access(playerPath, os.X_OK): return playerPath for path in os.environ['PATH'].split(':'): From b4292bf8cd43df00e3dd9d89e7b4397f8e3ca7a7 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Fri, 15 Jun 2018 09:42:13 +0200 Subject: [PATCH 62/95] Manually check for updates if automaticUpdateCheck fails --- syncplay/ui/gui.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index e5e8037..a34f182 100755 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -1555,16 +1555,20 @@ class MainWindow(QtWidgets.QMainWindow): currentDateTimeValue = QDateTime.currentDateTime() if not self.config['checkForUpdatesAutomatically']: return - if self.config['lastCheckedForUpdates']: - configLastChecked = datetime.strptime(self.config["lastCheckedForUpdates"], "%Y-%m-%d %H:%M:%S.%f") - if self.lastCheckedForUpdates is None or configLastChecked > self.lastCheckedForUpdates.toPython(): - self.lastCheckedForUpdates = QDateTime.fromString(self.config["lastCheckedForUpdates"],'yyyy-MM-dd HH-mm-ss') - if self.lastCheckedForUpdates is None: - self.checkForUpdates() - else: - timeDelta = currentDateTimeValue.toPython() - self.lastCheckedForUpdates.toPython() - if timeDelta.total_seconds() > constants.AUTOMATIC_UPDATE_CHECK_FREQUENCY: + try: + if self.config['lastCheckedForUpdates']: + configLastChecked = datetime.strptime(self.config["lastCheckedForUpdates"], "%Y-%m-%d %H:%M:%S.%f") + if self.lastCheckedForUpdates is None or configLastChecked > self.lastCheckedForUpdates.toPython(): + self.lastCheckedForUpdates = QDateTime.fromString(self.config["lastCheckedForUpdates"],'yyyy-MM-dd HH-mm-ss') + if self.lastCheckedForUpdates is None: self.checkForUpdates() + else: + timeDelta = currentDateTimeValue.toPython() - self.lastCheckedForUpdates.toPython() + if timeDelta.total_seconds() > constants.AUTOMATIC_UPDATE_CHECK_FREQUENCY: + self.checkForUpdates() + except: + self.showDebugMessage("Automatic check for updates failed. An update check was manually trigggered.") + self.checkForUpdates() def userCheckForUpdates(self): self.checkForUpdates(userInitiated=True) From ce2c37623a511a492238201a9b6e3afad812c523 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Fri, 15 Jun 2018 11:17:35 +0200 Subject: [PATCH 63/95] Detect portable versions of MPC-HC, MPC-BE and VLC --- syncplay/players/mpc.py | 8 +++++++- syncplay/players/mpcbe.py | 8 +++++++- syncplay/players/vlc.py | 6 ++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/syncplay/players/mpc.py b/syncplay/players/mpc.py index a407147..32eda91 100755 --- a/syncplay/players/mpc.py +++ b/syncplay/players/mpc.py @@ -496,7 +496,7 @@ class MPCHCAPIPlayer(BasePlayer): @staticmethod def getExpandedPath(path): if os.path.isfile(path): - if path.lower().endswith('mpc-hc.exe'.lower()) or path.lower().endswith('mpc-hc64.exe'.lower()) or path.lower().endswith('mpc-hc64_nvo.exe'.lower()) or path.lower().endswith('mpc-hc_nvo.exe'.lower()): + if path.lower().endswith('mpc-hc.exe'.lower()) or path.lower().endswith('mpc-hcportable.exe'.lower()) or path.lower().endswith('mpc-hc64.exe'.lower()) or path.lower().endswith('mpc-hc64_nvo.exe'.lower()) or path.lower().endswith('mpc-hc_nvo.exe'.lower()): return path if os.path.isfile(path + "mpc-hc.exe"): path += "mpc-hc.exe" @@ -504,6 +504,12 @@ class MPCHCAPIPlayer(BasePlayer): if os.path.isfile(path + "\\mpc-hc.exe"): path += "\\mpc-hc.exe" return path + if os.path.isfile(path + "mpc-hcportable.exe"): + path += "mpc-hcportable.exe" + return path + if os.path.isfile(path + "\\mpc-hcportable.exe"): + path += "\\mpc-hcportable.exe" + return path if os.path.isfile(path + "mpc-hc_nvo.exe"): path += "mpc-hc_nvo.exe" return path diff --git a/syncplay/players/mpcbe.py b/syncplay/players/mpcbe.py index fcda770..1c244b3 100755 --- a/syncplay/players/mpcbe.py +++ b/syncplay/players/mpcbe.py @@ -30,7 +30,7 @@ class MpcBePlayer(MPCHCAPIPlayer): @staticmethod def getExpandedPath(path): if os.path.isfile(path): - if path.lower().endswith('mpc-be.exe'.lower()) or path.lower().endswith('mpc-be64.exe'.lower()): + if path.lower().endswith('mpc-be.exe'.lower()) or path.lower().endswith('mpc-be64.exe'.lower() or path.lower().endswith('mpc-beportable.exe'.lower())): return path if os.path.isfile(path + "mpc-be.exe"): path += "mpc-be.exe" @@ -38,6 +38,12 @@ class MpcBePlayer(MPCHCAPIPlayer): if os.path.isfile(path + "\\mpc-be.exe"): path += "\\mpc-be.exe" return path + if os.path.isfile(path + "mpc-beportable.exe"): + path += "mpc-beportable.exe" + return path + if os.path.isfile(path + "\\mpc-beportable.exe"): + path += "\\mpc-beportable.exe" + return path if os.path.isfile(path + "mpc-be64.exe"): path += "mpc-be64.exe" return path diff --git a/syncplay/players/vlc.py b/syncplay/players/vlc.py index 7651267..7908ddf 100755 --- a/syncplay/players/vlc.py +++ b/syncplay/players/vlc.py @@ -285,6 +285,12 @@ class VlcPlayer(BasePlayer): elif os.path.isfile(playerPath + "\\vlc.exe"): playerPath += "\\vlc.exe" return playerPath + elif os.path.isfile(playerPath + "VLCPortable.exe"): + playerPath += "VLCPortable.exe" + return playerPath + elif os.path.isfile(playerPath + "\\VLCPortable.exe"): + playerPath += "\\VLCPortable.exe" + return playerPath if os.access(playerPath, os.X_OK): return playerPath for path in os.environ['PATH'].split(':'): From 6eec4883162669567417e6e645e3f18a7172d0b7 Mon Sep 17 00:00:00 2001 From: Etoh Date: Fri, 15 Jun 2018 09:33:42 +0100 Subject: [PATCH 64/95] Fixed missing VLC playerPath return --- syncplay/players/vlc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/syncplay/players/vlc.py b/syncplay/players/vlc.py index 9a0ebbb..1cc8943 100755 --- a/syncplay/players/vlc.py +++ b/syncplay/players/vlc.py @@ -287,6 +287,7 @@ class VlcPlayer(BasePlayer): return playerPath elif os.path.isfile(playerPath + u"VLCPortable.exe"): playerPath += u"VLCPortable.exe" + return playerPath elif os.path.isfile(playerPath + u"\\VLCPortable.exe"): playerPath += u"\\VLCPortable.exe" return playerPath From e30eb7d6faef1209f1e550b4964bdc4c67f29735 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Fri, 15 Jun 2018 18:42:29 +0200 Subject: [PATCH 65/95] Remove unused Travis cache scripts --- travis/cache-homebrew | 6 ------ travis/cache-python | 5 ----- travis/download-homebrew | 13 ------------- travis/download-python | 10 ---------- 4 files changed, 34 deletions(-) delete mode 100755 travis/cache-homebrew delete mode 100755 travis/cache-python delete mode 100755 travis/download-homebrew delete mode 100755 travis/download-python diff --git a/travis/cache-homebrew b/travis/cache-homebrew deleted file mode 100755 index d9dcc38..0000000 --- a/travis/cache-homebrew +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -mkdir travis-cache -if [ ! -f homebrew-cache.tar.gz ]; then - tar czf travis-cache/homebrew-cache.tar.gz --directory /usr/local/Cellar pkg-config readline sqlite gdbm makedepend openssl python@2 -fi diff --git a/travis/cache-python b/travis/cache-python deleted file mode 100755 index f7d1e65..0000000 --- a/travis/cache-python +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -if [ ! -f python-cache.tar.gz ]; then - tar czf travis-cache/python-cache.tar.gz --directory /usr/local/lib/python2.7 site-packages -fi diff --git a/travis/download-homebrew b/travis/download-homebrew deleted file mode 100755 index dd23237..0000000 --- a/travis/download-homebrew +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -curl -L "https://dl.bintray.com/alby128/Syncplay/homebrew-cache.tar.gz" -o homebrew-cache.tar.gz -if [ -f homebrew-cache.tar.gz ]; then - if ! tar tf homebrew-cache.tar.gz &>/dev/null; then - rm homebrew-cache.tar.gz - exit 0 - fi - tar zxf homebrew-cache.tar.gz --directory /usr/local/Cellar - brew unlink pkg-config - brew link pkg-config - brew link --force readline sqlite gdbm makedepend python@2 -fi diff --git a/travis/download-python b/travis/download-python deleted file mode 100755 index 870f07e..0000000 --- a/travis/download-python +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -curl -L "https://dl.bintray.com/alby128/Syncplay/python-cache.tar.gz" -o python-cache.tar.gz -if [ -f python-cache.tar.gz ]; then - if ! tar tf python-cache.tar.gz &>/dev/null; then - rm python-cache.tar.gz - exit 0 - fi - tar zxf python-cache.tar.gz --directory /usr/local/lib/python2.7 -fi From 19ec28a95eda3b410fa81d3bb9c1229bb2935da9 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Sat, 16 Jun 2018 14:16:04 +0200 Subject: [PATCH 66/95] Redesign About dialog to show environment info --- syncplay/messages_de.py | 2 +- syncplay/messages_en.py | 2 +- syncplay/messages_it.py | 2 +- syncplay/messages_ru.py | 2 +- syncplay/ui/gui.py | 27 +++++++++++++++------------ 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index ea987db..65b7a51 100644 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -311,7 +311,7 @@ de = { #About dialog - TODO: Translate "about-menu-label": u"&About Syncplay", "about-dialog-title": u"About Syncplay", - "about-dialog-release": u"Version {} release {} on {}", + "about-dialog-release": u"Version {} release {}", "about-dialog-license-text" : u"Licensed under the Apache License, Version 2.0", "about-dialog-license-button": u"License", "about-dialog-dependencies": u"Dependencies", diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 6f5d085..6e0fcbb 100644 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -316,7 +316,7 @@ en = { #About dialog "about-menu-label": u"&About Syncplay", "about-dialog-title": u"About Syncplay", - "about-dialog-release": u"Version {} release {} on {}", + "about-dialog-release": u"Version {} release {}", "about-dialog-license-text" : u"Licensed under the Apache License, Version 2.0", "about-dialog-license-button": u"License", "about-dialog-dependencies": u"Dependencies", diff --git a/syncplay/messages_it.py b/syncplay/messages_it.py index f73e6d0..d893421 100644 --- a/syncplay/messages_it.py +++ b/syncplay/messages_it.py @@ -316,7 +316,7 @@ it = { #About dialog "about-menu-label": u"&Informazioni su Syncplay", "about-dialog-title": u"Informazioni su Syncplay", - "about-dialog-release": u"Versione {} release {} con {}", + "about-dialog-release": u"Versione {} release {}", "about-dialog-license-text" : u"Rilasciato sotto Apache License, Version 2.0", "about-dialog-license-button": u"Licenza", "about-dialog-dependencies": u"Dipendenze", diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index ca790d6..975a397 100644 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -319,7 +319,7 @@ ru = { #About dialog - TODO: Translate "about-menu-label": u"&About Syncplay", "about-dialog-title": u"About Syncplay", - "about-dialog-release": u"Version {} release {} on {}", + "about-dialog-release": u"Version {} release {}", "about-dialog-license-text" : u"Licensed under the Apache License, Version 2.0", "about-dialog-license-button": u"License", "about-dialog-dependencies": u"Dependencies", diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 5d2c5e4..7e5d1ba 100755 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -1,6 +1,7 @@ from syncplay.vendor import Qt -from syncplay.vendor.Qt import QtWidgets, QtGui, __binding__, __binding_version__, IsPySide, IsPySide2 +from syncplay.vendor.Qt import QtWidgets, QtGui, __binding__, __binding_version__, __qt_version__, IsPySide, IsPySide2 from syncplay.vendor.Qt.QtCore import Qt, QSettings, QSize, QPoint, QUrl, QLine, QDateTime +from platform import python_version if IsPySide2: from PySide2.QtCore import QStandardPaths from syncplay import utils, constants, version, release_number @@ -106,33 +107,35 @@ class AboutDialog(QtWidgets.QDialog): super(AboutDialog, self).__init__(parent) if isMacOS(): self.setWindowTitle("") + self.setWindowFlags(Qt.Dialog | Qt.WindowTitleHint | Qt.WindowCloseButtonHint | Qt.CustomizeWindowHint) else: self.setWindowTitle(getMessage("about-dialog-title")) if isWindows(): self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) nameLabel = QtWidgets.QLabel("
Syncplay
") - nameLabel.setFont(QtGui.QFont("Helvetica", 20)) + nameLabel.setFont(QtGui.QFont("Helvetica", 18)) linkLabel = QtWidgets.QLabel("
syncplay.pl
") linkLabel.setOpenExternalLinks(True) - versionLabel = QtWidgets.QLabel("
" + getMessage("about-dialog-release").format(version, release_number, __binding__) + "
") - licenseLabel = QtWidgets.QLabel("

Copyright © 2017 Syncplay

" + getMessage("about-dialog-license-text") + "

") + versionLabel = QtWidgets.QLabel("

" + getMessage("about-dialog-release").format(version, release_number) + "
Python " + python_version() + " - " + __binding__ + " " + __binding_version__ + " - Qt " + __qt_version__ + "

") + licenseLabel = QtWidgets.QLabel("

Copyright © 2012–2018 Syncplay

" + getMessage("about-dialog-license-text") + "

") aboutIconPixmap = QtGui.QPixmap(resourcespath + u"syncplay.png") aboutIconLabel = QtWidgets.QLabel() - aboutIconLabel.setPixmap(aboutIconPixmap.scaled(120, 120, Qt.KeepAspectRatio)) + aboutIconLabel.setPixmap(aboutIconPixmap.scaled(65, 65, Qt.KeepAspectRatio)) aboutLayout = QtWidgets.QGridLayout() - aboutLayout.addWidget(aboutIconLabel, 0, 0, 4, 2) - aboutLayout.addWidget(nameLabel, 0, 2, 1, 2) - aboutLayout.addWidget(linkLabel, 1, 2, 1, 2) - aboutLayout.addWidget(versionLabel, 2, 2, 1, 2) - aboutLayout.addWidget(licenseLabel, 3, 2, 1, 2) + aboutLayout.addWidget(aboutIconLabel, 0, 0, 3, 4, Qt.AlignHCenter) + aboutLayout.addWidget(nameLabel, 3, 0, 1, 4) + aboutLayout.addWidget(linkLabel, 4, 0, 1, 4) + aboutLayout.addWidget(versionLabel, 5, 0, 1, 4) + aboutLayout.addWidget(licenseLabel, 6, 0, 1, 4) licenseButton = QtWidgets.QPushButton(getMessage("about-dialog-license-button")) licenseButton.setAutoDefault(False) licenseButton.clicked.connect(self.openLicense) - aboutLayout.addWidget(licenseButton, 4, 2) + aboutLayout.addWidget(licenseButton, 7, 0, 1, 2) dependenciesButton = QtWidgets.QPushButton(getMessage("about-dialog-dependencies")) dependenciesButton.setAutoDefault(False) dependenciesButton.clicked.connect(self.openDependencies) - aboutLayout.addWidget(dependenciesButton, 4, 3) + aboutLayout.addWidget(dependenciesButton, 7, 2, 1, 2) + aboutLayout.setVerticalSpacing(10) aboutLayout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) self.setSizeGripEnabled(False) self.setLayout(aboutLayout) From 32f589b9e2c43bccc7620ed3ebbe91b98e88f490 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Sun, 17 Jun 2018 14:41:09 +0200 Subject: [PATCH 67/95] Fix third-party-notices and add qt5reactor license --- resources/third-party-notices.rtf | 83 +++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/resources/third-party-notices.rtf b/resources/third-party-notices.rtf index 12e2102..216617d 100644 --- a/resources/third-party-notices.rtf +++ b/resources/third-party-notices.rtf @@ -1,4 +1,4 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1504\cocoasubrtf830 +{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf400 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} @@ -24,7 +24,7 @@ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ -\b PySide 1.2.1\ +\b PySide\ \b0 \ Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).\ @@ -68,7 +68,7 @@ along with this program. If not, see .\ \b Qt \b0 \ \ -This program uses Qt versions 4.8.7 and 5.11 under the GNU LGPL version 3.\ +This program uses Qt under the GNU LGPL version 3.\ \ Qt is a C++ toolkit for cross-platform application development.\ \ @@ -159,6 +159,83 @@ The above copyright notice and this permission notice shall be\ included in all copies or substantial portions of the Software.\ \b \ +qt5reactor\ +\ + +\b0 Copyright (c) 2001-2018\ +Allen Short\ +Andy Gayton\ +Andrew Bennetts\ +Antoine Pitrou\ +Apple Computer, Inc.\ +Ashwini Oruganti\ +bakbuk\ +Benjamin Bruheim\ +Bob Ippolito\ +Burak Nehbit\ +Canonical Limited\ +Christopher Armstrong\ +Christopher R. Wood\ +David Reid\ +Donovan Preston\ +Elvis Stansvik\ +Eric Mangold\ +Eyal Lotem\ +Glenn Tarbox\ +Google Inc.\ +Hybrid Logic Ltd.\ +Hynek Schlawack\ +Itamar Turner-Trauring\ +James Knight\ +Jason A. Mobarak\ +Jean-Paul Calderone\ +Jessica McKellar\ +Jonathan Jacobs\ +Jonathan Lange\ +Jonathan D. Simms\ +J\'fcrgen Hermann\ +Julian Berman\ +Kevin Horn\ +Kevin Turner\ +Kyle Altendorf\ +Laurens Van Houtven\ +Mary Gardiner\ +Matthew Lefkowitz\ +Massachusetts Institute of Technology\ +Moshe Zadka\ +Paul Swartz\ +Pavel Pergamenshchik\ +Ralph Meijer\ +Richard Wall\ +Sean Riley\ +Software Freedom Conservancy\ +Tarashish Mishra\ +Travis B. Hartwell\ +Thijs Triemstra\ +Thomas Herve\ +Timothy Allen\ +Tom Prince\ +\ +Permission is hereby granted, free of charge, to any person obtaining\ +a copy of this software and associated documentation files (the\ +"Software"), to deal in the Software without restriction, including\ +without limitation the rights to use, copy, modify, merge, publish,\ +distribute, sublicense, and/or sell copies of the Software, and to\ +permit persons to whom the Software is furnished to do so, subject to\ +the following conditions:\ +\ +The above copyright notice and this permission notice shall be\ +included in all copies or substantial portions of the Software.\ +\ +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\ +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\ +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\ +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\ +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\ +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\ +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +\b \ +\ appnope\ \b0 \ From b4534266e38e002d7b17abc5d02628315d68f4a6 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Sat, 16 Jun 2018 14:09:49 +0200 Subject: [PATCH 68/95] Redesign About dialog to show environment info --- syncplay/messages_de.py | 2 +- syncplay/messages_en.py | 2 +- syncplay/messages_it.py | 2 +- syncplay/messages_ru.py | 2 +- syncplay/ui/gui.py | 28 ++++++++++++++++------------ 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index a001aa9..da1fbad 100755 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -311,7 +311,7 @@ de = { #About dialog - TODO: Translate "about-menu-label": "&About Syncplay", "about-dialog-title": "About Syncplay", - "about-dialog-release": "Version {} release {} on {}", + "about-dialog-release": "Version {} release {}", "about-dialog-license-text" : "Licensed under the Apache License, Version 2.0", "about-dialog-license-button": "License", "about-dialog-dependencies": "Dependencies", diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 91bd135..6a3aca6 100755 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -316,7 +316,7 @@ en = { #About dialog "about-menu-label": "&About Syncplay", "about-dialog-title": "About Syncplay", - "about-dialog-release": "Version {} release {} on {}", + "about-dialog-release": "Version {} release {}", "about-dialog-license-text" : "Licensed under the Apache License, Version 2.0", "about-dialog-license-button": "License", "about-dialog-dependencies": "Dependencies", diff --git a/syncplay/messages_it.py b/syncplay/messages_it.py index 5c51810..a9a400a 100755 --- a/syncplay/messages_it.py +++ b/syncplay/messages_it.py @@ -316,7 +316,7 @@ it = { #About dialog "about-menu-label": "&Informazioni su Syncplay", "about-dialog-title": "Informazioni su Syncplay", - "about-dialog-release": "Versione {} release {} con {}", + "about-dialog-release": "Versione {} release {}", "about-dialog-license-text" : "Rilasciato sotto Apache License, Version 2.0", "about-dialog-license-button": "Licenza", "about-dialog-dependencies": "Dipendenze", diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index 459a0e6..e834ae3 100755 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -319,7 +319,7 @@ ru = { #About dialog - TODO: Translate "about-menu-label": "&About Syncplay", "about-dialog-title": "About Syncplay", - "about-dialog-release": "Version {} release {} on {}", + "about-dialog-release": "Version {} release {}", "about-dialog-license-text" : "Licensed under the Apache License, Version 2.0", "about-dialog-license-button": "License", "about-dialog-dependencies": "Dependencies", diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index a34f182..eb93766 100755 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -1,6 +1,7 @@ from syncplay.vendor import Qt -from syncplay.vendor.Qt import QtWidgets, QtGui, __binding__, __binding_version__, IsPySide, IsPySide2 +from syncplay.vendor.Qt import QtWidgets, QtGui, __binding__, __binding_version__, __qt_version__, IsPySide, IsPySide2 from syncplay.vendor.Qt.QtCore import Qt, QSettings, QSize, QPoint, QUrl, QLine, QDateTime +from platform import python_version if IsPySide2: from PySide2.QtCore import QStandardPaths from syncplay import utils, constants, version, release_number @@ -106,33 +107,36 @@ class AboutDialog(QtWidgets.QDialog): super(AboutDialog, self).__init__(parent) if isMacOS(): self.setWindowTitle("") + self.setWindowFlags(Qt.Dialog | Qt.WindowTitleHint | Qt.WindowCloseButtonHint | Qt.CustomizeWindowHint) else: self.setWindowTitle(getMessage("about-dialog-title")) if isWindows(): self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) nameLabel = QtWidgets.QLabel("
Syncplay
") - nameLabel.setFont(QtGui.QFont("Helvetica", 20)) + nameLabel.setFont(QtGui.QFont("Helvetica", 18)) linkLabel = QtWidgets.QLabel("
syncplay.pl
") linkLabel.setOpenExternalLinks(True) - versionLabel = QtWidgets.QLabel("
" + getMessage("about-dialog-release").format(version, release_number, __binding__) + "
") - licenseLabel = QtWidgets.QLabel("

Copyright © 2017 Syncplay

" + getMessage("about-dialog-license-text") + "

") + versionLabel = QtWidgets.QLabel("

" + getMessage("about-dialog-release").format(version, release_number) + "
Python " + python_version() + " - " + __binding__ + " " + __binding_version__ + " - Qt " + __qt_version__ + "

") + #versionLabel = QtWidgets.QLabel("

Version 1.5.4 release 62
Python 3.4.5 - PySide 1.2.4 - Qt 4.8.7

") + licenseLabel = QtWidgets.QLabel("

Copyright © 2012–2018 Syncplay

" + getMessage("about-dialog-license-text") + "

") aboutIconPixmap = QtGui.QPixmap(resourcespath + "syncplay.png") aboutIconLabel = QtWidgets.QLabel() - aboutIconLabel.setPixmap(aboutIconPixmap.scaled(120, 120, Qt.KeepAspectRatio)) + aboutIconLabel.setPixmap(aboutIconPixmap.scaled(65, 65, Qt.KeepAspectRatio)) aboutLayout = QtWidgets.QGridLayout() - aboutLayout.addWidget(aboutIconLabel, 0, 0, 4, 2) - aboutLayout.addWidget(nameLabel, 0, 2, 1, 2) - aboutLayout.addWidget(linkLabel, 1, 2, 1, 2) - aboutLayout.addWidget(versionLabel, 2, 2, 1, 2) - aboutLayout.addWidget(licenseLabel, 3, 2, 1, 2) + aboutLayout.addWidget(aboutIconLabel, 0, 0, 3, 4, Qt.AlignHCenter) + aboutLayout.addWidget(nameLabel, 3, 0, 1, 4) + aboutLayout.addWidget(linkLabel, 4, 0, 1, 4) + aboutLayout.addWidget(versionLabel, 5, 0, 1, 4) + aboutLayout.addWidget(licenseLabel, 6, 0, 1, 4) licenseButton = QtWidgets.QPushButton(getMessage("about-dialog-license-button")) licenseButton.setAutoDefault(False) licenseButton.clicked.connect(self.openLicense) - aboutLayout.addWidget(licenseButton, 4, 2) + aboutLayout.addWidget(licenseButton, 7, 0, 1, 2) dependenciesButton = QtWidgets.QPushButton(getMessage("about-dialog-dependencies")) dependenciesButton.setAutoDefault(False) dependenciesButton.clicked.connect(self.openDependencies) - aboutLayout.addWidget(dependenciesButton, 4, 3) + aboutLayout.addWidget(dependenciesButton, 7, 2, 1, 2) + aboutLayout.setVerticalSpacing(10) aboutLayout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) self.setSizeGripEnabled(False) self.setLayout(aboutLayout) From e3dd4e68e9e325abb037b069d4a2156d9321c3f2 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Sun, 17 Jun 2018 14:41:09 +0200 Subject: [PATCH 69/95] Fix third-party-notices and add qt5reactor license --- resources/third-party-notices.rtf | 104 +++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 3 deletions(-) diff --git a/resources/third-party-notices.rtf b/resources/third-party-notices.rtf index 436ba5f..cdb4d2e 100644 --- a/resources/third-party-notices.rtf +++ b/resources/third-party-notices.rtf @@ -1,4 +1,4 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1504\cocoasubrtf830 +{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf400 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} @@ -24,6 +24,27 @@ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ +\b PySide\ + +\b0 \ +Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).\ +Contact: PySide team \ +\ +This library is free software; you can redistribute it and/or\ +modify it under the terms of the GNU Lesser General Public\ +License as published by the Free Software Foundation; either\ +version 2.1 of the License, or (at your option) any later version.\ +This library is distributed in the hope that it will be useful,\ +but WITHOUT ANY WARRANTY; without even the implied warranty of\ +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\ +Lesser General Public License for more details.\ +\ +\pard\pardeftab720\partightenfactor0 +\cf0 You should have received a copy of the GNU Lesser General Public License\ +along with this program. If not, see \ +\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 +\cf0 \ + \b Qt for Python\ \b0 \ @@ -47,7 +68,7 @@ along with this program. If not, see .\ \b Qt \b0 \ \ -This program uses Qt versions 5.6 and 5.11 under the GNU LGPL version 3.\ +This program uses Qt under the GNU LGPL version 3.\ \ Qt is a C++ toolkit for cross-platform application development.\ \ @@ -138,6 +159,83 @@ The above copyright notice and this permission notice shall be\ included in all copies or substantial portions of the Software.\ \b \ +qt5reactor\ +\ + +\b0 Copyright (c) 2001-2018\ +Allen Short\ +Andy Gayton\ +Andrew Bennetts\ +Antoine Pitrou\ +Apple Computer, Inc.\ +Ashwini Oruganti\ +bakbuk\ +Benjamin Bruheim\ +Bob Ippolito\ +Burak Nehbit\ +Canonical Limited\ +Christopher Armstrong\ +Christopher R. Wood\ +David Reid\ +Donovan Preston\ +Elvis Stansvik\ +Eric Mangold\ +Eyal Lotem\ +Glenn Tarbox\ +Google Inc.\ +Hybrid Logic Ltd.\ +Hynek Schlawack\ +Itamar Turner-Trauring\ +James Knight\ +Jason A. Mobarak\ +Jean-Paul Calderone\ +Jessica McKellar\ +Jonathan Jacobs\ +Jonathan Lange\ +Jonathan D. Simms\ +J\'fcrgen Hermann\ +Julian Berman\ +Kevin Horn\ +Kevin Turner\ +Kyle Altendorf\ +Laurens Van Houtven\ +Mary Gardiner\ +Matthew Lefkowitz\ +Massachusetts Institute of Technology\ +Moshe Zadka\ +Paul Swartz\ +Pavel Pergamenshchik\ +Ralph Meijer\ +Richard Wall\ +Sean Riley\ +Software Freedom Conservancy\ +Tarashish Mishra\ +Travis B. Hartwell\ +Thijs Triemstra\ +Thomas Herve\ +Timothy Allen\ +Tom Prince\ +\ +Permission is hereby granted, free of charge, to any person obtaining\ +a copy of this software and associated documentation files (the\ +"Software"), to deal in the Software without restriction, including\ +without limitation the rights to use, copy, modify, merge, publish,\ +distribute, sublicense, and/or sell copies of the Software, and to\ +permit persons to whom the Software is furnished to do so, subject to\ +the following conditions:\ +\ +The above copyright notice and this permission notice shall be\ +included in all copies or substantial portions of the Software.\ +\ +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\ +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\ +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\ +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\ +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\ +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\ +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +\b \ +\ appnope\ \b0 \ @@ -222,4 +320,4 @@ http://www.apache.org/licenses/LICENSE-2.0\ Unless required by applicable law or agreed to in writing, software distributed under the \ License is distributed on an \'93AS IS\'94 BASIS, WITHOUT WARRANTIES OR CONDI-\ TIONS OF ANY KIND, either express or implied. See the License for the specific lang-\ -uage governing permissions and limitations under the License.} \ No newline at end of file +uage governing permissions and limitations under the License.} From 11220e1df49a2fb7dfd4032bb03f595188d8178f Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Sun, 17 Jun 2018 15:24:46 +0200 Subject: [PATCH 70/95] Set 10.11.0 as minimum macOS version in the .app bundle --- buildPy2app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/buildPy2app.py b/buildPy2app.py index 0821adf..13c274a 100644 --- a/buildPy2app.py +++ b/buildPy2app.py @@ -22,7 +22,8 @@ OPTIONS = { 'CFBundleName':'Syncplay', 'CFBundleShortVersionString':syncplay.version, 'CFBundleIdentifier':'pl.syncplay.Syncplay', - 'NSHumanReadableCopyright': '@ 2017 Syncplay All Rights Reserved' + 'LSMinimumSystemVersion':'10.11.0', + 'NSHumanReadableCopyright': '@ 2018 Syncplay All Rights Reserved' } } From 6b515e671c33458d736c6639a8b3391f94dfee73 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Sun, 17 Jun 2018 15:56:29 +0200 Subject: [PATCH 71/95] Set 10.11.0 as minimum macOS version in the .app bundle --- buildPy2app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/buildPy2app.py b/buildPy2app.py index 0821adf..13c274a 100755 --- a/buildPy2app.py +++ b/buildPy2app.py @@ -22,7 +22,8 @@ OPTIONS = { 'CFBundleName':'Syncplay', 'CFBundleShortVersionString':syncplay.version, 'CFBundleIdentifier':'pl.syncplay.Syncplay', - 'NSHumanReadableCopyright': '@ 2017 Syncplay All Rights Reserved' + 'LSMinimumSystemVersion':'10.11.0', + 'NSHumanReadableCopyright': '@ 2018 Syncplay All Rights Reserved' } } From 2ddc756a61a21dc68da07fee33e01def0f2b989c Mon Sep 17 00:00:00 2001 From: albertosottile Date: Mon, 18 Jun 2018 17:02:03 +0200 Subject: [PATCH 72/95] AppVeyor: migrate from conda to native Python --- .appveyor.yml | 41 +++++++++++++++++++++-------------------- buildPy2exe.py | 10 +++++----- syncplayClient.py | 6 +++--- syncplayServer.py | 6 +++--- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index b977262..88831d7 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,43 +1,44 @@ environment: MINICONDA: "C:\\Miniconda" + PYTHON: "C:\\Python34" + PYTHON_VERSION: 3.4 + PYTHON_ARCH: 32 platform: x86 configuration: Release init: - - set PATH=C:\Miniconda;C:\Miniconda\Scripts;C:\Program Files (x86)\NSIS;%PATH% - - conda create -n syncplay -y - - activate syncplay - - conda install python=3.5 -y - - conda install pywin32 -y - - conda install -c conda-forge pyside2 -y - - pip install twisted pyinstaller zope.interface - - type nul > C:\Miniconda\envs\syncplay\lib\site-packages\zope\__init__.py - - type nul > C:\Miniconda\envs\syncplay\lib\site-packages\pyinstaller\loader\rthooks\pyi_rth_twisted.py + - set PATH=%PYTHON%;%PYTHON%\Scripts;C:\Program Files (x86)\NSIS;%PATH% + - python --version + - python -m site + - python -m pip install -U pip setuptools wheel + - pip install -U pypiwin32==219 + - pip install pyside + - pip install twisted + - pip install py2exe + - pip install zope.interface + - type nul > C:\Python34\lib\site-packages\zope\__init__.py - pip freeze - - conda list install: - cd %APPVEYOR_BUILD_FOLDER% - for /F "tokens=2 delims='" %%a in ('findstr version syncplay\__init__.py') do @set ver=%%a - - pyinstaller pyinstaller-client.spec - - pyinstaller pyinstaller-server.spec - - python buildInstaller.py - - copy Syncplay-%ver%-Setup.exe Syncplay-%ver%-Setup-Py35.exe - - type nul > dist\syncplay.ini - - copy resources\win_lua_note.txt dist\"VLC LUA Script installation.txt" + - python buildPy2exe.py + - copy Syncplay-%ver%-Setup.exe Syncplay-%ver%-Setup-Py34.exe + - type nul > syncplay_v%ver%\syncplay.ini + - copy resources\win_lua_note.txt syncplay_v%ver%\"VLC LUA Script installation.txt" # Not a project with an msbuild file, build done at install. build: off artifacts: - - path: 'dist' + - path: 'syncplay_v$(ver)' type: zip - name: Syncplay_$(ver)_Portable_py35 + name: Syncplay_$(ver)_Portable_py34 - - path: Syncplay-$(ver)-Setup-Py35.exe - name: Syncplay-$(ver)-Setup-Py35 + - path: Syncplay-$(ver)-Setup-Py34.exe + name: Syncplay-$(ver)-Setup-Py34 # Push artefact to S3 bucket and list all before_deploy: diff --git a/buildPy2exe.py b/buildPy2exe.py index 721a442..dc5b42f 100755 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -18,7 +18,7 @@ from distutils.core import setup try: from py2exe.build_exe import py2exe except ImportError: - from py2exe.distutils_build_exe import py2exe + from py2exe.distutils_buildexe import py2exe from string import Template import syncplay @@ -629,7 +629,7 @@ class NSISScript(object): totalSize = totalSize, ) with codecs.open(SETUP_SCRIPT_PATH, "w", "utf-8-sig") as outfile: - outfile.write(contents.decode('utf-8')) + outfile.write(contents) def compile(self): if not os.path.isfile(NSIS_COMPILE): @@ -720,8 +720,8 @@ info = dict( # console=['syncplayServer.py', {"script":"syncplayClient.py", "icon_resources":[(1, "resources\\icon.ico")], 'dest_base': "Syncplay"}], options={'py2exe': { 'dist_dir': OUT_DIR, - 'packages': 'PySide2.QtUiTools', - 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide2, liburl, ast, unicodedata, _ssl', + 'packages': 'PySide.QtUiTools', + 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide, liburl, ast, unicodedata, _ssl', 'excludes': 'venv, doctest, pdb, unittest, win32clipboard, win32file, win32pdh, win32security, win32trace, win32ui, winxpgui, win32pipe, win32process, Tkinter', 'dll_excludes': 'msvcr71.dll, MSVCP90.dll, POWRPROF.dll', 'optimize': 2, @@ -733,5 +733,5 @@ info = dict( cmdclass = {"py2exe": build_installer}, ) -sys.argv.extend(['py2exe', '-p win32com ', '-i twisted.web.resource', '-i PySide2.QtCore', '-p PySide2.QtGui', '-i PySide2.QtWidgets']) +sys.argv.extend(['py2exe', '-p win32com ', '-i twisted.web.resource', '-i PySide.QtCore', '-i PySide.QtGui']) setup(**info) diff --git a/syncplayClient.py b/syncplayClient.py index 6ad0b89..5effaf8 100755 --- a/syncplayClient.py +++ b/syncplayClient.py @@ -5,11 +5,11 @@ import site, sys # libpath try: - if (sys.version_info.major != 3) or (sys.version_info.minor < 5): - raise Exception("You must run Syncplay with Python 3.5 or newer!") + if (sys.version_info.major != 3) or (sys.version_info.minor < 4): + raise Exception("You must run Syncplay with Python 3.4 or newer!") except AttributeError: import warnings - warnings.warn("You must run Syncplay with Python 3.5 or newer!") + warnings.warn("You must run Syncplay with Python 3.4 or newer!") from syncplay.clientManager import SyncplayClientManager from syncplay.utils import blackholeStdoutForFrozenWindow diff --git a/syncplayServer.py b/syncplayServer.py index 7cc3ac6..b2a2fa8 100755 --- a/syncplayServer.py +++ b/syncplayServer.py @@ -6,11 +6,11 @@ import site, sys # libpath try: - if (sys.version_info.major != 3) or (sys.version_info.minor < 5): - raise Exception("You must run Syncplay with Python 3.5 or newer!") + if (sys.version_info.major != 3) or (sys.version_info.minor < 4): + raise Exception("You must run Syncplay with Python 3.4 or newer!") except AttributeError: import warnings - warnings.warn("You must run Syncplay with Python 3.5 or newer!") + warnings.warn("You must run Syncplay with Python 3.4 or newer!") from twisted.internet import reactor From 7d088b30e4d8c9945b7fdab07ae5c3f0c279f4d7 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Mon, 18 Jun 2018 22:12:31 +0200 Subject: [PATCH 73/95] Bundle syncplayServer in py2app --- buildPy2app.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/buildPy2app.py b/buildPy2app.py index 13c274a..8e4d14a 100644 --- a/buildPy2app.py +++ b/buildPy2app.py @@ -14,17 +14,18 @@ DATA_FILES = [ ('resources', glob('resources/*.png') + glob('resources/*.rtf') + glob('resources/*.lua')), ] OPTIONS = { - 'iconfile':'resources/icon.icns', - 'includes': {'PySide2.QtCore', 'PySide2.QtUiTools', 'PySide2.QtGui','PySide2.QtWidgets', 'certifi'}, + 'iconfile':'resources/icon.icns', + 'extra_scripts': 'syncplayServer.py', + 'includes': {'PySide2.QtCore', 'PySide2.QtUiTools', 'PySide2.QtGui','PySide2.QtWidgets', 'certifi'}, 'excludes': {'PySide', 'PySide.QtCore', 'PySide.QtUiTools', 'PySide.QtGui'}, 'qt_plugins': ['platforms/libqcocoa.dylib', 'platforms/libqminimal.dylib','platforms/libqoffscreen.dylib', 'styles/libqmacstyle.dylib'], - 'plist': { - 'CFBundleName':'Syncplay', - 'CFBundleShortVersionString':syncplay.version, - 'CFBundleIdentifier':'pl.syncplay.Syncplay', - 'LSMinimumSystemVersion':'10.11.0', - 'NSHumanReadableCopyright': '@ 2018 Syncplay All Rights Reserved' - } + 'plist': { + 'CFBundleName':'Syncplay', + 'CFBundleShortVersionString':syncplay.version, + 'CFBundleIdentifier':'pl.syncplay.Syncplay', + 'LSMinimumSystemVersion':'10.11.0', + 'NSHumanReadableCopyright': '@ 2018 Syncplay All Rights Reserved' + } } setup( From 66993f6fbab01bb516c234f0eade4418485e295f Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Tue, 19 Jun 2018 22:52:12 +0200 Subject: [PATCH 74/95] AppVeyor: Set PYTHONPATH and PYTHONHOME env variables --- .appveyor.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 88831d7..b85d68b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,9 +9,10 @@ platform: x86 configuration: Release init: - - set PATH=%PYTHON%;%PYTHON%\Scripts;C:\Program Files (x86)\NSIS;%PATH% + - set PYTHONPATH=%PYTHON% + - set PYTHONHOME=%PYTHON% + - set PATH=%PYTHON%\Scripts;%PYTHON%;C:\Program Files (x86)\NSIS;%PATH% - python --version - - python -m site - python -m pip install -U pip setuptools wheel - pip install -U pypiwin32==219 - pip install pyside From 450a3eb103c05f621df63067d7f4625940b74bf1 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Tue, 19 Jun 2018 23:24:24 +0200 Subject: [PATCH 75/95] Remove import site --- syncplayClient.py | 2 +- syncplayServer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplayClient.py b/syncplayClient.py index 5effaf8..478b5be 100755 --- a/syncplayClient.py +++ b/syncplayClient.py @@ -1,6 +1,6 @@ #!/usr/bin/env python2 -import site, sys +import sys # libpath diff --git a/syncplayServer.py b/syncplayServer.py index b2a2fa8..2522d2f 100755 --- a/syncplayServer.py +++ b/syncplayServer.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 #coding:utf8 -import site, sys +import sys # libpath From 78ad9fb04bdf6a9f67b57d871157488122051f38 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Tue, 19 Jun 2018 23:39:39 +0200 Subject: [PATCH 76/95] Enable Travis --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 31ff06e..8f3b2e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ osx_image: xcode7.3 branches: only: - - py3-qtpy-pyside2 + - py34 script: - python3 buildPy2app.py py2app @@ -44,7 +44,7 @@ before_deploy: deploy: skip_cleanup: true - on: py3-qtpy-pyside2 + on: py34 provider: bintray file: "bintray.json" user: alby128 From 0afac4ab3971c66dfacabed8e89b8de594030dc4 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Wed, 20 Jun 2018 00:08:40 +0200 Subject: [PATCH 77/95] Bundle syncplayServer in py2app --- buildPy2app.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/buildPy2app.py b/buildPy2app.py index 13c274a..69aabdd 100755 --- a/buildPy2app.py +++ b/buildPy2app.py @@ -14,17 +14,18 @@ DATA_FILES = [ ('resources', glob('resources/*.png') + glob('resources/*.rtf') + glob('resources/*.lua')), ] OPTIONS = { - 'iconfile':'resources/icon.icns', - 'includes': {'PySide2.QtCore', 'PySide2.QtUiTools', 'PySide2.QtGui','PySide2.QtWidgets', 'certifi'}, + 'iconfile':'resources/icon.icns', + 'extra_scripts': 'syncplayServer.py', + 'includes': {'PySide2.QtCore', 'PySide2.QtUiTools', 'PySide2.QtGui','PySide2.QtWidgets', 'certifi'}, 'excludes': {'PySide', 'PySide.QtCore', 'PySide.QtUiTools', 'PySide.QtGui'}, 'qt_plugins': ['platforms/libqcocoa.dylib', 'platforms/libqminimal.dylib','platforms/libqoffscreen.dylib', 'styles/libqmacstyle.dylib'], - 'plist': { - 'CFBundleName':'Syncplay', - 'CFBundleShortVersionString':syncplay.version, - 'CFBundleIdentifier':'pl.syncplay.Syncplay', - 'LSMinimumSystemVersion':'10.11.0', - 'NSHumanReadableCopyright': '@ 2018 Syncplay All Rights Reserved' - } + 'plist': { + 'CFBundleName':'Syncplay', + 'CFBundleShortVersionString':syncplay.version, + 'CFBundleIdentifier':'pl.syncplay.Syncplay', + 'LSMinimumSystemVersion':'10.11.0', + 'NSHumanReadableCopyright': '@ 2018 Syncplay All Rights Reserved' + } } setup( From 3d30a78409cda40f1ea736c9d01d240d7595dc9b Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 20 Jun 2018 15:15:45 +0200 Subject: [PATCH 78/95] Fix settings crash when switching back from Python 3 versions --- syncplay/utils.py | 7 +++++++ 1 file changed, 7 insertions(+) mode change 100644 => 100755 syncplay/utils.py diff --git a/syncplay/utils.py b/syncplay/utils.py old mode 100644 new mode 100755 index 5ad8b6c..24e207c --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -339,7 +339,14 @@ def getPlayerArgumentsByPathAsText(arguments, path): argsToReturn = getPlayerArgumentsByPathAsArray(arguments, path) return " ".join(argsToReturn) if argsToReturn else "" +def unicodeReplaceFromFuture(s): + return ''.join(chr(ord(c)) for c in s).decode('utf-8') + def getListAsMultilineString(pathArray): + try: + pathArray = map(unicodeReplaceFromFuture, pathArray) + except: + pass return u"\n".join(pathArray) if pathArray else "" def convertMultilineStringToList(multilineString): From aa7a9e7a8eb0d62153f456c87ac967d02dc169c4 Mon Sep 17 00:00:00 2001 From: Etoh Date: Thu, 5 Jul 2018 10:30:11 +0100 Subject: [PATCH 79/95] Set recent client threshold to 1.5.4 --- syncplay/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index f999ed0..5efa046 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -9,7 +9,7 @@ MPLAYER_OSD_LEVEL = 1 UI_TIME_FORMAT = "[%X] " CONFIG_NAMES = [".syncplay", "syncplay.ini"] #Syncplay searches first to last DEFAULT_CONFIG_NAME = "syncplay.ini" -RECENT_CLIENT_THRESHOLD = "1.5.3" #This and higher considered 'recent' clients (no warnings) +RECENT_CLIENT_THRESHOLD = "1.5.4" #This and higher considered 'recent' clients (no warnings) WARN_OLD_CLIENTS = True #Use MOTD to inform old clients to upgrade LIST_RELATIVE_CONFIGS = True # Print list of relative configs loaded SHOW_CONTACT_INFO = True # Displays dev contact details below list in GUI From 78e7ae3fff27a726841c5e28911d9b18bbecff8a Mon Sep 17 00:00:00 2001 From: Etoh Date: Thu, 5 Jul 2018 10:31:52 +0100 Subject: [PATCH 80/95] Upver to release 63 (1.5.4) --- syncplay/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 874d028..1e42918 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ version = '1.5.4' milestone = 'Yoitsu' -release_number = '62' +release_number = '63' projectURL = 'https://syncplay.pl/' From ab94ef7dadfcd91ed895c403c785014f7ac56daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Wr=C3=B3bel?= Date: Fri, 6 Jul 2018 20:22:20 +0200 Subject: [PATCH 81/95] Fixed reference to missing constant on Linux --- syncplay/ui/ConfigurationGetter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index 068d321..7d018fa 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -438,7 +438,7 @@ class ConfigurationGetter(object): for location in locations: for name in constants.CONFIG_NAMES: path = location + os.path.sep + name - if os.path.isfile(path) and (os.name == 'nt' or path != os.path.join(os.getenv('HOME', '.'), constants.DEFAULT_CONFIG_NAME_LINUX)): + if os.path.isfile(path) and (os.name == 'nt' or path != os.path.join(os.getenv('HOME', '.'), name)): loadedPaths.append(u"'{}'".format(os.path.normpath(path))) self._parseConfigFile(path, createConfig=False) self._checkConfig() From 76a88bd191f747953d9a8eac2e88171db97ae6aa Mon Sep 17 00:00:00 2001 From: Etoh Date: Sat, 7 Jul 2018 19:18:05 +0100 Subject: [PATCH 82/95] Upver to release 1.5.5 / build 64 --- syncplay/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 1e42918..471e505 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ -version = '1.5.4' +version = '1.5.5' milestone = 'Yoitsu' -release_number = '63' +release_number = '64' projectURL = 'https://syncplay.pl/' From 45ccd01ba5a36d75a11228a98fe2b40e04e22f9c Mon Sep 17 00:00:00 2001 From: Etoh Date: Sat, 7 Jul 2018 19:32:04 +0100 Subject: [PATCH 83/95] Update recent client threshold to 1.5.5 --- syncplay/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index 5efa046..85bc966 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -9,7 +9,7 @@ MPLAYER_OSD_LEVEL = 1 UI_TIME_FORMAT = "[%X] " CONFIG_NAMES = [".syncplay", "syncplay.ini"] #Syncplay searches first to last DEFAULT_CONFIG_NAME = "syncplay.ini" -RECENT_CLIENT_THRESHOLD = "1.5.4" #This and higher considered 'recent' clients (no warnings) +RECENT_CLIENT_THRESHOLD = "1.5.5" #This and higher considered 'recent' clients (no warnings) WARN_OLD_CLIENTS = True #Use MOTD to inform old clients to upgrade LIST_RELATIVE_CONFIGS = True # Print list of relative configs loaded SHOW_CONTACT_INFO = True # Displays dev contact details below list in GUI From e9dde392ed68dfd5fdba20308712068f9ef7b895 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Sun, 8 Jul 2018 11:51:20 +0200 Subject: [PATCH 84/95] Fixed reference to missing constant on Linux --- syncplay/ui/ConfigurationGetter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index a4fa113..0df8650 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -438,7 +438,7 @@ class ConfigurationGetter(object): for location in locations: for name in constants.CONFIG_NAMES: path = location + os.path.sep + name - if os.path.isfile(path) and (os.name == 'nt' or path != os.path.join(os.getenv('HOME', '.'), constants.DEFAULT_CONFIG_NAME_LINUX)): + if os.path.isfile(path) and (os.name == 'nt' or path != os.path.join(os.getenv('HOME', '.'), name)): loadedPaths.append("'{}'".format(os.path.normpath(path))) self._parseConfigFile(path, createConfig=False) self._checkConfig() From f12d030c8867a832956d240efa0be3b82190b893 Mon Sep 17 00:00:00 2001 From: Etoh Date: Sat, 7 Jul 2018 19:18:05 +0100 Subject: [PATCH 85/95] Upver to release 1.5.5 / build 64 --- syncplay/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 874d028..471e505 100755 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ -version = '1.5.4' +version = '1.5.5' milestone = 'Yoitsu' -release_number = '62' +release_number = '64' projectURL = 'https://syncplay.pl/' From ca0074331b8c04b1e5c64afb60b83f0506114cc1 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Sun, 8 Jul 2018 11:50:05 +0200 Subject: [PATCH 86/95] Update recent client threshold to 1.5.5 --- syncplay/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index 4a60c5e..7ae6343 100755 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -9,7 +9,7 @@ MPLAYER_OSD_LEVEL = 1 UI_TIME_FORMAT = "[%X] " CONFIG_NAMES = [".syncplay", "syncplay.ini"] #Syncplay searches first to last DEFAULT_CONFIG_NAME = "syncplay.ini" -RECENT_CLIENT_THRESHOLD = "1.5.3" #This and higher considered 'recent' clients (no warnings) +RECENT_CLIENT_THRESHOLD = "1.5.5" #This and higher considered 'recent' clients (no warnings) WARN_OLD_CLIENTS = True #Use MOTD to inform old clients to upgrade LIST_RELATIVE_CONFIGS = True # Print list of relative configs loaded SHOW_CONTACT_INFO = True # Displays dev contact details below list in GUI From 29760ba9d1aeb092bf6392980da86ee097add1c0 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Sun, 8 Jul 2018 11:58:27 +0200 Subject: [PATCH 87/95] Add revision identifier in __init__ and about dialog --- syncplay/__init__.py | 1 + syncplay/ui/gui.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 471e505..123b84a 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,5 @@ version = '1.5.5' +revision = '' milestone = 'Yoitsu' release_number = '64' projectURL = 'https://syncplay.pl/' diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 7e5d1ba..4cc46e5 100755 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -4,7 +4,7 @@ from syncplay.vendor.Qt.QtCore import Qt, QSettings, QSize, QPoint, QUrl, QLine, from platform import python_version if IsPySide2: from PySide2.QtCore import QStandardPaths -from syncplay import utils, constants, version, release_number +from syncplay import utils, constants, version, revision, release_number from syncplay.messages import getMessage from syncplay.utils import resourcespath import sys @@ -116,7 +116,8 @@ class AboutDialog(QtWidgets.QDialog): nameLabel.setFont(QtGui.QFont("Helvetica", 18)) linkLabel = QtWidgets.QLabel("
syncplay.pl
") linkLabel.setOpenExternalLinks(True) - versionLabel = QtWidgets.QLabel("

" + getMessage("about-dialog-release").format(version, release_number) + "
Python " + python_version() + " - " + __binding__ + " " + __binding_version__ + " - Qt " + __qt_version__ + "

") + versionExtString = version + revision + versionLabel = QtWidgets.QLabel("

" + getMessage("about-dialog-release").format(versionExtString, release_number) + "
Python " + python_version() + " - " + __binding__ + " " + __binding_version__ + " - Qt " + __qt_version__ + "

") licenseLabel = QtWidgets.QLabel("

Copyright © 2012–2018 Syncplay

" + getMessage("about-dialog-license-text") + "

") aboutIconPixmap = QtGui.QPixmap(resourcespath + u"syncplay.png") aboutIconLabel = QtWidgets.QLabel() From 74974438f078c04aa00a371b863b0ca46148ab46 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Sun, 8 Jul 2018 12:14:46 +0200 Subject: [PATCH 88/95] Add revision identifier in __init__ and about dialog --- syncplay/__init__.py | 1 + syncplay/ui/gui.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 471e505..123b84a 100755 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,5 @@ version = '1.5.5' +revision = '' milestone = 'Yoitsu' release_number = '64' projectURL = 'https://syncplay.pl/' diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index eb93766..5c599f7 100755 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -4,7 +4,7 @@ from syncplay.vendor.Qt.QtCore import Qt, QSettings, QSize, QPoint, QUrl, QLine, from platform import python_version if IsPySide2: from PySide2.QtCore import QStandardPaths -from syncplay import utils, constants, version, release_number +from syncplay import utils, constants, version, revision, release_number from syncplay.messages import getMessage from syncplay.utils import resourcespath import sys @@ -116,7 +116,8 @@ class AboutDialog(QtWidgets.QDialog): nameLabel.setFont(QtGui.QFont("Helvetica", 18)) linkLabel = QtWidgets.QLabel("
syncplay.pl
") linkLabel.setOpenExternalLinks(True) - versionLabel = QtWidgets.QLabel("

" + getMessage("about-dialog-release").format(version, release_number) + "
Python " + python_version() + " - " + __binding__ + " " + __binding_version__ + " - Qt " + __qt_version__ + "

") + versionExtString = version + revision + versionLabel = QtWidgets.QLabel("

" + getMessage("about-dialog-release").format(versionExtString, release_number) + "
Python " + python_version() + " - " + __binding__ + " " + __binding_version__ + " - Qt " + __qt_version__ + "

") #versionLabel = QtWidgets.QLabel("

Version 1.5.4 release 62
Python 3.4.5 - PySide 1.2.4 - Qt 4.8.7

") licenseLabel = QtWidgets.QLabel("

Copyright © 2012–2018 Syncplay

" + getMessage("about-dialog-license-text") + "

") aboutIconPixmap = QtGui.QPixmap(resourcespath + "syncplay.png") From 8ba2774f0da79a68d56bc3beeff98218d63409f8 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Sun, 8 Jul 2018 12:17:57 +0200 Subject: [PATCH 89/95] Force Python 3.6 formula from homebrew history --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8f3b2e3..702e978 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,10 @@ script: before_install: - hg clone https://alby128@bitbucket.org/alby128/py2app +- brew uninstall python - brew update -- brew upgrade python +- curl -O https://raw.githubusercontent.com/Homebrew/homebrew-core/f2a764ef944b1080be64bd88dca9a1d80130c558/Formula/python.rb +- brew install ./python.rb - which python3 - python3 --version - which pip3 From 3c6241b5a13d980b31ccd463eee90babaa9fe11b Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Mon, 9 Jul 2018 23:22:17 +0200 Subject: [PATCH 90/95] Upver to 1.5.6 / build 65 --- .travis.yml | 4 ++-- syncplay/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 702e978..7962616 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ osx_image: xcode7.3 branches: only: - - py34 + - master_local script: - python3 buildPy2app.py py2app @@ -46,7 +46,7 @@ before_deploy: deploy: skip_cleanup: true - on: py34 + on: master_local provider: bintray file: "bintray.json" user: alby128 diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 123b84a..ac03334 100755 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,5 +1,5 @@ -version = '1.5.5' +version = '1.5.6' revision = '' milestone = 'Yoitsu' -release_number = '64' +release_number = '65' projectURL = 'https://syncplay.pl/' From cb5cca32cd5d7ca3535a04d83cbc9d53e74c0ba9 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Mon, 9 Jul 2018 23:34:58 +0200 Subject: [PATCH 91/95] Switch to production file names on both CI services --- .appveyor.yml | 7 +++---- .travis.yml | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index b85d68b..fd144bd 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -26,7 +26,6 @@ install: - cd %APPVEYOR_BUILD_FOLDER% - for /F "tokens=2 delims='" %%a in ('findstr version syncplay\__init__.py') do @set ver=%%a - python buildPy2exe.py - - copy Syncplay-%ver%-Setup.exe Syncplay-%ver%-Setup-Py34.exe - type nul > syncplay_v%ver%\syncplay.ini - copy resources\win_lua_note.txt syncplay_v%ver%\"VLC LUA Script installation.txt" @@ -36,10 +35,10 @@ build: off artifacts: - path: 'syncplay_v$(ver)' type: zip - name: Syncplay_$(ver)_Portable_py34 + name: Syncplay_$(ver)_Portable - - path: Syncplay-$(ver)-Setup-Py34.exe - name: Syncplay-$(ver)-Setup-Py34 + - path: Syncplay-$(ver)-Setup.exe + name: Syncplay-$(ver)-Setup # Push artefact to S3 bucket and list all before_deploy: diff --git a/.travis.yml b/.travis.yml index 7962616..c34a8fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ before_deploy: - mv resources/lua/intf/syncplay.lua resources/lua/intf/.syncplay.lua - mv resources/macOS_readme.pdf resources/.macOS_readme.pdf - export VER="$(cat syncplay/__init__.py | awk '/version/ {gsub("\047", "", $3); print $NF}')" -- dmgbuild -s appdmg.py "Syncplay" dist_dmg/Syncplay_${VER}_macOS_py36.dmg +- dmgbuild -s appdmg.py "Syncplay" dist_dmg/Syncplay_${VER}.dmg deploy: skip_cleanup: true From d8d389bbf88240786c881eb7a74f3c04950f111e Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Tue, 17 Jul 2018 23:32:49 +0200 Subject: [PATCH 92/95] Fixed https in InternetShortcut URL in buildPy2exe.py --- buildPy2exe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildPy2exe.py b/buildPy2exe.py index 8767d9c..8b643d6 100755 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -439,7 +439,7 @@ NSIS_SCRIPT_TEMPLATE = r""" CreateShortCut "$$SMPROGRAMS\Syncplay\Syncplay.lnk" "$$INSTDIR\Syncplay.exe" "" CreateShortCut "$$SMPROGRAMS\Syncplay\Syncplay Server.lnk" "$$INSTDIR\syncplayServer.exe" "" CreateShortCut "$$SMPROGRAMS\Syncplay\Uninstall.lnk" "$$INSTDIR\Uninstall.exe" "" - WriteINIStr "$$SMPROGRAMS\Syncplay\SyncplayWebsite.url" "InternetShortcut" "URL" "http://syncplay.pl" + WriteINIStr "$$SMPROGRAMS\Syncplay\SyncplayWebsite.url" "InternetShortcut" "URL" "https://syncplay.pl" $${EndIf} $${If} $$CheckBox_DesktopShortcut_State == $${BST_CHECKED} From 4367301a9e3f3fbf91d9e107d793f7316e099ee1 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 18 Jul 2018 16:53:34 +0200 Subject: [PATCH 93/95] Update vendored Qt.py to version 1.1.0 --- syncplay/vendor/Qt.py | 144 +++++++++++++++++++++++++++++++++++------- 1 file changed, 120 insertions(+), 24 deletions(-) diff --git a/syncplay/vendor/Qt.py b/syncplay/vendor/Qt.py index 2bc6693..f867f25 100755 --- a/syncplay/vendor/Qt.py +++ b/syncplay/vendor/Qt.py @@ -43,7 +43,7 @@ import types import shutil -__version__ = "1.1.0.b5" +__version__ = "1.1.0" # Enable support for `from Qt import *` __all__ = [] @@ -58,7 +58,7 @@ Qt = sys.modules[__name__] Qt.QtCompat = types.ModuleType("QtCompat") try: - int + long except NameError: # Python 3 compatibility long = int @@ -110,6 +110,7 @@ _common_members = { "QGenericArgument", "QGenericReturnArgument", "QHistoryState", + "QItemSelectionRange", "QIODevice", "QLibraryInfo", "QLine", @@ -393,6 +394,16 @@ _common_members = { "QGLFormat", "QGLWidget" ], + "QtPrintSupport": [ + "QAbstractPrintDialog", + "QPageSetupDialog", + "QPrintDialog", + "QPrintEngine", + "QPrintPreviewDialog", + "QPrintPreviewWidget", + "QPrinter", + "QPrinterInfo" + ], "QtSql": [ "QSql", "QSqlDatabase", @@ -611,6 +622,9 @@ _common_members = { "QWizard", "QWizardPage" ], + "QtX11Extras": [ + "QX11Info" + ], "QtXml": [ "QDomAttr", "QDomCDATASection", @@ -671,7 +685,7 @@ These members from the original submodule are misplaced relative PySide2 """ _misplaced_members = { "PySide2": { - #"QtGui.QStringListModel": "QtCore.QStringListModel", + "QtGui.QStringListModel": "QtCore.QStringListModel", "QtCore.Property": "QtCore.Property", "QtCore.Signal": "QtCore.Signal", "QtCore.Slot": "QtCore.Slot", @@ -679,6 +693,7 @@ _misplaced_members = { "QtCore.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel", "QtCore.QItemSelection": "QtCore.QItemSelection", "QtCore.QItemSelectionModel": "QtCore.QItemSelectionModel", + "QtCore.QItemSelectionRange": "QtCore.QItemSelectionRange", }, "PyQt5": { "QtCore.pyqtProperty": "QtCore.Property", @@ -689,6 +704,7 @@ _misplaced_members = { "QtCore.QStringListModel": "QtCore.QStringListModel", "QtCore.QItemSelection": "QtCore.QItemSelection", "QtCore.QItemSelectionModel": "QtCore.QItemSelectionModel", + "QtCore.QItemSelectionRange": "QtCore.QItemSelectionRange", }, "PySide": { "QtGui.QAbstractProxyModel": "QtCore.QAbstractProxyModel", @@ -699,7 +715,15 @@ _misplaced_members = { "QtCore.Property": "QtCore.Property", "QtCore.Signal": "QtCore.Signal", "QtCore.Slot": "QtCore.Slot", - + "QtGui.QItemSelectionRange": "QtCore.QItemSelectionRange", + "QtGui.QAbstractPrintDialog": "QtPrintSupport.QAbstractPrintDialog", + "QtGui.QPageSetupDialog": "QtPrintSupport.QPageSetupDialog", + "QtGui.QPrintDialog": "QtPrintSupport.QPrintDialog", + "QtGui.QPrintEngine": "QtPrintSupport.QPrintEngine", + "QtGui.QPrintPreviewDialog": "QtPrintSupport.QPrintPreviewDialog", + "QtGui.QPrintPreviewWidget": "QtPrintSupport.QPrintPreviewWidget", + "QtGui.QPrinter": "QtPrintSupport.QPrinter", + "QtGui.QPrinterInfo": "QtPrintSupport.QPrinterInfo", }, "PyQt4": { "QtGui.QAbstractProxyModel": "QtCore.QAbstractProxyModel", @@ -710,6 +734,15 @@ _misplaced_members = { "QtCore.pyqtProperty": "QtCore.Property", "QtCore.pyqtSignal": "QtCore.Signal", "QtCore.pyqtSlot": "QtCore.Slot", + "QtGui.QItemSelectionRange": "QtCore.QItemSelectionRange", + "QtGui.QAbstractPrintDialog": "QtPrintSupport.QAbstractPrintDialog", + "QtGui.QPageSetupDialog": "QtPrintSupport.QPageSetupDialog", + "QtGui.QPrintDialog": "QtPrintSupport.QPrintDialog", + "QtGui.QPrintEngine": "QtPrintSupport.QPrintEngine", + "QtGui.QPrintPreviewDialog": "QtPrintSupport.QPrintPreviewDialog", + "QtGui.QPrintPreviewWidget": "QtPrintSupport.QPrintPreviewWidget", + "QtGui.QPrinter": "QtPrintSupport.QPrinter", + "QtGui.QPrinterInfo": "QtPrintSupport.QPrinterInfo", } } @@ -869,12 +902,12 @@ def _wrapinstance(func, ptr, base=None): """ - assert isinstance(ptr, int), "Argument 'ptr' must be of type " + assert isinstance(ptr, long), "Argument 'ptr' must be of type " assert (base is None) or issubclass(base, Qt.QtCore.QObject), ( "Argument 'base' must be of type ") if base is None: - q_object = func(int(ptr), Qt.QtCore.QObject) + q_object = func(long(ptr), Qt.QtCore.QObject) meta_object = q_object.metaObject() class_name = meta_object.className() super_class_name = meta_object.superClass().className() @@ -888,7 +921,7 @@ def _wrapinstance(func, ptr, base=None): else: base = Qt.QtCore.QObject - return func(int(ptr), base) + return func(long(ptr), base) def _reassign_misplaced_members(binding): @@ -899,20 +932,38 @@ def _reassign_misplaced_members(binding): """ - for src, dst in list(_misplaced_members[binding].items()): + for src, dst in _misplaced_members[binding].items(): src_module, src_member = src.split(".") dst_module, dst_member = dst.split(".") + # Get the member we want to store in the namesapce. + try: + dst_value = getattr(getattr(Qt, "_" + src_module), src_member) + except AttributeError: + # If the member we want to store in the namespace does not exist, + # there is no need to continue. This can happen if a request was + # made to rename a member that didn't exist, for example + # if QtWidgets isn't available on the target platform. + _log("Misplaced member has no source: {}".format(src)) + continue + try: src_object = getattr(Qt, dst_module) except AttributeError: - # Skip reassignment of non-existing members. - # This can happen if a request was made to - # rename a member that didn't exist, for example - # if QtWidgets isn't available on the target platform. - continue - - dst_value = getattr(getattr(Qt, "_" + src_module), src_member) + if dst_module not in _common_members: + # Only create the Qt parent module if its listed in + # _common_members. Without this check, if you remove QtCore + # from _common_members, the default _misplaced_members will add + # Qt.QtCore so it can add Signal, Slot, etc. + msg = 'Not creating missing member module "{m}" for "{c}"' + _log(msg.format(m=dst_module, c=dst_member)) + continue + # If the dst is valid but the Qt parent module does not exist + # then go ahead and create a new module to contain the member. + setattr(Qt, dst_module, _new_module(dst_module)) + src_object = getattr(Qt, dst_module) + # Enable direct import of the new module + sys.modules[__name__ + "." + dst_module] = src_object setattr( src_object, @@ -948,9 +999,9 @@ def _build_compatibility_members(binding, decorators=None): _QtCompat = type("QtCompat", (object,), {}) - for classname, bindings in list(_compatibility_members[binding].items()): + for classname, bindings in _compatibility_members[binding].items(): attrs = {} - for target, binding in list(bindings.items()): + for target, binding in bindings.items(): namespaces = binding.split('.') try: src_object = getattr(Qt, "_" + namespaces[0]) @@ -1020,6 +1071,7 @@ def _pyside2(): if hasattr(Qt, "_QtCore"): Qt.__qt_version__ = Qt._QtCore.qVersion() + Qt.QtCompat.qInstallMessageHandler = _qInstallMessageHandler Qt.QtCompat.translate = Qt._QtCore.QCoreApplication.translate if hasattr(Qt, "_QtWidgets"): @@ -1062,12 +1114,16 @@ def _pyside(): if hasattr(Qt, "_QtGui"): setattr(Qt, "QtWidgets", _new_module("QtWidgets")) setattr(Qt, "_QtWidgets", Qt._QtGui) + if hasattr(Qt._QtGui, "QX11Info"): + setattr(Qt, "QtX11Extras", _new_module("QtX11Extras")) + Qt.QtX11Extras.QX11Info = Qt._QtGui.QX11Info Qt.QtCompat.setSectionResizeMode = Qt._QtGui.QHeaderView.setResizeMode if hasattr(Qt, "_QtCore"): Qt.__qt_version__ = Qt._QtCore.qVersion() QCoreApplication = Qt._QtCore.QCoreApplication + Qt.QtCompat.qInstallMessageHandler = _qInstallMessageHandler Qt.QtCompat.translate = ( lambda context, sourceText, disambiguation, n: QCoreApplication.translate( @@ -1107,6 +1163,7 @@ def _pyqt5(): if hasattr(Qt, "_QtCore"): Qt.__binding_version__ = Qt._QtCore.PYQT_VERSION_STR Qt.__qt_version__ = Qt._QtCore.QT_VERSION_STR + Qt.QtCompat.qInstallMessageHandler = _qInstallMessageHandler Qt.QtCompat.translate = Qt._QtCore.QCoreApplication.translate if hasattr(Qt, "_QtWidgets"): @@ -1175,6 +1232,9 @@ def _pyqt4(): if hasattr(Qt, "_QtGui"): setattr(Qt, "QtWidgets", _new_module("QtWidgets")) setattr(Qt, "_QtWidgets", Qt._QtGui) + if hasattr(Qt._QtGui, "QX11Info"): + setattr(Qt, "QtX11Extras", _new_module("QtX11Extras")) + Qt.QtX11Extras.QX11Info = Qt._QtGui.QX11Info Qt.QtCompat.setSectionResizeMode = \ Qt._QtGui.QHeaderView.setResizeMode @@ -1182,8 +1242,8 @@ def _pyqt4(): if hasattr(Qt, "_QtCore"): Qt.__binding_version__ = Qt._QtCore.PYQT_VERSION_STR Qt.__qt_version__ = Qt._QtCore.QT_VERSION_STR - QCoreApplication = Qt._QtCore.QCoreApplication + Qt.QtCompat.qInstallMessageHandler = _qInstallMessageHandler Qt.QtCompat.translate = ( lambda context, sourceText, disambiguation, n: QCoreApplication.translate( @@ -1233,7 +1293,7 @@ def _none(): Qt.QtCompat.loadUi = lambda uifile, baseinstance=None: None Qt.QtCompat.setSectionResizeMode = lambda *args, **kwargs: None - for submodule in list(_common_members.keys()): + for submodule in _common_members.keys(): setattr(Qt, submodule, Mock()) setattr(Qt, "_" + submodule, Mock()) @@ -1261,10 +1321,6 @@ def _loadUi(uifile, baseinstance=None): return the newly created instance of the user interface. """ - if hasattr(baseinstance, "layout") and baseinstance.layout(): - message = ("QLayout: Attempting to add Layout to %s which " - "already has a layout") - raise RuntimeError(message % (baseinstance)) if hasattr(Qt, "_uic"): return Qt._uic.loadUi(uifile, baseinstance) @@ -1345,6 +1401,43 @@ def _loadUi(uifile, baseinstance=None): raise NotImplementedError("No implementation available for loadUi") +def _qInstallMessageHandler(handler): + """Install a message handler that works in all bindings + + Args: + handler: A function that takes 3 arguments, or None + """ + def messageOutputHandler(*args): + # In Qt4 bindings, message handlers are passed 2 arguments + # In Qt5 bindings, message handlers are passed 3 arguments + # The first argument is a QtMsgType + # The last argument is the message to be printed + # The Middle argument (if passed) is a QMessageLogContext + if len(args) == 3: + msgType, logContext, msg = args + elif len(args) == 2: + msgType, msg = args + logContext = None + else: + raise TypeError( + "handler expected 2 or 3 arguments, got {0}".format(len(args))) + + if isinstance(msg, bytes): + # In python 3, some bindings pass a bytestring, which cannot be + # used elsewhere. Decoding a python 2 or 3 bytestring object will + # consistently return a unicode object. + msg = msg.decode() + + handler(msgType, logContext, msg) + + passObject = messageOutputHandler if handler else handler + if Qt.IsPySide or Qt.IsPyQt4: + return Qt._QtCore.qInstallMsgHandler(passObject) + elif Qt.IsPySide2 or Qt.IsPyQt5: + return Qt._QtCore.qInstallMessageHandler(passObject) + + + def _convert(lines): """Convert compiled .ui file from PySide2 to Qt.py @@ -1472,7 +1565,7 @@ def _install(): raise ImportError("No Qt binding were found.") # Install individual members - for name, members in list(_common_members.items()): + for name, members in _common_members.items(): try: their_submodule = getattr(Qt, "_%s" % name) except AttributeError: @@ -1497,6 +1590,9 @@ def _install(): setattr(our_submodule, member, their_member) + # Enable direct import of QtCompat + sys.modules['Qt.QtCompat'] = Qt.QtCompat + # Backwards compatibility if hasattr(Qt.QtCompat, 'loadUi'): Qt.QtCompat.load_ui = Qt.QtCompat.loadUi From 76a6390a2b82c7ab022dbf86a5530e9c0b7766e7 Mon Sep 17 00:00:00 2001 From: Etoh Date: Fri, 20 Jul 2018 11:49:37 +0100 Subject: [PATCH 94/95] Merge duplicate INI sections from NSIS BOM bug --- syncplay/ui/ConfigurationGetter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index 0df8650..f7d311f 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -347,7 +347,7 @@ class ConfigurationGetter(object): return path def _parseConfigFile(self, iniPath, createConfig=True): - parser = SafeConfigParserUnicode() + parser = SafeConfigParserUnicode(strict=False) if not os.path.isfile(iniPath): if createConfig: open(iniPath, 'w').close() @@ -395,7 +395,7 @@ class ConfigurationGetter(object): changed = False if self._config['noStore']: return - parser = SafeConfigParserUnicode() + parser = SafeConfigParserUnicode(strict=False) parser.readfp(codecs.open(iniPath, "r", "utf_8_sig")) for section, options in list(self._iniStructure.items()): if not parser.has_section(section): From 2b8602602229baa591324fff16ecdd89680f7c8c Mon Sep 17 00:00:00 2001 From: albertosottile Date: Fri, 20 Jul 2018 18:38:38 +0200 Subject: [PATCH 95/95] Add 'beta' revision identifier --- syncplay/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index ac03334..1a4d01e 100755 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,5 +1,5 @@ version = '1.5.6' -revision = '' +revision = ' beta' milestone = 'Yoitsu' release_number = '65' projectURL = 'https://syncplay.pl/'