diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644
index 7cb048b..0000000
--- a/.appveyor.yml
+++ /dev/null
@@ -1,54 +0,0 @@
-environment:
- MINICONDA: "C:\\Miniconda"
- clone_folder: c:\projects\syncplay
-
-image:
- - Visual Studio 2013
-
-platform: x86
-
-configuration: Release
-
-init:
- - set PATH=C:\Miniconda;C:\Miniconda\Scripts;%PATH%
- - cmd: conda create -n syncplay -y
- - cmd: activate syncplay
- - cmd: conda install python pywin32 pyside -y
- - cmd: pip install twisted py2exe_py2 zope.interface
- - cmd: type nul > C:\Miniconda\envs\syncplay\lib\site-packages\zope\__init__.py
- - cmd: pip freeze
- - cmd: conda list
-
-install:
- - cmd: cd c:\projects\syncplay
- - cmd: python buildPy2exe.py
- - cmd: del C:\projects\syncplay\syncplay_v1.5.0\lib\DNSAPI.dll
- - cmd: del C:\projects\syncplay\syncplay_v1.5.0\lib\MPR.dll
- - cmd: mkdir C:\projects\syncplay\syncplay_v1.5.0\platforms
- #- cmd: copy C:\Miniconda\envs\syncplay\library\plugins\platforms\qwindows.dll C:\projects\syncplay\syncplay_v1.5.0\platforms\
-
-# Not a project with an msbuild file, build done at install.
-build: off
-
-artifacts:
- path: 'syncplay_v1.5.0'
- type: zip
- name: Syncplay_chat-osd_win
-
-# Push artefact to S3 bucket and list all
-before_deploy:
- - cmd: dir
- #- cmd: python -c "from PySide2 import QtCore; print QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.PluginsPath)"
-
-# Deploy build to BinTray
-deploy:
- provider: BinTray
- username: alby128
- api_key:
- secure: lAocj5KA9Z9x4BefQBIgNlQJbeW4qPBfCgYVBHMyOP3NgyhnMLmvR57ZCqtCKBlQ
- subject: Syncplay
- repo: Syncplay
- package: Syncplay
- version: "test"
- publish: true
- override: true
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 8309fad..6335c67 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,3 @@ syncplay_setup.nsi
dist.7z
.*
!.travis.yml
-!.appveyor.yml
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/appdmg.py b/appdmg.py
index 58112cf..3e2dc8c 100644
--- a/appdmg.py
+++ b/appdmg.py
@@ -27,10 +27,10 @@ compression_level = 9
size = defines.get('size', None)
# Files to include
-files = [ application, 'resources/lua/intf/syncplay.lua', 'resources/.macos_vlc_install.command' ]
+files = [ application, 'resources/lua/intf/.syncplay.lua', 'resources/.macos_vlc_install.command', 'resources/.macOS_readme.pdf' ]
# Symlinks to create
-symlinks = { 'Applications': '/Applications', 'Install in VLC': '.macos_vlc_install.command' }
+symlinks = { 'Applications': '/Applications', 'Install for VLC': '.macos_vlc_install.command', 'Read Me': '.macOS_readme.pdf' }
# Volume icon
#
@@ -43,10 +43,10 @@ badge_icon = icon_from_app(application)
# Where to put the icons
icon_locations = {
- appname: (80, 80),
- 'Applications': (280, 80),
- 'syncplay.lua': (80, 240),
- 'Install in VLC': (280, 240)
+ appname: (150, 110),
+ 'Applications': (450, 110),
+ 'Read Me': (100, 285),
+ 'Install for VLC': (500, 285)
}
# .. Window configuration ......................................................
@@ -71,7 +71,7 @@ icon_locations = {
#
# Other color components may be expressed either in the range 0 to 1, or
# as percentages (e.g. 60% is equivalent to 0.6).
-background = '#bacbe0'
+background = 'resources/macOS_dmg_bkg.tiff'
show_status_bar = False
show_tab_view = False
@@ -81,7 +81,7 @@ show_sidebar = False
sidebar_width = 180
# Window position in ((x, y), (w, h)) format
-window_rect = ((100, 100), (360, 400))
+window_rect = ((100, 100), (600, 460))
# Select the default view; must be one of
#
diff --git a/resources/macOS_dmg_bkg.tiff b/resources/macOS_dmg_bkg.tiff
new file mode 100644
index 0000000..9ead2f6
Binary files /dev/null and b/resources/macOS_dmg_bkg.tiff differ
diff --git a/resources/macOS_readme.pdf b/resources/macOS_readme.pdf
new file mode 100644
index 0000000..f18c6db
Binary files /dev/null and b/resources/macOS_readme.pdf differ
diff --git a/resources/macos_vlc_install.command b/resources/macos_vlc_install.command
index b03f213..4774144 100755
--- a/resources/macos_vlc_install.command
+++ b/resources/macos_vlc_install.command
@@ -2,4 +2,4 @@
mkdir -p $HOME/Library/Application\ Support/org.videolan.vlc/lua/intf/
-cp /Volumes/Syncplay/syncplay.lua $HOME/Library/Application\ Support/org.videolan.vlc/lua/intf/
+cp /Volumes/Syncplay/.syncplay.lua $HOME/Library/Application\ Support/org.videolan.vlc/lua/intf/syncplay.lua
diff --git a/syncplay/__init__.py b/syncplay/__init__.py
index d06a5e0..fc4ca02 100644
--- a/syncplay/__init__.py
+++ b/syncplay/__init__.py
@@ -1,4 +1,4 @@
version = '1.5.0'
milestone = 'Yoitsu'
-release_number = '40'
+release_number = '45'
projectURL = 'http://syncplay.pl/'
diff --git a/syncplay/client.py b/syncplay/client.py
index 4a53481..2d1b4c1 100644
--- a/syncplay/client.py
+++ b/syncplay/client.py
@@ -92,6 +92,8 @@ class SyncplayClient(object):
if config['password']:
config['password'] = hashlib.md5(config['password']).hexdigest()
self._serverPassword = config['password']
+ self._host = u"{}:{}".format(config['host'],config['port'])
+ self._publicServers = config["publicServers"]
if not config['file']:
self.__getUserlistOnLogon = True
else:
@@ -648,7 +650,20 @@ class SyncplayClient(object):
self.userlist.showUserList(altUI)
def getPassword(self):
- return self._serverPassword
+ if self.thisIsPublicServer():
+ return ""
+ else:
+ return self._serverPassword
+
+ def thisIsPublicServer(self):
+ self._publicServers = []
+ if self._publicServers and self._host in self._publicServers:
+ return True
+ i = 0
+ for server in constants.FALLBACK_PUBLIC_SYNCPLAY_SERVERS:
+ if server[1] == self._host:
+ return True
+ i += 1
def setPosition(self, position):
if self._lastPlayerUpdate:
@@ -1620,11 +1635,23 @@ class SyncplayPlaylist():
self.changePlaylist(newPlaylist, username=None)
@needsSharedPlaylistsEnabled
- def shufflePlaylist(self):
+ def shuffleRemainingPlaylist(self):
+ if self._playlist and len(self._playlist) > 0:
+ shuffledPlaylist = deepcopy(self._playlist)
+ shufflePoint = self._playlistIndex + 1
+ partToKeep = shuffledPlaylist[:shufflePoint]
+ partToShuffle = shuffledPlaylist[shufflePoint:]
+ random.shuffle(partToShuffle)
+ shuffledPlaylist = partToKeep + partToShuffle
+ self.changePlaylist(shuffledPlaylist, username=None, resetIndex=False)
+
+ @needsSharedPlaylistsEnabled
+ def shuffleEntirePlaylist(self):
if self._playlist and len(self._playlist) > 0:
shuffledPlaylist = deepcopy(self._playlist)
random.shuffle(shuffledPlaylist)
self.changePlaylist(shuffledPlaylist, username=None, resetIndex=True)
+ self.switchToNewPlaylistIndex(0, resetPosition=True)
def canUndoPlaylist(self, currentPlaylist):
return self._previousPlaylist is not None and currentPlaylist <> self._previousPlaylist
diff --git a/syncplay/constants.py b/syncplay/constants.py
index 8efa7a5..41653cc 100644
--- a/syncplay/constants.py
+++ b/syncplay/constants.py
@@ -55,6 +55,7 @@ SYNC_ON_PAUSE = True # Client seek to global position - subtitles may disappear
PLAYLIST_MAX_CHARACTERS = 10000
PLAYLIST_MAX_ITEMS = 250
MAXIMUM_TAB_WIDTH = 350
+TAB_PADDING = 30
DEFAULT_WINDOWS_MONOSPACE_FONT = "Consolas"
DEFAULT_OSX_MONOSPACE_FONT = "Menlo"
FALLBACK_MONOSPACE_FONT = "Monospace"
@@ -186,6 +187,7 @@ MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND = ["MaxChatMessageLength={}".format(MAX_CHAT_
MPV_SYNCPLAYINTF_LANGUAGE_TO_SEND = ["mpv-key-hint", "alphakey-mode-warning-first-line", "alphakey-mode-warning-second-line"]
VLC_SLAVE_ARGS = ['--extraintf=luaintf', '--lua-intf=syncplay', '--no-quiet', '--no-input-fast-seek',
'--play-and-pause', '--start-time=0']
+VLC_SLAVE_OSX_ARGS = ['--verbose=2', '--no-file-logging']
VLC_SLAVE_NONOSX_ARGS = ['--no-one-instance', '--no-one-instance-when-started-from-file']
MPV_SUPERSEDE_IF_DUPLICATE_COMMANDS = ["no-osd set time-pos ", "loadfile "]
MPV_REMOVE_BOTH_IF_DUPLICATE_COMMANDS = ["cycle pause"]
@@ -208,6 +210,8 @@ INPUT_POSITION_TOP = "Top"
INPUT_POSITION_MIDDLE = "Middle"
INPUT_POSITION_BOTTOM = "Bottom"
+VLC_EOF_DURATION_THRESHOLD = 2.0
+
PRIVACY_HIDDENFILENAME = "**Hidden filename**"
INVERTED_STATE_MARKER = "*"
ERROR_MESSAGE_MARKER = "*"
diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py
index 82717c1..c90d26b 100644
--- a/syncplay/messages_de.py
+++ b/syncplay/messages_de.py
@@ -93,6 +93,7 @@ de = {
"language-changed-msgbox-label" : u"Die Sprache wird geändert, wenn du Syncplay neu startest.",
"promptforupdate-label" : u"Soll Syncplay regelmäßig nach Updates suchen?",
+ "vlc-version-mismatch": u"This version of VLC does not support Syncplay. VLC {}+ supports Syncplay but VLC 3 does not. Please use an alternative media player.", # VLC min version # TODO: Translate
"vlc-interface-version-mismatch": u"Du nutzt Version {} des VLC-Syncplay Interface-Moduls, Syncplay benötigt aber mindestens Version {}. In der Syncplay-Anleitung unter http://syncplay.pl/guide/ [Englisch] findest du Details zur Installation des syncplay.lua-Skripts.", # VLC interface version, VLC interface min version
"vlc-interface-oldversion-warning": u"Warnung: Es ist eine alte Version des Syncplay Interface-Moduls für VLC im VLC-Verzeichnis installiert. In der Syncplay-Anleitung unter 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.",
@@ -131,14 +132,9 @@ de = {
"vlc-failed-connection": u"Kann nicht zu VLC verbinden. Wenn du syncplay.lua nicht installiert hast, findest du auf http://syncplay.pl/LUA/ [Englisch] eine Anleitung.",
"vlc-failed-noscript": u"Laut VLC ist das syncplay.lua Interface-Skript nicht installiert. Auf http://syncplay.pl/LUA/ [Englisch] findest du eine Anleitung.",
"vlc-failed-versioncheck": u"Diese VLC-Version wird von Syncplay nicht unterstützt. Bitte nutze VLC 2.0",
+ "vlc-failed-other" : u"Beim Laden des syncplay.lua Interface-Skripts durch VLC trat folgender Fehler auf: {}", # Syncplay Error
- "feature-sharedPlaylists": u"shared playlists", # used for not-supported-by-server-error # TODO: Translate
- "feature-chat": u"chat", # used for not-supported-by-server-error # TODO: Translate
- "feature-readiness": u"readiness", # used for not-supported-by-server-error # TODO: Translate
- "feature-managedRooms": u"managed rooms", # used for not-supported-by-server-error # TODO: Translate
-
- "not-supported-by-server-error": u"The {} feature is not supported by this server..", # feature # TODO: Translate
- #OLD TRANSLATION: "not-supported-by-server-error" : u"Dieses Feature wird vom Server nicht unterstützt. Es wird ein Server mit Syncplay Version {}+ benötigt, aktuell verwendet wird jedoch Version {}.", #minVersion, serverVersion
+ "not-supported-by-server-error" : 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
"shared-playlists-not-supported-by-server-error" : "The shared playlists feature may not be supported by the server. To ensure that it works correctly requires a server running Syncplay {}+, but the server is running Syncplay {}.", #minVersion, serverVersion # TODO: Translate
"shared-playlists-disabled-by-server-error" : "The shared playlist feature has been disabled in the server configuration. To use this feature you will need to connect to a different server.", # TODO: Translate
@@ -185,7 +181,7 @@ de = {
"media-setting-title" : u"Media-Player Einstellungen",
"executable-path-label" : u"Pfad zum Media-Player:",
- "media-path-label" : u"Pfad zur Datei:",
+ "media-path-label" : u"Pfad zur Datei:", # Todo: Translate to 'Path to video (optional)'
"player-arguments-label" : u"Playerparameter:",
"browse-label" : u"Durchsuchen",
"update-server-list-label" : u"Liste aktualisieren",
@@ -262,7 +258,7 @@ de = {
"run-label" : u"Syncplay starten",
"storeandrun-label" : u"Konfiguration speichern und Syncplay starten",
- "contact-label" : u"Du hast eine Idee, einen Bug gefunden oder möchtest Feedback geben? Sende eine E-Mail an dev@syncplay.pl, chatte auf dem #Syncplay IRC-Kanal auf irc.freenode.net oder öffne eine Fehlermeldung auf GitHub. Außerdem findest du auf http://syncplay.pl/ weitere Informationen, Hilfestellungen und Updates. NOTE: Chat messages are not encrypted so do not use Syncplay to send sensitive information.", # TODO: Translate message at end
+ "contact-label" : u"Du hast eine Idee, einen Bug gefunden oder möchtest Feedback geben? Sende eine E-Mail an dev@syncplay.pl, chatte auf dem #Syncplay IRC-Kanal auf irc.freenode.net oder öffne eine Fehlermeldung auf GitHub. Außerdem findest du auf http://syncplay.pl/ weitere Informationen, Hilfestellungen und Updates. OTE: Chat messages are not encrypted so do not use Syncplay to send sensitive information.", # TODO: Translate last sentence
"joinroom-label" : u"Raum beitreten",
"joinroom-menu-label" : u"Raum beitreten {}", #TODO: Might want to fix this
@@ -345,7 +341,7 @@ de = {
"room-tooltip" : u"Der Raum, der betreten werden soll, kann ein x-beliebiger sein. Allerdings werden nur Clients im selben Raum synchronisiert.",
"executable-path-tooltip" : u"Pfad zum ausgewählten, unterstützten Mediaplayer (MPC-HC, VLC, mplayer2 or mpv).",
- "media-path-tooltip" : u"Pfad zum wiederzugebenden Video oder Stream. Notwendig für mpv und mplayer2.",
+ "media-path-tooltip" : u"Pfad zum wiederzugebenden Video oder Stream. Notwendig für mplayer2.", # TODO: Confirm translation
"player-arguments-tooltip" : u"Zusätzliche Kommandozeilenparameter / -schalter für diesen Mediaplayer.",
"mediasearcdirectories-arguments-tooltip" : u"Verzeichnisse, in denen Syncplay nach Mediendateien suchen soll, z.B. wenn du das Click-to-switch-Feature verwendest. Syncplay wird rekursiv Unterordner durchsuchen.", # TODO: Translate Click-to-switch? (or use as name for feature)
@@ -451,7 +447,8 @@ de = {
"cannot-add-unsafe-path-error" : u"Could not automatically load {} because it is not on a trusted domain. You can switch to the URL manually by double clicking it in the playlist, and add trusted domains via File->Advanced->Set Trusted Domains. If you right click on a URL then you can add its domain as a trusted domain via the context menu.", # Filename
"sharedplaylistenabled-label" : u"Enable shared playlists",
"removefromplaylist-menu-label" : u"Remove from playlist",
- "shuffleplaylist-menuu-label" : u"Shuffle playlist",
+ "shuffleremainingplaylist-menu-label" : u"Shuffle remaining playlist",
+ "shuffleentireplaylist-menuu-label" : u"Shuffle entire playlist",
"undoplaylist-menu-label" : u"Undo last change to playlist",
"addfilestoplaylist-menu-label" : u"Add file(s) to bottom of playlist",
"addurlstoplaylist-menu-label" : u"Add URL(s) to bottom of playlist",
diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py
index 2525116..97d1698 100644
--- a/syncplay/messages_en.py
+++ b/syncplay/messages_en.py
@@ -109,7 +109,7 @@ en = {
"mpc-version-insufficient-error" : "MPC version not sufficient, please use `mpc-hc` >= `{}`",
"mpv-version-error" : "Syncplay is not compatible with this version of mpv. Please use a different version of mpv (e.g. Git HEAD).",
"player-file-open-error" : "Player failed opening file",
- "player-path-error" : "Player path is not set properly",
+ "player-path-error" : "Player path is not set properly. Supported players are: mpv, VLC, MPC-HC and mplayer2",
"hostname-empty-error" : "Hostname can't be empty",
"empty-error" : "{} can't be empty", # Configuration
"media-player-error": "Media player error: \"{}\"", # Error line
@@ -119,7 +119,7 @@ en = {
"unable-to-start-client-error" : "Unable to start client",
- "player-path-config-error": "Player path is not set properly",
+ "player-path-config-error": "Player path is not set properly. Supported players are: mpv, VLC, MPC-HC and mplayer2.",
"no-file-path-config-error" :"File must be selected before starting your player",
"no-hostname-config-error": "Hostname can't be empty",
"invalid-port-config-error" : "Port must be valid",
@@ -184,7 +184,7 @@ en = {
"media-setting-title" : "Media player settings",
"executable-path-label" : "Path to media player:",
- "media-path-label" : "Path to media file:",
+ "media-path-label" : "Path to video (optional):",
"player-arguments-label" : "Player arguments (if any):",
"browse-label" : "Browse",
"update-server-list-label" : u"Update list",
@@ -345,8 +345,8 @@ en = {
"password-tooltip" : "Passwords are only needed for connecting to private servers.",
"room-tooltip" : "Room to join upon connection can be almost anything, but you will only be synchronised with people in the same room.",
- "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.",
+ "executable-path-tooltip" : "Location of your chosen supported media player (mpv, VLC, MPC-HC or mplayer2).",
+ "media-path-tooltip" : "Location of video or stream to be opened. Necessary for mplayer2.",
"player-arguments-tooltip" : "Additional command line arguments / switches to pass on to this media player.",
"mediasearcdirectories-arguments-tooltip" : u"Directories where Syncplay will search for media files, e.g. when you are using the click to switch feature. Syncplay will look recursively through sub-folders.",
@@ -451,7 +451,8 @@ en = {
"cannot-add-unsafe-path-error" : u"Could not automatically load {} because it is not on a trusted domain. You can switch to the URL manually by double clicking it in the playlist, and add trusted domains via File->Advanced->Set Trusted Domains. If you right click on a URL then you can add its domain as a trusted domain via the context menu.", # Filename
"sharedplaylistenabled-label" : u"Enable shared playlists",
"removefromplaylist-menu-label" : u"Remove from playlist",
- "shuffleplaylist-menuu-label" : u"Shuffle playlist",
+ "shuffleremainingplaylist-menu-label" : u"Shuffle remaining playlist",
+ "shuffleentireplaylist-menuu-label" : u"Shuffle entire playlist",
"undoplaylist-menu-label" : u"Undo last change to playlist",
"addfilestoplaylist-menu-label" : u"Add file(s) to bottom of playlist",
"addurlstoplaylist-menu-label" : u"Add URL(s) to bottom of playlist",
diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py
index 604f4f0..6f5d7d9 100644
--- a/syncplay/messages_ru.py
+++ b/syncplay/messages_ru.py
@@ -93,6 +93,7 @@ ru = {
"language-changed-msgbox-label" : u"Язык переключится при следующем запуске Syncplay.",
"promptforupdate-label" : u"Вы не против, если Syncplay будет автоматически изредка проверять наличие обновлений?",
+ "vlc-version-mismatch": u"Syncplay не поддерживает данную версию VLC. Syncplay поддерживает VLC {}+, но не VLC 3. Используйте другой проигрыватель.", # VLC min version
"vlc-interface-version-mismatch" : u"Вы используете модуль интерфейса Syncplay устаревшей версии {} для VLC. К сожалению, Syncplay способен работать с версией {} и выше. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (http://syncplay.pl/guide/) за инструкциями о том, как установить syncplay.lua.", # 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.",
@@ -109,7 +110,7 @@ ru = {
"mpc-version-insufficient-error" : u"Версия MPC слишком старая, пожалуйста, используйте `mpc-hc` >= `{}`",
"mpv-version-error" : u"Syncplay не совместим с данной версией mpv. Пожалуйста, используйте другую версию mpv (лучше свежайшую).",
"player-file-open-error" : u"Проигрыватель не может открыть файл.",
- "player-path-error" : u"Путь к проигрывателю задан неверно.",
+ "player-path-error" : u"Путь к проигрывателю задан неверно. Supported players are: mpv, VLC, MPC-HC and mplayer2.", # TODO: Translate last sentence
"hostname-empty-error" : u"Имя пользователя не может быть пустым.",
"empty-error" : u"{} не может быть пустым.", # Configuration
"media-player-error" : u"Ошибка проигрывателя: \"{}\"", # Error line
@@ -119,7 +120,7 @@ ru = {
"unable-to-start-client-error" : u"Невозможно запустить клиент",
- "player-path-config-error": u"Путь к проигрывателю установлен неверно",
+ "player-path-config-error": u"Путь к проигрывателю установлен неверно. Supported players are: mpv, VLC, MPC-HC and mplayer2", # To do: Translate end
"no-file-path-config-error" : u"Файл должен быть указан до включения проигрывателя",
"no-hostname-config-error": u"Имя сервера не может быть пустым",
"invalid-port-config-error" : u"Неверный номер порта",
@@ -131,6 +132,7 @@ ru = {
"vlc-failed-connection" : u"Ошибка подключения к VLC. Если у Вас не установлен syncplay.lua, то обратитесь к http://syncplay.pl/LUA/ за инструкциями.",
"vlc-failed-noscript" : u"VLC сообщает, что скрипт интерфейса syncplay.lua не установлен. Пожалуйста, обратитесь к http://syncplay.pl/LUA/ за инструкциями.",
"vlc-failed-versioncheck" : u"Данная версия VLC не поддерживается Syncplay. Пожалуйста, используйте VLC версии 2 или выше.",
+ "vlc-failed-other" : u"Во время загрузки скрипта интерфейса syncplay.lua в VLC произошла следующая ошибка: {}", # Syncplay Error
"feature-sharedPlaylists": u"shared playlists", # used for not-supported-by-server-error # TODO: Translate
"feature-chat": u"chat", # used for not-supported-by-server-error # TODO: Translate
@@ -185,7 +187,7 @@ ru = {
"media-setting-title" : u"Воспроизведение",
"executable-path-label" : u"Путь к проигрывателю:",
- "media-path-label" : u"Путь к видеофайлу:",
+ "media-path-label" : u"Путь к видеофайлу:", # Todo: Translate to 'Path to video (optional)'
"player-arguments-label" : u"Аргументы запуска проигрывателя:",
"browse-label" : u"Выбрать",
"update-server-list-label" : u"Обновить список",
@@ -265,7 +267,7 @@ ru = {
"run-label" : u"Запустить",
"storeandrun-label" : u"Сохранить и запустить",
- "contact-label" : u"Есть идея, нашли ошибку или хотите оставить отзыв? Пишите на dev@syncplay.pl, в IRC канал #Syncplay на irc.freenode.net или задавайте вопросы через GitHub. Кроме того, заходите на www.syncplay.pl за инорфмацией, помощью и обновлениями! NOTE: Chat messages are not encrypted so do not use Syncplay to send sensitive information.", # TODO: Translate end of message
+ "contact-label" : u"Есть идея, нашли ошибку или хотите оставить отзыв? Пишите на dev@syncplay.pl, в IRC канал #Syncplay на irc.freenode.net или задавайте вопросы через GitHub. Кроме того, заходите на www.syncplay.pl за инорфмацией, помощью и обновлениями! NOTE: Chat messages are not encrypted so do not use Syncplay to send sensitive information.", # TODO: Translate last sentence
"joinroom-label" : u"Зайти в комнату",
"joinroom-menu-label" : u"Зайти в комнату {}",
@@ -347,7 +349,7 @@ ru = {
"room-tooltip" : u"Комната, в которую Вы попадете сразу после подключения. Синхронизация возможна только между людьми в одной и той же комнате.",
"executable-path-tooltip" : u"Расположение Вашего видеопроигрывателя (MPC-HC, VLC, mplayer2 или mpv).",
- "media-path-tooltip" : u"Расположение видеофайла или потока для просмотра. Обязательно для mpv и mplayer2.",
+ "media-path-tooltip" : u"Расположение видеофайла или потока для просмотра. Обязательно для mplayer2.", # TODO: Confirm translation
"player-arguments-tooltip" : u"Передавать дополнительные аргументы командной строки этому проигрывателю.",
"mediasearcdirectories-arguments-tooltip" : u"Папки, где Syncplay будет искать медиа файлы, включая подпапки.",
@@ -450,7 +452,8 @@ ru = {
"cannot-add-unsafe-path-error" : u"Не удалось автоматически переключиться на {}, потому что ссылка не соответствует доверенным сайтам. Её можно включить вручную, дважны кливнув по ссылке в списке воспроизведения. Добавить доверенный сайт можно в выпадающем меню 'Дополнительно' или просто кликнув по ссылке правой кнопкой мыши.", # Filename
"sharedplaylistenabled-label" : u"Включить общий список воспроизведения",
"removefromplaylist-menu-label" : u"Удалить",
- "shuffleplaylist-menuu-label" : u"Перемешать список",
+ "shufflepremaininglaylist-menuu-label" : u"Shuffle remaining playlist", # Was: Перемешать список # TODO: Translate
+ "shuffleentireplaylist-menuu-label" : u"Shuffle entire playlist", # TODO: Translate
"undoplaylist-menu-label" : u"Отменить последнее действие",
"addfilestoplaylist-menu-label" : u"Добавить файлы в очередь",
"addurlstoplaylist-menu-label" : u"Добавить ссылку в очередь",
diff --git a/syncplay/players/mplayer.py b/syncplay/players/mplayer.py
index e8aac44..e34aa77 100644
--- a/syncplay/players/mplayer.py
+++ b/syncplay/players/mplayer.py
@@ -395,7 +395,12 @@ class MplayerPlayer(BasePlayer):
for itemID, deletionCandidate in enumerate(self.sendQueue):
if deletionCandidate.startswith(command):
self.__playerController._client.ui.showDebugMessage(u" Remove duplicate (supersede): {}".format(self.sendQueue[itemID]))
- self.sendQueue.remove(self.sendQueue[itemID])
+ try:
+ self.sendQueue.remove(self.sendQueue[itemID])
+ except UnicodeWarning:
+ self.__playerController._client.ui.showDebugMessage(u" Unicode mismatch occured when trying to remove duplicate")
+ # TODO: Prevent this from being triggered
+ pass
break
break
if constants.MPV_REMOVE_BOTH_IF_DUPLICATE_COMMANDS:
diff --git a/syncplay/players/vlc.py b/syncplay/players/vlc.py
old mode 100644
new mode 100755
index 8153e0e..d55b8d4
--- a/syncplay/players/vlc.py
+++ b/syncplay/players/vlc.py
@@ -21,8 +21,10 @@ class VlcPlayer(BasePlayer):
RE_ANSWER = re.compile(constants.VLC_ANSWER_REGEX)
SLAVE_ARGS = constants.VLC_SLAVE_ARGS
- if not sys.platform.startswith('darwin'):
- SLAVE_ARGS.extend(constants.VLC_SLAVE_NONOSX_ARGS)
+ if sys.platform.startswith('darwin'):
+ SLAVE_ARGS.extend(constants.VLC_SLAVE_OSX_ARGS)
+ else:
+ SLAVE_ARGS.extend(constants.VLC_SLAVE_NONOSX_ARGS)
vlcport = random.randrange(constants.VLC_MIN_PORT, constants.VLC_MAX_PORT) if (constants.VLC_MIN_PORT < constants.VLC_MAX_PORT) else constants.VLC_MIN_PORT
def __init__(self, client, playerPath, filePath, args):
@@ -207,18 +209,22 @@ class VlcPlayer(BasePlayer):
self._durationAsk.set()
elif name == "playstate":
self._paused = bool(value != 'playing') if(value != "no-input" and self._filechanged == False) else self._client.getGlobalPaused()
+ diff = time.time() - self._lastVLCPositionUpdate if self._lastVLCPositionUpdate else 0
if self._paused == False \
and self._position == self._previousPreviousPosition \
and self._previousPosition == self._position \
and self._duration > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH \
- and self._position == self._duration:
- self._paused = True
+ and (self._duration - self._position) < constants.VLC_EOF_DURATION_THRESHOLD \
+ and diff > constants.VLC_LATENCY_ERROR_THRESHOLD:
self._client.ui.showDebugMessage("Treating 'playing' response as 'paused' due to VLC EOF bug")
+ self.setPaused(True)
self._pausedAsk.set()
elif name == "position":
newPosition = float(value.replace(",", ".")) if (value != "no-input" and self._filechanged == False) else self._client.getGlobalPosition()
if newPosition == self._previousPosition and newPosition <> self._duration and not self._paused:
self._client.ui.showDebugMessage("Not considering position {} duplicate as new time because of VLC time precision bug".format(newPosition))
+ self._previousPreviousPosition = self._previousPosition
+ self._previousPosition = self._position
self._positionAsk.set()
return
self._previousPreviousPosition = self._previousPosition
@@ -382,7 +388,12 @@ class VlcPlayer(BasePlayer):
playerController._client.ui.showErrorMessage(
getMessage("media-player-error").format(line), True)
break
- self.__process.stderr = None
+ if not sys.platform.startswith('darwin'):
+ self.__process.stderr = None
+ else:
+ vlcoutputthread = threading.Thread(target = self.handle_vlcoutput, args=())
+ vlcoutputthread.setDaemon(True)
+ vlcoutputthread.start()
threading.Thread.__init__(self, name="VLC Listener")
asynchat.async_chat.__init__(self)
self.set_terminator("\n")
@@ -428,6 +439,15 @@ class VlcPlayer(BasePlayer):
asynchat.async_chat.handle_close(self)
self.__playerController.drop(getMessage("vlc-failed-connection").format(constants.VLC_MIN_VERSION))
+ def handle_vlcoutput(self):
+ out = self.__process.stderr
+ for line in iter(out.readline, ''):
+ if '[syncplay] core interface debug: removing module' in line:
+ self.__playerController.drop()
+ break
+ out.close()
+
+
def found_terminator(self):
self.vlcHasResponded = True
self.__playerController.lineReceived("".join(self._ibuffer))
diff --git a/syncplay/protocols.py b/syncplay/protocols.py
index 2ebe064..ba0e187 100644
--- a/syncplay/protocols.py
+++ b/syncplay/protocols.py
@@ -348,13 +348,19 @@ class SyncServerProtocol(JSONCommandProtocol):
def _extractHelloArguments(self, hello):
roomName = None
- username = hello["username"] if hello.has_key("username") else None
- username = username.strip()
+ if hello.has_key("username"):
+ username = hello["username"]
+ username = username.strip()
+ else:
+ username = None
serverPassword = hello["password"] if hello.has_key("password") else None
room = hello["room"] if hello.has_key("room") else None
if room:
- roomName = room["name"] if room.has_key("name") else None
- roomName = roomName.strip()
+ if room.has_key("name"):
+ roomName = room["name"]
+ roomName = roomName.strip()
+ else:
+ roomName = None
version = hello["version"] if hello.has_key("version") else None
version = hello["realversion"] if hello.has_key("realversion") else version
features = hello["features"] if hello.has_key("features") else None
diff --git a/syncplay/server.py b/syncplay/server.py
index 4761875..d9b0ed6 100644
--- a/syncplay/server.py
+++ b/syncplay/server.py
@@ -397,7 +397,6 @@ class Watcher(object):
reactor.callLater(0.1, self._scheduleSendState)
def setFile(self, file_):
- print file_
if file_ and file_.has_key("name"):
file_["name"] = truncateText(file_["name"],constants.MAX_FILENAME_LENGTH)
self._file = file_
diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py
index 8a014c8..de561c7 100755
--- a/syncplay/ui/ConfigurationGetter.py
+++ b/syncplay/ui/ConfigurationGetter.py
@@ -85,6 +85,7 @@ class ConfigurationGetter(object):
"notificationTimeout": 3,
"alertTimeout": 5,
"chatTimeout": 7,
+ "publicServers" : []
}
self._defaultConfig = self._config.copy()
@@ -142,6 +143,7 @@ class ConfigurationGetter(object):
"perPlayerArguments",
"mediaSearchDirectories",
"trustedDomains",
+ "publicServers",
]
self._numeric = [
@@ -179,7 +181,7 @@ class ConfigurationGetter(object):
"autoplayInitialState", "mediaSearchDirectories",
"sharedPlaylistEnabled", "loopAtEndOfPlaylist",
"loopSingleFiles",
- "onlySwitchToTrustedDomains", "trustedDomains"],
+ "onlySwitchToTrustedDomains", "trustedDomains","publicServers"],
"gui": ["showOSD", "showOSDWarnings", "showSlowdownOSD",
"showDifferentRoomOSD", "showSameRoomOSD",
"showNonControllerOSD", "showDurationNotification",
diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py
index fc7ba4e..30219e6 100644
--- a/syncplay/ui/GuiConfiguration.py
+++ b/syncplay/ui/GuiConfiguration.py
@@ -89,6 +89,9 @@ class ConfigDialog(QtGui.QDialog):
self.resetButton.show()
self.playerargsTextbox.show()
self.playerargsLabel.show()
+ self.mediapathTextbox.show()
+ self.mediapathLabel.show()
+ self.mediabrowseButton.show()
self.runButton.show()
self.saveMoreState(True)
self.tabListWidget.setCurrentRow(0)
@@ -100,6 +103,14 @@ class ConfigDialog(QtGui.QDialog):
self.playerargsTextbox.hide()
self.playerargsLabel.hide()
self.runButton.hide()
+ if self.mediapathTextbox.text() == "":
+ self.mediapathTextbox.hide()
+ self.mediapathLabel.hide()
+ self.mediabrowseButton.hide()
+ else:
+ self.mediapathTextbox.show()
+ self.mediapathLabel.show()
+ self.mediabrowseButton.show()
self.saveMoreState(False)
self.stackedLayout.setCurrentIndex(0)
newHeight = self.connectionSettingsGroup.minimumSizeHint().height()+self.mediaplayerSettingsGroup.minimumSizeHint().height()+self.bottomButtonFrame.minimumSizeHint().height()+3
@@ -110,7 +121,6 @@ class ConfigDialog(QtGui.QDialog):
self.setFixedSize(self.sizeHint())
self.moreToggling = False
self.setFixedWidth(self.minimumSizeHint().width())
- self.executablepathCombobox.setFixedWidth(self.mediapathTextbox.width())
def openHelp(self):
self.QtGui.QDesktopServices.openUrl(QUrl("http://syncplay.pl/guide/client/"))
@@ -372,6 +382,8 @@ class ConfigDialog(QtGui.QDialog):
self.config["mediaSearchDirectories"] = utils.convertMultilineStringToList(self.mediasearchTextEdit.toPlainText())
self.config["trustedDomains"] = utils.convertMultilineStringToList(self.trusteddomainsTextEdit.toPlainText())
+ if self.serverpassTextbox.isEnabled():
+ self.config['password'] = self.serverpassTextbox.text()
self.processWidget(self, lambda w: self.saveValues(w))
if self.hostCombobox.currentText():
self.config['host'] = self.hostCombobox.currentText() if ":" in self.hostCombobox.currentText() else self.hostCombobox.currentText() + ":" + unicode(constants.DEFAULT_PORT)
@@ -386,6 +398,7 @@ class ConfigDialog(QtGui.QDialog):
self.config['file'] = os.path.abspath(self.mediapathTextbox.text())
else:
self.config['file'] = unicode(self.mediapathTextbox.text())
+ self.config['publicServers'] = self.publicServerAddresses
self.pressedclosebutton = False
self.close()
@@ -415,6 +428,16 @@ class ConfigDialog(QtGui.QDialog):
self.executablepathCombobox.setEditText(dropfilepath)
else:
self.mediapathTextbox.setText(dropfilepath)
+ self.mediapathTextbox.show()
+ self.mediapathLabel.show()
+ self.mediabrowseButton.show()
+ if not self.showmoreCheckbox.isChecked():
+ 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)
+ self.adjustSize()
+ self.setFixedSize(self.sizeHint())
def processWidget(self, container, torun):
for widget in container.children():
@@ -514,8 +537,12 @@ class ConfigDialog(QtGui.QDialog):
if self.publicServers:
i = 0
for publicServer in self.publicServers:
- self.hostCombobox.addItem(publicServer[1])
- self.hostCombobox.setItemData(i, publicServer[0], Qt.ToolTipRole)
+ serverTitle = publicServer[0]
+ serverAddressPort = publicServer[1]
+ self.hostCombobox.addItem(serverAddressPort)
+ self.hostCombobox.setItemData(i, serverTitle, Qt.ToolTipRole)
+ if not serverAddressPort in self.publicServerAddresses:
+ self.publicServerAddresses.append(serverAddressPort)
i += 1
self.hostCombobox.setEditable(True)
self.hostCombobox.setEditText(host)
@@ -531,6 +558,7 @@ class ConfigDialog(QtGui.QDialog):
self.defaultroomTextbox = QLineEdit(self)
self.usernameLabel = QLabel(getMessage("name-label"), self)
self.serverpassTextbox = QLineEdit(self)
+ self.serverpassTextbox.setText(self.storedPassword)
self.defaultroomLabel = QLabel(getMessage("room-label"), self)
self.hostLabel.setObjectName("host")
@@ -538,7 +566,9 @@ class ConfigDialog(QtGui.QDialog):
self.usernameLabel.setObjectName("name")
self.usernameTextbox.setObjectName("name")
self.serverpassLabel.setObjectName("password")
- self.serverpassTextbox.setObjectName("password")
+ self.serverpassTextbox.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "password")
+ self.hostCombobox.editTextChanged.connect(self.updatePasswordVisibilty)
+ self.hostCombobox.currentIndexChanged.connect(self.updatePasswordVisibilty)
self.defaultroomLabel.setObjectName("room")
self.defaultroomTextbox.setObjectName("room")
@@ -566,11 +596,12 @@ class ConfigDialog(QtGui.QDialog):
self.executableiconImage = QtGui.QImage()
self.executableiconLabel = QLabel(self)
self.executableiconLabel.setMinimumWidth(16)
+ self.executableiconLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.executablepathCombobox = QtGui.QComboBox(self)
self.executablepathCombobox.setEditable(True)
self.executablepathCombobox.currentIndexChanged.connect(self.updateExecutableIcon)
self.executablepathCombobox.setEditText(self._tryToFillPlayerPath(config['playerPath'], playerpaths))
- self.executablepathCombobox.setFixedWidth(165)
+ self.executablepathCombobox.setFixedWidth(250)
self.executablepathCombobox.editTextChanged.connect(self.updateExecutableIcon)
self.executablepathLabel = QLabel(getMessage("executable-path-label"), self)
@@ -1111,7 +1142,7 @@ class ConfigDialog(QtGui.QDialog):
self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"cog.png"),getMessage("misc-label")))
self.tabListLayout.addWidget(self.tabListWidget)
self.tabListFrame.setLayout(self.tabListLayout)
- self.tabListFrame.setFixedWidth(self.tabListFrame.minimumSizeHint().width())
+ self.tabListFrame.setFixedWidth(self.tabListFrame.minimumSizeHint().width() + constants.TAB_PADDING)
self.tabListWidget.setStyleSheet(constants.STYLE_TABLIST)
self.tabListWidget.currentItemChanged.connect(self.tabChange)
@@ -1136,7 +1167,6 @@ class ConfigDialog(QtGui.QDialog):
def showEvent(self, *args, **kwargs):
self.ensureTabListIsVisible()
self.setFixedWidth(self.minimumSizeHint().width())
- self.executablepathCombobox.setFixedWidth(self.mediapathTextbox.width())
def clearGUIData(self, leaveMore=False):
settings = QSettings("Syncplay", "PlayerList")
@@ -1168,8 +1198,22 @@ class ConfigDialog(QtGui.QDialog):
for server in self.publicServers:
self.hostCombobox.addItem(server[1])
self.hostCombobox.setItemData(i, server[0], Qt.ToolTipRole)
+ if not server[1] in self.publicServerAddresses:
+ self.publicServerAddresses.append(server[1])
i += 1
self.hostCombobox.setEditText(currentServer)
+
+ def updatePasswordVisibilty(self):
+ if (self.hostCombobox.currentText() == "" and self.serverpassTextbox.text() == "") or unicode(self.hostCombobox.currentText()) in self.publicServerAddresses:
+ self.serverpassTextbox.setDisabled(True)
+ self.serverpassTextbox.setReadOnly(True)
+ if self.serverpassTextbox.text() != "":
+ self.storedPassword = self.serverpassTextbox.text()
+ self.serverpassTextbox.setText("")
+ else:
+ self.serverpassTextbox.setEnabled(True)
+ self.serverpassTextbox.setReadOnly(False)
+ self.serverpassTextbox.setText(self.storedPassword)
def __init__(self, config, playerpaths, error, defaultConfig):
self.config = config
@@ -1179,6 +1223,7 @@ class ConfigDialog(QtGui.QDialog):
self.config['resetConfig'] = False
self.subitems = {}
self.publicServers = None
+ self.publicServerAddresses = []
self._playerProbeThread = GetPlayerIconThread()
self._playerProbeThread.done.connect(self._updateExecutableIcon)
@@ -1211,6 +1256,7 @@ class ConfigDialog(QtGui.QDialog):
self.mainLayout.setSpacing(0)
self.mainLayout.setContentsMargins(0,0,0,0)
+ self.storedPassword = self.config['password']
self.addBasicTab()
self.addReadinessTab()
self.addSyncTab()
@@ -1221,6 +1267,7 @@ class ConfigDialog(QtGui.QDialog):
self.mainLayout.addWidget(self.stackedFrame, 0, 1)
self.addBottomLayout()
+ self.updatePasswordVisibilty()
if self.getMoreState() == False:
self.tabListFrame.hide()
@@ -1228,6 +1275,14 @@ class ConfigDialog(QtGui.QDialog):
self.playerargsTextbox.hide()
self.playerargsLabel.hide()
self.runButton.hide()
+ if self.mediapathTextbox.text() == "":
+ self.mediapathTextbox.hide()
+ self.mediapathLabel.hide()
+ self.mediabrowseButton.hide()
+ else:
+ self.mediapathTextbox.show()
+ self.mediapathLabel.show()
+ self.mediabrowseButton.show()
newHeight = self.connectionSettingsGroup.minimumSizeHint().height()+self.mediaplayerSettingsGroup.minimumSizeHint().height()+self.bottomButtonFrame.minimumSizeHint().height()+3
if self.error:
newHeight +=self.errorLabel.height()+3
diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py
index c9a2455..78a2023 100644
--- a/syncplay/ui/gui.py
+++ b/syncplay/ui/gui.py
@@ -410,6 +410,9 @@ class MainWindow(QtGui.QMainWindow):
def setFeatures(self, featureList):
if not featureList["readiness"]:
self.readyPushButton.setEnabled(False)
+ if not featureList["chat"]:
+ self.chatFrame.setEnabled(False)
+ self.chatInput.setReadOnly(True)
if not featureList["sharedPlaylists"]:
self.playlistGroup.setEnabled(False)
@@ -531,7 +534,7 @@ class MainWindow(QtGui.QMainWindow):
filenameitem.setForeground(QtGui.QBrush(QtGui.QColor(constants.STYLE_DIFFERENTITEM_COLOR)))
filenameitem.setFont(underlinefont)
if not sameSize:
- if currentUser.file is not None and formatSize(user.file['size']) == formatSize(currentUser.file['size']):
+ if formatSize(user.file['size']) == formatSize(currentUser.file['size']):
filesizeitem = QtGui.QStandardItem(formatSize(user.file['size'],precise=True))
filesizeitem.setFont(underlinefont)
filesizeitem.setForeground(QtGui.QBrush(QtGui.QColor(constants.STYLE_DIFFERENTITEM_COLOR)))
@@ -570,8 +573,12 @@ class MainWindow(QtGui.QMainWindow):
self._syncplayClient.playlist.undoPlaylistChange()
@needsClient
- def shufflePlaylist(self):
- self._syncplayClient.playlist.shufflePlaylist()
+ def shuffleRemainingPlaylist(self):
+ self._syncplayClient.playlist.shuffleRemainingPlaylist()
+
+ @needsClient
+ def shuffleEntirePlaylist(self):
+ self._syncplayClient.playlist.shuffleEntirePlaylist()
@needsClient
def openPlaylistMenu(self, position):
@@ -603,7 +610,8 @@ class MainWindow(QtGui.QMainWindow):
menu.addAction(QtGui.QPixmap(resourcespath + u"shield_add.png"),getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain))
menu.addAction(QtGui.QPixmap(resourcespath + u"delete.png"), getMessage("removefromplaylist-menu-label"), lambda: self.deleteSelectedPlaylistItems())
menu.addSeparator()
- menu.addAction(QtGui.QPixmap(resourcespath + u"arrow_switch.png"), getMessage("shuffleplaylist-menuu-label"), lambda: self.shufflePlaylist())
+ menu.addAction(QtGui.QPixmap(resourcespath + u"arrow_switch.png"), getMessage("shuffleremainingplaylist-menu-label"), lambda: self.shuffleRemainingPlaylist())
+ menu.addAction(QtGui.QPixmap(resourcespath + u"arrow_switch.png"), getMessage("shuffleentireplaylist-menuu-label"), lambda: self.shuffleEntirePlaylist())
menu.addAction(QtGui.QPixmap(resourcespath + u"arrow_undo.png"), getMessage("undoplaylist-menu-label"), lambda: self.undoPlaylistChange())
menu.addAction(QtGui.QPixmap(resourcespath + u"film_edit.png"), getMessage("editplaylist-menu-label"), lambda: self.openEditPlaylistDialog())
menu.addAction(QtGui.QPixmap(resourcespath + u"film_add.png"),getMessage("addfilestoplaylist-menu-label"), lambda: self.OpenAddFilesToPlaylistDialog())
diff --git a/syncplay/utils.py b/syncplay/utils.py
index 8ab8def..4a35da9 100644
--- a/syncplay/utils.py
+++ b/syncplay/utils.py
@@ -42,8 +42,8 @@ def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
try_one_last_time = True
while mtries > 1:
try:
+ #try_one_last_time = False
return f(*args, **kwargs)
- try_one_last_time = False
break
except ExceptionToCheck, e:
if logger: