diff --git a/buildPy2exe.py b/buildPy2exe.py
index 3d7ef7b..fa428f9 100644
--- a/buildPy2exe.py
+++ b/buildPy2exe.py
@@ -68,6 +68,7 @@ NSIS_SCRIPT_TEMPLATE = r"""
LangString ^StartMenu $${LANG_ENGLISH} "Start Menu"
LangString ^Desktop $${LANG_ENGLISH} "Desktop"
LangString ^QuickLaunchBar $${LANG_ENGLISH} "Quick Launch Bar"
+ LangString ^AutomaticUpdates $${LANG_ENGLISH} "Check for updates automatically"
LangString ^UninstConfig $${LANG_ENGLISH} "Delete configuration file."
LangString ^SyncplayLanguage $${LANG_POLISH} "pl"
@@ -88,6 +89,7 @@ NSIS_SCRIPT_TEMPLATE = r"""
LangString ^StartMenu $${LANG_RUSSIAN} "в меню Пуск"
LangString ^Desktop $${LANG_RUSSIAN} "на рабочем столе"
LangString ^QuickLaunchBar $${LANG_RUSSIAN} "в меню быстрого запуска"
+ LangString ^AutomaticUpdates $${LANG_RUSSIAN} "Проверять обновления автоматически"; TODO: Confirm Russian translation ("Check for updates automatically")
LangString ^UninstConfig $${LANG_RUSSIAN} "Удалить файл настроек."
LangString ^SyncplayLanguage $${LANG_GERMAN} "de"
@@ -98,6 +100,7 @@ NSIS_SCRIPT_TEMPLATE = r"""
LangString ^StartMenu $${LANG_GERMAN} "Startmenü"
LangString ^Desktop $${LANG_GERMAN} "Desktop"
LangString ^QuickLaunchBar $${LANG_GERMAN} "Schnellstartleiste"
+ LangString ^AutomaticUpdates $${LANG_GERMAN} "Automatisch nach Updates suchen"; TODO: Confirm German translation ("Check for updates automatically")
LangString ^UninstConfig $${LANG_GERMAN} "Konfigurationsdatei löschen."
; Remove text to save space
@@ -117,11 +120,13 @@ NSIS_SCRIPT_TEMPLATE = r"""
Var Icon_Syncplay_Handle
Var CheckBox_Associate
Var CheckBox_VLC
+ Var CheckBox_AutomaticUpdates
Var CheckBox_StartMenuShortcut
Var CheckBox_DesktopShortcut
Var CheckBox_QuickLaunchShortcut
Var CheckBox_Associate_State
Var CheckBox_VLC_State
+ Var CheckBox_AutomaticUpdates_State
Var CheckBox_StartMenuShortcut_State
Var CheckBox_DesktopShortcut_State
Var CheckBox_QuickLaunchShortcut_State
@@ -237,10 +242,10 @@ NSIS_SCRIPT_TEMPLATE = r"""
$${NSD_CreateGroupBox} 1u 27u 264u 30u "$$(^DirSubText)"
Pop $$GroupBox_DirSub
- $${NSD_CreateLabel} 0u 111u 265u 8u "$$(^SpaceRequired)$$SizeMB"
+ $${NSD_CreateLabel} 0u 122u 132 8u "$$(^SpaceRequired)$$SizeMB"
Pop $$Label_Size
- $${NSD_CreateLabel} 0u 122u 265u 8u "$$(^SpaceAvailable)$$AvailibleSpaceGiB.$$AvailibleSpaceGB"
+ $${NSD_CreateLabel} 321u 122u 132 8u "$$(^SpaceAvailable)$$AvailibleSpaceGiB.$$AvailibleSpaceGB"
Pop $$Label_Space
$${NSD_CreateCheckBox} 8u 59u 187u 10u "$$(^Associate)"
@@ -253,16 +258,20 @@ NSIS_SCRIPT_TEMPLATE = r"""
$${NSD_CreateCheckBox} 8u 72u 250u 10u "$$(^VLC)"
Pop $$CheckBox_VLC
- $${NSD_CreateLabel} 8u 85u 187u 10u "$$(^Shortcut)"
+ $${NSD_CreateCheckBox} 8u 85u 250u 10u "$$(^AutomaticUpdates)"
+ Pop $$CheckBox_AutomaticUpdates
+ $${NSD_Check} $$CheckBox_AutomaticUpdates
+
+ $${NSD_CreateLabel} 8u 98u 187u 10u "$$(^Shortcut)"
Pop $$Label_Shortcut
- $${NSD_CreateCheckbox} 8u 98u 60u 10u "$$(^StartMenu)"
+ $${NSD_CreateCheckbox} 8u 111u 60u 10u "$$(^StartMenu)"
Pop $$CheckBox_StartMenuShortcut
- $${NSD_CreateCheckbox} 78u 98u 70u 10u "$$(^Desktop)"
+ $${NSD_CreateCheckbox} 78u 111u 70u 10u "$$(^Desktop)"
Pop $$CheckBox_DesktopShortcut
- $${NSD_CreateCheckbox} 158u 98u 130u 10u "$$(^QuickLaunchBar)"
+ $${NSD_CreateCheckbox} 158u 111u 130u 10u "$$(^QuickLaunchBar)"
Pop $$CheckBox_QuickLaunchShortcut
$${If} $$CheckBox_Associate_State == $${BST_CHECKED}
@@ -287,6 +296,10 @@ NSIS_SCRIPT_TEMPLATE = r"""
$${NSD_Check} $$CheckBox_QuickLaunchShortcut
$${EndIf}
+ $${If} $$CheckBox_AutomaticUpdates_State == $${BST_CHECKED}
+ $${NSD_Check} $$CheckBox_AutomaticUpdates
+ $${EndIf}
+
nsDialogs::Show
$${NSD_FreeIcon} $$Icon_Syncplay_Handle
@@ -297,6 +310,7 @@ NSIS_SCRIPT_TEMPLATE = r"""
$${NSD_GetText} $$Text_Directory $$INSTDIR
$${NSD_GetState} $$CheckBox_Associate $$CheckBox_Associate_State
$${NSD_GetState} $$CheckBox_VLC $$CheckBox_VLC_State
+ $${NSD_GetState} $$CheckBox_AutomaticUpdates $$CheckBox_AutomaticUpdates_State
$${NSD_GetState} $$CheckBox_StartMenuShortcut $$CheckBox_StartMenuShortcut_State
$${NSD_GetState} $$CheckBox_DesktopShortcut $$CheckBox_DesktopShortcut_State
$${NSD_GetState} $$CheckBox_QuickLaunchShortcut $$CheckBox_QuickLaunchShortcut_State
@@ -444,6 +458,11 @@ NSIS_SCRIPT_TEMPLATE = r"""
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "NoRepair" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Syncplay" "EstimatedSize" "$$SizeHex"
WriteINIStr $$APPDATA\syncplay.ini general language $$(^SyncplayLanguage)
+ $${If} $$CheckBox_AutomaticUpdates_State == $${BST_CHECKED}
+ WriteINIStr $$APPDATA\syncplay.ini general CheckForUpdatesAutomatically "True"
+ $${Else}
+ WriteINIStr $$APPDATA\syncplay.ini general CheckForUpdatesAutomatically "False"
+ $${EndIf}
FunctionEnd
Function un.installConfirm
@@ -625,7 +644,7 @@ 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/cross_checkbox.png', 'resources/tick_checkbox.png',
- 'resources/world_explore.png'
+ 'resources/world_explore.png', 'resources/application_get.png'
]
resources = ["resources/icon.ico", "resources/syncplay.png"]
resources.extend(guiIcons)
diff --git a/resources/application_get.png b/resources/application_get.png
new file mode 100644
index 0000000..28e41ea
Binary files /dev/null and b/resources/application_get.png differ
diff --git a/syncplay/__init__.py b/syncplay/__init__.py
index e40e81d..e84a065 100644
--- a/syncplay/__init__.py
+++ b/syncplay/__init__.py
@@ -1,3 +1,4 @@
version = '1.3.0'
milestone = 'Chami'
+release_number = '1'
projectURL = 'http://syncplay.pl/'
diff --git a/syncplay/client.py b/syncplay/client.py
index 5bbaf88..00899b8 100644
--- a/syncplay/client.py
+++ b/syncplay/client.py
@@ -548,6 +548,21 @@ class SyncplayClient(object):
if self.controlpasswords.has_key(room):
return self.controlpasswords[room]
+ def checkForUpdate(self):
+ try:
+ import urllib, syncplay, sys, messages, json
+ params = urllib.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number,
+ 'language': messages.messages["CURRENT"], 'platform': sys.platform})
+ print params
+
+ f = urllib.urlopen(constants.SYNCPLAY_UPDATE_URL.format(params))
+ response = f.read()
+ response = response.replace("
","").replace("
","").replace("
","").replace("“","\"").replace("”","\"") # Fix Wordpress
+ response = json.loads(response)
+ 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
+ except:
+ return "failed", getMessage("update-check-failed-notification").format(syncplay.version), constants.SYNCPLAY_DOWNLOAD_URL
+
class _WarningManager(object):
def __init__(self, player, userlist, ui, client):
self._client = client
diff --git a/syncplay/constants.py b/syncplay/constants.py
index c99355a..3e741f1 100644
--- a/syncplay/constants.py
+++ b/syncplay/constants.py
@@ -29,6 +29,7 @@ SHOW_DURATION_NOTIFICATION = True
DEBUG_MODE = False
#Changing these might be ok
+AUTOMATIC_UPDATE_CHECK_FREQUENCY = 7 * 86400 # Days converted into seconds
DEFAULT_REWIND_THRESHOLD = 4
MINIMUM_REWIND_THRESHOLD = 3
DEFAULT_FASTFORWARD_THRESHOLD = 5
@@ -157,3 +158,6 @@ CONFIG_NAME_MARKER = ":"
CONFIG_VALUE_MARKER = "="
USERITEM_CONTROLLER_ROLE = 0
USERITEM_READY_ROLE = 1
+
+SYNCPLAY_UPDATE_URL = u"http://syncplay.pl/checkforupdate?{}" # Params
+SYNCPLAY_DOWNLOAD_URL = "http://syncplay.pl/download/"
\ No newline at end of file
diff --git a/syncplay/messages.py b/syncplay/messages.py
index 0f3532e..3e4b0bf 100755
--- a/syncplay/messages.py
+++ b/syncplay/messages.py
@@ -57,6 +57,10 @@ en = {
"userlist-file-notification" : "File",
"controller-userlist-userflag" : "Controller",
"ready-userlist-userflag" : "Ready",
+
+ "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
+ "syncplay-uptodate-notification" : u"Syncplay is up to date",
+ "syncplay-updateavailable-notification" : u"A new version of Syncplay is available. Do you want to download it?",
"mplayer-file-required-notification" : "Syncplay using mplayer requires you to provide file when starting",
"mplayer-file-required-notification/example" : "Usage example: syncplay [options] [url|path/]filename",
@@ -78,6 +82,7 @@ en = {
"gui-data-cleared-notification" : "Syncplay has cleared the path and window state data used by the GUI.",
"language-changed-msgbox-label" : "Language will be changed when you run Syncplay.",
+ "promptforupdate-label" : u"Is it okay for Syncplay to automatically check for updates from time to time?",
"vlc-version-mismatch": "Warning: You are running VLC version {}, but Syncplay is designed to run on VLC {} and above.", # VLC version, VLC min version
"vlc-interface-version-mismatch": "Warning: You are running version {} of the Syncplay interface module for VLC, but Syncplay is designed to run with version {} and above.", # VLC interface version, VLC interface min version
@@ -163,6 +168,7 @@ en = {
"privacy-dontsend-option" : "Don't send",
"filename-privacy-label" : "Filename information:",
"filesize-privacy-label" : "File size information:",
+ "checkforupdatesautomatically-label" : "Check for updates automatically",
"slowondesync-label" : "Slow down on minor desync (not supported on MPC-HC)",
"rewindondesync-label" : "Rewind on major desync (highly recommended)",
"fastforwardondesync-label" : "Fast-forward if lagging behind (recommended)",
@@ -231,6 +237,7 @@ en = {
"help-menu-label" : "&Help",
"userguide-menu-label" : "Open user &guide",
+ "update-menu-label" : "Check for &update",
"setoffset-msgbox-label" : "Set offset",
"offsetinfo-msgbox-label" : "Offset (see http://syncplay.pl/guide/ for usage instructions):",
@@ -265,6 +272,7 @@ en = {
"privacy-sendraw-tooltip" : "Send this information without obfuscation. This is the default option with most functionality.",
"privacy-sendhashed-tooltip" : "Send a hashed version of the information, making it less visible to other clients.",
"privacy-dontsend-tooltip" : "Do not send this information to the server. This provides for maximum privacy.",
+ "checkforupdatesautomatically-tooltip" : "Regularly check with the Syncplay website to see whether a new version of Syncplay is available.",
"slowondesync-tooltip" : "Reduce playback rate temporarily when needed to bring you back in sync with other viewers. Not supported on MPC-HC.",
"dontslowdownwithme-tooltip" : "Means others do not get slowed down or rewinded if your playback is lagging. Useful for room controllers.",
"pauseonleave-tooltip" : "Pause playback if you get disconnected or someone leaves from your room.",
@@ -386,6 +394,10 @@ ru = {
"userlist-file-notification" : u"File", # TODO: Translate into Russian (Файл?)
"controller-userlist-userflag" : u"Controller", # TODO: Translate into Russian (this is to indicate a user is a controller 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)
+
+ "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 download it?", # TODO: Translate into Russian
"mplayer-file-required-notification" : u"Для использования Syncplay с mplayer необходимо передать файл в качестве параметра",
"mplayer-file-required-notification/example" : u"Пример использования: syncplay [options] [url|path/]filename",
@@ -406,7 +418,8 @@ ru = {
"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.",
+ "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
"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
@@ -492,6 +505,7 @@ ru = {
"privacy-dontsend-option" : u"не отпр.",
"filename-privacy-label" : u"Имя файла:",
"filesize-privacy-label" : u"Размер файла:",
+ "checkforupdatesautomatically-label" : u"Проверять обновления автоматически", # TODO: Confirm Russian translation
"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)
@@ -560,6 +574,7 @@ ru = {
"help-menu-label" : u"&Помощь",
"userguide-menu-label" : u"&Руководство Пользователя",
+ "update-menu-label" : u"Check for &update", # TODO: Translate into Russian
"setoffset-msgbox-label" : u"Установить смещение",
"offsetinfo-msgbox-label" : u"Смещение (см. как использовать на http://syncplay.pl/guide/):",
@@ -594,6 +609,7 @@ 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
"slowondesync-tooltip" : u"Временно уменьшить скорость воспроизведения в целях синхронизации с другими зрителями. Не поддерживается в MPC-HC.",
"dontslowdownwithme-tooltip" : u"Ваши лаги не будут влиять на других зрителей.",
"pauseonleave-tooltip" : u"Приостановить воспроизведение, если Вы покинули комнату или кто-то из зрителей отключился от сервера.",
@@ -714,7 +730,11 @@ de = {
"userlist-room-notification" : u"In Raum '{}':", # Room
"userlist-file-notification" : u"File", # TODO: Translate into German (Datei?)
"controller-userlist-userflag" : u"Controller", # TODO: Translate into German (this is to indicate a user is a controller in the ConsoleUI userlist)
- "ready-userlist-userflag" : u"Ready", # TODO: Translate into Germany (this is to indicate a user is ready to watch in the ConsoleUI userlist)
+ "ready-userlist-userflag" : u"Ready", # TODO: Translate into German (this is to indicate a user is ready to watch in the ConsoleUI userlist)
+
+ "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 German
+ "syncplay-uptodate-notification" : u"Syncplay is up to date", # TODO: Translate into German
+ "syncplay-updateavailable-notification" : u"A new version of Syncplay is available. Do you want to download it?", # TODO: Translate into German
"mplayer-file-required-notification" : u"Syncplay für mplayer benötigt eine Datei-Angabe beim Start",
"mplayer-file-required-notification/example" : u"Anwendungsbeispiel: syncplay [optionen] [url|pfad/]Dateiname",
@@ -736,6 +756,7 @@ de = {
"gui-data-cleared-notification" : u"Syncplay hat die Pfad und Fensterdaten der Syncplay-GUI zurückgesetzt.",
"language-changed-msgbox-label" : u"Die Sprache wird geändert, wenn du Syncplay neu startest.",
+ "promptforupdate-label" : u"Is it okay for Syncplay to automatically check for updates from time to time?", # TODO: Translate into German
"vlc-version-mismatch": u"Warnung: Du nutzt VLC Version {}, aber Syncplay wurde für VLC ab Version {} entwickelt.", # VLC version, VLC min version
"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
@@ -821,6 +842,7 @@ de = {
"privacy-dontsend-option" : u"Nicht senden",
"filename-privacy-label" : u"Dateiname:",
"filesize-privacy-label" : u"Dateigröße:",
+ "checkforupdatesautomatically-label" : u"Automatisch nach Updates suche", # TODO: Confirm German translation
"slowondesync-label" : u"Verlangsamen wenn nicht synchron (nicht unterstützt mit MPC-HC)",
"dontslowdownwithme-label" : u"Nie verlangsamen oder andere zurückspulen (Experimentell)",
"pauseonleave-label" : u"Pausieren wenn ein Benutzer austritt",
@@ -887,6 +909,7 @@ de = {
"help-menu-label" : u"&Hilfe",
"userguide-menu-label" : u"&Benutzerhandbuch öffnen",
+ "update-menu-label" : u"Check for &update", # TODO: Translate into Russian
"setoffset-msgbox-label" : u"Offset einstellen",
"offsetinfo-msgbox-label" : u"Offset (siehe http://syncplay.pl/guide/ für eine Anleitung [Englisch]):",
@@ -921,6 +944,7 @@ de = {
"privacy-sendraw-tooltip" : u"Die Information im Klartext übertragen. Dies ist die Standard-Einstellung mit der besten Funktionalität.",
"privacy-sendhashed-tooltip" : u"Die Informationen gehasht übertragen, um sie für andere Clients schwerer lesbar zu machen.",
"privacy-dontsend-tooltip" : u"Diese Information nicht übertragen. Dies garantiert den größtmöglichen Datanschutz.",
+ "checkforupdatesautomatically-tooltip" : u"Regularly check with the Syncplay website to see whether a new version of Syncplay is available.", # TODO: Translate into German
"slowondesync-tooltip" : u"Reduziert die Abspielgeschwindigkeit zeitweise, um die Synchronität zu den anderen Clients wiederherzustellen.",
"rewindondesync-label" : u"Zurückspulen bei großer Zeitdifferenz (empfohlen)",
"fastforwardondesync-label" : u"Vorspulen wenn das Video lagt (empfohlen)",
diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py
index 89ce26b..adcb86a 100755
--- a/syncplay/ui/ConfigurationGetter.py
+++ b/syncplay/ui/ConfigurationGetter.py
@@ -47,6 +47,8 @@ class ConfigurationGetter(object):
"readyAtStart": False,
"clearGUIData": False,
"language" : "",
+ "checkForUpdatesAutomatically" : None,
+ "lastCheckedForUpdates" : "",
"resetConfig" : False,
"showOSD" : True,
"showOSDWarnings" : True,
@@ -95,6 +97,9 @@ class ConfigurationGetter(object):
"showContactInfo" ,
"showDurationNotification"
]
+ self._tristate = [
+ "checkForUpdatesAutomatically"
+ ]
self._numeric = [
"slowdownThreshold",
@@ -106,7 +111,7 @@ class ConfigurationGetter(object):
"server_data": ["host", "port", "password"],
"client_settings": ["name", "room", "playerPath", "slowdownThreshold", "rewindThreshold", "fastforwardThreshold", "slowOnDesync", "rewindOnDesync", "fastforwardOnDesync", "dontSlowDownWithMe", "forceGuiPrompt", "filenamePrivacyMode", "filesizePrivacyMode", "pauseOnLeave", "readyAtStart"],
"gui": ["showOSD", "showOSDWarnings", "showSlowdownOSD", "showDifferentRoomOSD", "showSameRoomOSD", "showNonControllerOSD", "showContactInfo" , "showDurationNotification"],
- "general": ["language"]
+ "general": ["language", "checkForUpdatesAutomatically", "lastCheckedForUpdates"]
}
self._playerFactory = PlayerFactory()
@@ -139,6 +144,14 @@ class ConfigurationGetter(object):
elif self._config[key] == "False":
self._config[key] = False
+ for key in self._tristate:
+ if self._config[key] == "True":
+ self._config[key] = True
+ elif self._config[key] == "False":
+ self._config[key] = False
+ elif self._config[key] == "None":
+ self._config[key] = None
+
for key in self._numeric:
self._config[key] = float(self._config[key])
diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py
index 9c1b0f1..adbf1b6 100644
--- a/syncplay/ui/GuiConfiguration.py
+++ b/syncplay/ui/GuiConfiguration.py
@@ -1,8 +1,8 @@
from PySide import QtCore, QtGui
-from PySide.QtCore import QSettings, Qt, QCoreApplication
+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
import os
import sys
from syncplay.messages import getMessage, getLanguages, setLanguage, getInitialLanguage
@@ -35,6 +35,15 @@ class ConfigDialog(QtGui.QDialog):
pressedclosebutton = False
moreToggling = False
+ def automaticUpdatePromptCheck(self):
+ if self.automaticupdatesCheckbox.checkState() == Qt.PartiallyChecked and not self.nostoreCheckbox.isChecked():
+ reply = QtGui.QMessageBox.question(self, "Syncplay",
+ getMessage("promptforupdate-label"), QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.No)
+ if reply == QtGui.QMessageBox.Yes:
+ self.automaticupdatesCheckbox.setChecked(True)
+ else:
+ self.automaticupdatesCheckbox.setChecked(False)
+
def moreToggled(self):
if self.moreToggling == False:
self.moreToggling = True
@@ -68,7 +77,7 @@ class ConfigDialog(QtGui.QDialog):
self.runButton.setText(getMessage("storeandrun-label"))
def openHelp(self):
- self.QtGui.QDesktopServices.openUrl("http://syncplay.pl/guide/client/")
+ self.QtGui.QDesktopServices.openUrl(QUrl("http://syncplay.pl/guide/client/"))
def _isURL(self, path):
if path is None:
@@ -170,6 +179,17 @@ class ConfigDialog(QtGui.QDialog):
if fileName:
self.executablepathCombobox.setEditText(os.path.normpath(fileName))
+ def loadLastUpdateCheckDate(self):
+ settings = QSettings("Syncplay", "Interface")
+ settings.beginGroup("Update")
+ self.lastCheckedForUpdates = settings.value("lastChecked", None)
+ if self.lastCheckedForUpdates:
+ if self.config["lastCheckedForUpdates"] is not None and self.config["lastCheckedForUpdates"] is not "":
+ if self.lastCheckedForUpdates > datetime.strptime(self.config["lastCheckedForUpdates"], "%Y-%m-%d %H:%M:%S.%f"):
+ self.config["lastCheckedForUpdates"] = str(self.lastCheckedForUpdates)
+ else:
+ self.config["lastCheckedForUpdates"] = str(self.lastCheckedForUpdates)
+
def loadMediaBrowseSettings(self):
settings = QSettings("Syncplay", "MediaBrowseDialog")
settings.beginGroup("MediaBrowseDialog")
@@ -218,6 +238,9 @@ class ConfigDialog(QtGui.QDialog):
self.saveMediaBrowseSettings()
def _saveDataAndLeave(self):
+ self.automaticUpdatePromptCheck()
+ self.loadLastUpdateCheckDate()
+
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)
@@ -293,7 +316,12 @@ class ConfigDialog(QtGui.QDialog):
inverted = True
else:
inverted = False
- widget.setChecked(self.config[valueName] != inverted)
+ if self.config[valueName] is None:
+ widget.setTristate(True)
+ widget.setCheckState(Qt.PartiallyChecked)
+ widget.stateChanged.connect(lambda: widget.setTristate(False))
+ else:
+ widget.setChecked(self.config[valueName] != inverted)
elif isinstance(widget, QRadioButton):
radioName, radioValue = valueName.split(constants.CONFIG_NAME_MARKER)[1].split(constants.CONFIG_VALUE_MARKER)
if self.config[radioName] == radioValue:
@@ -307,12 +335,15 @@ class ConfigDialog(QtGui.QDialog):
return
if isinstance(widget, QCheckBox) and widget.objectName():
- if valueName[:1] == constants.INVERTED_STATE_MARKER:
- valueName = valueName[1:]
- inverted = True
+ if widget.checkState() == Qt.PartiallyChecked:
+ self.config[valueName] = None
else:
- inverted = False
- self.config[valueName] = widget.isChecked() != inverted
+ if valueName[:1] == constants.INVERTED_STATE_MARKER:
+ valueName = valueName[1:]
+ inverted = True
+ else:
+ inverted = False
+ self.config[valueName] = widget.isChecked() != inverted
elif isinstance(widget, QRadioButton):
radioName, radioValue = valueName.split(constants.CONFIG_NAME_MARKER)[1].split(constants.CONFIG_VALUE_MARKER)
if widget.isChecked():
@@ -691,6 +722,9 @@ class ConfigDialog(QtGui.QDialog):
self.filesizeprivacySendHashedOption.setObjectName("privacy-sendhashed" + constants.CONFIG_NAME_MARKER + "filesizePrivacyMode" + constants.CONFIG_VALUE_MARKER + constants.PRIVACY_SENDHASHED_MODE)
self.filesizeprivacyDontSendOption.setObjectName("privacy-dontsend" + constants.CONFIG_NAME_MARKER + "filesizePrivacyMode" + constants.CONFIG_VALUE_MARKER + constants.PRIVACY_DONTSEND_MODE)
+ self.automaticupdatesCheckbox = QCheckBox(getMessage("checkforupdatesautomatically-label"))
+ self.automaticupdatesCheckbox.setObjectName("checkForUpdatesAutomatically")
+
self.privacyLayout.addWidget(self.filenameprivacyLabel, 1, 0)
self.privacyLayout.addWidget(self.filenameprivacySendRawOption, 1, 1, Qt.AlignLeft)
self.privacyLayout.addWidget(self.filenameprivacySendHashedOption, 1, 2, Qt.AlignLeft)
@@ -699,6 +733,7 @@ class ConfigDialog(QtGui.QDialog):
self.privacyLayout.addWidget(self.filesizeprivacySendRawOption, 2, 1, Qt.AlignLeft)
self.privacyLayout.addWidget(self.filesizeprivacySendHashedOption, 2, 2, Qt.AlignLeft)
self.privacyLayout.addWidget(self.filesizeprivacyDontSendOption, 2, 3, Qt.AlignLeft)
+ self.privacyLayout.addWidget(self.automaticupdatesCheckbox, 3, 0, 1, 3, Qt.AlignLeft)
self.privacyFrame.setLayout(self.privacyLayout)
self.privacySettingsGroup.setLayout(self.privacyLayout)
@@ -796,6 +831,10 @@ class ConfigDialog(QtGui.QDialog):
settings.clear()
settings = QSettings("Syncplay", "MainWindow")
settings.clear()
+ settings = QSettings("Syncplay", "Interface")
+ settings.beginGroup("Update")
+ settings.setValue("lastChecked", None)
+ settings.endGroup()
if not leaveMore:
settings = QSettings("Syncplay", "MoreSettings")
settings.clear()
diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py
index c8f11af..06927c4 100644
--- a/syncplay/ui/gui.py
+++ b/syncplay/ui/gui.py
@@ -1,12 +1,14 @@
from PySide import QtGui
-from PySide.QtCore import Qt, QSettings, QSize, QPoint
+from PySide.QtCore import Qt, QSettings, QSize, QPoint, QUrl
from syncplay import utils, constants, version
from syncplay.messages import getMessage
import sys
import time
+from datetime import datetime
import re
import os
from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize
+lastCheckedForUpdates = None
class UserlistItemDelegate(QtGui.QStyledItemDelegate):
def __init__(self):
@@ -81,6 +83,7 @@ class MainWindow(QtGui.QMainWindow):
self.hideMiscLabels()
except ():
pass
+ self.automaticUpdateCheck()
def promptFor(self, prompt=">", message=""):
# TODO: Prompt user
@@ -428,11 +431,11 @@ class MainWindow(QtGui.QMainWindow):
def openUserGuide(self):
if sys.platform.startswith('linux'):
- self.QtGui.QDesktopServices.openUrl("http://syncplay.pl/guide/linux/")
+ self.QtGui.QDesktopServices.openUrl(QUrl("http://syncplay.pl/guide/linux/"))
elif sys.platform.startswith('win'):
- self.QtGui.QDesktopServices.openUrl("http://syncplay.pl/guide/windows/")
+ self.QtGui.QDesktopServices.openUrl(QUrl("http://syncplay.pl/guide/windows/"))
else:
- self.QtGui.QDesktopServices.openUrl("http://syncplay.pl/guide/")
+ self.QtGui.QDesktopServices.openUrl(QUrl("http://syncplay.pl/guide/"))
def drop(self):
self.close()
@@ -613,6 +616,9 @@ class MainWindow(QtGui.QMainWindow):
window.userguideAction = window.helpMenu.addAction(QtGui.QIcon(self.resourcespath + 'help.png'),
getMessage("userguide-menu-label"))
window.userguideAction.triggered.connect(self.openUserGuide)
+ window.updateAction = window.helpMenu.addAction(QtGui.QIcon(self.resourcespath + 'application_get.png'),
+ getMessage("update-menu-label"))
+ window.updateAction.triggered.connect(self.userCheckForUpdates)
window.menuBar.addMenu(window.helpMenu)
window.mainLayout.setMenuBar(window.menuBar)
@@ -652,6 +658,47 @@ class MainWindow(QtGui.QMainWindow):
self.readyPushButton.setIcon(QtGui.QIcon(self.resourcespath + 'cross_checkbox.png'))
self.readyPushButton.setText(getMessage("notready-guipushbuttonlabel"))
+ def automaticUpdateCheck(self):
+ if not self.config['checkForUpdatesAutomatically']:
+ return
+ if self.config['lastCheckedForUpdates']:
+ configLastChecked = datetime.strptime(self.config["lastCheckedForUpdates"], "%Y-%m-%d %H:%M:%S.%f")
+ if self.lastCheckedForUpdates is None or configLastChecked > self.lastCheckedForUpdates:
+ self.lastCheckedForUpdates = configLastChecked
+ currentDateTime = datetime.utcnow()
+ if self.lastCheckedForUpdates is None:
+ self.checkForUpdates()
+ else:
+ timeDelta = currentDateTime - self.lastCheckedForUpdates
+ if timeDelta.total_seconds() > constants.AUTOMATIC_UPDATE_CHECK_FREQUENCY:
+ self.checkForUpdates()
+
+ def userCheckForUpdates(self):
+ self.checkForUpdates(userInitiated=True)
+
+ def checkForUpdates(self, userInitiated=False):
+ self.lastCheckedForUpdates = datetime.utcnow()
+ updateStatus, updateMessage, updateURL = self._syncplayClient.checkForUpdate()
+ if updateMessage is None:
+ if updateStatus == "uptodate":
+ updateMessage = getMessage("syncplay-uptodate-notification")
+ elif updateStatus == "updateavailale":
+ updateMessage = getMessage("syncplay-updateavailable-notification")
+ else:
+ import syncplay
+ updateMessage = getMessage("update-check-failed-notification").format(syncplay.version)
+ if userInitiated == True:
+ updateURL = constants.SYNCPLAY_DOWNLOAD_URL
+ if updateURL is not None:
+ reply = QtGui.QMessageBox.question(self, "Syncplay",
+ updateMessage, QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.No)
+ if reply == QtGui.QMessageBox.Yes:
+ self.QtGui.QDesktopServices.openUrl(QUrl(updateURL))
+ elif userInitiated:
+ QtGui.QMessageBox.information(self, "Syncplay", updateMessage)
+ else:
+ self.showMessage(updateMessage)
+
def dragEnterEvent(self, event):
data = event.mimeData()
urls = data.urls()
@@ -680,6 +727,10 @@ class MainWindow(QtGui.QMainWindow):
settings.setValue("size", self.size())
settings.setValue("pos", self.pos())
settings.endGroup()
+ settings = QSettings("Syncplay", "Interface")
+ settings.beginGroup("Update")
+ settings.setValue("lastChecked", self.lastCheckedForUpdates)
+ settings.endGroup()
def loadSettings(self):
settings = QSettings("Syncplay", "MainWindow")
@@ -687,6 +738,9 @@ class MainWindow(QtGui.QMainWindow):
self.resize(settings.value("size", QSize(700, 500)))
self.move(settings.value("pos", QPoint(200, 200)))
settings.endGroup()
+ settings = QSettings("Syncplay", "Interface")
+ settings.beginGroup("Update")
+ self.lastCheckedForUpdates = settings.value("lastChecked", None)
def __init__(self):
super(MainWindow, self).__init__()