diff --git a/buildPy2exe.py b/buildPy2exe.py index 6b2cb7c..21dc5fe 100644 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -209,7 +209,7 @@ NSIS_SCRIPT_TEMPLATE = r""" Pop $$CheckBox_QuickLaunchShortcut $${If} $$CheckBox_Associate_State == $${BST_CHECKED} - $${NSD_Check} $$CheckBox_Associate + $${NSD_Check} $$CheckBox_Associate $${EndIf} $${If} $$CheckBox_VLC_State == $${BST_CHECKED} @@ -522,9 +522,10 @@ info = dict( console=[{"script":"syncplayClient.py", "icon_resources":[(1, "resources\\icon.ico")], 'dest_base': "Syncplay"}, 'syncplayServer.py'], options={'py2exe': { 'dist_dir': OUT_DIR, - 'includes': 'cairo, pango, pangocairo, atk, gobject, twisted', + 'packages': 'PySide.QtUiTools', + 'includes': 'cairo, pango, pangocairo, atk, gobject, twisted, sys, encodings, datetime, os, time, math, PySide', 'excludes': 'venv, _ssl, doctest, pdb, unittest, win32clipboard, win32event, win32file, win32pdh, win32security, win32trace, win32ui, winxpgui, win32pipe, win32process', - 'dll_excludes': 'msvcr71.dll', + 'dll_excludes': 'msvcr71.dll, MSVCP90.dll', 'optimize': 2, 'compressed': 1 } @@ -536,4 +537,3 @@ info = dict( sys.argv.extend(['py2exe', '-p win32com ', '-i twisted.web.resource']) setup(**info) - diff --git a/syncplay/client.py b/syncplay/client.py index c055d93..8be0f93 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -212,7 +212,7 @@ class SyncplayClient(object): madeChangeOnPlayer = self._serverSeeked(position, setBy) if (diff > constants.REWIND_THRESHOLD and not doSeek): madeChangeOnPlayer = self._rewindPlayerDueToTimeDifference(position, setBy) - if (self._player.speedSupported and not doSeek and not paused): + if (self._player.speedSupported and not doSeek and not paused and not self._config['slowOnDesync'] == False): madeChangeOnPlayer = self._slowDownToCoverTimeDifference(diff, setBy) if (paused == False and pauseChanged): madeChangeOnPlayer = self._serverUnpaused(setBy) diff --git a/syncplay/constants.py b/syncplay/constants.py index 2c1c5cb..54283fb 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -9,7 +9,7 @@ DEFAULT_CONFIG_NAME = ".syncplay" #Changing these might be ok REWIND_THRESHOLD = 4 -SEEK_THRESHOLD = 0.9 +SEEK_THRESHOLD = 1 SLOWDOWN_RATE = 0.95 SLOWDOWN_KICKIN_THRESHOLD = 1.5 SLOWDOWN_RESET_THRESHOLD = 0.1 diff --git a/syncplay/players/vlc.py b/syncplay/players/vlc.py index e763a14..fb07709 100644 --- a/syncplay/players/vlc.py +++ b/syncplay/players/vlc.py @@ -28,6 +28,7 @@ class VlcPlayer(BasePlayer): self._duration = None self._filename = None self._filepath = None + self._filechanged = False self._durationAsk = threading.Event() self._filenameAsk = threading.Event() @@ -68,12 +69,16 @@ class VlcPlayer(BasePlayer): self.setPosition(self._client.getGlobalPosition()) def askForStatus(self): + self._filechanged = False self._positionAsk.clear() self._pausedAsk.clear() self._listener.sendLine(".") - self._positionAsk.wait() - self._pausedAsk.wait() - self._client.updatePlayerStatus(self._paused, self._position) + if self._filechanged == False: + self._positionAsk.wait() + self._pausedAsk.wait() + self._client.updatePlayerStatus(self._paused, self._position) + else: + self._client.updatePlayerStatus(self._client.getGlobalPaused(), self._client.getGlobalPosition()) def displayMessage(self, message, duration = constants.OSD_DURATION * 1000): duration /= 1000 @@ -101,10 +106,12 @@ class VlcPlayer(BasePlayer): name, value = match.group('command'), match.group('argument') if(line == "filepath-change-notification"): + self._filechanged = True t = threading.Thread(target=self._onFileUpdate) t.setDaemon(True) t.start() elif (name == "filepath" and value != "no-input"): + self._filechanged = True if("file://" in value): value = value.replace("file://", "") if(not os.path.isfile(value)): @@ -115,12 +122,13 @@ class VlcPlayer(BasePlayer): self._duration = float(value.replace(",", ".")) self._durationAsk.set() elif(name == "playstate"): - self._paused = bool(value != 'playing') if(value != "no-input") else self._client.getGlobalPaused() + self._paused = bool(value != 'playing') if(value != "no-input" and self._filechanged == False) else self._client.getGlobalPaused() self._pausedAsk.set() elif(name == "position"): - self._position = float(value.replace(",", ".")) if (value != "no-input") else self._client.getGlobalPosition() + self._position = float(value.replace(",", ".")) if (value != "no-input" and self._filechanged == False) else self._client.getGlobalPosition() self._positionAsk.set() elif(name == "filename"): + self._filechanged = True self._filename = value self._filenameAsk.set() elif (line[:16] == "VLC media player"): @@ -217,4 +225,4 @@ class VlcPlayer(BasePlayer): def sendLine(self, line): if(self.connected): # print "send: {}".format(line) - self.push(line + "\n") + self.push(line + "\n") diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 453b1ff..adc8cba 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -1,132 +1,256 @@ -import pygtk +from PySide import QtCore, QtGui +from PySide.QtCore import QSettings, Qt +from PySide.QtGui import QApplication, QLineEdit, QCursor, QLabel, QCheckBox, QDesktopServices + import os -pygtk.require('2.0') -import gtk -gtk.set_interactive(False) -import cairo, gio, pango, atk, pangocairo, gobject #@UnusedImport +import sys from syncplay.messages import getMessage class GuiConfiguration: def __init__(self, config): self.config = config self._availablePlayerPaths = [] - self.closedAndNotSaved = False - + def run(self): - self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) - self.window.set_title(getMessage("en", "config-window-title")) - self.window.connect("delete_event", lambda w, e: self._windowClosed()) - vbox = gtk.VBox(False, 0) - self.window.add(vbox) - vbox.show() - self._addLabeledEntries(self.config, vbox) - self._addCheckboxEntries(self.config, vbox) - self.hostEntry.select_region(0, len(self.hostEntry.get_text())) - button = gtk.Button(stock=gtk.STOCK_SAVE) - button.connect("clicked", lambda w: self._saveDataAndLeave()) - guideLink = gtk.LinkButton("http://syncplay.pl/guide/", "Configuration Guide") - guideLink.show() - vbox.add(guideLink) - vbox.pack_start(button, True, True, 0) - button.set_flags(gtk.CAN_DEFAULT) - button.grab_default() - button.show() - self.window.show() - gtk.main() - - def _windowClosed(self): - self.window.destroy() - gtk.main_quit() - self.closedAndNotSaved = True + self.app = QtGui.QApplication(sys.argv) + dialog = ConfigDialog(self.config, self._availablePlayerPaths) + dialog.exec_() + + def setAvailablePaths(self, paths): + self._availablePlayerPaths = paths + + def getProcessedConfiguration(self): + return self.config + + class WindowClosed(Exception): + pass + +class ConfigDialog(QtGui.QDialog): + + pressedclosebutton = False + + malToggling = False + + def malToggled(self): + if self.malToggling == False: + self.malToggling = True + + if self.malenabledCheckbox.isChecked() and self.malenabledCheckbox.isVisible(): + self.malenabledCheckbox.setChecked(False) + self.malSettingsGroup.setChecked(True) + self.malSettingsGroup.show() + self.malpasswordLabel.show() + self.malpasswordTextbox.show() + self.malusernameLabel.show() + self.malusernameTextbox.show() + self.malenabledCheckbox.hide() + else: + self.malSettingsGroup.setChecked(False) + self.malSettingsGroup.hide() + self.malpasswordLabel.hide() + self.malpasswordTextbox.hide() + self.malusernameLabel.hide() + self.malusernameTextbox.hide() + self.malenabledCheckbox.show() + + self.malToggling = False + self.adjustSize() + self.setFixedSize(self.sizeHint()) + + def runButtonTextUpdate(self): + if (self.donotstoreCheckbox.isChecked()): + self.runButton.setText("Run Syncplay") + else: + self.runButton.setText("Store configuration and run Syncplay") + + def openHelp(self): + self.QtGui.QDesktopServices.openUrl("http://syncplay.pl/guide/") + + def _tryToFillPlayerPath(self, playerpath, playerpathlist): + foundpath = "" - def _addLabeledEntries(self, config, vbox): + if playerpath != None and playerpath != "" and os.path.isfile(playerpath): + foundpath = playerpath + self.executablepathCombobox.addItem(foundpath) + + for path in playerpathlist: + if(os.path.isfile(path) and path.lower() != foundpath.lower()): + self.executablepathCombobox.addItem(path) + if foundpath == None: + foundpath = path + + if foundpath: + return(foundpath) + else: + return("") + + def browsePlayerpath(self): + options = QtGui.QFileDialog.Options() + defaultdirectory = "" + browserfilter = "All Files (*)" + + if os.name == 'nt': + browserfilter = "Executable files (*.exe);;All Files (*)" + if "PROGRAMFILES(X86)" in os.environ: + defaultdirectory = os.environ["ProgramFiles(x86)"] + elif "PROGRAMFILES" in os.environ: + defaultdirectory = os.environ["ProgramFiles"] + elif "PROGRAMW6432" in os.environ: + defaultdirectory = os.environ["ProgramW6432"] + elif sys.platform.startswith('linux'): + defaultdirectory = "/usr/bin" + + fileName, filtr = QtGui.QFileDialog.getOpenFileName(self, + "Browse for media player executable", + defaultdirectory, + browserfilter, "", options) + if fileName: + self.executablepathCombobox.setEditText(fileName) + + def _saveDataAndLeave(self): + self.config['host'] = self.hostTextbox.text() + self.config['name'] = self.usernameTextbox.text() + self.config['room'] = self.defaultroomTextbox.text() + self.config['password'] = self.serverpassTextbox.text() + self.config['playerPath'] = self.executablepathCombobox.currentText() + if self.alwaysshowCheckbox.isChecked() == True: + self.config['forceGuiPrompt'] = True + else: + self.config['forceGuiPrompt'] = False + if self.donotstoreCheckbox.isChecked() == True: + self.config['noStore'] = True + else: + self.config['noStore'] = False + if self.slowdownCheckbox.isChecked() == True: + self.config['slowOnDesync'] = True + else: + self.config['slowOnDesync'] = False + self.config['malUsername'] = self.malusernameTextbox.text() + if self.malSettingsGroup.isChecked(): + self.config['malPassword'] = self.malpasswordTextbox.text() + else: + self.config['malPassword'] = "" + self.pressedclosebutton = True + self.close() + return + + def closeEvent(self, event): + if self.pressedclosebutton == False: + sys.exit() + raise GuiConfiguration.WindowClosed + event.accept() + + def __init__(self, config, playerpaths): + + self.config = config + self.QtGui = QtGui + + super(ConfigDialog, self).__init__() + + self.setWindowTitle(getMessage("en", "config-window-title")) + if(config['host'] == None): host = "" elif(":" in config['host']): host = config['host'] else: host = config['host']+":"+str(config['port']) - - self.hostEntry = self._addLabeledEntryToVbox(getMessage("en", "host-label"), host, vbox, lambda __, _: self._saveDataAndLeave()) - self.userEntry = self._addLabeledEntryToVbox(getMessage("en", "username-label"), config['name'], vbox, lambda __, _: self._saveDataAndLeave()) - self.roomEntry = self._addLabeledEntryToVbox(getMessage("en", "room-label"), config['room'], vbox, lambda __, _: self._saveDataAndLeave()) - self.passEntry = self._addLabeledEntryToVbox(getMessage("en", "password-label"), config['password'], vbox, lambda __, _: self._saveDataAndLeave()) - self.mpcEntry = self._addLabeledEntryToVbox(getMessage("en", "path-label"), self._tryToFillPlayerPath(), vbox, lambda __, _: self._saveDataAndLeave()) + + self.connectionSettingsGroup = QtGui.QGroupBox("Connection Settings") + self.hostTextbox = QLineEdit(host, self) + self.hostLabel = QLabel(getMessage("en", "host-label"), self) + self.usernameTextbox = QLineEdit(config['name'],self) + self.serverpassLabel = QLabel(getMessage("en", "password-label"), self) + self.defaultroomTextbox = QLineEdit(config['room'],self) + self.usernameLabel = QLabel(getMessage("en", "username-label"), self) + self.serverpassTextbox = QLineEdit(config['password'],self) + self.defaultroomLabel = QLabel(getMessage("en", "room-label"), self) + self.connectionSettingsLayout = QtGui.QGridLayout() + self.connectionSettingsLayout.addWidget(self.hostLabel, 0, 0) + self.connectionSettingsLayout.addWidget(self.hostTextbox, 0, 1) + self.connectionSettingsLayout.addWidget(self.serverpassLabel, 1, 0) + self.connectionSettingsLayout.addWidget(self.serverpassTextbox, 1, 1) + self.connectionSettingsLayout.addWidget(self.usernameLabel, 2, 0) + self.connectionSettingsLayout.addWidget(self.usernameTextbox, 2, 1) + self.connectionSettingsLayout.addWidget(self.defaultroomLabel, 3, 0) + self.connectionSettingsLayout.addWidget(self.defaultroomTextbox, 3, 1) + self.connectionSettingsGroup.setLayout(self.connectionSettingsLayout) + + self.mediaplayerSettingsGroup = QtGui.QGroupBox("Media Player Settings") + self.executablepathCombobox = QtGui.QComboBox(self) + self.executablepathCombobox.setEditable(True) + self.executablepathCombobox.setEditText(self._tryToFillPlayerPath(config['playerPath'],playerpaths)) + self.executablepathCombobox.setMinimumWidth(200) + self.executablepathCombobox.setMaximumWidth(200) + self.executablepathLabel = QLabel("Path to player executable:", self) + self.executablebrowseButton = QtGui.QPushButton("Browse") + self.executablebrowseButton.clicked.connect(self.browsePlayerpath) + self.slowdownCheckbox = QCheckBox("Slow down on desync") + self.mediaplayerSettingsLayout = QtGui.QGridLayout() + self.mediaplayerSettingsLayout.addWidget(self.executablepathLabel, 0, 0) + self.mediaplayerSettingsLayout.addWidget(self.executablepathCombobox , 0, 1) + self.mediaplayerSettingsLayout.addWidget(self.executablebrowseButton , 0, 2) + self.mediaplayerSettingsLayout.addWidget(self.slowdownCheckbox, 1, 0) + self.mediaplayerSettingsGroup.setLayout(self.mediaplayerSettingsLayout) + if config['slowOnDesync'] == True: + self.slowdownCheckbox.setChecked(True) - def _tryToFillPlayerPath(self): - for path in self._availablePlayerPaths: - if(os.path.isfile(path)): - return path - return self.config["playerPath"] - - def getProcessedConfiguration(self): - if(self.closedAndNotSaved): - raise self.WindowClosed - return self.config - - def _saveDataAndLeave(self): - self.config['host'] = self.hostEntry.get_text() - self.config['name'] = self.userEntry.get_text() - self.config['room'] = self.roomEntry.get_text() - self.config['password'] = self.passEntry.get_text() - self.config['playerPath'] = self.mpcEntry.get_text() - if self.alwaysShowCheck.get_active() == True: - self.config['forceGuiPrompt'] = True + self.malSettingsGroup = QtGui.QGroupBox("Enable MyAnimeList Updater (EXPERIMENTAL)") + self.malSettingsGroup.setCheckable(True) + self.malSettingsGroup.toggled.connect(self.malToggled) + self.malSettingsSplit = QtGui.QSplitter(self) + self.malusernameTextbox = QLineEdit(config['malUsername'],self) + self.malusernameLabel = QLabel("MAL Username:", self) + self.malpasswordTextbox = QLineEdit(config['malPassword'],self) + self.malpasswordTextbox.setEchoMode(QtGui.QLineEdit.Password) + self.malpasswordLabel = QLabel("MAL Password:", self) + self.malSettingsLayout = QtGui.QGridLayout() + self.malSettingsLayout.addWidget(self.malusernameLabel , 0, 0) + self.malSettingsLayout.addWidget(self.malusernameTextbox, 0, 1) + self.malSettingsLayout.addWidget(self.malpasswordLabel , 1, 0) + self.malSettingsLayout.addWidget(self.malpasswordTextbox, 1, 1) + self.malSettingsGroup.setLayout(self.malSettingsLayout) + + self.malenabledCheckbox = QCheckBox("Enable MyAnimeList Updater (EXPERIMENTAL)") + self.malenabledCheckbox.toggled.connect(self.malToggled) + if config['malPassword'] == None or config['malPassword'] == "": + self.malenabledCheckbox.setChecked(False) + self.malSettingsGroup.hide() else: - self.config['forceGuiPrompt'] = False - if self.storeConfigCheck.get_active() == True: - self.config['noStore'] = True - else: - self.config['noStore'] = False - if self.slowOnDesyncCheck.get_active() == True: - self.config['slowOnDesync'] = True - else: - self.config['slowOnDesync'] = False - self.window.destroy() - gtk.main_quit() - - def _addLabeledEntryToVbox(self, label, initialEntryValue, vbox, callback): - hbox = gtk.HBox(False, 0) - hbox.set_border_width(3) - vbox.pack_start(hbox, False, False, 0) - hbox.show() - label_ = gtk.Label() - label_.set_text(label) - label_.set_alignment(xalign=0, yalign=0.5) - hbox.pack_start(label_, False, False, 0) - label_.show() - entry = gtk.Entry() - entry.connect("activate", callback, entry) - if(initialEntryValue == None): - initialEntryValue = "" - entry.set_text(initialEntryValue) - hbox.pack_end(entry, False, False, 0) - entry.set_usize(200, -1) - entry.show() - return entry - - def _addCheckboxEntries(self, config, vbox): - CheckVbox = gtk.VBox(False, 0) - vbox.pack_start(CheckVbox, False, False, 0) - self.alwaysShowCheck = gtk.CheckButton("Always Show This Dialog") - if self.config['forceGuiPrompt'] == True: - self.alwaysShowCheck.set_active(True) - self.alwaysShowCheck.show() - self.storeConfigCheck = gtk.CheckButton("Do Not Store This Configuration") - if self.config['noStore'] == True: - self.storeConfigCheck.set_active(True) - self.storeConfigCheck.show() - self.slowOnDesyncCheck = gtk.CheckButton("Slow Down On Desync") - if self.config['slowOnDesync'] == True: - self.slowOnDesyncCheck.set_active(True) - self.slowOnDesyncCheck.show() - CheckVbox.pack_start(self.alwaysShowCheck, False, False, 0) - CheckVbox.add(self.storeConfigCheck) - CheckVbox.add(self.slowOnDesyncCheck) - CheckVbox.show() - - def setAvailablePaths(self, paths): - self._availablePlayerPaths = paths - - class WindowClosed(Exception): - pass + self.malenabledCheckbox.hide() + + self.alwaysshowCheckbox = QCheckBox("Always Show This Dialog") + if config['forceGuiPrompt'] == True: + self.alwaysshowCheckbox.setChecked(True) + + self.donotstoreCheckbox = QCheckBox("Do Not Store This Configuration") + if config['noStore'] == True: + self.donotstoreCheckbox.setChecked(True) + + self.donotstoreCheckbox.toggled.connect(self.runButtonTextUpdate) + + self.mainLayout = QtGui.QVBoxLayout() + self.mainLayout.addWidget(self.connectionSettingsGroup) + self.mainLayout.addSpacing(12) + self.mainLayout.addWidget(self.mediaplayerSettingsGroup) + self.mainLayout.addSpacing(12) + self.mainLayout.addWidget(self.malenabledCheckbox) + self.mainLayout.addWidget(self.malSettingsGroup) + + self.topLayout = QtGui.QHBoxLayout() + self.helpButton = QtGui.QPushButton("Help") + self.helpButton.setMaximumSize(self.helpButton.sizeHint()) + self.helpButton.pressed.connect(self.openHelp) + self.runButton = QtGui.QPushButton("Store configuration and run Syncplay") + self.runButton.pressed.connect(self._saveDataAndLeave) + self.runButtonTextUpdate + self.topLayout.addWidget(self.helpButton, Qt.AlignLeft) + self.topLayout.addWidget(self.runButton, Qt.AlignRight) + self.mainLayout.addWidget(self.alwaysshowCheckbox) + self.mainLayout.addWidget(self.donotstoreCheckbox) + self.mainLayout.addLayout(self.topLayout) + + self.mainLayout.addStretch(1) + + self.setLayout(self.mainLayout) + self.setFixedSize(self.sizeHint())