From 4fa0a9b9217c7b012dba4f050ec9397a4080b6f7 Mon Sep 17 00:00:00 2001 From: Et0h Date: Thu, 18 Jun 2015 14:46:39 +0100 Subject: [PATCH 01/58] Pass on mpv/youtube-dl out of date errors --- syncplay/players/mpv.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/syncplay/players/mpv.py b/syncplay/players/mpv.py index 09abe2a..4b79181 100644 --- a/syncplay/players/mpv.py +++ b/syncplay/players/mpv.py @@ -87,6 +87,9 @@ class OldMpvPlayer(MpvPlayer): self.reactor.callFromThread(self._client.ui.showErrorMessage, getMessage("mpv-version-error"), True) self.drop() + elif "[ytdl_hook] Your version of youtube-dl is too old" in line: + self._client.ui.showErrorMessage(line) + def _handleUnknownLine(self, line): self.mpvVersionErrorCheck(line) if "Playing: " in line: From 4931cc37205efa2a2a117453ed0fdd28951e8796 Mon Sep 17 00:00:00 2001 From: Et0h Date: Thu, 18 Jun 2015 17:54:10 +0100 Subject: [PATCH 02/58] Initial code for double click to change to user's file/stream (#65 suggested by bitingsock) --- syncplay/client.py | 7 ++++--- syncplay/ui/GuiConfiguration.py | 13 ++++--------- syncplay/ui/gui.py | 23 +++++++++++++++++++++-- syncplay/utils.py | 10 ++++++++++ 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/syncplay/client.py b/syncplay/client.py index 9aa5e5f..944724e 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -399,7 +399,7 @@ class SyncplayClient(object): except: size = 0 filename, size = self.__executePrivacySettings(filename, size) - self.userlist.currentUser.setFile(filename, duration, size) + self.userlist.currentUser.setFile(filename, duration, size, path) self.sendFile() def __executePrivacySettings(self, filename, size): @@ -796,11 +796,12 @@ class SyncplayUser(object): self.file = file_ self._controller = False - def setFile(self, filename, duration, size): + def setFile(self, filename, duration, size, path=None): file_ = { "name": filename, "duration": duration, - "size": size + "size": size, + "path": path } self.file = file_ diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index b3b0f35..935b4d7 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -3,6 +3,7 @@ from PySide.QtCore import QSettings, Qt, QCoreApplication, QUrl from PySide.QtGui import QApplication, QLineEdit, QCursor, QLabel, QCheckBox, QDesktopServices, QIcon, QImage, QButtonGroup, QRadioButton, QDoubleSpinBox from syncplay.players.playerFactory import PlayerFactory from datetime import datetime +from syncplay import utils import os import sys from syncplay.messages import getMessage, getLanguages, setLanguage, getInitialLanguage @@ -77,15 +78,10 @@ class ConfigDialog(QtGui.QDialog): def openHelp(self): self.QtGui.QDesktopServices.openUrl(QUrl("http://syncplay.pl/guide/client/")) - def _isURL(self, path): - if path is None: - return False - if "http://" in path: - return True def safenormcaseandpath(self, path): - if self._isURL(path): + if utils.isURL(path): return path else: return os.path.normcase(os.path.normpath(path)) @@ -104,7 +100,7 @@ class ConfigDialog(QtGui.QDialog): foundpath = "" if playerpath != None and playerpath != "": - if self._isURL(playerpath): + if utils.isURL(playerpath): foundpath = playerpath self.executablepathCombobox.addItem(foundpath) @@ -119,7 +115,7 @@ class ConfigDialog(QtGui.QDialog): self.executablepathCombobox.addItem(foundpath) for path in playerpathlist: - if self._isURL(path): + if utils.isURL(path): if foundpath == "": foundpath = path if path != playerpath: @@ -821,7 +817,6 @@ class ConfigDialog(QtGui.QDialog): def __init__(self, config, playerpaths, error, defaultConfig): - from syncplay import utils self.config = config self.defaultConfig = defaultConfig self.playerpaths = playerpaths diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index cdea875..d5382c6 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -7,7 +7,7 @@ import time from datetime import datetime import re import os -from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize +from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize, isURL from functools import wraps lastCheckedForUpdates = None @@ -230,9 +230,28 @@ class MainWindow(QtGui.QMainWindow): self.updateReadyIcon() def roomClicked(self, item): + username = item.sibling(item.row(), 0).data() + filename = item.sibling(item.row(), 3).data() while item.parent().row() != -1: item = item.parent() - self.joinRoom(item.sibling(item.row(), 0).data()) + roomToJoin = item.sibling(item.row(), 0).data() + if roomToJoin <> self._syncplayClient.getRoom(): + self.joinRoom(item.sibling(item.row(), 0).data()) + elif username and filename and username <> self._syncplayClient.userlist.currentUser.username: + if self._syncplayClient.userlist.currentUser.file and filename == self._syncplayClient.userlist.currentUser.file: + return + if isURL(filename): + self._syncplayClient._player.openFile(filename) #bob + else: + currentPath = self._syncplayClient.userlist.currentUser.file["path"] + if currentPath is not None: + currentDirectory = os.path.dirname(currentPath) + newPath = os.path.join(currentDirectory, filename) + if os.path.isfile(newPath): + self._syncplayClient._player.openFile(newPath) + # TODO: Add error messages + # TODO: Change media players (mpv/VLC) to give URL of stream + @needsClient def userListChange(self): diff --git a/syncplay/utils.py b/syncplay/utils.py index e4267c1..ceb54d4 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -218,6 +218,16 @@ def meetsMinVersion(version, minVersion): return tuple(map(int, ver.split("."))) return versiontotuple(version) >= versiontotuple(minVersion) +def isURL(path): + if path is None: + return False + + if "http://" in path: + return True + + elif "https://" in path: + return True + class RoomPasswordProvider(object): CONTROLLED_ROOM_REGEX = re.compile("^\+(.*):(\w{12})$") PASSWORD_REGEX = re.compile("[A-Z]{2}-\d{3}-\d{3}") From aa0db2517b610ce002a8651ec67d959237faa073 Mon Sep 17 00:00:00 2001 From: Et0h Date: Thu, 18 Jun 2015 21:21:57 +0100 Subject: [PATCH 03/58] Use stream URL as filename if stream (#65) --- syncplay/client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/syncplay/client.py b/syncplay/client.py index 944724e..5b151d7 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -388,6 +388,9 @@ class SyncplayClient(object): return self._globalPaused def updateFile(self, filename, duration, path): + if utils.isURL(path): + filename = path + if not path: return try: From d4eabcebcaa8d5088c66ed9af9996729696bcbca Mon Sep 17 00:00:00 2001 From: Et0h Date: Thu, 18 Jun 2015 21:22:41 +0100 Subject: [PATCH 04/58] Expand definition of URL --- syncplay/utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/syncplay/utils.py b/syncplay/utils.py index ceb54d4..86a2d85 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -222,10 +222,7 @@ def isURL(path): if path is None: return False - if "http://" in path: - return True - - elif "https://" in path: + if "://" in path: return True class RoomPasswordProvider(object): From 843e5a05652b2f8f7e988bc8dd2ddb3532902a70 Mon Sep 17 00:00:00 2001 From: Et0h Date: Fri, 19 Jun 2015 00:02:03 +0100 Subject: [PATCH 05/58] Make VLC show URL instead of Title for #65 --- resources/lua/intf/syncplay.lua | 6 +++--- syncplay/constants.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/lua/intf/syncplay.lua b/resources/lua/intf/syncplay.lua index 66bac0c..bacf9d6 100644 --- a/resources/lua/intf/syncplay.lua +++ b/resources/lua/intf/syncplay.lua @@ -5,7 +5,7 @@ Principal author: Etoh Other contributors: DerGenaue, jb Project: http://syncplay.pl/ - Version: 0.2.6 + Version: 0.2.7 Note: * This interface module is intended to be used in conjunction with Syncplay. @@ -84,7 +84,7 @@ You may also need to re-copy the syncplay.lua file when you update VLC. --]==========================================================================] -local connectorversion = "0.2.6" +local connectorversion = "0.2.7" local vlcversion = vlc.misc.version() local durationdelay = 500000 -- Pause for get_duration command etc for increased reliability (uses microseconds) local loopsleepduration = 2500 -- Pause for every event loop (uses microseconds) @@ -330,7 +330,7 @@ function get_filepath () else local metas = item:metas() if metas and metas["title"] and string.len(metas["title"]) > 0 then - response = ":::(Stream: "..metas["title"]..")" + response = metas["url"] else response = unknownstream end diff --git a/syncplay/constants.py b/syncplay/constants.py index 498848f..adb63ea 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -63,7 +63,7 @@ COMMANDS_AUTH = ['a','auth'] COMMANDS_TOGGLE = ['t','toggle'] MPC_MIN_VER = "1.6.4" VLC_MIN_VERSION = "2.2.1" -VLC_INTERFACE_MIN_VERSION = "0.2.6" +VLC_INTERFACE_MIN_VERSION = "0.2.7" VLC_LATENCY_ERROR_THRESHOLD = 2.0 CONTROLLED_ROOMS_MIN_VERSION = "1.3.0" USER_READY_MIN_VERSION = "1.3.0" From 41566ede4532f91a4e3cf9abfa802c02f52a18eb Mon Sep 17 00:00:00 2001 From: Et0h Date: Fri, 19 Jun 2015 00:05:35 +0100 Subject: [PATCH 06/58] Check for url rather than title before returning URL in Syncplay Lua --- resources/lua/intf/syncplay.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lua/intf/syncplay.lua b/resources/lua/intf/syncplay.lua index bacf9d6..4541a34 100644 --- a/resources/lua/intf/syncplay.lua +++ b/resources/lua/intf/syncplay.lua @@ -329,7 +329,7 @@ function get_filepath () response = ":::DVD:::" else local metas = item:metas() - if metas and metas["title"] and string.len(metas["title"]) > 0 then + if metas and metas["url"] and string.len(metas["url"]) > 0 then response = metas["url"] else response = unknownstream From f41cb535df91a4499b478f6761346f0a127731d1 Mon Sep 17 00:00:00 2001 From: Et0h Date: Fri, 19 Jun 2015 00:16:14 +0100 Subject: [PATCH 07/58] Upver to release 11 --- syncplay/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index d2ba9f6..ef4829f 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ version = '1.3.1' milestone = 'Chami' -release_number = '10' +release_number = '11' projectURL = 'http://syncplay.pl/' From 763829a00eb1555ff2eee6a9071e603fea2f8f6e Mon Sep 17 00:00:00 2001 From: Et0h Date: Mon, 22 Jun 2015 16:42:58 +0100 Subject: [PATCH 08/58] Double click switch file: Add errors, tidy code, don't get path if no file open, upver to 12 --- syncplay/__init__.py | 2 +- syncplay/messages.py | 9 +++++++++ syncplay/ui/gui.py | 13 +++++++------ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index ef4829f..8e02ebc 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ version = '1.3.1' milestone = 'Chami' -release_number = '11' +release_number = '12' projectURL = 'http://syncplay.pl/' diff --git a/syncplay/messages.py b/syncplay/messages.py index 58852de..bdf6957 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -134,6 +134,9 @@ en = { "invalid-seek-value" : u"Invalid seek value", "invalid-offset-value" : u"Invalid offset value", + "switch-file-not-found-error" : u"Could not switch to file '{0}' as it was not found in folder '{1}'.", # File not found, folder it was not found in + "switch-no-folder-error" : u"Could not switch to file '{0}'. Syncplay only looks in the folder of the currently playing file, and no file is currently playing.", # File not found + # Client arguments "argument-description" : 'Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network.', "argument-epilog" : 'If no options supplied _config values will be used', @@ -489,6 +492,9 @@ ru = { "invalid-seek-value" : u"Invalid seek value", # TODO: Translate into Russian "invalid-offset-value" : u"Invalid offset value", # TODO: Translate into Russian + "switch-file-not-found-error" : u"Could not switch to file '{0}' as it was not found in folder '{1}'.", # File not found, folder it was not found in # TODO: Translate into Russian + "switch-no-folder-error" : u"Could not switch to file '{0}'. Syncplay only looks in the folder of the currently playing file, and no file is currently playing.", # File not found # TODO: Translate into Russian + # Client arguments "argument-description" : u'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC через Интернет.', "argument-epilog" : u'Если параметр не будет передан, то будет использоваться значение, указанное в _config.', @@ -844,6 +850,9 @@ de = { "invalid-seek-value" : u"Ungültige Zeitangabe", "invalid-offset-value" : u"Ungültiger Offset-Wert", + "switch-file-not-found-error" : u"Could not switch to file '{0}' as it was not found in folder '{1}'.", # File not found, folder it was not found in # TODO: Translate into German + "switch-no-folder-error" : u"Could not switch to file '{0}'. Syncplay only looks in the folder of the currently playing file, and no file is currently playing.", # File not found # TODO: Translate into German + # Client arguments "argument-description" : u'Syncplay ist eine Anwendung um mehrere MPlayer, MPC-HC und VLC-Instanzen über das Internet zu synchronisieren.', "argument-epilog" : u'Wenn keine Optionen angegeben sind, werden die _config-Werte verwendet', diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index d5382c6..57f6e5d 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -241,17 +241,18 @@ class MainWindow(QtGui.QMainWindow): if self._syncplayClient.userlist.currentUser.file and filename == self._syncplayClient.userlist.currentUser.file: return if isURL(filename): - self._syncplayClient._player.openFile(filename) #bob + self._syncplayClient._player.openFile(filename) else: - currentPath = self._syncplayClient.userlist.currentUser.file["path"] - if currentPath is not None: + currentPath = self._syncplayClient.userlist.currentUser.file["path"] if self._syncplayClient.userlist.currentUser.file else None + if currentPath: currentDirectory = os.path.dirname(currentPath) newPath = os.path.join(currentDirectory, filename) if os.path.isfile(newPath): self._syncplayClient._player.openFile(newPath) - # TODO: Add error messages - # TODO: Change media players (mpv/VLC) to give URL of stream - + else: + self.showErrorMessage(getMessage("switch-file-not-found-error").format(filename, currentDirectory)) + else: + self.showErrorMessage(getMessage("switch-no-folder-error").format(filename)) @needsClient def userListChange(self): From 6c56432fbde50a53c9eb6913f491a359701ef6cd Mon Sep 17 00:00:00 2001 From: Et0h Date: Wed, 24 Jun 2015 00:01:55 +0100 Subject: [PATCH 09/58] Allow per-player arguments (#59) --- buildPy2exe.py | 2 +- syncplay/__init__.py | 2 +- syncplay/client.py | 5 ++++- syncplay/messages.py | 6 ++++++ syncplay/ui/ConfigurationGetter.py | 14 +++++++++++++- syncplay/ui/GuiConfiguration.py | 28 +++++++++++++++++++++++++++- syncplay/utils.py | 10 ++++++++++ 7 files changed, 62 insertions(+), 5 deletions(-) diff --git a/buildPy2exe.py b/buildPy2exe.py index 6c81bff..3f7e0b5 100644 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -676,7 +676,7 @@ info = dict( options={'py2exe': { 'dist_dir': OUT_DIR, 'packages': 'PySide.QtUiTools', - 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide, liburl', + 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide, liburl, ast', 'excludes': 'venv, _ssl, doctest, pdb, unittest, win32clipboard, win32file, win32pdh, win32security, win32trace, win32ui, winxpgui, win32pipe, win32process, Tkinter', 'dll_excludes': 'msvcr71.dll, MSVCP90.dll, POWRPROF.dll', 'optimize': 2, diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 8e02ebc..cfcf059 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ version = '1.3.1' milestone = 'Chami' -release_number = '12' +release_number = '13' projectURL = 'http://syncplay.pl/' diff --git a/syncplay/client.py b/syncplay/client.py index 5b151d7..7ac9b56 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -497,7 +497,10 @@ class SyncplayClient(object): return self._running = True if self._playerClass: - reactor.callLater(0.1, self._playerClass.run, self, self._config['playerPath'], self._config['file'], self._config['playerArgs']) + perPlayerArguments = utils.getPlayerArgumentsByPathAsArray(self._config['perPlayerArguments'],self._config['playerPath']) + if perPlayerArguments: + self._config['playerArgs'].extend(perPlayerArguments) + reactor.callLater(0.1, self._playerClass.run, self, self._config['playerPath'], self._config['file'], self._config['playerArgs'], ) self._playerClass = None self.protocolFactory = SyncClientFactory(self) port = int(port) diff --git a/syncplay/messages.py b/syncplay/messages.py index bdf6957..def8500 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -169,6 +169,7 @@ en = { "media-setting-title" : "Media player settings", "executable-path-label" : "Path to media player:", "media-path-label" : "Path to media file:", + "player-arguments-label" : "Player arguments (if any):", "browse-label" : "Browse", "more-title" : "Show more settings", @@ -284,6 +285,7 @@ en = { "executable-path-tooltip" : "Location of your chosen supported media player (MPC-HC, VLC, mplayer2 or mpv).", "media-path-tooltip" : "Location of video or stream to be opened. Necessary for mpv and mplayer2.", + "player-arguments-tooltip" : "Additional command line arguments / switches to pass on to this media player.", "more-tooltip" : "Display less frequently used settings.", "filename-privacy-tooltip" : "Privacy mode for sending currently playing filename to server.", @@ -527,6 +529,7 @@ ru = { "media-setting-title" : u"Параметры проигрывателя", "executable-path-label" : u"Путь к проигрывателю:", "media-path-label" : u"Путь к видеофайлу:", + "player-arguments-label" : "Player arguments:", # TODO: Translate into Russian "browse-label" : u"Выбрать", "more-title" : u"Больше настроек", @@ -642,6 +645,7 @@ ru = { "executable-path-tooltip" : u"Расположение Вашего видеопроигрывателя (MPC-HC, VLC, mplayer2 или mpv).", "media-path-tooltip" : u"Расположение видеофайла или потока для просмотра. Обязательно для mpv и mplayer2.", + "player-arguments-tooltip" : "Additional command line arguments / switches to pass on to this media player.", # TODO: Translate into Russian "more-tooltip" : u"Показать дополнительные настройки.", "filename-privacy-tooltip" : u"Режим приватности для передачи имени воспроизводимого файла на сервер.", @@ -885,6 +889,7 @@ de = { "media-setting-title" : u"Media-Player Einstellungen", "executable-path-label" : u"Pfad zum Media-Player:", "media-path-label" : u"Pfad zur Datei:", + "player-arguments-label" : "Player arguments:", # TODO: Translate into German "browse-label" : u"Durchsuchen", "more-title" : u"Mehr Einstellungen zeigen", @@ -998,6 +1003,7 @@ de = { "executable-path-tooltip" : u"Pfad zum ausgewählten, unterstützten Mediaplayer (MPC-HC, VLC, mplayer2 or mpv).", "media-path-tooltip" : u"Pfad zum wiederzugebenden Video oder Stream. Notwendig für mpv und mplayer2.", + "player-arguments-tooltip" : "Additional command line arguments / switches to pass on to this media player.", # TODO: Translate into German "more-tooltip" : u"Weitere Einstellungen anzeigen.", "filename-privacy-tooltip" : u"Privatheitsmodus beim Senden des Namens der aktuellen Datei zum Server.", diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index 55323d1..c73cf60 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -2,6 +2,7 @@ from ConfigParser import SafeConfigParser, DEFAULTSECT import argparse import os import sys +import ast from syncplay import constants, utils, version, milestone from syncplay.messages import getMessage, setLanguage, isValidLanguage from syncplay.players.playerFactory import PlayerFactory @@ -31,6 +32,7 @@ class ConfigurationGetter(object): "room": "", "password": None, "playerPath": None, + "perPlayerArguments": None, "file": None, "playerArgs": [], "playerClass": None, @@ -104,6 +106,10 @@ class ConfigurationGetter(object): "autoplayInitialState", ] + self._serialised = [ + "perPlayerArguments", + ] + self._numeric = [ "slowdownThreshold", "rewindThreshold", @@ -113,7 +119,7 @@ class ConfigurationGetter(object): self._iniStructure = { "server_data": ["host", "port", "password"], - "client_settings": ["name", "room", "playerPath", "slowdownThreshold", "rewindThreshold", "fastforwardThreshold", "slowOnDesync", "rewindOnDesync", "fastforwardOnDesync", "dontSlowDownWithMe", "forceGuiPrompt", "filenamePrivacyMode", "filesizePrivacyMode", "unpauseAction", "pauseOnLeave", "readyAtStart", "autoplayMinUsers", "autoplayInitialState"], + "client_settings": ["name", "room", "playerPath", "perPlayerArguments", "slowdownThreshold", "rewindThreshold", "fastforwardThreshold", "slowOnDesync", "rewindOnDesync", "fastforwardOnDesync", "dontSlowDownWithMe", "forceGuiPrompt", "filenamePrivacyMode", "filesizePrivacyMode", "unpauseAction", "pauseOnLeave", "readyAtStart", "autoplayMinUsers", "autoplayInitialState"], "gui": ["showOSD", "showOSDWarnings", "showSlowdownOSD", "showDifferentRoomOSD", "showSameRoomOSD", "showNonControllerOSD", "showDurationNotification"], "general": ["language", "checkForUpdatesAutomatically", "lastCheckedForUpdates"] } @@ -150,6 +156,12 @@ class ConfigurationGetter(object): elif self._config[key] == "False": self._config[key] = False + 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)): + self._config[key] = ast.literal_eval(self._config[key]) + for key in self._tristate: if self._config[key] == "True": self._config[key] = True diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 935b4d7..5ec1a7e 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -134,13 +134,27 @@ class ConfigDialog(QtGui.QDialog): return foundpath def updateExecutableIcon(self): - currentplayerpath = unicode(self.executablepathCombobox.currentText()) + currentplayerpath = self.executablepathCombobox.currentText() iconpath = PlayerFactory().getPlayerIconByPath(currentplayerpath) if iconpath != None and iconpath != "": self.executableiconImage.load(self.resourcespath + iconpath) self.executableiconLabel.setPixmap(QtGui.QPixmap.fromImage(self.executableiconImage)) else: self.executableiconLabel.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage())) + self.updatePlayerArguments(currentplayerpath) + + def updatePlayerArguments(self, currentplayerpath): + argumentsForPath = utils.getPlayerArgumentsByPathAsText(self.perPlayerArgs, currentplayerpath) + self.playerargsTextbox.blockSignals(True) + self.playerargsTextbox.setText(argumentsForPath) + self.playerargsTextbox.blockSignals(False) + + def changedPlayerArgs(self): + currentplayerpath = self.executablepathCombobox.currentText() + + if currentplayerpath: + NewPlayerArgs = self.playerargsTextbox.text().split(u" ") if self.playerargsTextbox.text() else "" + self.perPlayerArgs[self.executablepathCombobox.currentText()]=NewPlayerArgs def languageChanged(self): setLanguage(unicode(self.languageCombobox.itemData(self.languageCombobox.currentIndex()))) @@ -235,6 +249,8 @@ class ConfigDialog(QtGui.QDialog): self.automaticUpdatePromptCheck() self.loadLastUpdateCheckDate() + self.config["perPlayerArguments"] = self.perPlayerArgs + self.processWidget(self, lambda w: self.saveValues(w)) if self.hostTextbox.text(): self.config['host'] = self.hostTextbox.text() if ":" in self.hostTextbox.text() else self.hostTextbox.text() + ":" + unicode(constants.DEFAULT_PORT) @@ -364,6 +380,8 @@ class ConfigDialog(QtGui.QDialog): else: host = config['host'] + ":" + str(config['port']) + self.perPlayerArgs = self.config["perPlayerArguments"] + self.connectionSettingsGroup = QtGui.QGroupBox(getMessage("connection-group-title")) self.hostTextbox = QLineEdit(host, self) self.hostLabel = QLabel(getMessage("host-label"), self) @@ -396,6 +414,10 @@ class ConfigDialog(QtGui.QDialog): self.connectionSettingsGroup.setLayout(self.connectionSettingsLayout) self.connectionSettingsGroup.setMaximumHeight(self.connectionSettingsGroup.minimumSizeHint().height()) + self.playerargsTextbox = QLineEdit("", self) + self.playerargsTextbox.textEdited.connect(self.changedPlayerArgs) + self.playerargsLabel = QLabel(getMessage("player-arguments-label"), self) + self.mediaplayerSettingsGroup = QtGui.QGroupBox(getMessage("media-setting-title")) self.executableiconImage = QtGui.QImage() self.executableiconLabel = QLabel(self) @@ -419,6 +441,8 @@ class ConfigDialog(QtGui.QDialog): self.executablepathCombobox.setObjectName("executable-path") self.mediapathLabel.setObjectName("media-path") self.mediapathTextbox.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "media-path") + self.playerargsLabel.setObjectName("player-arguments") + self.playerargsTextbox.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "player-arguments") self.mediaplayerSettingsLayout = QtGui.QGridLayout() self.mediaplayerSettingsLayout.addWidget(self.executablepathLabel, 0, 0) @@ -428,6 +452,8 @@ class ConfigDialog(QtGui.QDialog): self.mediaplayerSettingsLayout.addWidget(self.mediapathLabel, 1, 0) self.mediaplayerSettingsLayout.addWidget(self.mediapathTextbox , 1, 2) self.mediaplayerSettingsLayout.addWidget(self.mediabrowseButton , 1, 3) + self.mediaplayerSettingsLayout.addWidget(self.playerargsLabel, 2, 0, 1, 2) + self.mediaplayerSettingsLayout.addWidget(self.playerargsTextbox, 2, 2, 1, 2) self.mediaplayerSettingsGroup.setLayout(self.mediaplayerSettingsLayout) self.showmoreCheckbox = QCheckBox(getMessage("more-title")) diff --git a/syncplay/utils.py b/syncplay/utils.py index 86a2d85..6bc2828 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -225,6 +225,16 @@ def isURL(path): if "://" in path: return True +def getPlayerArgumentsByPathAsArray(arguments, path): + if arguments and not isinstance(arguments, (str, unicode)) and arguments.has_key(path): + return arguments[path] + else: + return None + +def getPlayerArgumentsByPathAsText(arguments, path): + argsToReturn = getPlayerArgumentsByPathAsArray(arguments, path) + return " ".join(argsToReturn) if argsToReturn else "" + class RoomPasswordProvider(object): CONTROLLED_ROOM_REGEX = re.compile("^\+(.*):(\w{12})$") PASSWORD_REGEX = re.compile("[A-Z]{2}-\d{3}-\d{3}") From 03f4394a0ceca0fcdacfad1907eb06261326b85d Mon Sep 17 00:00:00 2001 From: Etoh Date: Wed, 24 Jun 2015 00:30:06 +0100 Subject: [PATCH 10/58] Undo removal of unicode() in updateExecutableIcon --- syncplay/ui/GuiConfiguration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 5ec1a7e..e059611 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -134,7 +134,7 @@ class ConfigDialog(QtGui.QDialog): return foundpath def updateExecutableIcon(self): - currentplayerpath = self.executablepathCombobox.currentText() + currentplayerpath = unicode(self.executablepathCombobox.currentText()) iconpath = PlayerFactory().getPlayerIconByPath(currentplayerpath) if iconpath != None and iconpath != "": self.executableiconImage.load(self.resourcespath + iconpath) @@ -906,4 +906,4 @@ class ConfigDialog(QtGui.QDialog): if constants.SHOW_TOOLTIPS: self.processWidget(self, lambda w: self.loadTooltips(w)) self.processWidget(self, lambda w: self.loadValues(w)) - self.processWidget(self, lambda w: self.connectChildren(w)) \ No newline at end of file + self.processWidget(self, lambda w: self.connectChildren(w)) From a5d99c5f2533aea4fbbcbb0a47d99c2296724bd1 Mon Sep 17 00:00:00 2001 From: Et0h Date: Thu, 25 Jun 2015 15:20:12 +0100 Subject: [PATCH 11/58] Append to, rather than replace, mpv audio filters (suggested by wiiaboo) --- syncplay/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index adb63ea..9ab1ada 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -134,7 +134,7 @@ USERLIST_GUI_USERNAME_OFFSET = 21 # Pixels MPLAYER_SLAVE_ARGS = ['-slave', '--hr-seek=always', '-nomsgcolor', '-msglevel', 'all=1:global=4:cplayer=4', '-af', 'scaletempo'] # --quiet works with both mpv 0.2 and 0.3 -MPV_SLAVE_ARGS = ['--force-window', '--idle', '--hr-seek=always', '--quiet', '--keep-open', '--af=scaletempo', '--input-terminal=no', '--input-file=/dev/stdin'] +MPV_SLAVE_ARGS = ['--force-window', '--idle', '--hr-seek=always', '--quiet', '--keep-open', '--af-add=scaletempo', '--input-terminal=no', '--input-file=/dev/stdin'] MPV_SLAVE_ARGS_NEW = ['--term-playing-msg=\nANS_filename=${filename}\nANS_length=${=length}\nANS_path=${path}\n', '--terminal=yes'] MPV_NEW_VERSION = False VLC_SLAVE_ARGS = ['--extraintf=luaintf', '--lua-intf=syncplay', '--no-quiet', '--no-input-fast-seek', From b06edbc91ddb2eec46488f4467f296ebae1dfac3 Mon Sep 17 00:00:00 2001 From: Et0h Date: Tue, 7 Jul 2015 20:35:30 +0100 Subject: [PATCH 12/58] Add icons when file switch is possible --- resources/film_go.png | Bin 0 -> 813 bytes resources/world_go.png | Bin 0 -> 944 bytes syncplay/constants.py | 6 +++++ syncplay/ui/gui.py | 52 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 resources/film_go.png create mode 100644 resources/world_go.png diff --git a/resources/film_go.png b/resources/film_go.png new file mode 100644 index 0000000000000000000000000000000000000000..dd0168ea92e50f4c56768a8a4307774445448f4a GIT binary patch literal 813 zcmV+|1JeA7P)s+KnkY`Eo6h&V=bWA+7NR@wyLq4U|DET3pYsaU)z#lsRm~+9=1NbpotRP@QR?BSgMY-(!CUtKd&Fi%mGr^Lm|%1SMffWkU;#IdzCBL&UGNwUk% z3w3;hXa?gNmU=Q(-80<)DkYZ9q5-ko%lN?eIUdbj+ zNQ|)`L?h9?t^*3D5`Kc)lD^?%tP?)NJ&4LSybYf~_iP~;JICqkrwCgjflLIcSm-hX zC7Z06WmSkScoB}2V{qQ7?_VYKcURCGd(G=TlEDO~ z(CBCi0p}ZRwi-}gR@J|x$~&{u^2NTfu7%?dax5>aMwTT;I$0A5atT96>=BBk3YfJF zh@+)=F+%g#I1b!O-j!j`Lw@>R%*SKs{{1;xde?4ixgL({f}|CJkYXH0d-x8DlCQz4 zG8`#53q@0*s0vhQ7>|siFmpemY*G9g`VjHn7VY{LBE)mzioa$e3H2M{(9cMCAqXOR#lX@1Tgnr->YYTBeqS6v!3>T6kRG3WqCo3x$(B3 zLi6Ce2?`$`uhseluh-j80yoh!G&JN(G)7612HZE7ww8Df;r(Fq7~K;+Jh1#9Xj;Mx rxbAV+2r)%9SQb^qJz1*-|E>Q8ss6iyye%@B00000NkvXXu0mjfIy`F+ literal 0 HcmV?d00001 diff --git a/resources/world_go.png b/resources/world_go.png new file mode 100644 index 0000000000000000000000000000000000000000..aee9c97f8232fd21bdd23804c83eb721ff597a96 GIT binary patch literal 944 zcmV;h15f;kP)Vt{;GNX|PV@wQxfRAQ-FeWBO z_okAVsCzMc+6+1!88|n0#f5DY;>y+0w55ffp3Cpz=VNHC0RRA)nx7m4J_e2fEr1D> zf$Li7b6<{q_X_|3fT6VpU}}ES1g3#EU(cKfCPFP#tOhI>gtQSn;qulcA%*-^2>JFm zr+(T4FtpZ8%})k^KL(S1BPTO2QL%$KxGYx;>U5BTrO?79gvFL~m7AM47lbE{|M1p@ zvJU{5KHA(pGTJssQM!0O10j6WL>(4&6rCX3c8K+IfcD4>45pqv>^k0$0RGbW>L~Ep z>F(G3_hpqWYSffq&yG-Z0#t0sZtPO7RtY8w81XE_$%9;8ywUmn_33;5p~)j(OtcLh zGW;R7d;;`7T4jV=PyEzuAB!7%sQMDYjz>7LEO}MJEJ+SEXMDFFKWs9AW0_c*yVfT8 zdLO<111JQ!Z5P|F5l?92}SW-(dg4!8-@ngsi3V^Oiyy@FAt+W#MZLO z+G>n8Cxq}c`;$Ecm+#=&HP9L%gkegdiWRq#ZuU14t$&X7yov1vXjLB(4@;a4kKLRg z-PA=-YKUFC%EJ2Z6sr?Rp~|N4#O=q$)pS=l!*Bo1(-`ie&YwXjm+`*$kXj*?M4{O% zuhGAMggv*$Cl@{hx*zyFSA6yJ!&%W0X~DEMC>Q=Dk#Mkui`0r-AY6 ziYp9c#&{<6JiCE=qrPunJwM|*-o=^4Szb&J5cfBbh$w7fBc$M|SUag$2d(i=0{##! z(KNT$=DD_V!?IjrCV=O7?_9~=opWmL;fW*1d9-cvk8qg2_BpO{v4zWlWG};=C;2-! z$Cag7CoJ20md|EuhSnN@SH6BZDm-yscyj#Rk@rnx3a!H!K7(Yu!lxHc)7Lu8)up-J z2HoDfaAs*8z|dL)001y@X6Owm") + def getFileSwitchState(self, filename): + if filename: + if self._syncplayClient.userlist.currentUser.file and filename == self._syncplayClient.userlist.currentUser.file['name']: + return constants.FILEITEM_SWITCH_NO_SWITCH + if isURL(filename): + return constants.FILEITEM_SWITCH_FILE_SWITCH + else: + currentPath = self._syncplayClient.userlist.currentUser.file["path"] if self._syncplayClient.userlist.currentUser.file else None + if currentPath: + currentDirectory = os.path.dirname(currentPath) + newPath = os.path.join(currentDirectory, filename) + if os.path.isfile(newPath): + return constants.FILEITEM_SWITCH_FILE_SWITCH + else: + return constants.FILEITEM_SWITCH_NO_SWITCH + else: + return constants.FILEITEM_SWITCH_NO_SWITCH + return constants.FILEITEM_SWITCH_NO_SWITCH + def showUserList(self, currentUser, rooms): self._usertreebuffer = QtGui.QStandardItemModel() self._usertreebuffer.setHorizontalHeaderLabels( @@ -157,6 +199,8 @@ class MainWindow(QtGui.QMainWindow): filesizeitem = QtGui.QStandardItem(formatSize(user.file['size'])) filedurationitem = QtGui.QStandardItem("({})".format(formatTime(user.file['duration']))) filenameitem = QtGui.QStandardItem((user.file['name'])) + fileSwitchState = self.getFileSwitchState(user.file['name']) if room == currentUser.room else None + filenameitem.setData(fileSwitchState, Qt.UserRole + constants.FILEITEM_SWITCH_ROLE) if currentUser.file: sameName = sameFilename(user.file['name'], currentUser.file['name']) sameSize = sameFilesize(user.file['size'], currentUser.file['size']) From c70546a9a3192f79a98d50cf3f029bb0b8642e50 Mon Sep 17 00:00:00 2001 From: Et0h Date: Tue, 7 Jul 2015 21:39:29 +0100 Subject: [PATCH 13/58] Use proper icon for stream switching --- syncplay/ui/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index e608499..d992c75 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -146,7 +146,7 @@ class MainWindow(QtGui.QMainWindow): if self._syncplayClient.userlist.currentUser.file and filename == self._syncplayClient.userlist.currentUser.file['name']: return constants.FILEITEM_SWITCH_NO_SWITCH if isURL(filename): - return constants.FILEITEM_SWITCH_FILE_SWITCH + return constants.FILEITEM_SWITCH_STREAM_SWITCH else: currentPath = self._syncplayClient.userlist.currentUser.file["path"] if self._syncplayClient.userlist.currentUser.file else None if currentPath: From 0245f865883d70bbfe8a48817faa09cecd7ec3ef Mon Sep 17 00:00:00 2001 From: Etoh Date: Sun, 12 Jul 2015 23:54:29 +0100 Subject: [PATCH 14/58] URIs without URL metadata were not being registered for VLC (reported by Phaseout) --- resources/lua/intf/syncplay.lua | 6 ++++-- syncplay/constants.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/resources/lua/intf/syncplay.lua b/resources/lua/intf/syncplay.lua index 4541a34..2cbeee0 100644 --- a/resources/lua/intf/syncplay.lua +++ b/resources/lua/intf/syncplay.lua @@ -5,7 +5,7 @@ Principal author: Etoh Other contributors: DerGenaue, jb Project: http://syncplay.pl/ - Version: 0.2.7 + Version: 0.2.8 Note: * This interface module is intended to be used in conjunction with Syncplay. @@ -84,7 +84,7 @@ You may also need to re-copy the syncplay.lua file when you update VLC. --]==========================================================================] -local connectorversion = "0.2.7" +local connectorversion = "0.2.8" local vlcversion = vlc.misc.version() local durationdelay = 500000 -- Pause for get_duration command etc for increased reliability (uses microseconds) local loopsleepduration = 2500 -- Pause for every event loop (uses microseconds) @@ -331,6 +331,8 @@ function get_filepath () local metas = item:metas() if metas and metas["url"] and string.len(metas["url"]) > 0 then response = metas["url"] + elseif item:uri() and string.len(item:uri()) > 0 then + response = item:uri() else response = unknownstream end diff --git a/syncplay/constants.py b/syncplay/constants.py index dc7dcfd..ab599d3 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -63,7 +63,7 @@ COMMANDS_AUTH = ['a','auth'] COMMANDS_TOGGLE = ['t','toggle'] MPC_MIN_VER = "1.6.4" VLC_MIN_VERSION = "2.2.1" -VLC_INTERFACE_MIN_VERSION = "0.2.7" +VLC_INTERFACE_MIN_VERSION = "0.2.8" VLC_LATENCY_ERROR_THRESHOLD = 2.0 CONTROLLED_ROOMS_MIN_VERSION = "1.3.0" USER_READY_MIN_VERSION = "1.3.0" From 9cbfcd99b281ced8b46bb6a0a6e164c3d46f4711 Mon Sep 17 00:00:00 2001 From: Etoh Date: Sun, 19 Jul 2015 13:49:32 +0100 Subject: [PATCH 15/58] strip URL when comparing difference between URLs and local files to avoid false positive --- syncplay/utils.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/syncplay/utils.py b/syncplay/utils.py index 6bc2828..044c869 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -9,6 +9,7 @@ import itertools import hashlib import random import string +import urllib def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None): """Retry calling the decorated function using an exponential backoff. @@ -158,8 +159,11 @@ def blackholeStdoutForFrozenWindow(): # Relate to file hashing / difference checking: -def stripfilename(filename): +def stripfilename(filename, stripURL): if filename: + if stripURL: + filename = urllib.unquote(filename) + filename = filename.split(u"/")[-1] return re.sub(constants.FILENAME_STRIP_REGEX, "", filename) else: return "" @@ -173,8 +177,8 @@ def stripRoomName(RoomName): else: return "" -def hashFilename(filename): - return hashlib.sha256(stripfilename(filename).encode('utf-8')).hexdigest()[:12] +def hashFilename(filename, stripURL = False): + return hashlib.sha256(stripfilename(filename, stripURL).encode('utf-8')).hexdigest()[:12] def hashFilesize(size): return hashlib.sha256(str(size)).hexdigest()[:12] @@ -190,9 +194,10 @@ def sameHashed(string1raw, string1hashed, string2raw, string2hashed): return True def sameFilename (filename1, filename2): + stripURL = True if isURL(filename1) ^ isURL(filename2) else False if filename1 == constants.PRIVACY_HIDDENFILENAME or filename2 == constants.PRIVACY_HIDDENFILENAME: return True - elif sameHashed(stripfilename(filename1), hashFilename(filename1), stripfilename(filename2), hashFilename(filename2)): + elif sameHashed(stripfilename(filename1, stripURL), hashFilename(filename1, stripURL), stripfilename(filename2, stripURL), hashFilename(filename2, stripURL)): return True else: return False @@ -221,9 +226,10 @@ def meetsMinVersion(version, minVersion): def isURL(path): if path is None: return False - - if "://" in path: + elif "://" in path: return True + else: + return False def getPlayerArgumentsByPathAsArray(arguments, path): if arguments and not isinstance(arguments, (str, unicode)) and arguments.has_key(path): From b9292fcac49e6c52532cb8aedbaea96562b84881 Mon Sep 17 00:00:00 2001 From: Etoh Date: Sun, 19 Jul 2015 13:59:57 +0100 Subject: [PATCH 16/58] For consistent hashing/comparing of filenames, always decode URLs --- syncplay/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/utils.py b/syncplay/utils.py index 044c869..398a77c 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -161,8 +161,8 @@ def blackholeStdoutForFrozenWindow(): def stripfilename(filename, stripURL): if filename: + filename = urllib.unquote(filename) if stripURL: - filename = urllib.unquote(filename) filename = filename.split(u"/")[-1] return re.sub(constants.FILENAME_STRIP_REGEX, "", filename) else: From a82fe7c82c90f16b2d980228f8adc4c3c38cb80b Mon Sep 17 00:00:00 2001 From: Etoh Date: Sun, 19 Jul 2015 14:07:55 +0100 Subject: [PATCH 17/58] Decode URLs shown in GUI userlist --- syncplay/ui/gui.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index d992c75..28c32b2 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -4,6 +4,7 @@ from syncplay import utils, constants, version from syncplay.messages import getMessage import sys import time +import urllib from datetime import datetime import re import os @@ -198,7 +199,10 @@ class MainWindow(QtGui.QMainWindow): if user.file: filesizeitem = QtGui.QStandardItem(formatSize(user.file['size'])) filedurationitem = QtGui.QStandardItem("({})".format(formatTime(user.file['duration']))) - filenameitem = QtGui.QStandardItem((user.file['name'])) + filename = user.file['name'] + if isURL(filename): + filename = urllib.unquote(filename) + filenameitem = QtGui.QStandardItem(filename) fileSwitchState = self.getFileSwitchState(user.file['name']) if room == currentUser.room else None filenameitem.setData(fileSwitchState, Qt.UserRole + constants.FILEITEM_SWITCH_ROLE) if currentUser.file: From 950538740aebe1de77fe3ad1b53290aea63eabf9 Mon Sep 17 00:00:00 2001 From: Etoh Date: Sun, 19 Jul 2015 14:11:46 +0100 Subject: [PATCH 18/58] Show full undecoded path as tooltip in userlist filename items --- syncplay/ui/gui.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 28c32b2..59ad9b1 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -200,9 +200,11 @@ class MainWindow(QtGui.QMainWindow): filesizeitem = QtGui.QStandardItem(formatSize(user.file['size'])) filedurationitem = QtGui.QStandardItem("({})".format(formatTime(user.file['duration']))) filename = user.file['name'] + rawfilename = filename if isURL(filename): filename = urllib.unquote(filename) filenameitem = QtGui.QStandardItem(filename) + filenameitem.setToolTip(rawfilename) fileSwitchState = self.getFileSwitchState(user.file['name']) if room == currentUser.room else None filenameitem.setData(fileSwitchState, Qt.UserRole + constants.FILEITEM_SWITCH_ROLE) if currentUser.file: From ced2a04ef0b08467563aad22f7f43285118a39a3 Mon Sep 17 00:00:00 2001 From: Etoh Date: Sun, 19 Jul 2015 14:15:25 +0100 Subject: [PATCH 19/58] Include file switch icons in Syncplay Windows installer --- buildPy2exe.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/buildPy2exe.py b/buildPy2exe.py index 3f7e0b5..3b3a375 100644 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -653,7 +653,8 @@ guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock '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/world_explore.png', 'resources/application_get.png', 'resources/cog.png', + 'resources/film_go.png', 'resources/world_go.png' ] resources = ["resources/icon.ico", "resources/syncplay.png"] resources.extend(guiIcons) From b3bc4f5f446b8c62bd65ba9926d066f148a1dcd5 Mon Sep 17 00:00:00 2001 From: Etoh Date: Mon, 20 Jul 2015 22:11:30 +0100 Subject: [PATCH 20/58] Use decoded filename in tooltip --- syncplay/ui/gui.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 59ad9b1..06618a7 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -200,11 +200,10 @@ class MainWindow(QtGui.QMainWindow): filesizeitem = QtGui.QStandardItem(formatSize(user.file['size'])) filedurationitem = QtGui.QStandardItem("({})".format(formatTime(user.file['duration']))) filename = user.file['name'] - rawfilename = filename if isURL(filename): filename = urllib.unquote(filename) filenameitem = QtGui.QStandardItem(filename) - filenameitem.setToolTip(rawfilename) + filenameitem.setToolTip(filename) fileSwitchState = self.getFileSwitchState(user.file['name']) if room == currentUser.room else None filenameitem.setData(fileSwitchState, Qt.UserRole + constants.FILEITEM_SWITCH_ROLE) if currentUser.file: From 7b2a623dda4a32c81412e8737722439f74f4cc04 Mon Sep 17 00:00:00 2001 From: RiCON Date: Mon, 3 Aug 2015 19:11:44 +0100 Subject: [PATCH 21/58] Don't overwrite user arguments with style defaults This allows the per-player user arguments to overwrite Syncplay's style defaults in mpv and mplayer2. Be careful that, if you turn off force-window and idle in mpv, you have to supply Syncplay with the filename before opening the video window. --- syncplay/constants.py | 6 +++--- syncplay/players/mplayer.py | 12 +++++++----- syncplay/players/mpv.py | 7 +++++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index ab599d3..7aefb2d 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -134,9 +134,9 @@ USERLIST_GUI_USERNAME_OFFSET = 21 # Pixels USERLIST_GUI_USERNAME_COLUMN = 0 USERLIST_GUI_FILENAME_COLUMN = 3 -MPLAYER_SLAVE_ARGS = ['-slave', '--hr-seek=always', '-nomsgcolor', '-msglevel', 'all=1:global=4:cplayer=4', '-af', 'scaletempo'] -# --quiet works with both mpv 0.2 and 0.3 -MPV_SLAVE_ARGS = ['--force-window', '--idle', '--hr-seek=always', '--quiet', '--keep-open', '--af-add=scaletempo', '--input-terminal=no', '--input-file=/dev/stdin'] +MPLAYER_SLAVE_ARGS = ['-slave', '--hr-seek=always', '-nomsgcolor', '-msglevel', 'all=1:global=4:cplayer=4', '-af-add', 'scaletempo'] +MPV_ARGS = ['--force-window', '--idle', '--hr-seek=always', '--keep-open', '--af-add=scaletempo'] +MPV_SLAVE_ARGS = ['--quiet', '--input-terminal=no', '--input-file=/dev/stdin'] MPV_SLAVE_ARGS_NEW = ['--term-playing-msg=\nANS_filename=${filename}\nANS_length=${=length}\nANS_path=${path}\n', '--terminal=yes'] MPV_NEW_VERSION = False VLC_SLAVE_ARGS = ['--extraintf=luaintf', '--lua-intf=syncplay', '--no-quiet', '--no-input-fast-seek', diff --git a/syncplay/players/mplayer.py b/syncplay/players/mplayer.py index c013b19..dd35762 100644 --- a/syncplay/players/mplayer.py +++ b/syncplay/players/mplayer.py @@ -207,8 +207,12 @@ class MplayerPlayer(BasePlayer): return constants.MPLAYER_ICONPATH @staticmethod - def getStartupArgs(path): - return constants.MPLAYER_SLAVE_ARGS + def getStartupArgs(path, userArgs): + args = [] + if userArgs: + args.extend(userArgs) + args.extend(constants.MPLAYER_SLAVE_ARGS) + return args @staticmethod def isValidPlayerPath(path): @@ -270,9 +274,7 @@ class MplayerPlayer(BasePlayer): filePath = None else: call.extend([filePath]) - if args: - call.extend(args) - call.extend(playerController.getStartupArgs(playerPath)) + call.extend(playerController.getStartupArgs(playerPath, args)) # At least mpv may output escape sequences which result in syncplay # trying to parse something like # "\x1b[?1l\x1b>ANS_filename=blah.mkv". Work around this by diff --git a/syncplay/players/mpv.py b/syncplay/players/mpv.py index 4b79181..4236cef 100644 --- a/syncplay/players/mpv.py +++ b/syncplay/players/mpv.py @@ -22,8 +22,11 @@ class MpvPlayer(MplayerPlayer): return OldMpvPlayer(client, MpvPlayer.getExpandedPath(playerPath), filePath, args) @staticmethod - def getStartupArgs(path): - args = constants.MPV_SLAVE_ARGS + def getStartupArgs(path, userArgs): + args = constants.MPV_ARGS + if userArgs: + args.extend(userArgs) + args.extend(constants.MPV_SLAVE_ARGS) if constants.MPV_NEW_VERSION: args.extend(constants.MPV_SLAVE_ARGS_NEW) return args From 76024846d38bb8633fa406e0ce0c2c66ee996697 Mon Sep 17 00:00:00 2001 From: Andrew Semakin Date: Fri, 14 Aug 2015 13:12:10 +0500 Subject: [PATCH 22/58] Update messages.py Translation to Russian. --- syncplay/messages.py | 182 +++++++++++++++++++++---------------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/syncplay/messages.py b/syncplay/messages.py index def8500..f37d1bb 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -377,7 +377,7 @@ ru = { "retrying-notification" : u"%s, следующая попытка через %d секунд(ы)...", # Seconds "rewind-notification" : u"Перемотано из-за разницы во времени с <{}>", # User - "fastforward-notification" : u"Fast-forwarded due to time difference with <{}>", # User # TODO: Translate into Russian + "fastforward-notification" : u"Ускорено из-за разницы во времени с <{}>", # User "slowdown-notification" : u"Воспроизведение замедлено из-за разницы во времени с <{}>", # User "revert-notification" : u"Возвращаемся к нормальной скорости воспроизведения", @@ -393,39 +393,39 @@ ru = { "playing-notification" : u"<{}> включил '{}' ({})", # User, file, duration "playing-notification/room-addendum" : u" в комнате: '{}'", # Room - "not-all-ready" : u"Not ready: {}", # Usernames # TODO: Translate into Russian - "all-users-ready" : u"Everyone is ready ({} users)", #Number of ready users # TODO: Translate into Russian - "ready-to-unpause-notification" : u"You are now set as ready - unpause again to unpause", # TODO: Translate into Russian - "set-as-ready-notification" : u"You are now set as ready", # TODO: Translate into Russian - "set-as-not-ready-notification" : u"You are now set as not ready", # TODO: Translate into Russian - "autoplaying-notification" : u"Auto-playing in {}...", # TODO: Translate into Russian + "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"Автовоспроизведение через {}...", # TODO: What's the variable? - "identifying-as-controller-notification" : u"Identifying as room operator with password '{}'...", # TODO: Translate into Russian - "failed-to-identify-as-controller-notification" : u"<{}> failed to identify as a room operator.", # TODO: Translate into Russian - "authenticated-as-controller-notification" : u"<{}> authenticated as a room operator", # TODO: Translate into Russian - "created-controlled-room-notification" : u"Created managed room '{}' with password '{}'. Please save this information for future reference!", # RoomName, operatorPassword # TODO: Translate into Russian + "identifying-as-controller-notification" : u"Идентификация как оператора комнаты с паролем '{}'...", + "failed-to-identify-as-controller-notification" : u"<{}> не прошел идентификацию в качестве оператора комнаты.", + "authenticated-as-controller-notification" : u"<{}> вошел как оператор комнаты.", + "created-controlled-room-notification" : u"Создана управляемая комната '{}' с паролем '{}'. Сохраните эти данные!", # RoomName, operatorPassword "file-different-notification" : u"Вероятно, файл, который Вы смотрите, отличается от того, который смотрит <{}>.", # User "file-differences-notification" : u"Ваш файл отличается: {}", # Differences - "room-file-differences" : u"File differences: {}", # File differences (filename, size, and/or duration) - "file-difference-filename" : u"name", # TODO: Translate into Russian - "file-difference-filesize" : u"size", # TODO: Translate into Russian - "file-difference-duration" : u"duration", # TODO: Translate into Russian + "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"В этой комнате кроме Вас никого нет.", "different-filesize-notification" : u" (размер Вашего файла не совпадает с размером их файла!)", - "userlist-playing-notification" : u"{} is playing:", #Username # TODO: Translate into Russian (same as playing-notification?) + "userlist-playing-notification" : u"{} смотрит:", #Username "file-played-by-notification" : u"Файл: {} просматривают:", # File - "no-file-played-notification" : u"{} is not playing a file", # Username # TODO: Translate into Russian + "no-file-played-notification" : u"{} не смотрит ничего", # Username "notplaying-notification" : u"Люди, которые не смотрят ничего:", "userlist-room-notification" : u"В комнате '{}':", # Room - "userlist-file-notification" : u"File", # TODO: Translate into Russian (Файл?) - "controller-userlist-userflag" : u"Operator", # TODO: Translate into Russian (this is to indicate a user is an operator in the ConsoleUI userlist) - "ready-userlist-userflag" : u"Ready", # TODO: Translate into Russian (this is to indicate a user is ready to watch in the ConsoleUI userlist) + "userlist-file-notification" : u"Файл", + "controller-userlist-userflag" : u"Оператор", + "ready-userlist-userflag" : u"Готов", - "update-check-failed-notification" : u"Could not automatically check whether Syncplay {} is up to date. Want to visit http://syncplay.pl/ to manually check for updates?", #Syncplay version # TODO: Translate into Russian - "syncplay-uptodate-notification" : u"Syncplay is up to date", # TODO: Translate into Russian - "syncplay-updateavailable-notification" : u"A new version of Syncplay is available. Do you want to visit the release page?", # TODO: Translate into Russian + "update-check-failed-notification" : u"Невозможно автоматически проверить, что версия Syncplay {} все еще актуальна. Хотите зайти на http://syncplay.pl/ и вручную проверить наличие обновлений?", + "syncplay-uptodate-notification" : u"Syncplay обновлен", + "syncplay-updateavailable-notification" : u"Доступна новая версия Syncplay. Хотите открыть страницу релиза?", "mplayer-file-required-notification" : u"Для использования Syncplay с mplayer необходимо передать файл в качестве параметра", "mplayer-file-required-notification/example" : u"Пример использования: syncplay [options] [url|path/]filename", @@ -439,21 +439,21 @@ ru = { "commandlist-notification/pause" : u"\tp - вкл./выкл. паузу", "commandlist-notification/seek" : u"\t[s][+-]time - перемотать к заданному моменту времени, если не указан + или -, то время считается абсолютным (от начала файла) в секундах или мин:сек", "commandlist-notification/help" : u"\th - помощь", - "commandlist-notification/toggle" : u"\tt - toggles whether you are ready to watch or not", # TODO: Translate into Russian - "commandlist-notification/create" : u"\tc [name] - create managed room using name of current room", # TODO: Translate into Russian - "commandlist-notification/auth" : u"\ta [password] - authenticate as room operator with operator password", # TODO: Translate into Russian + "commandlist-notification/toggle" : u"\tt - переключить статус готов/неготов к просмотру", + "commandlist-notification/create" : u"\tc [name] - создать управляемую комнату с таким же именем, как у текущей", + "commandlist-notification/auth" : u"\ta [password] - авторизоваться как оператор комнаты с помощью пароля", "syncplay-version-notification" : u"Версия Syncplay: {}", # syncplay.version "more-info-notification" : u"Больше информации на {}", # projectURL "gui-data-cleared-notification" : u"Syncplay очистил путь и информацию о состоянии окна, использованного GUI.", - "language-changed-msgbox-label" : u"Language will be changed when you run Syncplay.", # TODO: Translate into Russian - "promptforupdate-label" : u"Is it okay for Syncplay to automatically check for updates from time to time?", # TODO: Translate into Russian + "language-changed-msgbox-label" : u"Язык переключится при следующем запуске SYncplay.", + "promptforupdate-label" : u"Вы не против, если Syncplay будет автоматически изредка проверять наличие обновлений?", "vlc-version-mismatch" : u"Внимание: Вы используете VLC устаревшей версии {}. К сожалению, Syncplay способен работать с VLC {} и выше.", # VLC version, VLC min version "vlc-interface-version-mismatch" : u"Внимание: В используете модуль интерфейса Syncplay устаревшей версии {} для VLC. К сожалению, Syncplay способен работать с версией {} и выше.", # VLC interface version, VLC interface min version "vlc-interface-oldversion-warning" : u"Внимание: Syncplay обнаружил, что старая версия модуля интерфейса Syncplay для VLC уже установлена в директорию VLC. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (http://syncplay.pl/guide/) за инструкциями о том, как установить syncplay.lua.", "vlc-interface-not-installed" : u"Внимание: Модуль интерфейса Syncplay для VLC не обнаружен в директории VLC. По существу, если Вы используете VLC 2.0, то VLC будет использовать модуль syncplay.lua из директории Syncplay, но в таком случае другие пользовательские скрипты и расширения интерфейса не будут работать. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (http://syncplay.pl/guide/) за инструкциями о том, как установить 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 # TODO: Translate into Russian + "media-player-latency-warning": u"Внимание: У Вашего проигрывателя слишком большой отклик ({} секунд). Если Вы замечаете проблемы с синхронизацией, то закройте ресурсоемкие приложения, а если это не помогло - попробуйте другой проигрыватель.", # Seconds to respond # Client prompts "enter-to-exit-prompt" : u"Для выхода нажмите Enter\n", @@ -468,18 +468,18 @@ ru = { "player-path-error" : u"Путь к проигрывателю задан неверно.", "hostname-empty-error" : u"Имя пользователя не может быть пустым.", "empty-error" : u"{} не может быть пустым.", # Configuration - "media-player-error" : u"Ошибка Media player: \"{}\"", # Error line + "media-player-error" : u"Ошибка проигрывателя: \"{}\"", # Error line "unable-import-gui-error" : u"Невозможно импортировать библиотеки GUI (графического интерфейса). Необходимо установить PySide, иначе графический интерфейс не будет работать.", "arguments-missing-error" : u"Некоторые необходимые аргументы отсутствуют, обратитесь к --help", "unable-to-start-client-error" : u"Невозможно запустить клиент", - "player-path-config-error": u"Player path is not set properly", # TODO: Translate into Russian - "no-file-path-config-error" : u"File must be selected before starting your player", # TODO: Translate into Russian - "no-hostname-config-error": u"Hostname can't be empty", # TODO: Translate into Russian - "invalid-port-config-error" : u"Port must be valid", # TODO: Translate into Russian - "empty-value-config-error" : u"{} can't be empty", # Config option # TODO: Translate into Russian + "player-path-config-error": u"Путь к проигрывателю установлен неверно", + "no-file-path-config-error" : u"Файл должен быть указан до включения проигрывателя", + "no-hostname-config-error": u"Имя сервера не может быть пустым", + "invalid-port-config-error" : u"Неверный номер порта", + "empty-value-config-error" : u"Поле '{}' не может быть пустым", # Config option "not-json-error" : u"Не является закодированной json-строкой\n", "hello-arguments-error" : u"Не хватает аргументов Hello\n", @@ -489,13 +489,13 @@ ru = { "vlc-failed-versioncheck" : u"Данная версия VLC не поддерживается Syncplay. Пожалуйста, используйте VLC версии 2 или выше.", "vlc-failed-other" : u"Во время загрузки скрипта интерфейса syncplay.lua в VLC произошла следующая ошибка: {}", # Syncplay Error - "not-supported-by-server-error" : "This feature is not supported by the server. The feature requires a server running Syncplay {}+, but the server is running Syncplay {}.", #minVersion, serverVersion #TODO: Translate into Russian + "not-supported-by-server-error" : "Эта возможность не поддерживается сервером. The feature requires a server running Syncplay {}+, but the server is running Syncplay {}.", #minVersion, serverVersion #TODO: Translate into Russian - "invalid-seek-value" : u"Invalid seek value", # TODO: Translate into Russian - "invalid-offset-value" : u"Invalid offset value", # TODO: Translate into Russian + "invalid-seek-value" : u"Некорректное значение для перемотки", + "invalid-offset-value" : u"Некорректное смещение", - "switch-file-not-found-error" : u"Could not switch to file '{0}' as it was not found in folder '{1}'.", # File not found, folder it was not found in # TODO: Translate into Russian - "switch-no-folder-error" : u"Could not switch to file '{0}'. Syncplay only looks in the folder of the currently playing file, and no file is currently playing.", # File not found # TODO: Translate into Russian + "switch-file-not-found-error" : u"Невозможно переключиться на файл '{0}', т.к. в папке '{1}' его не обнаружено.", # File not found, folder it was not found in + "switch-no-folder-error" : u"Невозможно переключиться на файл '{0}'. Syncplay ищет только в папке проигрываемого файла, а в настоящий момент ничего не проигрывается.", # File not found # Client arguments "argument-description" : u'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC через Интернет.', @@ -529,7 +529,7 @@ ru = { "media-setting-title" : u"Параметры проигрывателя", "executable-path-label" : u"Путь к проигрывателю:", "media-path-label" : u"Путь к видеофайлу:", - "player-arguments-label" : "Player arguments:", # TODO: Translate into Russian + "player-arguments-label" : "Аргументы для запуска проигрывателя:", "browse-label" : u"Выбрать", "more-title" : u"Больше настроек", @@ -540,38 +540,38 @@ ru = { "privacy-dontsend-option" : u"не отпр.", "filename-privacy-label" : u"Имя файла:", "filesize-privacy-label" : u"Размер файла:", - "checkforupdatesautomatically-label" : u"Проверять обновления автоматически", # TODO: Confirm Russian translation + "checkforupdatesautomatically-label" : u"Проверять обновления автоматически", "slowondesync-label" : u"Замедлять при небольших рассинхронизациях (не поддерживаетя в MPC-HC)", "rewindondesync-label" : u"Перемотка при больших рассинхронизациях (настоятельно рекомендуется)", - "dontslowdownwithme-label" : u"Никогда не замедлять или перематывать видео другим", # TODO: Update new wording into Russian (should state "Experimental" in brackets at the end) + "dontslowdownwithme-label" : u"Никогда не замедлять и не перематывать видео другим (функция тестируется)", "pauseonleave-label" : u"Приостанавливать, когда кто-то уходит (например, отключился)", - "readyatstart-label" : u"Set me as 'ready to watch' by default", # TODO: Translate into Russian - "fastforwardondesync-label" : u"Fast-forward if lagging behind (recommended)", # TODO: Translate into Russian + "readyatstart-label" : u"Выставить статус 'готово к просмотру' по умолчанию", + "fastforwardondesync-label" : u"Ускорять видео при отставании (рекомендуется)", "forceguiprompt-label" : u"Не показывать больше этот диалог", # (Inverted) "nostore-label" : u"Не сохранять текущую конфигурацию", # (Inverted) "showosd-label" : u"Включить экранные сообщения (поверх видео)", "showosdwarnings-label" : u"Показывать предупреждения (напр., когда файлы не совпадают)", "showsameroomosd-label" : u"Показывать события Вашей комнаты", - "shownoncontrollerosd-label" : u"Include events from non-operators in managed rooms", # TODO: Translate into Russiann + "shownoncontrollerosd-label" : u"Включить события, связанные с не-операторами в управляемой комнате.", "showdifferentroomosd-label" : u"Показывать события других комнат", "showslowdownosd-label" : u"Показывать уведомления о замедлении/перемотке", - "language-label" : u"Language:", # TODO: Translate into Russian - "automatic-language" : u"Default ({})", # Automatic language # TODO: Translate into Russian + "language-label" : u"Язык:", + "automatic-language" : u"По умолчанию ({})", # Automatic language "showdurationnotification-label" : u"Предупреждать о несовпадении продолжительности видео", "basics-label" : u"Основное", - "readiness-label" : u"Play/Pause", # TODO: Translate into Russian - "misc-label" : u"Misc", # TODO: Translate into Russian + "readiness-label" : u"Воспроизвести/Приостановить", + "misc-label" : u"Прочее", "core-behaviour-title" : "Core room behaviour", # TODO: Translate into Russian "syncplay-internals-title" : u"Syncplay internals", # TODO: Translate into Russian "sync-label" : u"Синхронизация", - "sync-otherslagging-title" : u"If others are lagging behind...", # TODO: Translate into Russian - "sync-youlaggging-title" : u"If you are lagging behind...", # TODO: Translate into Russian + "sync-otherslagging-title" : u"При отставании других зрителей...", + "sync-youlaggging-title" : u"Когда я отстаю ...", "messages-label" : u"Сообщения", - "messages-osd-title" : u"On-screen Display settings", # TODO: Translate into Russian - "messages-other-title" : u"Other display settings", # TODO: Translate into Russian + "messages-osd-title" : u"Настройки OSD", + "messages-other-title" : u"Другие настройки отображения", "privacy-label" : u"Приватность", - "privacy-title" : u"Privacy settings", # TODO: Translate into Russian + "privacy-title" : u"Настройки приватности", "unpause-title" : u"If you press play, set as ready and:", # TODO: Translate into Russian "unpause-ifalreadyready-option" : u"Unpause if already set as ready", # TODO: Translate into Russian "unpause-ifothersready-option" : u"Unpause if already ready or others in room are ready (default)", # TODO: Translate into Russian @@ -590,17 +590,17 @@ ru = { "undoseek-menu-label" : u"Отменить перемотку", "play-menu-label" : u"Play", "pause-menu-label" : u"Пауза", - "playbackbuttons-menu-label" : u"Show playback buttons", # TODO: Translate into Russian - "autoplay-menu-label" : u"Show auto-play button", # TODO: Translate into Russian - "autoplay-guipushbuttonlabel" : u"Auto-play when everyone is ready", # TODO: Translate into Russian - "autoplay-minimum-label" : u"Min users:", # TODO: Translate into Russian + "playbackbuttons-menu-label" : u"Показывать кнопки воспроизведения", + "autoplay-menu-label" : u"Показывать кнопку автовоспроизведения", + "autoplay-guipushbuttonlabel" : u"Воспроизвести автоматически, когда все будут готовы", + "autoplay-minimum-label" : u"Минимум пользователей:", - "ready-guipushbuttonlabel" : u"I'm ready to watch!", # TODO: Translate into Russian + "ready-guipushbuttonlabel" : u"Я готов к просмотру!", "roomuser-heading-label" : u"Комната / Пользователь", - "size-heading-label" : u"Size", # TODO: Translate into Russian - "duration-heading-label" : u"Length", # TODO: Translate into Russian - "filename-heading-label" : u"Filename", # TODO: Translate into Russian + "size-heading-label" : u"Размер", + "duration-heading-label" : u"Длительность", + "filename-heading-label" : u"Имя файла", "notifications-heading-label" : u"Уведомления", "userlist-heading-label" : u"Кто что смотрит", @@ -608,33 +608,33 @@ ru = { "file-menu-label" : u"&Файл", # & precedes shortcut key "openmedia-menu-label" : u"&Открыть видеофайл", - "openstreamurl-menu-label" : u"Open &media stream URL", # TODO: Translate into Russian + "openstreamurl-menu-label" : u"Открыть URL &потокового вещания", "exit-menu-label" : u"&Выход", "advanced-menu-label" : u"&Дополнительно", - "window-menu-label" : u"&Window", # TODO: Translate into Russian + "window-menu-label" : u"&Окна", "setoffset-menu-label" : u"Установить &смещение", - "createcontrolledroom-menu-label" : u"&Create managed room", # TODO: Translate into Russian - "identifyascontroller-menu-label" : u"&Identify as room operator", # TODO: Translate into Russian + "createcontrolledroom-menu-label" : u"&Создать управляемую комнату", + "identifyascontroller-menu-label" : u"&Войти как оператор комнаты", - "playback-menu-label" : u"&Playback", # TODO: Translate into Russian + "playback-menu-label" : u"&Воспроизведение", "help-menu-label" : u"&Помощь", "userguide-menu-label" : u"&Руководство Пользователя", - "update-menu-label" : u"Check for &update", # TODO: Translate into Russian + "update-menu-label" : u"Проверить &обновления", "setoffset-msgbox-label" : u"Установить смещение", - "offsetinfo-msgbox-label" : u"Смещение (см. как использовать на http://syncplay.pl/guide/):", + "offsetinfo-msgbox-label" : u"Смещение (см. инструкцию на странице http://syncplay.pl/guide/):", - "promptforstreamurl-msgbox-label" : u"Open media stream URL", # TODO: Translate into Russian - "promptforstreamurlinfo-msgbox-label" : u"Stream URL", # TODO: Translate into Russian + "promptforstreamurl-msgbox-label" : u"Открыть URL потокового вещания", + "promptforstreamurlinfo-msgbox-label" : u"URL потока", - "createcontrolledroom-msgbox-label" : u"Create managed room", # TODO: Translate into Russian - "controlledroominfo-msgbox-label" : u"Enter name of managed room\r\n(see http://syncplay.pl/guide/ for usage instructions):", # TODO: Translate into Russian + "createcontrolledroom-msgbox-label" : u"Создать управляемую комнату", + "controlledroominfo-msgbox-label" : u"Введите имя управляемой комнаты\r\n(см. инструкцию на странице http://syncplay.pl/guide/):", - "identifyascontroller-msgbox-label" : u"Identify as room operator", # TODO: Translate into Russian - "identifyinfo-msgbox-label" : u"Enter operator password for this room\r\n(see http://syncplay.pl/guide/ for usage instructions):", # TODO: Translate into Russian + "identifyascontroller-msgbox-label" : u"Войти как оператор комнаты", + "identifyinfo-msgbox-label" : u"Введите пароль оператора комнаты\r\n(см. инструкцию на странице http://syncplay.pl/guide/):", - "megabyte-suffix" : u" MB", # Technically it is a mebibyte # TODO: Translate into Russian + "megabyte-suffix" : u" МБ", # Technically it is a mebibyte # Tooltips @@ -645,7 +645,7 @@ ru = { "executable-path-tooltip" : u"Расположение Вашего видеопроигрывателя (MPC-HC, VLC, mplayer2 или mpv).", "media-path-tooltip" : u"Расположение видеофайла или потока для просмотра. Обязательно для mpv и mplayer2.", - "player-arguments-tooltip" : "Additional command line arguments / switches to pass on to this media player.", # TODO: Translate into Russian + "player-arguments-tooltip" : "Передавать дополнительные аргументы командной строки этому проигрывателю.", "more-tooltip" : u"Показать дополнительные настройки.", "filename-privacy-tooltip" : u"Режим приватности для передачи имени воспроизводимого файла на сервер.", @@ -653,23 +653,23 @@ ru = { "privacy-sendraw-tooltip" : u"Отправляет эту информацию без шифрования. Рекомендуемая опция с наибольшей функциональностью.", "privacy-sendhashed-tooltip" : u"Отправляет хэш-сумму этой информации, делая ее невидимой для других пользователей.", "privacy-dontsend-tooltip" : u"Не отправлять эту информацию на сервер. Предоставляет наибольшую приватность.", - "checkforupdatesautomatically-tooltip" : u"Regularly check with the Syncplay website to see whether a new version of Syncplay is available.", # TODO: Translate into Russian + "checkforupdatesautomatically-tooltip" : u"Syncplay будет регулярно заходить на сайт и проверять наличие новых версий.", "slowondesync-tooltip" : u"Временно уменьшить скорость воспроизведения в целях синхронизации с другими зрителями. Не поддерживается в MPC-HC.", "dontslowdownwithme-tooltip" : u"Ваши лаги не будут влиять на других зрителей.", "pauseonleave-tooltip" : u"Приостановить воспроизведение, если Вы покинули комнату или кто-то из зрителей отключился от сервера.", - "readyatstart-tooltip" : u"Set yourself as 'ready' at start (otherwise you are set as 'not ready' until you change your readiness state)", # TODO: Translate into Russian + "readyatstart-tooltip" : u"Отметить Вас готовым к просмотру сразу же (по умолчанию Вы отмечены не готовым)", "forceguiprompt-tooltip" : u"Окно настройки не будет отображаться при открытии файла в Syncplay.", # (Inverted) "nostore-tooltip" : u"Запустить Syncplay с данной конфигурацией, но не сохранять изменения навсегда.", # (Inverted) "rewindondesync-tooltip" : u"Перематывать назад, когда это необходимо для синхронизации. Отключение этой опции может привести к большим рассинхронизациям!", - "fastforwardondesync-tooltip" : u"Jump forward when out of sync with room operator (or your pretend position if 'Never slow down or rewind others' enabled).", # TODO: Translate into Russian + "fastforwardondesync-tooltip" : u"Перематывать вперед при рассинхронизации с оператором комнаты (или если включена опция 'Никогда не замедлять и не перематывать видео другим').", "showosd-tooltip" : u"Отправлять сообщения Syncplay в видеопроигрыватель и отображать их поверх видео (OSD - On Screen Display).", "showosdwarnings-tooltip" : u"Показывать OSC-предупреждения, если проигрываются разные файлы или если Вы в комнате больше никого нет.", "showsameroomosd-tooltip" : u"Показывать OSD-уведомления о событиях, относящихся к комнате, в которой Вы находитесь.", - "shownoncontrollerosd-tooltip" : u"Show OSD notifications for events relating to non-operators who are in controllerd rooms.", # TODO: Translate into Russian + "shownoncontrollerosd-tooltip" : u"Показывать OSD-уведомления о событиях, относящихся к не-операторам в управляемой комнате.", "showdifferentroomosd-tooltip" : u"Показывать OSD-уведомления о событиях, относящихся к любым другим комнатам.", "showslowdownosd-tooltip" : u"Показывать уведомления о замедлении или перемотке в целях синхронизации.", "showdurationnotification-tooltip" : u"Полезно, когда сегмент составного файла отсутствует. Возможны ложные срабатывания.", - "language-tooltip" : u"Language to be used by Syncplay.", # TODO: Translate into Russian + "language-tooltip" : u"Язык, используемый Syncplay.", "unpause-always-tooltip" : u"If you press unpause it always sets you as ready and unpause, rather than just setting you as ready.", # TODO: Translate into Russian "unpause-ifalreadyready-tooltip" : u"If you press unpause when not ready it will set you as ready - press unpause again to unpause.", # TODO: Translate into Russian "unpause-ifothersready-tooltip" : u"If you press unpause when not ready, it will only upause if others are ready.", # TODO: Translate into Russian @@ -680,8 +680,8 @@ ru = { "joinroom-tooltip" : u"Покинуть комнату и зайти в другую, указанную комнату.", "seektime-msgbox-label" : u"Перемотать к определенному моменту времени (указывать в секундах или мин:сек). Используйте +/-, чтобы перемотать вперед/назад относительно настоящего момента.", - "ready-tooltip" : "Indicates whether you are ready to watch.", # TODO: Translate into Russian - "autoplay-tooltip" : "Auto-play when all users who have readiness indicator are ready and minimum user threshold met.", # TODO: Translate into Russian + "ready-tooltip" : "Показывает, готовы ли Вы к просмотру или нет.", + "autoplay-tooltip" : "Автоматическое воспроизведение, когда все пользователи с индикаторами готовности будут готовы и присутствует достаточное число пользователей.", # In-userlist notes (GUI) "differentsize-note" : u"Размер файла не совпадает!", @@ -693,10 +693,10 @@ ru = { "new-syncplay-available-motd-message" : u" Вы используете Syncplay версии {}. Доступна более новая версия на http://syncplay.pl/ . ", # ClientVersion # Server notifications - "welcome-server-notification" : u"Welcome to Syncplay server, ver. {0}", # version # TODO: Translate into Russian - "client-connected-room-server-notification" : u"{0}({2}) connected to room '{1}'", # username, host, room # TODO: Translate into Russian - "client-left-server-notification" : u"{0} left server", # name # TODO: Translate into Russian - "no-salt-notification" : u"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 # TODO: Translate into Russian + "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 # Server arguments "server-argument-description" : u'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC через Интернет. Серверная часть', @@ -704,8 +704,8 @@ ru = { "server-port-argument" : u'номер TCP порта сервера', "server-password-argument" : u'пароль к серверу', "server-isolate-room-argument" : u'должны ли комнаты быть изолированными?', - "server-salt-argument" : u"random string used to generate managed room passwords", # TODO: Translate into Russian (note: as you may already be aware, 'salt' means Соль (криптография)) - "server-disable-ready-argument" : u"disable readiness feature", # TODO: Translate into Russian + "server-salt-argument" : u"генерировать пароли к управляемым комнатам на основании указанной строки (соли)", + "server-disable-ready-argument" : u"отключить статусы готов/не готов", "server-motd-argument" : u"путь к файлу, из которого будет извлекаться MOTD-сообщение", "server-messed-up-motd-unescaped-placeholders" : u"MOTD-сообщение содержит неэкранированные спец.символы. Все знаки $ должны быть продублированы ($$).", "server-messed-up-motd-too-long" : u"MOTD-сообщение слишком длинное: максимальная длина - {} символ(ов), текущая длина - {} символ(ов).", From b4c0b03871757ce637873e83b64ed626bd4343ae Mon Sep 17 00:00:00 2001 From: Etoh Date: Fri, 14 Aug 2015 14:07:40 +0100 Subject: [PATCH 23/58] Unicodify messages --- syncplay/messages.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/syncplay/messages.py b/syncplay/messages.py index f37d1bb..47a05d7 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -307,7 +307,7 @@ en = { "showsameroomosd-tooltip" : "Show OSD notifications for events relating to room user is in.", "shownoncontrollerosd-tooltip" : "Show OSD notifications for events relating to non-operators who are in managed rooms.", "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.", + "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.", @@ -489,7 +489,7 @@ ru = { "vlc-failed-versioncheck" : u"Данная версия VLC не поддерживается Syncplay. Пожалуйста, используйте VLC версии 2 или выше.", "vlc-failed-other" : u"Во время загрузки скрипта интерфейса syncplay.lua в VLC произошла следующая ошибка: {}", # Syncplay Error - "not-supported-by-server-error" : "Эта возможность не поддерживается сервером. The feature requires a server running Syncplay {}+, but the server is running Syncplay {}.", #minVersion, serverVersion #TODO: Translate into Russian + "not-supported-by-server-error" : u"Эта возможность не поддерживается сервером. The feature requires a server running Syncplay {}+, but the server is running Syncplay {}.", #minVersion, serverVersion #TODO: Translate into Russian "invalid-seek-value" : u"Некорректное значение для перемотки", "invalid-offset-value" : u"Некорректное смещение", @@ -529,7 +529,7 @@ ru = { "media-setting-title" : u"Параметры проигрывателя", "executable-path-label" : u"Путь к проигрывателю:", "media-path-label" : u"Путь к видеофайлу:", - "player-arguments-label" : "Аргументы для запуска проигрывателя:", + "player-arguments-label" : u"Аргументы для запуска проигрывателя:", "browse-label" : u"Выбрать", "more-title" : u"Больше настроек", @@ -562,7 +562,7 @@ ru = { "basics-label" : u"Основное", "readiness-label" : u"Воспроизвести/Приостановить", "misc-label" : u"Прочее", - "core-behaviour-title" : "Core room behaviour", # TODO: Translate into Russian + "core-behaviour-title" : u"Core room behaviour", # TODO: Translate into Russian "syncplay-internals-title" : u"Syncplay internals", # TODO: Translate into Russian "sync-label" : u"Синхронизация", "sync-otherslagging-title" : u"При отставании других зрителей...", @@ -645,7 +645,7 @@ ru = { "executable-path-tooltip" : u"Расположение Вашего видеопроигрывателя (MPC-HC, VLC, mplayer2 или mpv).", "media-path-tooltip" : u"Расположение видеофайла или потока для просмотра. Обязательно для mpv и mplayer2.", - "player-arguments-tooltip" : "Передавать дополнительные аргументы командной строки этому проигрывателю.", + "player-arguments-tooltip" : u"Передавать дополнительные аргументы командной строки этому проигрывателю.", "more-tooltip" : u"Показать дополнительные настройки.", "filename-privacy-tooltip" : u"Режим приватности для передачи имени воспроизводимого файла на сервер.", @@ -680,8 +680,8 @@ ru = { "joinroom-tooltip" : u"Покинуть комнату и зайти в другую, указанную комнату.", "seektime-msgbox-label" : u"Перемотать к определенному моменту времени (указывать в секундах или мин:сек). Используйте +/-, чтобы перемотать вперед/назад относительно настоящего момента.", - "ready-tooltip" : "Показывает, готовы ли Вы к просмотру или нет.", - "autoplay-tooltip" : "Автоматическое воспроизведение, когда все пользователи с индикаторами готовности будут готовы и присутствует достаточное число пользователей.", + "ready-tooltip" : u"Показывает, готовы ли Вы к просмотру или нет.", + "autoplay-tooltip" : u"Автоматическое воспроизведение, когда все пользователи с индикаторами готовности будут готовы и присутствует достаточное число пользователей.", # In-userlist notes (GUI) "differentsize-note" : u"Размер файла не совпадает!", @@ -849,7 +849,7 @@ de = { "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-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 + "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 "invalid-seek-value" : u"Ungültige Zeitangabe", "invalid-offset-value" : u"Ungültiger Offset-Wert", @@ -889,7 +889,7 @@ de = { "media-setting-title" : u"Media-Player Einstellungen", "executable-path-label" : u"Pfad zum Media-Player:", "media-path-label" : u"Pfad zur Datei:", - "player-arguments-label" : "Player arguments:", # TODO: Translate into German + "player-arguments-label" : u"Player arguments:", # TODO: Translate into German "browse-label" : u"Durchsuchen", "more-title" : u"Mehr Einstellungen zeigen", @@ -1003,7 +1003,7 @@ de = { "executable-path-tooltip" : u"Pfad zum ausgewählten, unterstützten Mediaplayer (MPC-HC, VLC, mplayer2 or mpv).", "media-path-tooltip" : u"Pfad zum wiederzugebenden Video oder Stream. Notwendig für mpv und mplayer2.", - "player-arguments-tooltip" : "Additional command line arguments / switches to pass on to this media player.", # TODO: Translate into German + "player-arguments-tooltip" : u"Additional command line arguments / switches to pass on to this media player.", # TODO: Translate into German "more-tooltip" : u"Weitere Einstellungen anzeigen.", "filename-privacy-tooltip" : u"Privatheitsmodus beim Senden des Namens der aktuellen Datei zum Server.", @@ -1040,8 +1040,8 @@ de = { "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" : "Zeigt an, ob du bereit zum anschauen bist", - "autoplay-tooltip" : "Automatisch abspielen, wenn alle Nutzer bereit sind und die minimale Nutzerzahl erreicht ist.", + "ready-tooltip" : u"Zeigt an, ob du bereit zum anschauen bist", + "autoplay-tooltip" : u"Automatisch abspielen, wenn alle Nutzer bereit sind und die minimale Nutzerzahl erreicht ist.", # In-userlist notes (GUI) "differentsize-note" : u"Verschiedene Größe!", From 57db3c257a788370c12308044e1c3e1aaf5abc1e Mon Sep 17 00:00:00 2001 From: Etoh Date: Fri, 14 Aug 2015 14:17:08 +0100 Subject: [PATCH 24/58] Shorten RU translation of "play/pause" so it fits --- syncplay/messages.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/messages.py b/syncplay/messages.py index 47a05d7..78d7368 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -560,7 +560,7 @@ ru = { "automatic-language" : u"По умолчанию ({})", # Automatic language "showdurationnotification-label" : u"Предупреждать о несовпадении продолжительности видео", "basics-label" : u"Основное", - "readiness-label" : u"Воспроизвести/Приостановить", + "readiness-label" : u"Воспроизведение/Пауза", # TODO: Confirm translation of play/pause "misc-label" : u"Прочее", "core-behaviour-title" : u"Core room behaviour", # TODO: Translate into Russian "syncplay-internals-title" : u"Syncplay internals", # TODO: Translate into Russian From 0a88887953973f079f95c513bb0c849860846422 Mon Sep 17 00:00:00 2001 From: Et0h Date: Fri, 14 Aug 2015 15:59:39 +0100 Subject: [PATCH 25/58] Switch file now looks in user-specified media directories --- syncplay/messages.py | 15 +++++++++------ syncplay/ui/ConfigurationGetter.py | 4 +++- syncplay/ui/GuiConfiguration.py | 17 ++++++++++++++++- syncplay/ui/gui.py | 14 +++++++++++--- syncplay/utils.py | 15 +++++++++++++++ 5 files changed, 54 insertions(+), 11 deletions(-) diff --git a/syncplay/messages.py b/syncplay/messages.py index 78d7368..bc014aa 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -134,8 +134,7 @@ en = { "invalid-seek-value" : u"Invalid seek value", "invalid-offset-value" : u"Invalid offset value", - "switch-file-not-found-error" : u"Could not switch to file '{0}' as it was not found in folder '{1}'.", # File not found, folder it was not found in - "switch-no-folder-error" : u"Could not switch to file '{0}'. Syncplay only looks in the folder of the currently playing file, and no file is currently playing.", # File not found + "switch-file-not-found-error" : u"Could not switch to file '{0}'. Syncplay looks in the folder of the currently playing file and specified media directories.", # File not found # Client arguments "argument-description" : 'Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network.', @@ -204,6 +203,7 @@ 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)", "sync-label" : "Sync", "sync-otherslagging-title" : "If others are lagging behind...", "sync-youlaggging-title" : "If you are lagging behind...", @@ -286,6 +286,7 @@ en = { "executable-path-tooltip" : "Location of your chosen supported media player (MPC-HC, VLC, mplayer2 or mpv).", "media-path-tooltip" : "Location of video or stream to be opened. Necessary for mpv and 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.", "more-tooltip" : "Display less frequently used settings.", "filename-privacy-tooltip" : "Privacy mode for sending currently playing filename to server.", @@ -494,8 +495,7 @@ ru = { "invalid-seek-value" : u"Некорректное значение для перемотки", "invalid-offset-value" : u"Некорректное смещение", - "switch-file-not-found-error" : u"Невозможно переключиться на файл '{0}', т.к. в папке '{1}' его не обнаружено.", # File not found, folder it was not found in - "switch-no-folder-error" : u"Невозможно переключиться на файл '{0}'. Syncplay ищет только в папке проигрываемого файла, а в настоящий момент ничего не проигрывается.", # File not found + "switch-file-not-found-error" : u"Невозможно переключиться на файл '{0}'. Syncplay looks in the folder of the currently playing file and specified media directories.", # File not found # TODO: Translate last part into Russian # Client arguments "argument-description" : u'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC через Интернет.', @@ -564,6 +564,7 @@ ru = { "misc-label" : u"Прочее", "core-behaviour-title" : u"Core room behaviour", # TODO: Translate into Russian "syncplay-internals-title" : u"Syncplay internals", # TODO: Translate into Russian + "syncplay-mediasearchdirectories-title" : u"Directories to search for media (one path per line)", # TODO: Translate into Russian "sync-label" : u"Синхронизация", "sync-otherslagging-title" : u"При отставании других зрителей...", "sync-youlaggging-title" : u"Когда я отстаю ...", @@ -646,6 +647,7 @@ ru = { "executable-path-tooltip" : u"Расположение Вашего видеопроигрывателя (MPC-HC, VLC, mplayer2 или mpv).", "media-path-tooltip" : u"Расположение видеофайла или потока для просмотра. Обязательно для mpv и mplayer2.", "player-arguments-tooltip" : u"Передавать дополнительные аргументы командной строки этому проигрывателю.", + "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.", # TODO: Translate into Russian "more-tooltip" : u"Показать дополнительные настройки.", "filename-privacy-tooltip" : u"Режим приватности для передачи имени воспроизводимого файла на сервер.", @@ -854,8 +856,7 @@ de = { "invalid-seek-value" : u"Ungültige Zeitangabe", "invalid-offset-value" : u"Ungültiger Offset-Wert", - "switch-file-not-found-error" : u"Could not switch to file '{0}' as it was not found in folder '{1}'.", # File not found, folder it was not found in # TODO: Translate into German - "switch-no-folder-error" : u"Could not switch to file '{0}'. Syncplay only looks in the folder of the currently playing file, and no file is currently playing.", # File not found # TODO: Translate into German + "switch-file-not-found-error" : u"Could not switch to file '{0}'. Syncplay looks in the folder of the currently playing file and specified media directories.", # File not found, folder it was not found in # TODO: Translate into German # Client arguments "argument-description" : u'Syncplay ist eine Anwendung um mehrere MPlayer, MPC-HC und VLC-Instanzen über das Internet zu synchronisieren.', @@ -922,6 +923,7 @@ de = { "misc-label" : u"Diverse", "core-behaviour-title" : u"Verhalten des Raumes", "syncplay-internals-title" : u"Syncplay intern", + "syncplay-mediasearchdirectories-title" : u"Directories to search for media (one path per line)", # TODO: Translate into Russian "sync-label" : u"Synchronisation", "sync-otherslagging-title" : u"Wenn andere laggen...", "sync-youlaggging-title" : u"Wenn du laggst...", @@ -1004,6 +1006,7 @@ de = { "executable-path-tooltip" : u"Pfad zum ausgewählten, unterstützten Mediaplayer (MPC-HC, VLC, mplayer2 or mpv).", "media-path-tooltip" : u"Pfad zum wiederzugebenden Video oder Stream. Notwendig für mpv und mplayer2.", "player-arguments-tooltip" : u"Additional command line arguments / switches to pass on to this media player.", # TODO: Translate into German + "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.", # TODO: Translate into German "more-tooltip" : u"Weitere Einstellungen anzeigen.", "filename-privacy-tooltip" : u"Privatheitsmodus beim Senden des Namens der aktuellen Datei zum Server.", diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index c73cf60..0fa6284 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -33,6 +33,7 @@ class ConfigurationGetter(object): "password": None, "playerPath": None, "perPlayerArguments": None, + "mediaSearchDirectories": None, "file": None, "playerArgs": [], "playerClass": None, @@ -108,6 +109,7 @@ class ConfigurationGetter(object): self._serialised = [ "perPlayerArguments", + "mediaSearchDirectories", ] self._numeric = [ @@ -119,7 +121,7 @@ class ConfigurationGetter(object): self._iniStructure = { "server_data": ["host", "port", "password"], - "client_settings": ["name", "room", "playerPath", "perPlayerArguments", "slowdownThreshold", "rewindThreshold", "fastforwardThreshold", "slowOnDesync", "rewindOnDesync", "fastforwardOnDesync", "dontSlowDownWithMe", "forceGuiPrompt", "filenamePrivacyMode", "filesizePrivacyMode", "unpauseAction", "pauseOnLeave", "readyAtStart", "autoplayMinUsers", "autoplayInitialState"], + "client_settings": ["name", "room", "playerPath", "perPlayerArguments", "slowdownThreshold", "rewindThreshold", "fastforwardThreshold", "slowOnDesync", "rewindOnDesync", "fastforwardOnDesync", "dontSlowDownWithMe", "forceGuiPrompt", "filenamePrivacyMode", "filesizePrivacyMode", "unpauseAction", "pauseOnLeave", "readyAtStart", "autoplayMinUsers", "autoplayInitialState", "mediaSearchDirectories"], "gui": ["showOSD", "showOSDWarnings", "showSlowdownOSD", "showDifferentRoomOSD", "showSameRoomOSD", "showNonControllerOSD", "showDurationNotification"], "general": ["language", "checkForUpdatesAutomatically", "lastCheckedForUpdates"] } diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index e059611..c9b5539 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -1,6 +1,6 @@ from PySide import QtCore, QtGui from PySide.QtCore import QSettings, Qt, QCoreApplication, QUrl -from PySide.QtGui import QApplication, QLineEdit, QCursor, QLabel, QCheckBox, QDesktopServices, QIcon, QImage, QButtonGroup, QRadioButton, QDoubleSpinBox +from PySide.QtGui import QApplication, QLineEdit, QCursor, QLabel, QCheckBox, QDesktopServices, QIcon, QImage, QButtonGroup, QRadioButton, QDoubleSpinBox, QPlainTextEdit from syncplay.players.playerFactory import PlayerFactory from datetime import datetime from syncplay import utils @@ -250,6 +250,7 @@ class ConfigDialog(QtGui.QDialog): self.loadLastUpdateCheckDate() self.config["perPlayerArguments"] = self.perPlayerArgs + self.config["mediaSearchDirectories"] = utils.convertMultilineStringToList(self.mediasearchTextEdit.toPlainText()) self.processWidget(self, lambda w: self.saveValues(w)) if self.hostTextbox.text(): @@ -381,6 +382,7 @@ class ConfigDialog(QtGui.QDialog): host = config['host'] + ":" + str(config['port']) self.perPlayerArgs = self.config["perPlayerArguments"] + self.mediaSearchDirectories = self.config["mediaSearchDirectories"] self.connectionSettingsGroup = QtGui.QGroupBox(getMessage("connection-group-title")) self.hostTextbox = QLineEdit(host, self) @@ -597,8 +599,21 @@ class ConfigDialog(QtGui.QDialog): self.automaticupdatesCheckbox.setObjectName("checkForUpdatesAutomatically") self.internalSettingsLayout.addWidget(self.automaticupdatesCheckbox) + ## Media path directories + + self.mediasearchSettingsGroup = QtGui.QGroupBox(getMessage("syncplay-mediasearchdirectories-title")) + self.mediasearchSettingsLayout = QtGui.QVBoxLayout() + self.mediasearchSettingsGroup.setLayout(self.mediasearchSettingsLayout) + + self.mediasearchTextEdit = QPlainTextEdit(utils.getListAsMultilineString(self.mediaSearchDirectories)) + self.mediasearchTextEdit.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "mediasearcdirectories-arguments") + self.mediasearchTextEdit.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap) + self.mediasearchSettingsLayout.addWidget(self.mediasearchTextEdit) + self.mediasearchSettingsGroup.setMaximumHeight(self.mediasearchSettingsGroup.minimumSizeHint().height()) + self.miscLayout.addWidget(self.coreSettingsGroup) self.miscLayout.addWidget(self.internalSettingsGroup) + self.miscLayout.addWidget(self.mediasearchSettingsGroup) self.miscLayout.setAlignment(Qt.AlignTop) self.stackedLayout.addWidget(self.miscFrame) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 06618a7..3bff6cf 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -144,13 +144,18 @@ class MainWindow(QtGui.QMainWindow): def getFileSwitchState(self, filename): if filename: + if filename == getMessage("nofile-note"): + return constants.FILEITEM_SWITCH_NO_SWITCH if self._syncplayClient.userlist.currentUser.file and filename == self._syncplayClient.userlist.currentUser.file['name']: return constants.FILEITEM_SWITCH_NO_SWITCH if isURL(filename): return constants.FILEITEM_SWITCH_STREAM_SWITCH else: currentPath = self._syncplayClient.userlist.currentUser.file["path"] if self._syncplayClient.userlist.currentUser.file else None - if currentPath: + if utils.findFilenameInDirectories(filename, self.config["mediaSearchDirectories"]): + return constants.FILEITEM_SWITCH_FILE_SWITCH + + elif currentPath: currentDirectory = os.path.dirname(currentPath) newPath = os.path.join(currentDirectory, filename) if os.path.isfile(newPath): @@ -293,7 +298,10 @@ class MainWindow(QtGui.QMainWindow): self._syncplayClient._player.openFile(filename) else: currentPath = self._syncplayClient.userlist.currentUser.file["path"] if self._syncplayClient.userlist.currentUser.file else None - if currentPath: + pathFound = utils.findFilenameInDirectories(filename, self.config["mediaSearchDirectories"]) + if pathFound: + self._syncplayClient._player.openFile(pathFound) + elif currentPath: currentDirectory = os.path.dirname(currentPath) newPath = os.path.join(currentDirectory, filename) if os.path.isfile(newPath): @@ -301,7 +309,7 @@ class MainWindow(QtGui.QMainWindow): else: self.showErrorMessage(getMessage("switch-file-not-found-error").format(filename, currentDirectory)) else: - self.showErrorMessage(getMessage("switch-no-folder-error").format(filename)) + self.showErrorMessage(getMessage("switch-file-not-found-error").format(filename)) @needsClient def userListChange(self): diff --git a/syncplay/utils.py b/syncplay/utils.py index 398a77c..ecada19 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -241,6 +241,21 @@ def getPlayerArgumentsByPathAsText(arguments, path): argsToReturn = getPlayerArgumentsByPathAsArray(arguments, path) return " ".join(argsToReturn) if argsToReturn else "" +def getListAsMultilineString(pathArray): + return u"\n".join(pathArray) if pathArray else "" + +def convertMultilineStringToList(multilineString): + return unicode.split(multilineString,u"\n") if multilineString else "" + +def findFilenameInDirectories(filename, directoryList): + if filename and directoryList: + for directory in directoryList: + for root, dirs, files in os.walk(directory): + candidatePath = os.path.join(root,filename) + if os.path.isfile(candidatePath): + return candidatePath + return None + class RoomPasswordProvider(object): CONTROLLED_ROOM_REGEX = re.compile("^\+(.*):(\w{12})$") PASSWORD_REGEX = re.compile("[A-Z]{2}-\d{3}-\d{3}") From bedf14903af6f999d572e4ad080198c74928ac0c Mon Sep 17 00:00:00 2001 From: Et0h Date: Fri, 14 Aug 2015 16:00:37 +0100 Subject: [PATCH 26/58] Upver to Build 14 --- syncplay/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index cfcf059..6698fa9 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ version = '1.3.1' milestone = 'Chami' -release_number = '13' +release_number = '14' projectURL = 'http://syncplay.pl/' From 10427d27b537b6422fbd380ec424a7d66702c1b0 Mon Sep 17 00:00:00 2001 From: Et0h Date: Fri, 14 Aug 2015 16:53:02 +0100 Subject: [PATCH 27/58] Upver to v1.3.2 --- syncplay/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 6698fa9..44393ea 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ -version = '1.3.1' +version = '1.3.2' milestone = 'Chami' release_number = '14' projectURL = 'http://syncplay.pl/' From c66ae60d43191e9c9d5aad63c89655c0578672c2 Mon Sep 17 00:00:00 2001 From: Et0h Date: Sat, 15 Aug 2015 18:34:18 +0100 Subject: [PATCH 28/58] Note double click to switch in file tooltip (r5) --- syncplay/__init__.py | 2 +- syncplay/messages.py | 3 +++ syncplay/ui/gui.py | 7 +++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 44393ea..a2a77ef 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ version = '1.3.2' milestone = 'Chami' -release_number = '14' +release_number = '15' projectURL = 'http://syncplay.pl/' diff --git a/syncplay/messages.py b/syncplay/messages.py index bc014aa..8179cfe 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -323,6 +323,7 @@ en = { "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" : "Double click to switch to {}", # Filename # In-userlist notes (GUI) "differentsize-note" : "Different size!", @@ -684,6 +685,7 @@ ru = { "seektime-msgbox-label" : u"Перемотать к определенному моменту времени (указывать в секундах или мин:сек). Используйте +/-, чтобы перемотать вперед/назад относительно настоящего момента.", "ready-tooltip" : u"Показывает, готовы ли Вы к просмотру или нет.", "autoplay-tooltip" : u"Автоматическое воспроизведение, когда все пользователи с индикаторами готовности будут готовы и присутствует достаточное число пользователей.", + "switch-to-file-tooltip" : u"Double click to switch to {}", # Filename # TODO: Translate to Russian # In-userlist notes (GUI) "differentsize-note" : u"Размер файла не совпадает!", @@ -1045,6 +1047,7 @@ de = { "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 und die minimale Nutzerzahl erreicht ist.", + "switch-to-file-tooltip" : u"Double click to switch to {}", # Filename # TODO: Translate to German # In-userlist notes (GUI) "differentsize-note" : u"Verschiedene Größe!", diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 3bff6cf..b99c94f 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -154,7 +154,6 @@ class MainWindow(QtGui.QMainWindow): currentPath = self._syncplayClient.userlist.currentUser.file["path"] if self._syncplayClient.userlist.currentUser.file else None if utils.findFilenameInDirectories(filename, self.config["mediaSearchDirectories"]): return constants.FILEITEM_SWITCH_FILE_SWITCH - elif currentPath: currentDirectory = os.path.dirname(currentPath) newPath = os.path.join(currentDirectory, filename) @@ -208,8 +207,12 @@ class MainWindow(QtGui.QMainWindow): if isURL(filename): filename = urllib.unquote(filename) filenameitem = QtGui.QStandardItem(filename) - filenameitem.setToolTip(filename) fileSwitchState = self.getFileSwitchState(user.file['name']) if room == currentUser.room else None + if fileSwitchState != constants.FILEITEM_SWITCH_NO_SWITCH: + filenameTooltip = getMessage("switch-to-file-tooltip").format(filename) + else: + filenameTooltip = filename + filenameitem.setToolTip(filenameTooltip) filenameitem.setData(fileSwitchState, Qt.UserRole + constants.FILEITEM_SWITCH_ROLE) if currentUser.file: sameName = sameFilename(user.file['name'], currentUser.file['name']) From bb2308c0c9a64a378e40d474c4b827d383fe6132 Mon Sep 17 00:00:00 2001 From: Et0h Date: Tue, 25 Aug 2015 10:01:41 +0100 Subject: [PATCH 29/58] Use first mediaSearch directory as first choice for open file initial directory, followed by directory of currently open file --- syncplay/ui/GuiConfiguration.py | 4 +++- syncplay/ui/gui.py | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index c9b5539..12996f6 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -229,7 +229,9 @@ class ConfigDialog(QtGui.QDialog): def browseMediapath(self): self.loadMediaBrowseSettings() options = QtGui.QFileDialog.Options() - if os.path.isdir(self.mediadirectory): + if self.config["mediaSearchDirectories"] and os.path.isdir(self.config["mediaSearchDirectories"][0]): + defaultdirectory = self.config["mediaSearchDirectories"][0] + elif os.path.isdir(self.mediadirectory): defaultdirectory = self.mediadirectory elif os.path.isdir(QDesktopServices.storageLocation(QDesktopServices.MoviesLocation)): defaultdirectory = QDesktopServices.storageLocation(QDesktopServices.MoviesLocation) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index b99c94f..a19762c 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -417,7 +417,13 @@ class MainWindow(QtGui.QMainWindow): self.loadMediaBrowseSettings() options = QtGui.QFileDialog.Options() - if os.path.isdir(self.mediadirectory): + self.mediadirectory = "" + currentdirectory = os.path.dirname(self._syncplayClient.userlist.currentUser.file["path"]) if self._syncplayClient.userlist.currentUser.file else None + if currentdirectory and os.path.isdir(currentdirectory): + defaultdirectory = currentdirectory + elif self.config["mediaSearchDirectories"] and os.path.isdir(self.config["mediaSearchDirectories"][0]): + defaultdirectory = self.config["mediaSearchDirectories"][0] + elif os.path.isdir(self.mediadirectory): defaultdirectory = self.mediadirectory elif os.path.isdir(QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.MoviesLocation)): defaultdirectory = QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.MoviesLocation) From 88775149872309a247d7ca6425f54f54c5fd07ce Mon Sep 17 00:00:00 2001 From: Et0h Date: Tue, 25 Aug 2015 10:02:40 +0100 Subject: [PATCH 30/58] Clarify mediasearch tooltip --- syncplay/messages.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syncplay/messages.py b/syncplay/messages.py index 8179cfe..ba560c6 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -286,7 +286,7 @@ en = { "executable-path-tooltip" : "Location of your chosen supported media player (MPC-HC, VLC, mplayer2 or mpv).", "media-path-tooltip" : "Location of video or stream to be opened. Necessary for mpv and 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.", + "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.", "more-tooltip" : "Display less frequently used settings.", "filename-privacy-tooltip" : "Privacy mode for sending currently playing filename to server.", @@ -648,7 +648,7 @@ ru = { "executable-path-tooltip" : u"Расположение Вашего видеопроигрывателя (MPC-HC, VLC, mplayer2 или mpv).", "media-path-tooltip" : u"Расположение видеофайла или потока для просмотра. Обязательно для mpv и mplayer2.", "player-arguments-tooltip" : u"Передавать дополнительные аргументы командной строки этому проигрывателю.", - "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.", # TODO: Translate into Russian + "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.", # TODO: Translate into Russian "more-tooltip" : u"Показать дополнительные настройки.", "filename-privacy-tooltip" : u"Режим приватности для передачи имени воспроизводимого файла на сервер.", @@ -1008,7 +1008,7 @@ de = { "executable-path-tooltip" : u"Pfad zum ausgewählten, unterstützten Mediaplayer (MPC-HC, VLC, mplayer2 or mpv).", "media-path-tooltip" : u"Pfad zum wiederzugebenden Video oder Stream. Notwendig für mpv und mplayer2.", "player-arguments-tooltip" : u"Additional command line arguments / switches to pass on to this media player.", # TODO: Translate into German - "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.", # TODO: Translate into German + "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.", # TODO: Translate into German "more-tooltip" : u"Weitere Einstellungen anzeigen.", "filename-privacy-tooltip" : u"Privatheitsmodus beim Senden des Namens der aktuellen Datei zum Server.", From f0f29653cee9d49769a2c78e04a05f85b96da2c7 Mon Sep 17 00:00:00 2001 From: Et0h Date: Wed, 26 Aug 2015 15:52:39 +0100 Subject: [PATCH 31/58] Add 1 second timeout for media search, and make it more efficient --- syncplay/constants.py | 2 ++ syncplay/messages.py | 3 +++ syncplay/ui/gui.py | 22 ++++++++++++++++++++-- syncplay/utils.py | 10 +++++++--- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index 7aefb2d..59b406f 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -48,6 +48,8 @@ SERVER_STATE_INTERVAL = 1 WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1 AUTOPLAY_DELAY = 3.0 SYNC_ON_PAUSE = True # Client seek to global position - subtitles may disappear on some media players +FOLDER_SEARCH_TIMEOUT = 1.0 # Secs + #Usually there's no need to adjust these LAST_PAUSED_DIFF_THRESHOLD = 2 FILENAME_STRIP_REGEX = u"[-~_\.\[\](): ]" diff --git a/syncplay/messages.py b/syncplay/messages.py index ba560c6..2e9f336 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -135,6 +135,7 @@ en = { "invalid-offset-value" : u"Invalid offset value", "switch-file-not-found-error" : u"Could not switch to file '{0}'. Syncplay looks in the folder of the currently playing file and specified media directories.", # File not found + "folder-search-timeout-error" : u"The search for media in '{}' was aborted as it took too long. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. Until Syncplay is restarted only the directory of the currently open file will be checked.", #Folder # Client arguments "argument-description" : 'Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network.', @@ -497,6 +498,7 @@ ru = { "invalid-offset-value" : u"Некорректное смещение", "switch-file-not-found-error" : u"Невозможно переключиться на файл '{0}'. Syncplay looks in the folder of the currently playing file and specified media directories.", # File not found # TODO: Translate last part into Russian + "folder-search-timeout-error" : u"The search for media in '{}' was aborted as it took too long. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. Until Syncplay is restarted only the directory of the currently open file will be checked.", #Folder # TODO: Translate into Russian # Client arguments "argument-description" : u'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC через Интернет.', @@ -859,6 +861,7 @@ de = { "invalid-offset-value" : u"Ungültiger Offset-Wert", "switch-file-not-found-error" : u"Could not switch to file '{0}'. Syncplay looks in the folder of the currently playing file and specified media directories.", # File not found, folder it was not found in # TODO: Translate into German + "folder-search-timeout-error" : u"The search for media in '{}' was aborted as it took too long. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. Until Syncplay is restarted only the directory of the currently open file will be checked.", #Folder # TODO: Translate into German # Client arguments "argument-description" : u'Syncplay ist eine Anwendung um mehrere MPlayer, MPC-HC und VLC-Instanzen über das Internet zu synchronisieren.', diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index a19762c..cc001fd 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -152,7 +152,16 @@ class MainWindow(QtGui.QMainWindow): return constants.FILEITEM_SWITCH_STREAM_SWITCH else: currentPath = self._syncplayClient.userlist.currentUser.file["path"] if self._syncplayClient.userlist.currentUser.file else None - if utils.findFilenameInDirectories(filename, self.config["mediaSearchDirectories"]): + if self.folderSearchEnabled: + try: + filenamesInDirectories = utils.findFilenameInDirectories(filename, self.config["mediaSearchDirectories"]) + except IOError as errorMessage: + self.showErrorMessage(errorMessage) + filenamesInDirectories = None + self.folderSearchEnabled = False + else: + filenamesInDirectories = None + if filenamesInDirectories: return constants.FILEITEM_SWITCH_FILE_SWITCH elif currentPath: currentDirectory = os.path.dirname(currentPath) @@ -301,7 +310,15 @@ class MainWindow(QtGui.QMainWindow): self._syncplayClient._player.openFile(filename) else: currentPath = self._syncplayClient.userlist.currentUser.file["path"] if self._syncplayClient.userlist.currentUser.file else None - pathFound = utils.findFilenameInDirectories(filename, self.config["mediaSearchDirectories"]) + if self.folderSearchEnabled: + try: + pathFound = utils.findFilenameInDirectories(filename, self.config["mediaSearchDirectories"]) + except IOError as errorMessage: + self.showErrorMessage(errorMessage) + pathFound = None + self.folderSearchEnabled = False + else: + pathFound = None if pathFound: self._syncplayClient._player.openFile(pathFound) elif currentPath: @@ -907,6 +924,7 @@ class MainWindow(QtGui.QMainWindow): def __init__(self): super(MainWindow, self).__init__() self._syncplayClient = None + self.folderSearchEnabled = True self.QtGui = QtGui if sys.platform.startswith('win'): self.resourcespath = utils.findWorkingDir() + "\\resources\\" diff --git a/syncplay/utils.py b/syncplay/utils.py index ecada19..2e76541 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -11,6 +11,8 @@ import random import string import urllib +folderSearchEnabled = True + def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None): """Retry calling the decorated function using an exponential backoff. @@ -249,11 +251,13 @@ def convertMultilineStringToList(multilineString): def findFilenameInDirectories(filename, directoryList): if filename and directoryList: + startTime = time.time() for directory in directoryList: for root, dirs, files in os.walk(directory): - candidatePath = os.path.join(root,filename) - if os.path.isfile(candidatePath): - return candidatePath + if filename in files: + return os.path.join(root,filename) + if time.time() - startTime > constants.FOLDER_SEARCH_TIMEOUT: + raise IOError(getMessage("folder-search-timeout-error").format(directory)) return None class RoomPasswordProvider(object): From 411808b3483ecbe9dbb7a17a2d3a84805c456dac Mon Sep 17 00:00:00 2001 From: Et0h Date: Wed, 26 Aug 2015 19:25:07 +0100 Subject: [PATCH 32/58] Add "find public server" feature --- buildPy2exe.py | 2 +- resources/report_magnify.png | Bin 0 -> 738 bytes syncplay/constants.py | 3 ++- syncplay/messages.py | 15 +++++++++++++ syncplay/ui/GuiConfiguration.py | 36 +++++++++++++++++++++++++++++--- syncplay/utils.py | 21 ++++++++++++++++++- 6 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 resources/report_magnify.png diff --git a/buildPy2exe.py b/buildPy2exe.py index 3b3a375..24175c1 100644 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -654,7 +654,7 @@ guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock '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/film_go.png', 'resources/world_go.png' + 'resources/film_go.png', 'resources/world_go.png', 'resources/report_magnify.png' ] resources = ["resources/icon.ico", "resources/syncplay.png"] resources.extend(guiIcons) diff --git a/resources/report_magnify.png b/resources/report_magnify.png new file mode 100644 index 0000000000000000000000000000000000000000..aeaa889534234e156bdcec30b73b591b10d8a019 GIT binary patch literal 738 zcmV<80v-K{P)JgP zpi~!%ir7|Bt5jR6QQK0ZCTW`7b7ovzy|oJBfwLHx`JHcmbH-6s1_lOpm2RKDZl(&D z@ey|9hdTJbGvW02_pi6sK9F!G?O~ZbbC_*sU;J0K@B1Bo5ClV=ot;M-U-ssk7Xk`l z7|ux)5ma?foJyr4k?!v9)z;cm(zYKD!}oiAf4$_oE+P`gj^o6FF+uWKZ$8teAW;Iq z$xGvifI?ItAi#;e+1WN?g4X?bj^W##yRm=>tXZ1+DG=Sk(Fy?8+PMV&;I({9R#DYi z0ac!lP9uPdqGAz^JaAoC{vbGj=NO8&@5Viz+?V6gn+Yb%HOjRH8WKt7r34#RF(y$3 z>@304tNBc8#Hp%?2p8^uV8Q$rHmu0vCp;#;)hU*0JbqhZQ+rDq@O~pWh=-wgt1ouC zF_or0*F>@AV5$~%JQlZQcrjd|P$=Mep6dodFjOcMj*R!7qx|GNVu?pouGUE=6BsLq zC^L1TZosHdE|k}>@gRw+&tz7K`>;DS-0`kjdx88QmDen$Oy(5j4`A|Se4f3J2wko zOJhvGAEb5pTDmrm9dmve*0XA1*Rtm1)<{9 literal 0 HcmV?d00001 diff --git a/syncplay/constants.py b/syncplay/constants.py index 59b406f..fc7836b 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -173,4 +173,5 @@ FILEITEM_SWITCH_FILE_SWITCH = 1 FILEITEM_SWITCH_STREAM_SWITCH = 2 SYNCPLAY_UPDATE_URL = u"http://syncplay.pl/checkforupdate?{}" # Params -SYNCPLAY_DOWNLOAD_URL = "http://syncplay.pl/download/" \ No newline at end of file +SYNCPLAY_DOWNLOAD_URL = "http://syncplay.pl/download/" +SYNCPLAY_PUBLIC_SERVER_LIST_URL = u"http://syncplay.pl/listpublicservers?{}" # Params \ No newline at end of file diff --git a/syncplay/messages.py b/syncplay/messages.py index 2e9f336..42df2a4 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -137,6 +137,8 @@ en = { "switch-file-not-found-error" : u"Could not switch to file '{0}'. Syncplay looks in the folder of the currently playing file and specified media directories.", # File not found "folder-search-timeout-error" : u"The search for media in '{}' was aborted as it took too long. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. Until Syncplay is restarted only the directory of the currently open file will be checked.", #Folder + "failed-to-load-server-list-error" : u"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 MPlayer and MPC-HC instances over the network.', "argument-epilog" : 'If no options supplied _config values will be used', @@ -171,6 +173,7 @@ en = { "media-path-label" : "Path to media file:", "player-arguments-label" : "Player arguments (if any):", "browse-label" : "Browse", + "list-servers-label" : u"Find public server", "more-title" : "Show more settings", "never-rewind-value" : "Never", @@ -275,6 +278,8 @@ en = { "identifyascontroller-msgbox-label" : "Identify as room operator", "identifyinfo-msgbox-label" : "Enter operator password for this room\r\n(see http://syncplay.pl/guide/ for usage instructions):", + "public-server-msgbox-label" : "Find public server (you must all use the same one!)", + "megabyte-suffix" : " MB", # Tooltips @@ -500,6 +505,8 @@ ru = { "switch-file-not-found-error" : u"Невозможно переключиться на файл '{0}'. Syncplay looks in the folder of the currently playing file and specified media directories.", # File not found # TODO: Translate last part into Russian "folder-search-timeout-error" : u"The search for media in '{}' was aborted as it took too long. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. Until Syncplay is restarted only the directory of the currently open file will be checked.", #Folder # TODO: Translate into Russian + "failed-to-load-server-list-error" : u"Failed to load public server list. Please visit http://www.syncplay.pl/ in your browser.", # TODO: Translate into Russian + # Client arguments "argument-description" : u'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC через Интернет.', "argument-epilog" : u'Если параметр не будет передан, то будет использоваться значение, указанное в _config.', @@ -534,6 +541,7 @@ ru = { "media-path-label" : u"Путь к видеофайлу:", "player-arguments-label" : u"Аргументы для запуска проигрывателя:", "browse-label" : u"Выбрать", + "list-servers-label" : u"Find public server", # TODO: Translate into Russian "more-title" : u"Больше настроек", "never-rewind-value" : u"Никогда", @@ -638,6 +646,8 @@ ru = { "identifyascontroller-msgbox-label" : u"Войти как оператор комнаты", "identifyinfo-msgbox-label" : u"Введите пароль оператора комнаты\r\n(см. инструкцию на странице http://syncplay.pl/guide/):", + "public-server-msgbox-label" : "Find public server (you must all use the same one!)", # TODO: Translate into Russian + "megabyte-suffix" : u" МБ", # Technically it is a mebibyte # Tooltips @@ -863,6 +873,8 @@ de = { "switch-file-not-found-error" : u"Could not switch to file '{0}'. Syncplay looks in the folder of the currently playing file and specified media directories.", # File not found, folder it was not found in # TODO: Translate into German "folder-search-timeout-error" : u"The search for media in '{}' was aborted as it took too long. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. Until Syncplay is restarted only the directory of the currently open file will be checked.", #Folder # TODO: Translate into German + "failed-to-load-server-list-error" : u"Failed to load public server list. Please visit http://www.syncplay.pl/ in your browser.", # TODO: Translate into German + # Client arguments "argument-description" : u'Syncplay ist eine Anwendung um mehrere MPlayer, MPC-HC und VLC-Instanzen über das Internet zu synchronisieren.', "argument-epilog" : u'Wenn keine Optionen angegeben sind, werden die _config-Werte verwendet', @@ -897,6 +909,7 @@ de = { "media-path-label" : u"Pfad zur Datei:", "player-arguments-label" : u"Player arguments:", # TODO: Translate into German "browse-label" : u"Durchsuchen", + "list-servers-label" : u"Find public server", # TODO: Translate into German "more-title" : u"Mehr Einstellungen zeigen", "never-rewind-value" : u"Niemals", @@ -999,6 +1012,8 @@ de = { "identifyascontroller-msgbox-label" : u"Als Raumleiter identifizieren", "identifyinfo-msgbox-label" : u"Passwort des zentral gesteuerten Raums eingeben\r\n(siehe http://syncplay.pl/guide/ für eine Anleitung [Englisch]):", + "public-server-msgbox-label" : "Find public server (you must all use the same one!)", # TODO: Translate into German + "megabyte-suffix" : u" MB", # Tooltips diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 12996f6..a3fdb3d 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -226,6 +226,32 @@ class ConfigDialog(QtGui.QDialog): settings.setValue("ShowMoreSettings", morestate) settings.endGroup() + + def findPublicServer(self): + try: + servers = utils.getListOfPublicServers() + except IOError as e: + self.showErrorMessage(unicode(e)) + return + dialog = QtGui.QInputDialog() + dialog.setOption(QtGui.QInputDialog.UseListViewForComboBoxItems) + dialog.setWindowTitle(getMessage("public-server-msgbox-label")) + dialog.setLabelText(getMessage("public-server-msgbox-label")) + serverTitles = [] + serverDict = {} + for server in servers: + serverTitle = server[0] + serverAddress = server[1] + serverTitles.append(serverTitle) + serverDict[serverTitle]=serverAddress + dialog.setComboBoxItems(serverTitles) + ok = dialog.exec_() + if ok: + self.hostTextbox.setText(serverDict[dialog.textValue()]) + + def showErrorMessage(self, errorMessage): + QtGui.QMessageBox.warning(self, "Syncplay", errorMessage) + def browseMediapath(self): self.loadMediaBrowseSettings() options = QtGui.QFileDialog.Options() @@ -389,7 +415,10 @@ class ConfigDialog(QtGui.QDialog): self.connectionSettingsGroup = QtGui.QGroupBox(getMessage("connection-group-title")) self.hostTextbox = QLineEdit(host, self) self.hostLabel = QLabel(getMessage("host-label"), self) + self.findServerButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + 'report_magnify.png'), getMessage("list-servers-label")) + self.findServerButton.clicked.connect(self.findPublicServer) self.usernameTextbox = QLineEdit(self) + self.usernameTextbox.setObjectName("name") self.serverpassLabel = QLabel(getMessage("password-label"), self) self.defaultroomTextbox = QLineEdit(self) @@ -409,12 +438,13 @@ class ConfigDialog(QtGui.QDialog): self.connectionSettingsLayout = QtGui.QGridLayout() self.connectionSettingsLayout.addWidget(self.hostLabel, 0, 0) self.connectionSettingsLayout.addWidget(self.hostTextbox, 0, 1) + self.connectionSettingsLayout.addWidget(self.findServerButton, 0, 2) self.connectionSettingsLayout.addWidget(self.serverpassLabel, 1, 0) - self.connectionSettingsLayout.addWidget(self.serverpassTextbox, 1, 1) + self.connectionSettingsLayout.addWidget(self.serverpassTextbox, 1, 1, 1, 2) self.connectionSettingsLayout.addWidget(self.usernameLabel, 2, 0) - self.connectionSettingsLayout.addWidget(self.usernameTextbox, 2, 1) + self.connectionSettingsLayout.addWidget(self.usernameTextbox, 2, 1, 1, 2) self.connectionSettingsLayout.addWidget(self.defaultroomLabel, 3, 0) - self.connectionSettingsLayout.addWidget(self.defaultroomTextbox, 3, 1) + self.connectionSettingsLayout.addWidget(self.defaultroomTextbox, 3, 1, 1, 2) self.connectionSettingsGroup.setLayout(self.connectionSettingsLayout) self.connectionSettingsGroup.setMaximumHeight(self.connectionSettingsGroup.minimumSizeHint().height()) diff --git a/syncplay/utils.py b/syncplay/utils.py index 2e76541..1e7263d 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -10,6 +10,7 @@ import hashlib import random import string import urllib +import ast folderSearchEnabled = True @@ -260,6 +261,23 @@ def findFilenameInDirectories(filename, directoryList): raise IOError(getMessage("folder-search-timeout-error").format(directory)) return None +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() + 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: + raise IOError + except: + raise IOError(getMessage("failed-to-load-server-list-error")) + class RoomPasswordProvider(object): CONTROLLED_ROOM_REGEX = re.compile("^\+(.*):(\w{12})$") PASSWORD_REGEX = re.compile("[A-Z]{2}-\d{3}-\d{3}") @@ -319,4 +337,5 @@ class RandomStringGenerator(object): return ''.join(random.choice(string.digits) for _ in xrange(quantity)) class NotControlledRoom(Exception): - pass \ No newline at end of file + pass + From 605c10be4a53dc29ae771818311ea5b3541c1d1f Mon Sep 17 00:00:00 2001 From: Et0h Date: Thu, 27 Aug 2015 23:16:23 +0100 Subject: [PATCH 33/58] Only show per-player args when "show more settings" is enabled --- syncplay/ui/GuiConfiguration.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index a3fdb3d..d7ddf33 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -46,6 +46,7 @@ class ConfigDialog(QtGui.QDialog): self.automaticupdatesCheckbox.setChecked(False) def moreToggled(self): + self.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) if self.moreToggling == False: self.moreToggling = True @@ -53,16 +54,21 @@ class ConfigDialog(QtGui.QDialog): self.tabListFrame.show() self.resetButton.show() self.nostoreCheckbox.show() + self.playerargsTextbox.show() + self.playerargsLabel.show() self.saveMoreState(True) self.tabListWidget.setCurrentRow(0) self.ensureTabListIsVisible() + self.stackedFrame.setFixedHeight(self.stackedFrame.minimumSizeHint().height()) else: self.tabListFrame.hide() self.resetButton.hide() self.nostoreCheckbox.hide() + self.playerargsTextbox.hide() + self.playerargsLabel.hide() self.saveMoreState(False) self.stackedLayout.setCurrentIndex(0) - + self.stackedFrame.setFixedHeight(self.connectionSettingsGroup.minimumSizeHint().height()+self.mediaplayerSettingsGroup.minimumSizeHint().height()+self.bottomButtonFrame.minimumSizeHint().height()+3) self.adjustSize() self.setFixedSize(self.sizeHint()) self.moreToggling = False @@ -78,8 +84,6 @@ class ConfigDialog(QtGui.QDialog): def openHelp(self): self.QtGui.QDesktopServices.openUrl(QUrl("http://syncplay.pl/guide/client/")) - - def safenormcaseandpath(self, path): if utils.isURL(path): return path @@ -510,9 +514,8 @@ class ConfigDialog(QtGui.QDialog): self.basicOptionsLayout.setAlignment(Qt.AlignTop) self.basicOptionsLayout.addWidget(self.connectionSettingsGroup) self.basicOptionsLayout.addSpacing(5) - self.mediaplayerSettingsGroup.setMaximumHeight(self.mediaplayerSettingsGroup.minimumSizeHint().height()) self.basicOptionsLayout.addWidget(self.mediaplayerSettingsGroup) - + self.basicOptionsFrame.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) self.basicOptionsFrame.setLayout(self.basicOptionsLayout) self.stackedLayout.addWidget(self.basicOptionsFrame) @@ -934,14 +937,17 @@ class ConfigDialog(QtGui.QDialog): self.mainLayout.addWidget(self.stackedFrame, 0, 1) self.addBottomLayout() - if self.getMoreState() == False: self.tabListFrame.hide() self.nostoreCheckbox.hide() self.resetButton.hide() + self.playerargsTextbox.hide() + self.playerargsLabel.hide() + self.stackedFrame.setFixedHeight(self.connectionSettingsGroup.minimumSizeHint().height()+self.mediaplayerSettingsGroup.minimumSizeHint().height()+self.bottomButtonFrame.minimumSizeHint().height()+3) else: self.showmoreCheckbox.setChecked(True) self.tabListWidget.setCurrentRow(0) + self.stackedFrame.setFixedHeight(self.stackedFrame.minimumSizeHint().height()) self.showmoreCheckbox.toggled.connect(self.moreToggled) From 44ff494f3bde5e24c41e767543bb6a1577d93384 Mon Sep 17 00:00:00 2001 From: Et0h Date: Thu, 27 Aug 2015 23:16:49 +0100 Subject: [PATCH 34/58] Upver to 1.3.3 / Release 16 --- syncplay/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index a2a77ef..639507b 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ -version = '1.3.2' +version = '1.3.3' milestone = 'Chami' -release_number = '15' +release_number = '16' projectURL = 'http://syncplay.pl/' From 965f68f9884672c5148abafcbdf5a1fc613cc32f Mon Sep 17 00:00:00 2001 From: Et0h Date: Sun, 30 Aug 2015 11:28:05 +0100 Subject: [PATCH 35/58] Adjust find public server UI --- syncplay/__init__.py | 2 +- syncplay/messages.py | 6 +++--- syncplay/ui/GuiConfiguration.py | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 639507b..1b0e2cc 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ version = '1.3.3' milestone = 'Chami' -release_number = '16' +release_number = '17' projectURL = 'http://syncplay.pl/' diff --git a/syncplay/messages.py b/syncplay/messages.py index 42df2a4..31a16bb 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -278,7 +278,7 @@ en = { "identifyascontroller-msgbox-label" : "Identify as room operator", "identifyinfo-msgbox-label" : "Enter operator password for this room\r\n(see http://syncplay.pl/guide/ for usage instructions):", - "public-server-msgbox-label" : "Find public server (you must all use the same one!)", + "public-server-msgbox-label" : "Public server (you must all use the same one!)", "megabyte-suffix" : " MB", @@ -646,7 +646,7 @@ ru = { "identifyascontroller-msgbox-label" : u"Войти как оператор комнаты", "identifyinfo-msgbox-label" : u"Введите пароль оператора комнаты\r\n(см. инструкцию на странице http://syncplay.pl/guide/):", - "public-server-msgbox-label" : "Find public server (you must all use the same one!)", # TODO: Translate into Russian + "public-server-msgbox-label" : "Public server (you must all use the same one!)", # TODO: Translate into Russian "megabyte-suffix" : u" МБ", # Technically it is a mebibyte @@ -1012,7 +1012,7 @@ de = { "identifyascontroller-msgbox-label" : u"Als Raumleiter identifizieren", "identifyinfo-msgbox-label" : u"Passwort des zentral gesteuerten Raums eingeben\r\n(siehe http://syncplay.pl/guide/ für eine Anleitung [Englisch]):", - "public-server-msgbox-label" : "Find public server (you must all use the same one!)", # TODO: Translate into German + "public-server-msgbox-label" : "Public server (you must all use the same one!)", # TODO: Translate into German "megabyte-suffix" : u" MB", diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index d7ddf33..cd55ae9 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -238,8 +238,7 @@ class ConfigDialog(QtGui.QDialog): self.showErrorMessage(unicode(e)) return dialog = QtGui.QInputDialog() - dialog.setOption(QtGui.QInputDialog.UseListViewForComboBoxItems) - dialog.setWindowTitle(getMessage("public-server-msgbox-label")) + dialog.setWindowTitle(getMessage("list-servers-label")) dialog.setLabelText(getMessage("public-server-msgbox-label")) serverTitles = [] serverDict = {} From ab009546685666db71921b880baafbcb7b051514 Mon Sep 17 00:00:00 2001 From: Et0h Date: Sun, 30 Aug 2015 13:21:20 +0100 Subject: [PATCH 36/58] Update public list text --- syncplay/messages.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syncplay/messages.py b/syncplay/messages.py index 31a16bb..ef0efd3 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -278,7 +278,7 @@ en = { "identifyascontroller-msgbox-label" : "Identify as room operator", "identifyinfo-msgbox-label" : "Enter operator password for this room\r\n(see http://syncplay.pl/guide/ for usage instructions):", - "public-server-msgbox-label" : "Public server (you must all use the same one!)", + "public-server-msgbox-label" : u"Select the public server for this viewing session", "megabyte-suffix" : " MB", @@ -646,7 +646,7 @@ ru = { "identifyascontroller-msgbox-label" : u"Войти как оператор комнаты", "identifyinfo-msgbox-label" : u"Введите пароль оператора комнаты\r\n(см. инструкцию на странице http://syncplay.pl/guide/):", - "public-server-msgbox-label" : "Public server (you must all use the same one!)", # TODO: Translate into Russian + "public-server-msgbox-label" : u"Select the public server for this viewing session", # TODO: Translate into Russian "megabyte-suffix" : u" МБ", # Technically it is a mebibyte @@ -1012,7 +1012,7 @@ de = { "identifyascontroller-msgbox-label" : u"Als Raumleiter identifizieren", "identifyinfo-msgbox-label" : u"Passwort des zentral gesteuerten Raums eingeben\r\n(siehe http://syncplay.pl/guide/ für eine Anleitung [Englisch]):", - "public-server-msgbox-label" : "Public server (you must all use the same one!)", # TODO: Translate into German + "public-server-msgbox-label" : u"Select the public server for this viewing session", # TODO: Translate into German "megabyte-suffix" : u" MB", From ea12a4ef9df4ec82e14165e0309d68d0398378d3 Mon Sep 17 00:00:00 2001 From: Et0h Date: Sun, 30 Aug 2015 13:21:56 +0100 Subject: [PATCH 37/58] List official public servers in host combobox --- syncplay/constants.py | 1 + syncplay/ui/GuiConfiguration.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index fc7836b..52d8977 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -16,6 +16,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" +PUBLIC_SYNCPLAY_SERVERS = ["syncplay.pl:8995","syncplay.pl:8996","syncplay.pl:8997","syncplay.pl:8998","syncplay.pl:8999"] #Overriden by config SHOW_OSD = True # Sends Syncplay messages to media player OSD diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index cd55ae9..0895434 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -250,7 +250,7 @@ class ConfigDialog(QtGui.QDialog): dialog.setComboBoxItems(serverTitles) ok = dialog.exec_() if ok: - self.hostTextbox.setText(serverDict[dialog.textValue()]) + self.hostCombobox.setEditText(serverDict[dialog.textValue()]) def showErrorMessage(self, errorMessage): QtGui.QMessageBox.warning(self, "Syncplay", errorMessage) @@ -284,8 +284,8 @@ class ConfigDialog(QtGui.QDialog): self.config["mediaSearchDirectories"] = utils.convertMultilineStringToList(self.mediasearchTextEdit.toPlainText()) self.processWidget(self, lambda w: self.saveValues(w)) - if self.hostTextbox.text(): - self.config['host'] = self.hostTextbox.text() if ":" in self.hostTextbox.text() else self.hostTextbox.text() + ":" + unicode(constants.DEFAULT_PORT) + if self.hostCombobox.currentText(): + self.config['host'] = self.hostCombobox.currentText() if ":" in self.hostCombobox.currentText() else self.hostCombobox.currentText() + ":" + unicode(constants.DEFAULT_PORT) else: self.config['host'] = None self.config['playerPath'] = unicode(self.safenormcaseandpath(self.executablepathCombobox.currentText())) @@ -416,7 +416,11 @@ class ConfigDialog(QtGui.QDialog): self.mediaSearchDirectories = self.config["mediaSearchDirectories"] self.connectionSettingsGroup = QtGui.QGroupBox(getMessage("connection-group-title")) - self.hostTextbox = QLineEdit(host, self) + self.hostCombobox = QtGui.QComboBox(self) + self.hostCombobox.addItems(constants.PUBLIC_SYNCPLAY_SERVERS) + self.hostCombobox.setEditable(True) + self.hostCombobox.setEditText(host) + self.hostCombobox.setFixedWidth(165) self.hostLabel = QLabel(getMessage("host-label"), self) self.findServerButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + 'report_magnify.png'), getMessage("list-servers-label")) self.findServerButton.clicked.connect(self.findPublicServer) @@ -430,7 +434,7 @@ class ConfigDialog(QtGui.QDialog): self.defaultroomLabel = QLabel(getMessage("room-label"), self) self.hostLabel.setObjectName("host") - self.hostTextbox.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "host") + self.hostCombobox.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "host") self.usernameLabel.setObjectName("name") self.usernameTextbox.setObjectName("name") self.serverpassLabel.setObjectName("password") @@ -440,7 +444,7 @@ class ConfigDialog(QtGui.QDialog): self.connectionSettingsLayout = QtGui.QGridLayout() self.connectionSettingsLayout.addWidget(self.hostLabel, 0, 0) - self.connectionSettingsLayout.addWidget(self.hostTextbox, 0, 1) + self.connectionSettingsLayout.addWidget(self.hostCombobox, 0, 1) self.connectionSettingsLayout.addWidget(self.findServerButton, 0, 2) self.connectionSettingsLayout.addWidget(self.serverpassLabel, 1, 0) self.connectionSettingsLayout.addWidget(self.serverpassTextbox, 1, 1, 1, 2) From da1b2894bfe4a789a395a222f1ef8bae5921bf70 Mon Sep 17 00:00:00 2001 From: Et0h Date: Sun, 30 Aug 2015 20:31:49 +0100 Subject: [PATCH 38/58] Move all server listings to combo box --- buildPy2exe.py | 2 +- resources/arrow_refresh.png | Bin 0 -> 685 bytes resources/report_magnify.png | Bin 738 -> 0 bytes syncplay/client.py | 10 +++++-- syncplay/constants.py | 2 +- syncplay/messages.py | 6 ++-- syncplay/ui/GuiConfiguration.py | 50 ++++++++++++++++++++------------ syncplay/ui/gui.py | 12 +++++++- 8 files changed, 56 insertions(+), 26 deletions(-) create mode 100644 resources/arrow_refresh.png delete mode 100644 resources/report_magnify.png diff --git a/buildPy2exe.py b/buildPy2exe.py index 24175c1..a4e7da3 100644 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -654,7 +654,7 @@ guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock '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/film_go.png', 'resources/world_go.png', 'resources/report_magnify.png' + 'resources/film_go.png', 'resources/world_go.png', 'resources/arrow_refresh.png' ] resources = ["resources/icon.ico", "resources/syncplay.png"] resources.extend(guiIcons) diff --git a/resources/arrow_refresh.png b/resources/arrow_refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..0de26566d4102eec080253c2d08985ec58b14838 GIT binary patch literal 685 zcmV;e0#f~nP)`!iy8(2_#ButL^3%VaH2WCpD^U)OZxp@C)2#hU)y+@T%ZNzJigNk%37 zz-WYJwT%teVfiEI+B*@v4ey@58(ld4VY_&5-ox`e@AKg+0U-I`y79bmuw_~y6+4rZ zBG5EdFDS+@M0OSE`>d7SUDOzKZ&h*4eB1iX7tOd9RiYtW2mQ--bUahxr1`i{RG@dM zL#}_X=DDO1{;UI$pFu=dLYT_=5d8WC-sLfjr7UO-HKMAwa=!>)kEhvuwre zuW3yF@ZxFCkI*+ad|5kOX%5zu8IQjhan)UqgSrFGA_0nQFn@Z08DSEUToCSz4Z1ls z&fDbq$T&7|6iq$_uDI$@q1_kQ@dfqk*0>{SDL6V)94@)ete)j++*>bIc9sj}Y;R1o z#OpH+Yt-^4wfv{nern^iVag8JgP zpi~!%ir7|Bt5jR6QQK0ZCTW`7b7ovzy|oJBfwLHx`JHcmbH-6s1_lOpm2RKDZl(&D z@ey|9hdTJbGvW02_pi6sK9F!G?O~ZbbC_*sU;J0K@B1Bo5ClV=ot;M-U-ssk7Xk`l z7|ux)5ma?foJyr4k?!v9)z;cm(zYKD!}oiAf4$_oE+P`gj^o6FF+uWKZ$8teAW;Iq z$xGvifI?ItAi#;e+1WN?g4X?bj^W##yRm=>tXZ1+DG=Sk(Fy?8+PMV&;I({9R#DYi z0ac!lP9uPdqGAz^JaAoC{vbGj=NO8&@5Viz+?V6gn+Yb%HOjRH8WKt7r34#RF(y$3 z>@304tNBc8#Hp%?2p8^uV8Q$rHmu0vCp;#;)hU*0JbqhZQ+rDq@O~pWh=-wgt1ouC zF_or0*F>@AV5$~%JQlZQcrjd|P$=Mep6dodFjOcMj*R!7qx|GNVu?pouGUE=6BsLq zC^L1TZosHdE|k}>@gRw+&tz7K`>;DS-0`kjdx88QmDen$Oy(5j4`A|Se4f3J2wko zOJhvGAEb5pTDmrm9dmve*0XA1*Rtm1)<{9 diff --git a/syncplay/client.py b/syncplay/client.py index 7ac9b56..41c6acd 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -3,6 +3,7 @@ import os.path import time import re import sys +import ast from twisted.internet.protocol import ClientFactory from twisted.internet import reactor, task from functools import wraps @@ -676,9 +677,14 @@ class SyncplayClient(object): response = f.read() response = response.replace("

","").replace("

","").replace("
","").replace("“","\"").replace("”","\"") # Fix Wordpress response = json.loads(response) - 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 = None + if response["public-servers"]: + publicServers = response["public-servers"].replace("”","'").replace(":’","'").replace("’","'").replace("′","'").replace("\n","").replace("\r","") + print publicServers + 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 except: - return "failed", getMessage("update-check-failed-notification").format(syncplay.version), constants.SYNCPLAY_DOWNLOAD_URL + return "failed", getMessage("update-check-failed-notification").format(syncplay.version), constants.SYNCPLAY_DOWNLOAD_URL, None class _WarningManager(object): def __init__(self, player, userlist, ui, client): diff --git a/syncplay/constants.py b/syncplay/constants.py index 52d8977..e0abbe5 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -16,7 +16,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" -PUBLIC_SYNCPLAY_SERVERS = ["syncplay.pl:8995","syncplay.pl:8996","syncplay.pl:8997","syncplay.pl:8998","syncplay.pl:8999"] +FALLBACK_PUBLIC_SYNCPLAY_SERVERS = ["syncplay.pl:8995","syncplay.pl:8996","syncplay.pl:8997","syncplay.pl:8998","syncplay.pl:8999"] #Overriden by config SHOW_OSD = True # Sends Syncplay messages to media player OSD diff --git a/syncplay/messages.py b/syncplay/messages.py index ef0efd3..14f9afe 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -173,7 +173,7 @@ en = { "media-path-label" : "Path to media file:", "player-arguments-label" : "Player arguments (if any):", "browse-label" : "Browse", - "list-servers-label" : u"Find public server", + "update-server-list-label" : u"Update list", "more-title" : "Show more settings", "never-rewind-value" : "Never", @@ -541,7 +541,7 @@ ru = { "media-path-label" : u"Путь к видеофайлу:", "player-arguments-label" : u"Аргументы для запуска проигрывателя:", "browse-label" : u"Выбрать", - "list-servers-label" : u"Find public server", # TODO: Translate into Russian + "update-server-list-label" : u"Update list", # TODO: Translate into Russian "more-title" : u"Больше настроек", "never-rewind-value" : u"Никогда", @@ -909,7 +909,7 @@ de = { "media-path-label" : u"Pfad zur Datei:", "player-arguments-label" : u"Player arguments:", # TODO: Translate into German "browse-label" : u"Durchsuchen", - "list-servers-label" : u"Find public server", # TODO: Translate into German + "update-server-list-label" : u"Update list", # TODO: Translate into German "more-title" : u"Mehr Einstellungen zeigen", "never-rewind-value" : u"Niemals", diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 0895434..c4586c6 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -202,6 +202,13 @@ class ConfigDialog(QtGui.QDialog): else: self.config["lastCheckedForUpdates"] = str(self.lastCheckedForUpdates) + def loadSavedPublicServerList(self): + settings = QSettings("Syncplay", "Interface") + settings.beginGroup("PublicServerList") + self.publicServers = settings.value("publicServers", constants.FALLBACK_PUBLIC_SYNCPLAY_SERVERS) + if self.publicServers is None: + self.publicServers = constants.FALLBACK_PUBLIC_SYNCPLAY_SERVERS + def loadMediaBrowseSettings(self): settings = QSettings("Syncplay", "MediaBrowseDialog") settings.beginGroup("MediaBrowseDialog") @@ -231,26 +238,24 @@ class ConfigDialog(QtGui.QDialog): settings.endGroup() - def findPublicServer(self): + def updateServerList(self): try: servers = utils.getListOfPublicServers() except IOError as e: self.showErrorMessage(unicode(e)) return - dialog = QtGui.QInputDialog() - dialog.setWindowTitle(getMessage("list-servers-label")) - dialog.setLabelText(getMessage("public-server-msgbox-label")) - serverTitles = [] - serverDict = {} - for server in servers: - serverTitle = server[0] - serverAddress = server[1] - serverTitles.append(serverTitle) - serverDict[serverTitle]=serverAddress - dialog.setComboBoxItems(serverTitles) - ok = dialog.exec_() - if ok: - self.hostCombobox.setEditText(serverDict[dialog.textValue()]) + currentServer = self.hostCombobox.currentText() + self.hostCombobox.clear() + if servers: + i = 0 + for server in servers: + self.hostCombobox.addItem(server[1]) + self.hostCombobox.setItemData(i, server[0], Qt.ToolTipRole) + i += 1 + settings = QSettings("Syncplay", "Interface") + settings.beginGroup("PublicServerList") + settings.setValue("publicServers", servers) + self.hostCombobox.setEditText(currentServer) def showErrorMessage(self, errorMessage): QtGui.QMessageBox.warning(self, "Syncplay", errorMessage) @@ -416,14 +421,20 @@ class ConfigDialog(QtGui.QDialog): self.mediaSearchDirectories = self.config["mediaSearchDirectories"] self.connectionSettingsGroup = QtGui.QGroupBox(getMessage("connection-group-title")) + self.loadSavedPublicServerList() self.hostCombobox = QtGui.QComboBox(self) - self.hostCombobox.addItems(constants.PUBLIC_SYNCPLAY_SERVERS) + if self.publicServers: + i = 0 + for publicServer in self.publicServers: + self.hostCombobox.addItem(publicServer[1]) + self.hostCombobox.setItemData(i, publicServer[0], Qt.ToolTipRole) + i += 1 self.hostCombobox.setEditable(True) self.hostCombobox.setEditText(host) self.hostCombobox.setFixedWidth(165) self.hostLabel = QLabel(getMessage("host-label"), self) - self.findServerButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + 'report_magnify.png'), getMessage("list-servers-label")) - self.findServerButton.clicked.connect(self.findPublicServer) + self.findServerButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + 'arrow_refresh.png'), getMessage("update-server-list-label")) + self.findServerButton.clicked.connect(self.updateServerList) self.usernameTextbox = QLineEdit(self) self.usernameTextbox.setObjectName("name") @@ -881,6 +892,8 @@ class ConfigDialog(QtGui.QDialog): def clearGUIData(self, leaveMore=False): settings = QSettings("Syncplay", "PlayerList") settings.clear() + settings = QSettings("Syncplay", "PublicServerList") + settings.clear() settings = QSettings("Syncplay", "MediaBrowseDialog") settings.clear() settings = QSettings("Syncplay", "MainWindow") @@ -902,6 +915,7 @@ class ConfigDialog(QtGui.QDialog): self.datacleared = False self.config['resetConfig'] = False self.subitems = {} + self.publicServers = None if self.config['clearGUIData'] == True: self.config['clearGUIData'] = False diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index cc001fd..23f43da 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -841,7 +841,8 @@ class MainWindow(QtGui.QMainWindow): @needsClient def checkForUpdates(self, userInitiated=False): self.lastCheckedForUpdates = datetime.utcnow() - updateStatus, updateMessage, updateURL = self._syncplayClient.checkForUpdate(userInitiated) + updateStatus, updateMessage, updateURL, self.publicServerList = self._syncplayClient.checkForUpdate(userInitiated) + if updateMessage is None: if updateStatus == "uptodate": updateMessage = getMessage("syncplay-uptodate-notification") @@ -898,6 +899,10 @@ class MainWindow(QtGui.QMainWindow): settings.beginGroup("Update") settings.setValue("lastChecked", self.lastCheckedForUpdates) settings.endGroup() + settings.beginGroup("PublicServerList") + if self.publicServerList: + settings.setValue("publicServers", self.publicServerList) + settings.endGroup() def loadSettings(self): settings = QSettings("Syncplay", "MainWindow") @@ -920,9 +925,14 @@ class MainWindow(QtGui.QMainWindow): settings = QSettings("Syncplay", "Interface") settings.beginGroup("Update") self.lastCheckedForUpdates = settings.value("lastChecked", None) + settings.endGroup() + settings.beginGroup("PublicServerList") + self.publicServerList = settings.value("publicServers", None) def __init__(self): super(MainWindow, self).__init__() + self.publicServerList = [] + self.lastCheckedForUpdates = None self._syncplayClient = None self.folderSearchEnabled = True self.QtGui = QtGui From 8f589e6278d6c65000563a46768bc223309022a4 Mon Sep 17 00:00:00 2001 From: Et0h Date: Sun, 30 Aug 2015 21:04:03 +0100 Subject: [PATCH 39/58] Fix window height where there is a notification bocx --- syncplay/ui/GuiConfiguration.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index c4586c6..2207847 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -68,7 +68,10 @@ class ConfigDialog(QtGui.QDialog): self.playerargsLabel.hide() self.saveMoreState(False) self.stackedLayout.setCurrentIndex(0) - self.stackedFrame.setFixedHeight(self.connectionSettingsGroup.minimumSizeHint().height()+self.mediaplayerSettingsGroup.minimumSizeHint().height()+self.bottomButtonFrame.minimumSizeHint().height()+3) + newHeight = self.connectionSettingsGroup.minimumSizeHint().height()+self.mediaplayerSettingsGroup.minimumSizeHint().height()+self.bottomButtonFrame.minimumSizeHint().height()+3 + if self.errorLabel.isVisible(): + newHeight +=self.errorLabel.height()+3 + self.stackedFrame.setFixedHeight(newHeight) self.adjustSize() self.setFixedSize(self.sizeHint()) self.moreToggling = False @@ -410,6 +413,7 @@ class ConfigDialog(QtGui.QDialog): error = self.error if self.datacleared == True: error = constants.ERROR_MESSAGE_MARKER + "{}".format(getMessage("gui-data-cleared-notification")) + self.error = error if config['host'] == None: host = "" elif ":" in config['host']: @@ -522,7 +526,6 @@ class ConfigDialog(QtGui.QDialog): self.errorLabel.setStyleSheet(constants.STYLE_SUCCESSLABEL) self.errorLabel.setText(error) self.errorLabel.setAlignment(Qt.AlignCenter) - self.basicOptionsLayout.addWidget(self.errorLabel, 0, 0) self.connectionSettingsGroup.setMaximumHeight(self.connectionSettingsGroup.minimumSizeHint().height()) self.basicOptionsLayout.setAlignment(Qt.AlignTop) @@ -960,7 +963,10 @@ class ConfigDialog(QtGui.QDialog): self.resetButton.hide() self.playerargsTextbox.hide() self.playerargsLabel.hide() - self.stackedFrame.setFixedHeight(self.connectionSettingsGroup.minimumSizeHint().height()+self.mediaplayerSettingsGroup.minimumSizeHint().height()+self.bottomButtonFrame.minimumSizeHint().height()+3) + newHeight = self.connectionSettingsGroup.minimumSizeHint().height()+self.mediaplayerSettingsGroup.minimumSizeHint().height()+self.bottomButtonFrame.minimumSizeHint().height()+3 + if self.error: + newHeight +=self.errorLabel.height()+3 + self.stackedFrame.setFixedHeight(newHeight) else: self.showmoreCheckbox.setChecked(True) self.tabListWidget.setCurrentRow(0) @@ -976,4 +982,4 @@ class ConfigDialog(QtGui.QDialog): if constants.SHOW_TOOLTIPS: self.processWidget(self, lambda w: self.loadTooltips(w)) self.processWidget(self, lambda w: self.loadValues(w)) - self.processWidget(self, lambda w: self.connectChildren(w)) + self.processWidget(self, lambda w: self.connectChildren(w)) \ No newline at end of file From 171a4c8f06a407cec42733543143f5c21e15c491 Mon Sep 17 00:00:00 2001 From: Et0h Date: Sun, 30 Aug 2015 21:34:46 +0100 Subject: [PATCH 40/58] Fix fallback public server list --- syncplay/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index e0abbe5..2383db4 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -16,7 +16,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 = ["syncplay.pl:8995","syncplay.pl:8996","syncplay.pl:8997","syncplay.pl:8998","syncplay.pl:8999"] +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']] #Overriden by config SHOW_OSD = True # Sends Syncplay messages to media player OSD From c00270aef90d412229f2e7d6bfc9bd33e08eae2d Mon Sep 17 00:00:00 2001 From: Et0h Date: Sun, 30 Aug 2015 21:35:26 +0100 Subject: [PATCH 41/58] Clear public server list properly --- syncplay/ui/GuiConfiguration.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 2207847..4e3e1f0 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -895,8 +895,6 @@ class ConfigDialog(QtGui.QDialog): def clearGUIData(self, leaveMore=False): settings = QSettings("Syncplay", "PlayerList") settings.clear() - settings = QSettings("Syncplay", "PublicServerList") - settings.clear() settings = QSettings("Syncplay", "MediaBrowseDialog") settings.clear() settings = QSettings("Syncplay", "MainWindow") @@ -905,6 +903,9 @@ class ConfigDialog(QtGui.QDialog): settings.beginGroup("Update") settings.setValue("lastChecked", None) settings.endGroup() + settings.beginGroup("PublicServerList") + settings.setValue("publicServers", None) + settings.endGroup() if not leaveMore: settings = QSettings("Syncplay", "MoreSettings") settings.clear() From 2983dbc69a17ee577bf378abe74b9d0e04c55e69 Mon Sep 17 00:00:00 2001 From: Et0h Date: Sun, 30 Aug 2015 21:46:13 +0100 Subject: [PATCH 42/58] Fix UI bug when toggling moreOptions --- syncplay/ui/GuiConfiguration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 4e3e1f0..fb7da26 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -69,8 +69,8 @@ class ConfigDialog(QtGui.QDialog): self.saveMoreState(False) self.stackedLayout.setCurrentIndex(0) newHeight = self.connectionSettingsGroup.minimumSizeHint().height()+self.mediaplayerSettingsGroup.minimumSizeHint().height()+self.bottomButtonFrame.minimumSizeHint().height()+3 - if self.errorLabel.isVisible(): - newHeight +=self.errorLabel.height()+3 + if self.error: + newHeight += self.errorLabel.height()+3 self.stackedFrame.setFixedHeight(newHeight) self.adjustSize() self.setFixedSize(self.sizeHint()) From 558e7e69bfea495795de3d0ca58c624f54aefc8f Mon Sep 17 00:00:00 2001 From: Et0h Date: Sun, 30 Aug 2015 23:34:29 +0100 Subject: [PATCH 43/58] Automatically update empty server list if updates are enabled --- syncplay/ui/GuiConfiguration.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index fb7da26..da0b2b1 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -208,9 +208,7 @@ class ConfigDialog(QtGui.QDialog): def loadSavedPublicServerList(self): settings = QSettings("Syncplay", "Interface") settings.beginGroup("PublicServerList") - self.publicServers = settings.value("publicServers", constants.FALLBACK_PUBLIC_SYNCPLAY_SERVERS) - if self.publicServers is None: - self.publicServers = constants.FALLBACK_PUBLIC_SYNCPLAY_SERVERS + self.publicServers = settings.value("publicServers", None) def loadMediaBrowseSettings(self): settings = QSettings("Syncplay", "MediaBrowseDialog") @@ -240,7 +238,6 @@ class ConfigDialog(QtGui.QDialog): settings.setValue("ShowMoreSettings", morestate) settings.endGroup() - def updateServerList(self): try: servers = utils.getListOfPublicServers() @@ -910,7 +907,21 @@ class ConfigDialog(QtGui.QDialog): settings = QSettings("Syncplay", "MoreSettings") settings.clear() self.datacleared = True - + + def populateEmptyServerList(self): + if self.publicServers is None: + if self.config["checkForUpdatesAutomatically"] == True: + self.updateServerList() + else: + currentServer = self.hostCombobox.currentText() + self.publicServers = constants.FALLBACK_PUBLIC_SYNCPLAY_SERVERS + i = 0 + for server in self.publicServers: + self.hostCombobox.addItem(server[1]) + self.hostCombobox.setItemData(i, server[0], Qt.ToolTipRole) + i += 1 + self.hostCombobox.setEditText(currentServer) + def __init__(self, config, playerpaths, error, defaultConfig): self.config = config @@ -983,4 +994,5 @@ class ConfigDialog(QtGui.QDialog): if constants.SHOW_TOOLTIPS: self.processWidget(self, lambda w: self.loadTooltips(w)) self.processWidget(self, lambda w: self.loadValues(w)) - self.processWidget(self, lambda w: self.connectChildren(w)) \ No newline at end of file + self.processWidget(self, lambda w: self.connectChildren(w)) + self.populateEmptyServerList() \ No newline at end of file From 9bee422b87db60a9617ca14f511fc1b0b21e02d5 Mon Sep 17 00:00:00 2001 From: Et0h Date: Mon, 31 Aug 2015 10:20:29 +0100 Subject: [PATCH 44/58] Add tooltip for update server list button --- syncplay/messages.py | 3 +++ syncplay/ui/GuiConfiguration.py | 1 + 2 files changed, 4 insertions(+) diff --git a/syncplay/messages.py b/syncplay/messages.py index 14f9afe..ee83346 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -324,6 +324,7 @@ en = { "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.", "joinroom-tooltip" : "Leave current room and joins specified room.", "seektime-msgbox-label" : "Jump to specified time (in seconds / min:sec). Use +/- for relative seek.", @@ -692,6 +693,7 @@ ru = { "help-tooltip" : u"Открыть Руководство Пользователя на Syncplay.pl.", "reset-tooltip" : u"Сбрасывает все настройки Syncplay в начальное состояние.", + "update-server-list-tooltip" : u"Connect to syncplay.pl to update list of public servers.", # TODO: Translate to Russian "joinroom-tooltip" : u"Покинуть комнату и зайти в другую, указанную комнату.", "seektime-msgbox-label" : u"Перемотать к определенному моменту времени (указывать в секундах или мин:сек). Используйте +/-, чтобы перемотать вперед/назад относительно настоящего момента.", @@ -1060,6 +1062,7 @@ de = { "help-tooltip" : u"Öffnet Hilfe auf syncplay.pl [Englisch]", "reset-tooltip" : u"Alle Einstellungen auf Standardwerte zurücksetzen.", + "update-server-list-tooltip" : u"Connect to syncplay.pl to update list of public servers.", # TODO: Translate to German "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.", diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index da0b2b1..bfa92ec 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -436,6 +436,7 @@ class ConfigDialog(QtGui.QDialog): self.hostLabel = QLabel(getMessage("host-label"), self) self.findServerButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + 'arrow_refresh.png'), getMessage("update-server-list-label")) self.findServerButton.clicked.connect(self.updateServerList) + self.findServerButton.setToolTip(getMessage("update-server-list-tooltip")) self.usernameTextbox = QLineEdit(self) self.usernameTextbox.setObjectName("name") From 2ca4d6b3f2b3b2a6a5acc8ba1f436e351c0ea55c Mon Sep 17 00:00:00 2001 From: Et0h Date: Mon, 31 Aug 2015 10:21:59 +0100 Subject: [PATCH 45/58] Upver to Release 18 --- syncplay/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 1b0e2cc..dc2127f 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ version = '1.3.3' milestone = 'Chami' -release_number = '17' +release_number = '18' projectURL = 'http://syncplay.pl/' From 6c55d9174995bf76c6e4032128991d23da6ed48f Mon Sep 17 00:00:00 2001 From: Et0h Date: Mon, 31 Aug 2015 10:57:29 +0100 Subject: [PATCH 46/58] Remove test print --- syncplay/client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/syncplay/client.py b/syncplay/client.py index 41c6acd..739f9de 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -680,7 +680,6 @@ class SyncplayClient(object): publicServers = None if response["public-servers"]: publicServers = response["public-servers"].replace("”","'").replace(":’","'").replace("’","'").replace("′","'").replace("\n","").replace("\r","") - print publicServers 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 except: From 8fcf343a9e419b9f930b35d64cd4edc90760f6cf Mon Sep 17 00:00:00 2001 From: Et0h Date: Tue, 1 Sep 2015 12:14:25 +0100 Subject: [PATCH 47/58] Double folder search timeout threshold --- syncplay/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index 2383db4..980bfb5 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -49,7 +49,7 @@ SERVER_STATE_INTERVAL = 1 WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1 AUTOPLAY_DELAY = 3.0 SYNC_ON_PAUSE = True # Client seek to global position - subtitles may disappear on some media players -FOLDER_SEARCH_TIMEOUT = 1.0 # Secs +FOLDER_SEARCH_TIMEOUT = 2.0 # Secs #Usually there's no need to adjust these LAST_PAUSED_DIFF_THRESHOLD = 2 From 2fe06fab1daed57db83f6ee3f80318430bb268ef Mon Sep 17 00:00:00 2001 From: Et0h Date: Tue, 1 Sep 2015 16:43:40 +0100 Subject: [PATCH 48/58] Upver to release 17 --- syncplay/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index dc2127f..1b0e2cc 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ version = '1.3.3' milestone = 'Chami' -release_number = '18' +release_number = '17' projectURL = 'http://syncplay.pl/' From 59c177dc765fecd5c778543b36f9626e2e4d3a30 Mon Sep 17 00:00:00 2001 From: Et0h Date: Tue, 1 Sep 2015 16:52:05 +0100 Subject: [PATCH 49/58] Upver to release 19 --- syncplay/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 1b0e2cc..7e08937 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ version = '1.3.3' milestone = 'Chami' -release_number = '17' +release_number = '19' projectURL = 'http://syncplay.pl/' From e90d3151afb17c51b3ef76c9deef493f22fc8d2e Mon Sep 17 00:00:00 2001 From: DerGenaue Date: Tue, 15 Sep 2015 21:46:16 +0200 Subject: [PATCH 50/58] Update German translation Translated all missing ones --- syncplay/messages.py | 52 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/syncplay/messages.py b/syncplay/messages.py index ee83346..7fd6802 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -774,8 +774,8 @@ de = { "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", # TODO: Check German translation - "set-as-not-ready-notification" : u"You are now set as not ready", # TODO: Translate into German + "set-as-ready-notification" : u"Du bist bereit", + "set-as-not-ready-notification" : u"Du bist nicht bereit", "autoplaying-notification" : u"Starte in {}...", "identifying-as-controller-notification" : u"Identifiziere als Raumleiter mit Passwort '{}'...", # TODO: find a better translation to "room operator" @@ -831,7 +831,7 @@ de = { "vlc-interface-version-mismatch": u"Warnung: Du nutzt Version {} des VLC-Syncplay Interface-Moduls, Syncplay benötigt aber mindestens Version {}.", # 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 http://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 http://syncplay.pl/guide/ [Englisch] findest du Details zur Installation des syncplay.lua-Skripts.", - "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 # TODO: Translate into German + "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 # Client prompts "enter-to-exit-prompt" : u"Enter drücken zum Beenden\n", @@ -872,10 +872,10 @@ de = { "invalid-seek-value" : u"Ungültige Zeitangabe", "invalid-offset-value" : u"Ungültiger Offset-Wert", - "switch-file-not-found-error" : u"Could not switch to file '{0}'. Syncplay looks in the folder of the currently playing file and specified media directories.", # File not found, folder it was not found in # TODO: Translate into German - "folder-search-timeout-error" : u"The search for media in '{}' was aborted as it took too long. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. Until Syncplay is restarted only the directory of the currently open file will be checked.", #Folder # TODO: Translate into German + "switch-file-not-found-error" : u"Konnte nicht zur Datei '{0}' wechseln. Syncplay sucht im Ordner der aktuellen Datei und angegebenen Medien-Verzeichnissen.", # File not found, folder it was not found in + "folder-search-timeout-error" : u"Die Suche nach Mediendateien in '{}' wurde abgebrochen weil sie zu lange gedauert hat. Dies tritt auf, wenn ein zu durchsuchender Medienordner zu viele Unterordner hat. Syncplay wird bis zum Neustart nur noch das Verzeichnis der aktuellen Datei durchsuchen.", #Folder - "failed-to-load-server-list-error" : u"Failed to load public server list. Please visit http://www.syncplay.pl/ in your browser.", # TODO: Translate into German + "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.", # Client arguments "argument-description" : u'Syncplay ist eine Anwendung um mehrere MPlayer, MPC-HC und VLC-Instanzen über das Internet zu synchronisieren.', @@ -909,9 +909,9 @@ de = { "media-setting-title" : u"Media-Player Einstellungen", "executable-path-label" : u"Pfad zum Media-Player:", "media-path-label" : u"Pfad zur Datei:", - "player-arguments-label" : u"Player arguments:", # TODO: Translate into German + "player-arguments-label" : u"Playerparameter:", "browse-label" : u"Durchsuchen", - "update-server-list-label" : u"Update list", # TODO: Translate into German + "update-server-list-label" : u"Liste aktualisieren", "more-title" : u"Mehr Einstellungen zeigen", "never-rewind-value" : u"Niemals", @@ -936,14 +936,14 @@ de = { "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 # TODO: Change to German translation of "default" / Current translation represents Idea better + "automatic-language" : u"Automatisch ({})", # Default language # NODO: Change to German translation of "default" / Current translation represents Idea better "showdurationnotification-label" : u"Zeige Warnung wegen unterschiedlicher Dauer", "basics-label" : u"Grundlagen", - "readiness-label" : u"Play/Pause", # TODO: Translate into German + "readiness-label" : u"Play/Pause", # NODO: Translate into German / Is used normally in German "misc-label" : u"Diverse", "core-behaviour-title" : u"Verhalten des Raumes", "syncplay-internals-title" : u"Syncplay intern", - "syncplay-mediasearchdirectories-title" : u"Directories to search for media (one path per line)", # TODO: Translate into Russian + "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...", @@ -952,11 +952,11 @@ de = { "messages-other-title" : u"Weitere Display-Einstellungen", "privacy-label" : u"Privatsphäre", "privacy-title" : u"Privatsphäreneinstellungen", - "unpause-title" : u"If you press play, set as ready and:", # TODO: Translate into German - "unpause-ifalreadyready-option" : u"Unpause if already set as ready", # TODO: Translate into German - "unpause-ifothersready-option" : u"Unpause if already ready or others in room are ready (default)", # TODO: Translate into German - "unpause-ifminusersready-option" : u"Unpause if already ready or if all others ready and min users ready", # TODO: Translate into German - "unpause-always" : u"Always unpause", # TODO: Translate into German + "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", "help-label" : u"Hilfe", "reset-label" : u"Standardwerte zurücksetzen", @@ -1014,7 +1014,7 @@ de = { "identifyascontroller-msgbox-label" : u"Als Raumleiter identifizieren", "identifyinfo-msgbox-label" : u"Passwort des zentral gesteuerten Raums eingeben\r\n(siehe http://syncplay.pl/guide/ für eine Anleitung [Englisch]):", - "public-server-msgbox-label" : u"Select the public server for this viewing session", # TODO: Translate into German + "public-server-msgbox-label" : u"Einen öffentlichen Server für diese Sitzung auswählen", "megabyte-suffix" : u" MB", @@ -1027,8 +1027,8 @@ de = { "executable-path-tooltip" : u"Pfad zum ausgewählten, unterstützten Mediaplayer (MPC-HC, VLC, mplayer2 or mpv).", "media-path-tooltip" : u"Pfad zum wiederzugebenden Video oder Stream. Notwendig für mpv und mplayer2.", - "player-arguments-tooltip" : u"Additional command line arguments / switches to pass on to this media player.", # TODO: Translate into German - "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.", # TODO: Translate into German + "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) "more-tooltip" : u"Weitere Einstellungen anzeigen.", "filename-privacy-tooltip" : u"Privatheitsmodus beim Senden des Namens der aktuellen Datei zum Server.", @@ -1055,20 +1055,20 @@ de = { "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"Fortsetzen startet immer (anstatt nur den Bereitschaftsstatus zu ändern)", - "unpause-ifalreadyready-tooltip" : u"If you press unpause when not ready it will set you as ready - press unpause again to unpause.", # TODO: Translate into German - "unpause-ifothersready-tooltip" : u"If you press unpause when not ready, it will only upause if others are ready.", # TODO: Translate into German - "unpause-ifminusersready-tooltip" : u"If you press unpause when not ready, it will only upause if others are ready and minimum users threshold is met.", # TODO: Translate into German + "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.", "help-tooltip" : u"Öffnet Hilfe auf syncplay.pl [Englisch]", "reset-tooltip" : u"Alle Einstellungen auf Standardwerte zurücksetzen.", - "update-server-list-tooltip" : u"Connect to syncplay.pl to update list of public servers.", # TODO: Translate to German + "update-server-list-tooltip" : u"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 und die minimale Nutzerzahl erreicht ist.", - "switch-to-file-tooltip" : u"Double click to switch to {}", # Filename # TODO: Translate to German + "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 # In-userlist notes (GUI) "differentsize-note" : u"Verschiedene Größe!", From 450553334a1275bf1ea6a6762d85861f31fa54f0 Mon Sep 17 00:00:00 2001 From: Et0h Date: Tue, 15 Sep 2015 23:41:02 +0100 Subject: [PATCH 51/58] Remove redundant messages comments and explain unspecified variable --- syncplay/messages.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/syncplay/messages.py b/syncplay/messages.py index 7fd6802..4bf6855 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -38,7 +38,7 @@ en = { "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 {}...", + "autoplaying-notification" : u"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.", @@ -407,7 +407,7 @@ ru = { "ready-to-unpause-notification" : u"Вы помечены как готовый - нажмите еще раз, чтобы продолжить воспроизведение", "set-as-ready-notification" : u"Вы помечены как готовый", "set-as-not-ready-notification" : u"Вы помечены как неготовый", - "autoplaying-notification" : u"Автовоспроизведение через {}...", # TODO: What's the variable? + "autoplaying-notification" : u"Автовоспроизведение через {}...", # Number of seconds until playback will start "identifying-as-controller-notification" : u"Идентификация как оператора комнаты с паролем '{}'...", "failed-to-identify-as-controller-notification" : u"<{}> не прошел идентификацию в качестве оператора комнаты.", @@ -776,7 +776,7 @@ de = { "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 {}...", + "autoplaying-notification" : u"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.", @@ -936,10 +936,10 @@ de = { "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 # NODO: Change to German translation of "default" / Current translation represents Idea better + "automatic-language" : u"Automatisch ({})", # Default language "showdurationnotification-label" : u"Zeige Warnung wegen unterschiedlicher Dauer", "basics-label" : u"Grundlagen", - "readiness-label" : u"Play/Pause", # NODO: Translate into German / Is used normally in German + "readiness-label" : u"Play/Pause", "misc-label" : u"Diverse", "core-behaviour-title" : u"Verhalten des Raumes", "syncplay-internals-title" : u"Syncplay intern", From 7bc9159d3318cd8c19531151832511e5f83f0334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Wr=C3=B3bel?= Date: Wed, 16 Sep 2015 20:53:59 +0200 Subject: [PATCH 52/58] Make mpv check for actual video length. --- syncplay/constants.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index 980bfb5..f20d44e 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -138,7 +138,7 @@ USERLIST_GUI_USERNAME_COLUMN = 0 USERLIST_GUI_FILENAME_COLUMN = 3 MPLAYER_SLAVE_ARGS = ['-slave', '--hr-seek=always', '-nomsgcolor', '-msglevel', 'all=1:global=4:cplayer=4', '-af-add', 'scaletempo'] -MPV_ARGS = ['--force-window', '--idle', '--hr-seek=always', '--keep-open', '--af-add=scaletempo'] +MPV_ARGS = ['--force-window', '--idle', '--hr-seek=always', '--keep-open', '--af-add=scaletempo', '--demuxer-mkv-probe-video-duration=yes'] MPV_SLAVE_ARGS = ['--quiet', '--input-terminal=no', '--input-file=/dev/stdin'] MPV_SLAVE_ARGS_NEW = ['--term-playing-msg=\nANS_filename=${filename}\nANS_length=${=length}\nANS_path=${path}\n', '--terminal=yes'] MPV_NEW_VERSION = False @@ -175,4 +175,4 @@ FILEITEM_SWITCH_STREAM_SWITCH = 2 SYNCPLAY_UPDATE_URL = u"http://syncplay.pl/checkforupdate?{}" # Params SYNCPLAY_DOWNLOAD_URL = "http://syncplay.pl/download/" -SYNCPLAY_PUBLIC_SERVER_LIST_URL = u"http://syncplay.pl/listpublicservers?{}" # Params \ No newline at end of file +SYNCPLAY_PUBLIC_SERVER_LIST_URL = u"http://syncplay.pl/listpublicservers?{}" # Params From 6607cd5a2d6950d3cbddf5d41e3ae6d6f879dde5 Mon Sep 17 00:00:00 2001 From: Et0h Date: Sun, 20 Sep 2015 10:43:43 +0100 Subject: [PATCH 53/58] Initial code to move FileCheck search into separate thread + add caching --- syncplay/__init__.py | 2 +- syncplay/constants.py | 6 +- syncplay/ui/gui.py | 209 ++++++++++++++++++++++++++++++++++-------- syncplay/utils.py | 11 --- 4 files changed, 177 insertions(+), 51 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 7e08937..c19d7d7 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ version = '1.3.3' milestone = 'Chami' -release_number = '19' +release_number = '20' projectURL = 'http://syncplay.pl/' diff --git a/syncplay/constants.py b/syncplay/constants.py index f20d44e..e778d69 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -49,7 +49,11 @@ SERVER_STATE_INTERVAL = 1 WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1 AUTOPLAY_DELAY = 3.0 SYNC_ON_PAUSE = True # Client seek to global position - subtitles may disappear on some media players -FOLDER_SEARCH_TIMEOUT = 2.0 # Secs + +# Options for the File Switch feature: +FOLDER_SEARCH_TIMEOUT = 60.0 # Secs - How long to wait until searches in folder to update cache are aborted (may be longer than this if hard drive needs to spin up) +FOLDER_SEARCH_DOUBLE_CHECK_INTERVAL = 120.0 # Secs - Frequency of updating cache when someone is playing a file not in current cache +MEDIA_CACHE_CHECK_INTERVAL = 0.2 # Secs - Frequency of checking for the cache being updated #Usually there's no need to adjust these LAST_PAUSED_DIFF_THRESHOLD = 2 diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 23f43da..d17ad22 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -10,6 +10,8 @@ import re import os from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize, isURL from functools import wraps +from twisted.internet import task +import threading lastCheckedForUpdates = None class UserlistItemDelegate(QtGui.QStyledItemDelegate): @@ -83,6 +85,153 @@ class UserlistItemDelegate(QtGui.QStyledItemDelegate): QtGui.QStyledItemDelegate.paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex) class MainWindow(QtGui.QMainWindow): + class FileSwitchManager(object): + def __init__(self): + self.fileSwitchTimer = task.LoopingCall(self.updateInfo) + self.fileSwitchTimer.start(constants.FOLDER_SEARCH_DOUBLE_CHECK_INTERVAL, True) + self.fileCheckTimer = task.LoopingCall(self.checkForUpdate) + self.fileCheckTimer.start(constants.MEDIA_CACHE_CHECK_INTERVAL, True) + + mediaFilesCache = {} + filenameWatchlist = [] + currentDirectory = None + mediaDirectories = None + lock = threading.Lock() + client = None + currentWindow = None + folderSearchEnabled = True + disabledDir = None + newInfo = False + currentlyUpdating = False + + @staticmethod + def setWindow(window): + MainWindow.FileSwitchManager.currentWindow = window + + @staticmethod + def setClient(newClient): + MainWindow.FileSwitchManager.client = newClient + + @staticmethod + def setCurrentDirectory(curDir): + MainWindow.FileSwitchManager.currentDirectory = curDir + MainWindow.FileSwitchManager.updateInfo() + + @staticmethod + def setMediaDirectories(mediaDirs): + MainWindow.FileSwitchManager.mediaDirectories = mediaDirs + MainWindow.FileSwitchManager.updateInfo() + + @staticmethod + def checkForUpdate(): + if MainWindow.FileSwitchManager.newInfo: + MainWindow.FileSwitchManager.newInfo = False + MainWindow.FileSwitchManager.infoUpdated() + + @staticmethod + def updateInfo(): + if len(MainWindow.FileSwitchManager.filenameWatchlist) > 0 or len(MainWindow.FileSwitchManager.mediaFilesCache) == 0 and MainWindow.FileSwitchManager.currentlyUpdating == False: + newThread = threading.Thread(target=MainWindow.FileSwitchManager._updateInfoThread) + newThread.setDaemon(True) + newThread.start() + + @staticmethod + def setFilenameWatchlist(unfoundFilenames): + MainWindow.FileSwitchManager.filenameWatchlist = unfoundFilenames + + @staticmethod + def _updateInfoThread(): + if not MainWindow.FileSwitchManager.folderSearchEnabled: + if MainWindow.FileSwitchManager.areWatchedFilenamesInCurrentDir(): + MainWindow.FileSwitchManager.newInfo = True + return + + with MainWindow.FileSwitchManager.lock: + try: + MainWindow.FileSwitchManager.currentlyUpdating = True + dirsToSearch = MainWindow.FileSwitchManager.mediaDirectories + + if dirsToSearch: + newMediaFilesCache = {} + startTime = time.time() + for directory in dirsToSearch: + for root, dirs, files in os.walk(directory): + newMediaFilesCache[root] = files + if time.time() - startTime > constants.FOLDER_SEARCH_TIMEOUT: + if MainWindow.FileSwitchManager.client is not None and MainWindow.FileSwitchManager.currentWindow is not None: + MainWindow.FileSwitchManager.disabledDir = directory + MainWindow.FileSwitchManager.folderSearchEnabled = False + if MainWindow.FileSwitchManager.areWatchedFilenamesInCurrentDir(): + MainWindow.FileSwitchManager.newInfo = True + return + + if MainWindow.FileSwitchManager.mediaFilesCache <> newMediaFilesCache: + MainWindow.FileSwitchManager.mediaFilesCache = newMediaFilesCache + MainWindow.FileSwitchManager.newInfo = True + elif MainWindow.FileSwitchManager.areWatchedFilenamesInCurrentDir(): + MainWindow.FileSwitchManager.newInfo = True + finally: + MainWindow.FileSwitchManager.currentlyUpdating = False + + @staticmethod + def infoUpdated(): + if MainWindow.FileSwitchManager.areWatchedFilenamesInCache() or MainWindow.FileSwitchManager.areWatchedFilenamesInCurrentDir(): + MainWindow.FileSwitchManager.updateListOfWhoIsPlayingWhat() + + @staticmethod + def updateListOfWhoIsPlayingWhat(): + if MainWindow.FileSwitchManager.client is not None: + MainWindow.FileSwitchManager.client.showUserList() + + @staticmethod + def findFilepath(filename): + if filename is None: + return + + if MainWindow.FileSwitchManager.currentDirectory is not None: + candidatePath = os.path.join(MainWindow.FileSwitchManager.currentDirectory,filename) + if os.path.isfile(candidatePath): + return candidatePath + + if MainWindow.FileSwitchManager.mediaFilesCache is not None: + for directory in MainWindow.FileSwitchManager.mediaFilesCache: + files = MainWindow.FileSwitchManager.mediaFilesCache[directory] + + if len(files) > 0 and filename in files: + filepath = os.path.join(directory, filename) + if os.path.isfile(filepath): + return filepath + + @staticmethod + def areWatchedFilenamesInCurrentDir(): + if MainWindow.FileSwitchManager.filenameWatchlist is not None and MainWindow.FileSwitchManager.currentDirectory is not None: + for filename in MainWindow.FileSwitchManager.filenameWatchlist: + potentialPath = os.path.join(MainWindow.FileSwitchManager.currentDirectory,filename) + if os.path.isfile(potentialPath): + return True + + @staticmethod + def areWatchedFilenamesInCache(): + if MainWindow.FileSwitchManager.filenameWatchlist is not None: + for filename in MainWindow.FileSwitchManager.filenameWatchlist: + if MainWindow.FileSwitchManager.isFilenameInCache(filename): + return True + + @staticmethod + def isFilenameInCurrentDir(filename): + if filename is not None and MainWindow.FileSwitchManager.currentDirectory is not None: + potentialPath = os.path.join(MainWindow.FileSwitchManager.currentDirectory,filename) + if os.path.isfile(potentialPath): + return True + + @staticmethod + def isFilenameInCache(filename): + if filename is not None and MainWindow.FileSwitchManager.mediaFilesCache is not None: + for directory in MainWindow.FileSwitchManager.mediaFilesCache: + files = MainWindow.FileSwitchManager.mediaFilesCache[directory] + if filename in files: + return True + class topSplitter(QtGui.QSplitter): def createHandle(self): return self.topSplitterHandle(self.orientation(), self) @@ -107,9 +256,11 @@ class MainWindow(QtGui.QMainWindow): def addClient(self, client): self._syncplayClient = client + MainWindow.FileSwitchManager.setClient(client) self.roomInput.setText(self._syncplayClient.getRoom()) self.config = self._syncplayClient.getConfig() try: + self.FileSwitchManager.setMediaDirectories(self.config["mediaSearchDirectories"]) self.updateReadyState(self.config['readyAtStart']) autoplayInitialState = self.config['autoplayInitialState'] if autoplayInitialState is not None: @@ -150,28 +301,11 @@ class MainWindow(QtGui.QMainWindow): return constants.FILEITEM_SWITCH_NO_SWITCH if isURL(filename): return constants.FILEITEM_SWITCH_STREAM_SWITCH - else: - currentPath = self._syncplayClient.userlist.currentUser.file["path"] if self._syncplayClient.userlist.currentUser.file else None - if self.folderSearchEnabled: - try: - filenamesInDirectories = utils.findFilenameInDirectories(filename, self.config["mediaSearchDirectories"]) - except IOError as errorMessage: - self.showErrorMessage(errorMessage) - filenamesInDirectories = None - self.folderSearchEnabled = False - else: - filenamesInDirectories = None - if filenamesInDirectories: + elif filename not in self.newWatchlist: + if MainWindow.FileSwitchManager.findFilepath(filename): return constants.FILEITEM_SWITCH_FILE_SWITCH - elif currentPath: - currentDirectory = os.path.dirname(currentPath) - newPath = os.path.join(currentDirectory, filename) - if os.path.isfile(newPath): - return constants.FILEITEM_SWITCH_FILE_SWITCH - else: - return constants.FILEITEM_SWITCH_NO_SWITCH else: - return constants.FILEITEM_SWITCH_NO_SWITCH + self.newWatchlist.extend([filename]) return constants.FILEITEM_SWITCH_NO_SWITCH def showUserList(self, currentUser, rooms): @@ -179,8 +313,11 @@ class MainWindow(QtGui.QMainWindow): self._usertreebuffer.setHorizontalHeaderLabels( (getMessage("roomuser-heading-label"), getMessage("size-heading-label"), getMessage("duration-heading-label"), getMessage("filename-heading-label") )) usertreeRoot = self._usertreebuffer.invisibleRootItem() + if self._syncplayClient.userlist.currentUser.file and self._syncplayClient.userlist.currentUser.file and os.path.isfile(self._syncplayClient.userlist.currentUser.file["path"]): + MainWindow.FileSwitchManager.setCurrentDirectory(os.path.dirname(self._syncplayClient.userlist.currentUser.file["path"])) for room in rooms: + self.newWatchlist = [] roomitem = QtGui.QStandardItem(room) font = QtGui.QFont() font.setItalic(True) @@ -266,6 +403,13 @@ class MainWindow(QtGui.QMainWindow): self.listTreeView.setRootIsDecorated(False) self.listTreeView.expandAll() self.updateListGeometry() + MainWindow.FileSwitchManager.setFilenameWatchlist(self.newWatchlist) + self.checkForDisabledDir() + + def checkForDisabledDir(self): + if MainWindow.FileSwitchManager.disabledDir is not None and MainWindow.FileSwitchManager.currentWindow is not None: + self.showErrorMessage(getMessage("folder-search-timeout-error").format(MainWindow.FileSwitchManager.disabledDir)) + MainWindow.FileSwitchManager.disabledDir = None def updateListGeometry(self): try: @@ -309,26 +453,11 @@ class MainWindow(QtGui.QMainWindow): if isURL(filename): self._syncplayClient._player.openFile(filename) else: - currentPath = self._syncplayClient.userlist.currentUser.file["path"] if self._syncplayClient.userlist.currentUser.file else None - if self.folderSearchEnabled: - try: - pathFound = utils.findFilenameInDirectories(filename, self.config["mediaSearchDirectories"]) - except IOError as errorMessage: - self.showErrorMessage(errorMessage) - pathFound = None - self.folderSearchEnabled = False - else: - pathFound = None + pathFound = MainWindow.FileSwitchManager.findFilepath(filename) if pathFound: self._syncplayClient._player.openFile(pathFound) - elif currentPath: - currentDirectory = os.path.dirname(currentPath) - newPath = os.path.join(currentDirectory, filename) - if os.path.isfile(newPath): - self._syncplayClient._player.openFile(newPath) - else: - self.showErrorMessage(getMessage("switch-file-not-found-error").format(filename, currentDirectory)) else: + MainWindow.FileSwitchManager.updateInfo() self.showErrorMessage(getMessage("switch-file-not-found-error").format(filename)) @needsClient @@ -455,6 +584,7 @@ class MainWindow(QtGui.QMainWindow): if sys.platform.startswith('win'): fileName = fileName.replace("/", "\\") self.mediadirectory = os.path.dirname(fileName) + self.FileSwitchManager.setCurrentDirectory(self.mediadirectory) self.saveMediaBrowseSettings() self._syncplayClient._player.openFile(fileName) @@ -785,7 +915,7 @@ class MainWindow(QtGui.QMainWindow): self._syncplayClient.changeReadyState(self.readyPushButton.isChecked()) else: self.showDebugMessage("Tried to change ready state too soon.") - + @needsClient def changeAutoplayThreshold(self, source=None): self._syncplayClient.changeAutoPlayThrehsold(self.autoplayThresholdSpinbox.value()) @@ -931,6 +1061,9 @@ class MainWindow(QtGui.QMainWindow): def __init__(self): super(MainWindow, self).__init__() + FileSwitchManager = self.FileSwitchManager() + FileSwitchManager.setWindow(self) + self.newWatchlist = [] self.publicServerList = [] self.lastCheckedForUpdates = None self._syncplayClient = None diff --git a/syncplay/utils.py b/syncplay/utils.py index 1e7263d..fb629df 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -250,17 +250,6 @@ def getListAsMultilineString(pathArray): def convertMultilineStringToList(multilineString): return unicode.split(multilineString,u"\n") if multilineString else "" -def findFilenameInDirectories(filename, directoryList): - if filename and directoryList: - startTime = time.time() - for directory in directoryList: - for root, dirs, files in os.walk(directory): - if filename in files: - return os.path.join(root,filename) - if time.time() - startTime > constants.FOLDER_SEARCH_TIMEOUT: - raise IOError(getMessage("folder-search-timeout-error").format(directory)) - return None - def getListOfPublicServers(): try: import urllib, syncplay, sys, messages, json From 7022865cd08c0a8ba5f7c32d0156cff0f8e69c4c Mon Sep 17 00:00:00 2001 From: Et0h Date: Sun, 20 Sep 2015 11:23:32 +0100 Subject: [PATCH 54/58] Remove --demuxer-mkv-probe-video-duration=yes to allow for more testing & upver to 1.3.4 --- syncplay/__init__.py | 2 +- syncplay/constants.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index c19d7d7..ec5a830 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ -version = '1.3.3' +version = '1.3.4' milestone = 'Chami' release_number = '20' projectURL = 'http://syncplay.pl/' diff --git a/syncplay/constants.py b/syncplay/constants.py index e778d69..70c0412 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -142,7 +142,7 @@ USERLIST_GUI_USERNAME_COLUMN = 0 USERLIST_GUI_FILENAME_COLUMN = 3 MPLAYER_SLAVE_ARGS = ['-slave', '--hr-seek=always', '-nomsgcolor', '-msglevel', 'all=1:global=4:cplayer=4', '-af-add', 'scaletempo'] -MPV_ARGS = ['--force-window', '--idle', '--hr-seek=always', '--keep-open', '--af-add=scaletempo', '--demuxer-mkv-probe-video-duration=yes'] +MPV_ARGS = ['--force-window', '--idle', '--hr-seek=always', '--keep-open', '--af-add=scaletempo'] MPV_SLAVE_ARGS = ['--quiet', '--input-terminal=no', '--input-file=/dev/stdin'] MPV_SLAVE_ARGS_NEW = ['--term-playing-msg=\nANS_filename=${filename}\nANS_length=${=length}\nANS_path=${path}\n', '--terminal=yes'] MPV_NEW_VERSION = False From 2b555134d1e4aec1f3e8fc18af30c202eff2ca1b Mon Sep 17 00:00:00 2001 From: Et0h Date: Sun, 27 Sep 2015 22:43:25 +0100 Subject: [PATCH 55/58] Use callback for FileSwitch (thanks Uriziel) --- syncplay/constants.py | 1 - syncplay/ui/gui.py | 10 +++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index 70c0412..e95d929 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -53,7 +53,6 @@ SYNC_ON_PAUSE = True # Client seek to global position - subtitles may disappear # Options for the File Switch feature: FOLDER_SEARCH_TIMEOUT = 60.0 # Secs - How long to wait until searches in folder to update cache are aborted (may be longer than this if hard drive needs to spin up) FOLDER_SEARCH_DOUBLE_CHECK_INTERVAL = 120.0 # Secs - Frequency of updating cache when someone is playing a file not in current cache -MEDIA_CACHE_CHECK_INTERVAL = 0.2 # Secs - Frequency of checking for the cache being updated #Usually there's no need to adjust these LAST_PAUSED_DIFF_THRESHOLD = 2 diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index d17ad22..e6e5be2 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -10,7 +10,7 @@ import re import os from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize, isURL from functools import wraps -from twisted.internet import task +from twisted.internet import task, threads import threading lastCheckedForUpdates = None @@ -89,8 +89,6 @@ class MainWindow(QtGui.QMainWindow): def __init__(self): self.fileSwitchTimer = task.LoopingCall(self.updateInfo) self.fileSwitchTimer.start(constants.FOLDER_SEARCH_DOUBLE_CHECK_INTERVAL, True) - self.fileCheckTimer = task.LoopingCall(self.checkForUpdate) - self.fileCheckTimer.start(constants.MEDIA_CACHE_CHECK_INTERVAL, True) mediaFilesCache = {} filenameWatchlist = [] @@ -123,7 +121,7 @@ class MainWindow(QtGui.QMainWindow): MainWindow.FileSwitchManager.updateInfo() @staticmethod - def checkForUpdate(): + def checkForUpdate(self=None): if MainWindow.FileSwitchManager.newInfo: MainWindow.FileSwitchManager.newInfo = False MainWindow.FileSwitchManager.infoUpdated() @@ -131,9 +129,7 @@ class MainWindow(QtGui.QMainWindow): @staticmethod def updateInfo(): if len(MainWindow.FileSwitchManager.filenameWatchlist) > 0 or len(MainWindow.FileSwitchManager.mediaFilesCache) == 0 and MainWindow.FileSwitchManager.currentlyUpdating == False: - newThread = threading.Thread(target=MainWindow.FileSwitchManager._updateInfoThread) - newThread.setDaemon(True) - newThread.start() + threads.deferToThread(MainWindow.FileSwitchManager._updateInfoThread).addCallback(MainWindow.FileSwitchManager.checkForUpdate) @staticmethod def setFilenameWatchlist(unfoundFilenames): From 04054c307820d95fcea49d76db48a350576778db Mon Sep 17 00:00:00 2001 From: Et0h Date: Thu, 1 Oct 2015 14:01:16 +0100 Subject: [PATCH 56/58] Handle mpv unresponsiveness by estimating position then critical error --- syncplay/__init__.py | 2 +- syncplay/constants.py | 1 + syncplay/messages.py | 3 +++ syncplay/players/mpv.py | 28 +++++++++++++++++++++++++++- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index ec5a830..b0ad1d2 100644 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,4 +1,4 @@ version = '1.3.4' milestone = 'Chami' -release_number = '20' +release_number = '21' projectURL = 'http://syncplay.pl/' diff --git a/syncplay/constants.py b/syncplay/constants.py index e95d929..7d6d3ec 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -71,6 +71,7 @@ MPC_MIN_VER = "1.6.4" VLC_MIN_VERSION = "2.2.1" VLC_INTERFACE_MIN_VERSION = "0.2.8" VLC_LATENCY_ERROR_THRESHOLD = 2.0 +MPV_UNRESPONSIVE_THRESHOLD = 60.0 CONTROLLED_ROOMS_MIN_VERSION = "1.3.0" USER_READY_MIN_VERSION = "1.3.0" MPC_PATHS = [ diff --git a/syncplay/messages.py b/syncplay/messages.py index 4bf6855..7abe1ad 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -94,6 +94,7 @@ en = { "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 http://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 http://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 # Client prompts "enter-to-exit-prompt" : "Press enter to exit\n", @@ -463,6 +464,7 @@ ru = { "vlc-interface-oldversion-warning" : u"Внимание: Syncplay обнаружил, что старая версия модуля интерфейса Syncplay для VLC уже установлена в директорию VLC. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (http://syncplay.pl/guide/) за инструкциями о том, как установить syncplay.lua.", "vlc-interface-not-installed" : u"Внимание: Модуль интерфейса Syncplay для VLC не обнаружен в директории VLC. По существу, если Вы используете VLC 2.0, то VLC будет использовать модуль syncplay.lua из директории Syncplay, но в таком случае другие пользовательские скрипты и расширения интерфейса не будут работать. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (http://syncplay.pl/guide/) за инструкциями о том, как установить syncplay.lua.", "media-player-latency-warning": u"Внимание: У Вашего проигрывателя слишком большой отклик ({} секунд). Если Вы замечаете проблемы с синхронизацией, то закройте ресурсоемкие приложения, а если это не помогло - попробуйте другой проигрыватель.", # Seconds to respond + "mpv-unresponsive-error": u"mpv has not responded for {} seconds so appears to have malfunctioned. Please restart Syncplay.", # Seconds to respond # TODO: Translate to Russian # Client prompts "enter-to-exit-prompt" : u"Для выхода нажмите Enter\n", @@ -832,6 +834,7 @@ de = { "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 http://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 http://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 has not responded for {} seconds so appears to have malfunctioned. Please restart Syncplay.", # Seconds to respond # TODO: Translate to German # Client prompts "enter-to-exit-prompt" : u"Enter drücken zum Beenden\n", diff --git a/syncplay/players/mpv.py b/syncplay/players/mpv.py index 4236cef..41d5a25 100644 --- a/syncplay/players/mpv.py +++ b/syncplay/players/mpv.py @@ -106,6 +106,14 @@ class OldMpvPlayer(MpvPlayer): class NewMpvPlayer(OldMpvPlayer): lastResetTime = None + lastMPVPositionUpdate = None + + def setPaused(self, value): + if self._paused <> value: + self._paused = not self._paused + self._listener.sendLine('cycle pause') + if value == False: + self.lastMPVPositionUpdate = time.time() def _getProperty(self, property_): floatProperties = ['length','time-pos'] @@ -115,7 +123,21 @@ class NewMpvPlayer(OldMpvPlayer): propertyID = property_ self._listener.sendLine(u"print_text ""ANS_{}=${{{}}}""".format(property_, propertyID)) + def getCalculatedPosition(self): + if self.lastMPVPositionUpdate is None: + return self._client.getGlobalPosition() + diff = time.time() - self.lastMPVPositionUpdate + if diff > constants.MPV_UNRESPONSIVE_THRESHOLD: + self.reactor.callFromThread(self._client.ui.showErrorMessage, getMessage("mpv-unresponsive-error").format(int(diff)), True) + self.drop() + if diff > constants.PLAYER_ASK_DELAY and not self._paused: + self._client.ui.showDebugMessage("mpv did not response in time, so assuming position is {} ({}+{})".format(self._position + diff, self._position, diff)) + return self._position + diff + else: + return self._position + def _storePosition(self, value): + self.lastMPVPositionUpdate = time.time() if self._recentlyReset(): self._position = 0 elif self._fileIsLoaded(): @@ -136,7 +158,7 @@ class NewMpvPlayer(OldMpvPlayer): self._getPosition() self._positionAsk.wait(constants.MPV_LOCK_WAIT_TIME) self._pausedAsk.wait(constants.MPV_LOCK_WAIT_TIME) - self._client.updatePlayerStatus(self._paused, self._position) + self._client.updatePlayerStatus(self._paused, self.getCalculatedPosition()) def _preparePlayer(self): if self.delayedFilePath: @@ -152,6 +174,10 @@ class NewMpvPlayer(OldMpvPlayer): self._clearFileLoaded() self._listener.sendLine(u'loadfile {}'.format(self._quoteArg(filePath))) + def setPosition(self, value): + super(self.__class__, self).setPosition(value) + self.lastMPVPositionUpdate = time.time() + def openFile(self, filePath, resetPosition=False): if resetPosition: self.lastResetTime = time.time() From a1694672842b5229778630815a79d3d54a9fc028 Mon Sep 17 00:00:00 2001 From: Et0h Date: Thu, 1 Oct 2015 18:17:49 +0100 Subject: [PATCH 57/58] Don't send file path to server (!) --- syncplay/client.py | 15 ++++++++++++++- syncplay/constants.py | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/syncplay/client.py b/syncplay/client.py index 739f9de..3d135e9 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -7,6 +7,7 @@ import ast from twisted.internet.protocol import ClientFactory from twisted.internet import reactor, task from functools import wraps +from copy import deepcopy from syncplay.protocols import SyncClientProtocol from syncplay import utils, constants from syncplay.messages import getMissingStrings, getMessage @@ -420,8 +421,20 @@ class SyncplayClient(object): def setServerVersion(self, version): self.serverVersion = version + def getSanitizedCurrentUserFile(self): + if self.userlist.currentUser.file: + file_ = deepcopy(self.userlist.currentUser.file) + if constants.PRIVATE_FILE_FIELDS: + for PrivateField in constants.PRIVATE_FILE_FIELDS: + if file_.has_key(PrivateField): + file_.pop(PrivateField) + return file_ + else: + return None + + def sendFile(self): - file_ = self.userlist.currentUser.file + file_ = self.getSanitizedCurrentUserFile() if self._protocol and self._protocol.logged and file_: self._protocol.sendFileSetting(file_) diff --git a/syncplay/constants.py b/syncplay/constants.py index 7d6d3ec..a39ec6a 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -180,3 +180,5 @@ FILEITEM_SWITCH_STREAM_SWITCH = 2 SYNCPLAY_UPDATE_URL = u"http://syncplay.pl/checkforupdate?{}" # Params SYNCPLAY_DOWNLOAD_URL = "http://syncplay.pl/download/" SYNCPLAY_PUBLIC_SERVER_LIST_URL = u"http://syncplay.pl/listpublicservers?{}" # Params + +PRIVATE_FILE_FIELDS = ["path"] \ No newline at end of file From 9752aad75db1c7a705a5e0d6dfda8737620aa0ec Mon Sep 17 00:00:00 2001 From: Et0h Date: Thu, 1 Oct 2015 18:26:12 +0100 Subject: [PATCH 58/58] (mpv) use global position and pause state if no file is loaded --- syncplay/players/mpv.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/syncplay/players/mpv.py b/syncplay/players/mpv.py index 41d5a25..ff52af2 100644 --- a/syncplay/players/mpv.py +++ b/syncplay/players/mpv.py @@ -124,6 +124,9 @@ class NewMpvPlayer(OldMpvPlayer): self._listener.sendLine(u"print_text ""ANS_{}=${{{}}}""".format(property_, propertyID)) def getCalculatedPosition(self): + if self.fileLoaded == False: + return self._client.getGlobalPosition() + if self.lastMPVPositionUpdate is None: return self._client.getGlobalPosition() diff = time.time() - self.lastMPVPositionUpdate @@ -158,7 +161,7 @@ class NewMpvPlayer(OldMpvPlayer): self._getPosition() self._positionAsk.wait(constants.MPV_LOCK_WAIT_TIME) self._pausedAsk.wait(constants.MPV_LOCK_WAIT_TIME) - self._client.updatePlayerStatus(self._paused, self.getCalculatedPosition()) + self._client.updatePlayerStatus(self._paused if self.fileLoaded else self._client.getGlobalPaused(), self.getCalculatedPosition()) def _preparePlayer(self): if self.delayedFilePath: