Merge origin
This commit is contained in:
commit
280922e928
@ -653,7 +653,8 @@ guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock
|
||||
'resources/eye.png', 'resources/comments.png', 'resources/cog_delete.png', 'resources/chevrons_right.png',
|
||||
'resources/user_key.png', 'resources/lock.png', 'resources/key_go.png', 'resources/page_white_key.png',
|
||||
'resources/tick.png', 'resources/lock_open.png', 'resources/empty_checkbox.png', 'resources/tick_checkbox.png',
|
||||
'resources/world_explore.png', 'resources/application_get.png', 'resources/cog.png'
|
||||
'resources/world_explore.png', 'resources/application_get.png', 'resources/cog.png',
|
||||
'resources/film_go.png', 'resources/world_go.png', 'resources/arrow_refresh.png'
|
||||
]
|
||||
resources = ["resources/icon.ico", "resources/syncplay.png"]
|
||||
resources.extend(guiIcons)
|
||||
@ -676,7 +677,7 @@ info = dict(
|
||||
options={'py2exe': {
|
||||
'dist_dir': OUT_DIR,
|
||||
'packages': 'PySide.QtUiTools',
|
||||
'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide, liburl',
|
||||
'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide, liburl, ast',
|
||||
'excludes': 'venv, _ssl, doctest, pdb, unittest, win32clipboard, win32file, win32pdh, win32security, win32trace, win32ui, winxpgui, win32pipe, win32process, Tkinter',
|
||||
'dll_excludes': 'msvcr71.dll, MSVCP90.dll, POWRPROF.dll',
|
||||
'optimize': 2,
|
||||
|
||||
BIN
resources/arrow_refresh.png
Normal file
BIN
resources/arrow_refresh.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 685 B |
BIN
resources/film_go.png
Normal file
BIN
resources/film_go.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 813 B |
@ -5,7 +5,7 @@
|
||||
Principal author: Etoh
|
||||
Other contributors: DerGenaue, jb
|
||||
Project: http://syncplay.pl/
|
||||
Version: 0.2.6
|
||||
Version: 0.2.8
|
||||
|
||||
Note:
|
||||
* This interface module is intended to be used in conjunction with Syncplay.
|
||||
@ -84,7 +84,7 @@ You may also need to re-copy the syncplay.lua file when you update VLC.
|
||||
|
||||
--]==========================================================================]
|
||||
|
||||
local connectorversion = "0.2.6"
|
||||
local connectorversion = "0.2.8"
|
||||
local vlcversion = vlc.misc.version()
|
||||
local durationdelay = 500000 -- Pause for get_duration command etc for increased reliability (uses microseconds)
|
||||
local loopsleepduration = 2500 -- Pause for every event loop (uses microseconds)
|
||||
@ -329,8 +329,10 @@ function get_filepath ()
|
||||
response = ":::DVD:::"
|
||||
else
|
||||
local metas = item:metas()
|
||||
if metas and metas["title"] and string.len(metas["title"]) > 0 then
|
||||
response = ":::(Stream: "..metas["title"]..")"
|
||||
if metas and metas["url"] and string.len(metas["url"]) > 0 then
|
||||
response = metas["url"]
|
||||
elseif item:uri() and string.len(item:uri()) > 0 then
|
||||
response = item:uri()
|
||||
else
|
||||
response = unknownstream
|
||||
end
|
||||
|
||||
BIN
resources/world_go.png
Normal file
BIN
resources/world_go.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 944 B |
@ -1,4 +1,4 @@
|
||||
version = '1.3.1'
|
||||
version = '1.3.4'
|
||||
milestone = 'Chami'
|
||||
release_number = '10'
|
||||
release_number = '21'
|
||||
projectURL = 'http://syncplay.pl/'
|
||||
|
||||
@ -3,9 +3,11 @@ import os.path
|
||||
import time
|
||||
import re
|
||||
import sys
|
||||
import ast
|
||||
from twisted.internet.protocol import ClientFactory
|
||||
from twisted.internet import reactor, task
|
||||
from functools import wraps
|
||||
from copy import deepcopy
|
||||
from syncplay.protocols import SyncClientProtocol
|
||||
from syncplay import utils, constants
|
||||
from syncplay.messages import getMissingStrings, getMessage
|
||||
@ -388,6 +390,9 @@ class SyncplayClient(object):
|
||||
return self._globalPaused
|
||||
|
||||
def updateFile(self, filename, duration, path):
|
||||
if utils.isURL(path):
|
||||
filename = path
|
||||
|
||||
if not path:
|
||||
return
|
||||
try:
|
||||
@ -399,7 +404,7 @@ class SyncplayClient(object):
|
||||
except:
|
||||
size = 0
|
||||
filename, size = self.__executePrivacySettings(filename, size)
|
||||
self.userlist.currentUser.setFile(filename, duration, size)
|
||||
self.userlist.currentUser.setFile(filename, duration, size, path)
|
||||
self.sendFile()
|
||||
|
||||
def __executePrivacySettings(self, filename, size):
|
||||
@ -416,8 +421,20 @@ class SyncplayClient(object):
|
||||
def setServerVersion(self, version):
|
||||
self.serverVersion = version
|
||||
|
||||
def getSanitizedCurrentUserFile(self):
|
||||
if self.userlist.currentUser.file:
|
||||
file_ = deepcopy(self.userlist.currentUser.file)
|
||||
if constants.PRIVATE_FILE_FIELDS:
|
||||
for PrivateField in constants.PRIVATE_FILE_FIELDS:
|
||||
if file_.has_key(PrivateField):
|
||||
file_.pop(PrivateField)
|
||||
return file_
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def sendFile(self):
|
||||
file_ = self.userlist.currentUser.file
|
||||
file_ = self.getSanitizedCurrentUserFile()
|
||||
if self._protocol and self._protocol.logged and file_:
|
||||
self._protocol.sendFileSetting(file_)
|
||||
|
||||
@ -494,7 +511,10 @@ class SyncplayClient(object):
|
||||
return
|
||||
self._running = True
|
||||
if self._playerClass:
|
||||
reactor.callLater(0.1, self._playerClass.run, self, self._config['playerPath'], self._config['file'], self._config['playerArgs'])
|
||||
perPlayerArguments = utils.getPlayerArgumentsByPathAsArray(self._config['perPlayerArguments'],self._config['playerPath'])
|
||||
if perPlayerArguments:
|
||||
self._config['playerArgs'].extend(perPlayerArguments)
|
||||
reactor.callLater(0.1, self._playerClass.run, self, self._config['playerPath'], self._config['file'], self._config['playerArgs'], )
|
||||
self._playerClass = None
|
||||
self.protocolFactory = SyncClientFactory(self)
|
||||
port = int(port)
|
||||
@ -670,9 +690,13 @@ class SyncplayClient(object):
|
||||
response = f.read()
|
||||
response = response.replace("<p>","").replace("</p>","").replace("<br />","").replace("“","\"").replace("”","\"") # Fix Wordpress
|
||||
response = json.loads(response)
|
||||
return response["version-status"], response["version-message"] if response.has_key("version-message") else None, response["version-url"] if response.has_key("version-url") else None
|
||||
publicServers = None
|
||||
if response["public-servers"]:
|
||||
publicServers = response["public-servers"].replace("”","'").replace(":’","'").replace("’","'").replace("′","'").replace("\n","").replace("\r","")
|
||||
publicServers = ast.literal_eval(publicServers)
|
||||
return response["version-status"], response["version-message"] if response.has_key("version-message") else None, response["version-url"] if response.has_key("version-url") else None, publicServers
|
||||
except:
|
||||
return "failed", getMessage("update-check-failed-notification").format(syncplay.version), constants.SYNCPLAY_DOWNLOAD_URL
|
||||
return "failed", getMessage("update-check-failed-notification").format(syncplay.version), constants.SYNCPLAY_DOWNLOAD_URL, None
|
||||
|
||||
class _WarningManager(object):
|
||||
def __init__(self, player, userlist, ui, client):
|
||||
@ -796,11 +820,12 @@ class SyncplayUser(object):
|
||||
self.file = file_
|
||||
self._controller = False
|
||||
|
||||
def setFile(self, filename, duration, size):
|
||||
def setFile(self, filename, duration, size, path=None):
|
||||
file_ = {
|
||||
"name": filename,
|
||||
"duration": duration,
|
||||
"size": size
|
||||
"size": size,
|
||||
"path": path
|
||||
}
|
||||
self.file = file_
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ SHOW_CONTACT_INFO = True # Displays dev contact details below list in GUI
|
||||
SHOW_TOOLTIPS = True
|
||||
WARN_ABOUT_MISSING_STRINGS = False # (If debug mode is enabled)
|
||||
FALLBACK_INITIAL_LANGUAGE = "en"
|
||||
FALLBACK_PUBLIC_SYNCPLAY_SERVERS = [[u'syncplay.pl:8995 (France)', u'syncplay.pl:8995'],[u'syncplay.pl:8996 (France)', u'syncplay.pl:8996'],[u'syncplay.pl:8997 (France)', u'syncplay.pl:8997'],[u'syncplay.pl:8998 (France)', u'syncplay.pl:8998'],[u'syncplay.pl:8999 (France)', u'syncplay.pl:8999']]
|
||||
|
||||
#Overriden by config
|
||||
SHOW_OSD = True # Sends Syncplay messages to media player OSD
|
||||
@ -48,6 +49,11 @@ SERVER_STATE_INTERVAL = 1
|
||||
WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1
|
||||
AUTOPLAY_DELAY = 3.0
|
||||
SYNC_ON_PAUSE = True # Client seek to global position - subtitles may disappear on some media players
|
||||
|
||||
# Options for the File Switch feature:
|
||||
FOLDER_SEARCH_TIMEOUT = 60.0 # Secs - How long to wait until searches in folder to update cache are aborted (may be longer than this if hard drive needs to spin up)
|
||||
FOLDER_SEARCH_DOUBLE_CHECK_INTERVAL = 120.0 # Secs - Frequency of updating cache when someone is playing a file not in current cache
|
||||
|
||||
#Usually there's no need to adjust these
|
||||
LAST_PAUSED_DIFF_THRESHOLD = 2
|
||||
FILENAME_STRIP_REGEX = u"[-~_\.\[\](): ]"
|
||||
@ -63,8 +69,9 @@ COMMANDS_AUTH = ['a','auth']
|
||||
COMMANDS_TOGGLE = ['t','toggle']
|
||||
MPC_MIN_VER = "1.6.4"
|
||||
VLC_MIN_VERSION = "2.2.1"
|
||||
VLC_INTERFACE_MIN_VERSION = "0.2.6"
|
||||
VLC_INTERFACE_MIN_VERSION = "0.2.8"
|
||||
VLC_LATENCY_ERROR_THRESHOLD = 2.0
|
||||
MPV_UNRESPONSIVE_THRESHOLD = 60.0
|
||||
CONTROLLED_ROOMS_MIN_VERSION = "1.3.0"
|
||||
USER_READY_MIN_VERSION = "1.3.0"
|
||||
MPC_PATHS = [
|
||||
@ -131,10 +138,12 @@ STYLE_NOFILEITEM_COLOR = 'blue'
|
||||
STYLE_NOTCONTROLLER_COLOR = 'grey'
|
||||
|
||||
USERLIST_GUI_USERNAME_OFFSET = 21 # Pixels
|
||||
USERLIST_GUI_USERNAME_COLUMN = 0
|
||||
USERLIST_GUI_FILENAME_COLUMN = 3
|
||||
|
||||
MPLAYER_SLAVE_ARGS = ['-slave', '--hr-seek=always', '-nomsgcolor', '-msglevel', 'all=1:global=4:cplayer=4', '-af', 'scaletempo']
|
||||
# --quiet works with both mpv 0.2 and 0.3
|
||||
MPV_SLAVE_ARGS = ['--force-window', '--idle', '--hr-seek=always', '--quiet', '--keep-open', '--af=scaletempo', '--input-terminal=no', '--input-file=/dev/stdin']
|
||||
MPLAYER_SLAVE_ARGS = ['-slave', '--hr-seek=always', '-nomsgcolor', '-msglevel', 'all=1:global=4:cplayer=4', '-af-add', 'scaletempo']
|
||||
MPV_ARGS = ['--force-window', '--idle', '--hr-seek=always', '--keep-open', '--af-add=scaletempo']
|
||||
MPV_SLAVE_ARGS = ['--quiet', '--input-terminal=no', '--input-file=/dev/stdin']
|
||||
MPV_SLAVE_ARGS_NEW = ['--term-playing-msg=<SyncplayUpdateFile>\nANS_filename=${filename}\nANS_length=${=length}\nANS_path=${path}\n</SyncplayUpdateFile>', '--terminal=yes']
|
||||
MPV_NEW_VERSION = False
|
||||
VLC_SLAVE_ARGS = ['--extraintf=luaintf', '--lua-intf=syncplay', '--no-quiet', '--no-input-fast-seek',
|
||||
@ -163,6 +172,13 @@ CONFIG_NAME_MARKER = ":"
|
||||
CONFIG_VALUE_MARKER = "="
|
||||
USERITEM_CONTROLLER_ROLE = 0
|
||||
USERITEM_READY_ROLE = 1
|
||||
FILEITEM_SWITCH_ROLE = 1
|
||||
FILEITEM_SWITCH_NO_SWITCH = 0
|
||||
FILEITEM_SWITCH_FILE_SWITCH = 1
|
||||
FILEITEM_SWITCH_STREAM_SWITCH = 2
|
||||
|
||||
SYNCPLAY_UPDATE_URL = u"http://syncplay.pl/checkforupdate?{}" # Params
|
||||
SYNCPLAY_DOWNLOAD_URL = "http://syncplay.pl/download/"
|
||||
SYNCPLAY_DOWNLOAD_URL = "http://syncplay.pl/download/"
|
||||
SYNCPLAY_PUBLIC_SERVER_LIST_URL = u"http://syncplay.pl/listpublicservers?{}" # Params
|
||||
|
||||
PRIVATE_FILE_FIELDS = ["path"]
|
||||
@ -38,7 +38,7 @@ en = {
|
||||
"ready-to-unpause-notification" : u"You are now set as ready - unpause again to unpause",
|
||||
"set-as-ready-notification" : u"You are now set as ready",
|
||||
"set-as-not-ready-notification" : u"You are now set as not ready",
|
||||
"autoplaying-notification" : u"Auto-playing in {}...",
|
||||
"autoplaying-notification" : u"Auto-playing in {}...", # Number of seconds until playback will start
|
||||
|
||||
"identifying-as-controller-notification" : u"Identifying as room operator with password '{}'...",
|
||||
"failed-to-identify-as-controller-notification" : u"<{}> failed to identify as a room operator.",
|
||||
@ -94,6 +94,7 @@ en = {
|
||||
"vlc-interface-oldversion-warning": "Warning: Syncplay detected that an old version version of the Syncplay interface module for VLC was installed in the VLC directory. Please refer to the Syncplay User Guide at http://syncplay.pl/guide/ for instructions on how to install syncplay.lua.",
|
||||
"vlc-interface-not-installed": "Warning: The Syncplay interface module for VLC was not found in the VLC directory. As such, if you are running VLC 2.0 then VLC will use the syncplay.lua module contained within the Syncplay directory, but this will mean that other custom interface scripts and extensions will not work. Please refer to the Syncplay User Guide at http://syncplay.pl/guide/ for instructions on how to install syncplay.lua.",
|
||||
"media-player-latency-warning": u"Warning: The media player took {} seconds to respond. If you experience syncing issues then close applications to free up system resources, and if that doesn't work then try a different media player.", # Seconds to respond
|
||||
"mpv-unresponsive-error": u"mpv has not responded for {} seconds so appears to have malfunctioned. Please restart Syncplay.", # Seconds to respond
|
||||
|
||||
# Client prompts
|
||||
"enter-to-exit-prompt" : "Press enter to exit\n",
|
||||
@ -134,6 +135,11 @@ en = {
|
||||
"invalid-seek-value" : u"Invalid seek value",
|
||||
"invalid-offset-value" : u"Invalid offset value",
|
||||
|
||||
"switch-file-not-found-error" : u"Could not switch to file '{0}'. Syncplay looks in the folder of the currently playing file and specified media directories.", # File not found
|
||||
"folder-search-timeout-error" : u"The search for media in '{}' was aborted as it took too long. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. Until Syncplay is restarted only the directory of the currently open file will be checked.", #Folder
|
||||
|
||||
"failed-to-load-server-list-error" : u"Failed to load public server list. Please visit http://www.syncplay.pl/ in your browser.",
|
||||
|
||||
# Client arguments
|
||||
"argument-description" : 'Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network.',
|
||||
"argument-epilog" : 'If no options supplied _config values will be used',
|
||||
@ -166,7 +172,9 @@ en = {
|
||||
"media-setting-title" : "Media player settings",
|
||||
"executable-path-label" : "Path to media player:",
|
||||
"media-path-label" : "Path to media file:",
|
||||
"player-arguments-label" : "Player arguments (if any):",
|
||||
"browse-label" : "Browse",
|
||||
"update-server-list-label" : u"Update list",
|
||||
|
||||
"more-title" : "Show more settings",
|
||||
"never-rewind-value" : "Never",
|
||||
@ -200,6 +208,7 @@ en = {
|
||||
"misc-label" : u"Misc",
|
||||
"core-behaviour-title" : "Core room behaviour",
|
||||
"syncplay-internals-title" : u"Syncplay internals",
|
||||
"syncplay-mediasearchdirectories-title" : u"Directories to search for media (one path per line)",
|
||||
"sync-label" : "Sync",
|
||||
"sync-otherslagging-title" : "If others are lagging behind...",
|
||||
"sync-youlaggging-title" : "If you are lagging behind...",
|
||||
@ -270,6 +279,8 @@ en = {
|
||||
"identifyascontroller-msgbox-label" : "Identify as room operator",
|
||||
"identifyinfo-msgbox-label" : "Enter operator password for this room\r\n(see http://syncplay.pl/guide/ for usage instructions):",
|
||||
|
||||
"public-server-msgbox-label" : u"Select the public server for this viewing session",
|
||||
|
||||
"megabyte-suffix" : " MB",
|
||||
|
||||
# Tooltips
|
||||
@ -281,6 +292,8 @@ en = {
|
||||
|
||||
"executable-path-tooltip" : "Location of your chosen supported media player (MPC-HC, VLC, mplayer2 or mpv).",
|
||||
"media-path-tooltip" : "Location of video or stream to be opened. Necessary for mpv and mplayer2.",
|
||||
"player-arguments-tooltip" : "Additional command line arguments / switches to pass on to this media player.",
|
||||
"mediasearcdirectories-arguments-tooltip" : u"Directories where Syncplay will search for media files, e.g. when you are using the click to switch feature. Syncplay will look recursively through sub-folders.",
|
||||
|
||||
"more-tooltip" : "Display less frequently used settings.",
|
||||
"filename-privacy-tooltip" : "Privacy mode for sending currently playing filename to server.",
|
||||
@ -302,7 +315,7 @@ en = {
|
||||
"showsameroomosd-tooltip" : "Show OSD notifications for events relating to room user is in.",
|
||||
"shownoncontrollerosd-tooltip" : "Show OSD notifications for events relating to non-operators who are in managed rooms.",
|
||||
"showdifferentroomosd-tooltip" : "Show OSD notifications for events relating to room user is not in.",
|
||||
"showslowdownosd-tooltip" :"Show notifications of slowing down / reverting on time difference.",
|
||||
"showslowdownosd-tooltip" : "Show notifications of slowing down / reverting on time difference.",
|
||||
"showdurationnotification-tooltip" : "Useful for when a segment in a multi-part file is missing, but can result in false positives.",
|
||||
"language-tooltip" : u"Language to be used by Syncplay.",
|
||||
"unpause-always-tooltip" : u"If you press unpause it always sets you as ready and unpause, rather than just setting you as ready.",
|
||||
@ -312,11 +325,13 @@ en = {
|
||||
|
||||
"help-tooltip" : "Opens the Syncplay.pl user guide.",
|
||||
"reset-tooltip" : "Reset all settings to the default configuration.",
|
||||
"update-server-list-tooltip" : u"Connect to syncplay.pl to update list of public servers.",
|
||||
|
||||
"joinroom-tooltip" : "Leave current room and joins specified room.",
|
||||
"seektime-msgbox-label" : "Jump to specified time (in seconds / min:sec). Use +/- for relative seek.",
|
||||
"ready-tooltip" : "Indicates whether you are ready to watch.",
|
||||
"autoplay-tooltip" : "Auto-play when all users who have readiness indicator are ready and minimum user threshold met.",
|
||||
"switch-to-file-tooltip" : "Double click to switch to {}", # Filename
|
||||
|
||||
# In-userlist notes (GUI)
|
||||
"differentsize-note" : "Different size!",
|
||||
@ -372,7 +387,7 @@ ru = {
|
||||
"retrying-notification" : u"%s, следующая попытка через %d секунд(ы)...", # Seconds
|
||||
|
||||
"rewind-notification" : u"Перемотано из-за разницы во времени с <{}>", # User
|
||||
"fastforward-notification" : u"Fast-forwarded due to time difference with <{}>", # User # TODO: Translate into Russian
|
||||
"fastforward-notification" : u"Ускорено из-за разницы во времени с <{}>", # User
|
||||
"slowdown-notification" : u"Воспроизведение замедлено из-за разницы во времени с <{}>", # User
|
||||
"revert-notification" : u"Возвращаемся к нормальной скорости воспроизведения",
|
||||
|
||||
@ -388,39 +403,39 @@ ru = {
|
||||
"playing-notification" : u"<{}> включил '{}' ({})", # User, file, duration
|
||||
"playing-notification/room-addendum" : u" в комнате: '{}'", # Room
|
||||
|
||||
"not-all-ready" : u"Not ready: {}", # Usernames # TODO: Translate into Russian
|
||||
"all-users-ready" : u"Everyone is ready ({} users)", #Number of ready users # TODO: Translate into Russian
|
||||
"ready-to-unpause-notification" : u"You are now set as ready - unpause again to unpause", # TODO: Translate into Russian
|
||||
"set-as-ready-notification" : u"You are now set as ready", # TODO: Translate into Russian
|
||||
"set-as-not-ready-notification" : u"You are now set as not ready", # TODO: Translate into Russian
|
||||
"autoplaying-notification" : u"Auto-playing in {}...", # TODO: Translate into Russian
|
||||
"not-all-ready" : u"Не готовы: {}", # Usernames
|
||||
"all-users-ready" : u"Все пользователи готовы ({} чел.)", #Number of ready users
|
||||
"ready-to-unpause-notification" : u"Вы помечены как готовый - нажмите еще раз, чтобы продолжить воспроизведение",
|
||||
"set-as-ready-notification" : u"Вы помечены как готовый",
|
||||
"set-as-not-ready-notification" : u"Вы помечены как неготовый",
|
||||
"autoplaying-notification" : u"Автовоспроизведение через {}...", # Number of seconds until playback will start
|
||||
|
||||
"identifying-as-controller-notification" : u"Identifying as room operator with password '{}'...", # TODO: Translate into Russian
|
||||
"failed-to-identify-as-controller-notification" : u"<{}> failed to identify as a room operator.", # TODO: Translate into Russian
|
||||
"authenticated-as-controller-notification" : u"<{}> authenticated as a room operator", # TODO: Translate into Russian
|
||||
"created-controlled-room-notification" : u"Created managed room '{}' with password '{}'. Please save this information for future reference!", # RoomName, operatorPassword # TODO: Translate into Russian
|
||||
"identifying-as-controller-notification" : u"Идентификация как оператора комнаты с паролем '{}'...",
|
||||
"failed-to-identify-as-controller-notification" : u"<{}> не прошел идентификацию в качестве оператора комнаты.",
|
||||
"authenticated-as-controller-notification" : u"<{}> вошел как оператор комнаты.",
|
||||
"created-controlled-room-notification" : u"Создана управляемая комната '{}' с паролем '{}'. Сохраните эти данные!", # RoomName, operatorPassword
|
||||
|
||||
"file-different-notification" : u"Вероятно, файл, который Вы смотрите, отличается от того, который смотрит <{}>.", # User
|
||||
"file-differences-notification" : u"Ваш файл отличается: {}", # Differences
|
||||
"room-file-differences" : u"File differences: {}", # File differences (filename, size, and/or duration)
|
||||
"file-difference-filename" : u"name", # TODO: Translate into Russian
|
||||
"file-difference-filesize" : u"size", # TODO: Translate into Russian
|
||||
"file-difference-duration" : u"duration", # TODO: Translate into Russian
|
||||
"room-file-differences" : u"Несовпадения файла: {}", # File differences (filename, size, and/or duration)
|
||||
"file-difference-filename" : u"имя",
|
||||
"file-difference-filesize" : u"размер",
|
||||
"file-difference-duration" : u"длительность",
|
||||
"alone-in-the-room" : u"В этой комнате кроме Вас никого нет.",
|
||||
|
||||
"different-filesize-notification" : u" (размер Вашего файла не совпадает с размером их файла!)",
|
||||
"userlist-playing-notification" : u"{} is playing:", #Username # TODO: Translate into Russian (same as playing-notification?)
|
||||
"userlist-playing-notification" : u"{} смотрит:", #Username
|
||||
"file-played-by-notification" : u"Файл: {} просматривают:", # File
|
||||
"no-file-played-notification" : u"{} is not playing a file", # Username # TODO: Translate into Russian
|
||||
"no-file-played-notification" : u"{} не смотрит ничего", # Username
|
||||
"notplaying-notification" : u"Люди, которые не смотрят ничего:",
|
||||
"userlist-room-notification" : u"В комнате '{}':", # Room
|
||||
"userlist-file-notification" : u"File", # TODO: Translate into Russian (Файл?)
|
||||
"controller-userlist-userflag" : u"Operator", # TODO: Translate into Russian (this is to indicate a user is an operator in the ConsoleUI userlist)
|
||||
"ready-userlist-userflag" : u"Ready", # TODO: Translate into Russian (this is to indicate a user is ready to watch in the ConsoleUI userlist)
|
||||
"userlist-file-notification" : u"Файл",
|
||||
"controller-userlist-userflag" : u"Оператор",
|
||||
"ready-userlist-userflag" : u"Готов",
|
||||
|
||||
"update-check-failed-notification" : u"Could not automatically check whether Syncplay {} is up to date. Want to visit http://syncplay.pl/ to manually check for updates?", #Syncplay version # TODO: Translate into Russian
|
||||
"syncplay-uptodate-notification" : u"Syncplay is up to date", # TODO: Translate into Russian
|
||||
"syncplay-updateavailable-notification" : u"A new version of Syncplay is available. Do you want to visit the release page?", # TODO: Translate into Russian
|
||||
"update-check-failed-notification" : u"Невозможно автоматически проверить, что версия Syncplay {} все еще актуальна. Хотите зайти на http://syncplay.pl/ и вручную проверить наличие обновлений?",
|
||||
"syncplay-uptodate-notification" : u"Syncplay обновлен",
|
||||
"syncplay-updateavailable-notification" : u"Доступна новая версия Syncplay. Хотите открыть страницу релиза?",
|
||||
|
||||
"mplayer-file-required-notification" : u"Для использования Syncplay с mplayer необходимо передать файл в качестве параметра",
|
||||
"mplayer-file-required-notification/example" : u"Пример использования: syncplay [options] [url|path/]filename",
|
||||
@ -434,21 +449,22 @@ ru = {
|
||||
"commandlist-notification/pause" : u"\tp - вкл./выкл. паузу",
|
||||
"commandlist-notification/seek" : u"\t[s][+-]time - перемотать к заданному моменту времени, если не указан + или -, то время считается абсолютным (от начала файла) в секундах или мин:сек",
|
||||
"commandlist-notification/help" : u"\th - помощь",
|
||||
"commandlist-notification/toggle" : u"\tt - toggles whether you are ready to watch or not", # TODO: Translate into Russian
|
||||
"commandlist-notification/create" : u"\tc [name] - create managed room using name of current room", # TODO: Translate into Russian
|
||||
"commandlist-notification/auth" : u"\ta [password] - authenticate as room operator with operator password", # TODO: Translate into Russian
|
||||
"commandlist-notification/toggle" : u"\tt - переключить статус готов/неготов к просмотру",
|
||||
"commandlist-notification/create" : u"\tc [name] - создать управляемую комнату с таким же именем, как у текущей",
|
||||
"commandlist-notification/auth" : u"\ta [password] - авторизоваться как оператор комнаты с помощью пароля",
|
||||
"syncplay-version-notification" : u"Версия Syncplay: {}", # syncplay.version
|
||||
"more-info-notification" : u"Больше информации на {}", # projectURL
|
||||
|
||||
"gui-data-cleared-notification" : u"Syncplay очистил путь и информацию о состоянии окна, использованного GUI.",
|
||||
"language-changed-msgbox-label" : u"Language will be changed when you run Syncplay.", # TODO: Translate into Russian
|
||||
"promptforupdate-label" : u"Is it okay for Syncplay to automatically check for updates from time to time?", # TODO: Translate into Russian
|
||||
"language-changed-msgbox-label" : u"Язык переключится при следующем запуске SYncplay.",
|
||||
"promptforupdate-label" : u"Вы не против, если Syncplay будет автоматически изредка проверять наличие обновлений?",
|
||||
|
||||
"vlc-version-mismatch" : u"Внимание: Вы используете VLC устаревшей версии {}. К сожалению, Syncplay способен работать с VLC {} и выше.", # VLC version, VLC min version
|
||||
"vlc-interface-version-mismatch" : u"Внимание: В используете модуль интерфейса Syncplay устаревшей версии {} для VLC. К сожалению, Syncplay способен работать с версией {} и выше.", # VLC interface version, VLC interface min version
|
||||
"vlc-interface-oldversion-warning" : u"Внимание: Syncplay обнаружил, что старая версия модуля интерфейса Syncplay для VLC уже установлена в директорию VLC. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (http://syncplay.pl/guide/) за инструкциями о том, как установить syncplay.lua.",
|
||||
"vlc-interface-not-installed" : u"Внимание: Модуль интерфейса Syncplay для VLC не обнаружен в директории VLC. По существу, если Вы используете VLC 2.0, то VLC будет использовать модуль syncplay.lua из директории Syncplay, но в таком случае другие пользовательские скрипты и расширения интерфейса не будут работать. Пожалуйста, обратитесь к Руководству Пользователя Syncplay (http://syncplay.pl/guide/) за инструкциями о том, как установить syncplay.lua.",
|
||||
"media-player-latency-warning": u"Warning: The media player took {} seconds to respond. If you experience syncing issues then close applications to free up system resources, and if that doesn't work then try a different media player.", # Seconds to respond # TODO: Translate into Russian
|
||||
"media-player-latency-warning": u"Внимание: У Вашего проигрывателя слишком большой отклик ({} секунд). Если Вы замечаете проблемы с синхронизацией, то закройте ресурсоемкие приложения, а если это не помогло - попробуйте другой проигрыватель.", # Seconds to respond
|
||||
"mpv-unresponsive-error": u"mpv has not responded for {} seconds so appears to have malfunctioned. Please restart Syncplay.", # Seconds to respond # TODO: Translate to Russian
|
||||
|
||||
# Client prompts
|
||||
"enter-to-exit-prompt" : u"Для выхода нажмите Enter\n",
|
||||
@ -463,18 +479,18 @@ ru = {
|
||||
"player-path-error" : u"Путь к проигрывателю задан неверно.",
|
||||
"hostname-empty-error" : u"Имя пользователя не может быть пустым.",
|
||||
"empty-error" : u"{} не может быть пустым.", # Configuration
|
||||
"media-player-error" : u"Ошибка Media player: \"{}\"", # Error line
|
||||
"media-player-error" : u"Ошибка проигрывателя: \"{}\"", # Error line
|
||||
"unable-import-gui-error" : u"Невозможно импортировать библиотеки GUI (графического интерфейса). Необходимо установить PySide, иначе графический интерфейс не будет работать.",
|
||||
|
||||
"arguments-missing-error" : u"Некоторые необходимые аргументы отсутствуют, обратитесь к --help",
|
||||
|
||||
"unable-to-start-client-error" : u"Невозможно запустить клиент",
|
||||
|
||||
"player-path-config-error": u"Player path is not set properly", # TODO: Translate into Russian
|
||||
"no-file-path-config-error" : u"File must be selected before starting your player", # TODO: Translate into Russian
|
||||
"no-hostname-config-error": u"Hostname can't be empty", # TODO: Translate into Russian
|
||||
"invalid-port-config-error" : u"Port must be valid", # TODO: Translate into Russian
|
||||
"empty-value-config-error" : u"{} can't be empty", # Config option # TODO: Translate into Russian
|
||||
"player-path-config-error": u"Путь к проигрывателю установлен неверно",
|
||||
"no-file-path-config-error" : u"Файл должен быть указан до включения проигрывателя",
|
||||
"no-hostname-config-error": u"Имя сервера не может быть пустым",
|
||||
"invalid-port-config-error" : u"Неверный номер порта",
|
||||
"empty-value-config-error" : u"Поле '{}' не может быть пустым", # Config option
|
||||
|
||||
"not-json-error" : u"Не является закодированной json-строкой\n",
|
||||
"hello-arguments-error" : u"Не хватает аргументов Hello\n",
|
||||
@ -484,10 +500,15 @@ ru = {
|
||||
"vlc-failed-versioncheck" : u"Данная версия VLC не поддерживается Syncplay. Пожалуйста, используйте VLC версии 2 или выше.",
|
||||
"vlc-failed-other" : u"Во время загрузки скрипта интерфейса syncplay.lua в VLC произошла следующая ошибка: {}", # Syncplay Error
|
||||
|
||||
"not-supported-by-server-error" : "This feature is not supported by the server. The feature requires a server running Syncplay {}+, but the server is running Syncplay {}.", #minVersion, serverVersion #TODO: Translate into Russian
|
||||
"not-supported-by-server-error" : u"Эта возможность не поддерживается сервером. The feature requires a server running Syncplay {}+, but the server is running Syncplay {}.", #minVersion, serverVersion #TODO: Translate into Russian
|
||||
|
||||
"invalid-seek-value" : u"Invalid seek value", # TODO: Translate into Russian
|
||||
"invalid-offset-value" : u"Invalid offset value", # TODO: Translate into Russian
|
||||
"invalid-seek-value" : u"Некорректное значение для перемотки",
|
||||
"invalid-offset-value" : u"Некорректное смещение",
|
||||
|
||||
"switch-file-not-found-error" : u"Невозможно переключиться на файл '{0}'. Syncplay looks in the folder of the currently playing file and specified media directories.", # File not found # TODO: Translate last part into Russian
|
||||
"folder-search-timeout-error" : u"The search for media in '{}' was aborted as it took too long. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. Until Syncplay is restarted only the directory of the currently open file will be checked.", #Folder # TODO: Translate into Russian
|
||||
|
||||
"failed-to-load-server-list-error" : u"Failed to load public server list. Please visit http://www.syncplay.pl/ in your browser.", # TODO: Translate into Russian
|
||||
|
||||
# Client arguments
|
||||
"argument-description" : u'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC через Интернет.',
|
||||
@ -521,7 +542,9 @@ ru = {
|
||||
"media-setting-title" : u"Параметры проигрывателя",
|
||||
"executable-path-label" : u"Путь к проигрывателю:",
|
||||
"media-path-label" : u"Путь к видеофайлу:",
|
||||
"player-arguments-label" : u"Аргументы для запуска проигрывателя:",
|
||||
"browse-label" : u"Выбрать",
|
||||
"update-server-list-label" : u"Update list", # TODO: Translate into Russian
|
||||
|
||||
"more-title" : u"Больше настроек",
|
||||
"never-rewind-value" : u"Никогда",
|
||||
@ -531,38 +554,39 @@ ru = {
|
||||
"privacy-dontsend-option" : u"не отпр.",
|
||||
"filename-privacy-label" : u"Имя файла:",
|
||||
"filesize-privacy-label" : u"Размер файла:",
|
||||
"checkforupdatesautomatically-label" : u"Проверять обновления автоматически", # TODO: Confirm Russian translation
|
||||
"checkforupdatesautomatically-label" : u"Проверять обновления автоматически",
|
||||
"slowondesync-label" : u"Замедлять при небольших рассинхронизациях (не поддерживаетя в MPC-HC)",
|
||||
"rewindondesync-label" : u"Перемотка при больших рассинхронизациях (настоятельно рекомендуется)",
|
||||
"dontslowdownwithme-label" : u"Никогда не замедлять или перематывать видео другим", # TODO: Update new wording into Russian (should state "Experimental" in brackets at the end)
|
||||
"dontslowdownwithme-label" : u"Никогда не замедлять и не перематывать видео другим (функция тестируется)",
|
||||
"pauseonleave-label" : u"Приостанавливать, когда кто-то уходит (например, отключился)",
|
||||
"readyatstart-label" : u"Set me as 'ready to watch' by default", # TODO: Translate into Russian
|
||||
"fastforwardondesync-label" : u"Fast-forward if lagging behind (recommended)", # TODO: Translate into Russian
|
||||
"readyatstart-label" : u"Выставить статус 'готово к просмотру' по умолчанию",
|
||||
"fastforwardondesync-label" : u"Ускорять видео при отставании (рекомендуется)",
|
||||
"forceguiprompt-label" : u"Не показывать больше этот диалог", # (Inverted)
|
||||
"nostore-label" : u"Не сохранять текущую конфигурацию", # (Inverted)
|
||||
"showosd-label" : u"Включить экранные сообщения (поверх видео)",
|
||||
|
||||
"showosdwarnings-label" : u"Показывать предупреждения (напр., когда файлы не совпадают)",
|
||||
"showsameroomosd-label" : u"Показывать события Вашей комнаты",
|
||||
"shownoncontrollerosd-label" : u"Include events from non-operators in managed rooms", # TODO: Translate into Russiann
|
||||
"shownoncontrollerosd-label" : u"Включить события, связанные с не-операторами в управляемой комнате.",
|
||||
"showdifferentroomosd-label" : u"Показывать события других комнат",
|
||||
"showslowdownosd-label" : u"Показывать уведомления о замедлении/перемотке",
|
||||
"language-label" : u"Language:", # TODO: Translate into Russian
|
||||
"automatic-language" : u"Default ({})", # Automatic language # TODO: Translate into Russian
|
||||
"language-label" : u"Язык:",
|
||||
"automatic-language" : u"По умолчанию ({})", # Automatic language
|
||||
"showdurationnotification-label" : u"Предупреждать о несовпадении продолжительности видео",
|
||||
"basics-label" : u"Основное",
|
||||
"readiness-label" : u"Play/Pause", # TODO: Translate into Russian
|
||||
"misc-label" : u"Misc", # TODO: Translate into Russian
|
||||
"core-behaviour-title" : "Core room behaviour", # TODO: Translate into Russian
|
||||
"readiness-label" : u"Воспроизведение/Пауза", # TODO: Confirm translation of play/pause
|
||||
"misc-label" : u"Прочее",
|
||||
"core-behaviour-title" : u"Core room behaviour", # TODO: Translate into Russian
|
||||
"syncplay-internals-title" : u"Syncplay internals", # TODO: Translate into Russian
|
||||
"syncplay-mediasearchdirectories-title" : u"Directories to search for media (one path per line)", # TODO: Translate into Russian
|
||||
"sync-label" : u"Синхронизация",
|
||||
"sync-otherslagging-title" : u"If others are lagging behind...", # TODO: Translate into Russian
|
||||
"sync-youlaggging-title" : u"If you are lagging behind...", # TODO: Translate into Russian
|
||||
"sync-otherslagging-title" : u"При отставании других зрителей...",
|
||||
"sync-youlaggging-title" : u"Когда я отстаю ...",
|
||||
"messages-label" : u"Сообщения",
|
||||
"messages-osd-title" : u"On-screen Display settings", # TODO: Translate into Russian
|
||||
"messages-other-title" : u"Other display settings", # TODO: Translate into Russian
|
||||
"messages-osd-title" : u"Настройки OSD",
|
||||
"messages-other-title" : u"Другие настройки отображения",
|
||||
"privacy-label" : u"Приватность",
|
||||
"privacy-title" : u"Privacy settings", # TODO: Translate into Russian
|
||||
"privacy-title" : u"Настройки приватности",
|
||||
"unpause-title" : u"If you press play, set as ready and:", # TODO: Translate into Russian
|
||||
"unpause-ifalreadyready-option" : u"Unpause if already set as ready", # TODO: Translate into Russian
|
||||
"unpause-ifothersready-option" : u"Unpause if already ready or others in room are ready (default)", # TODO: Translate into Russian
|
||||
@ -581,17 +605,17 @@ ru = {
|
||||
"undoseek-menu-label" : u"Отменить перемотку",
|
||||
"play-menu-label" : u"Play",
|
||||
"pause-menu-label" : u"Пауза",
|
||||
"playbackbuttons-menu-label" : u"Show playback buttons", # TODO: Translate into Russian
|
||||
"autoplay-menu-label" : u"Show auto-play button", # TODO: Translate into Russian
|
||||
"autoplay-guipushbuttonlabel" : u"Auto-play when everyone is ready", # TODO: Translate into Russian
|
||||
"autoplay-minimum-label" : u"Min users:", # TODO: Translate into Russian
|
||||
"playbackbuttons-menu-label" : u"Показывать кнопки воспроизведения",
|
||||
"autoplay-menu-label" : u"Показывать кнопку автовоспроизведения",
|
||||
"autoplay-guipushbuttonlabel" : u"Воспроизвести автоматически, когда все будут готовы",
|
||||
"autoplay-minimum-label" : u"Минимум пользователей:",
|
||||
|
||||
"ready-guipushbuttonlabel" : u"I'm ready to watch!", # TODO: Translate into Russian
|
||||
"ready-guipushbuttonlabel" : u"Я готов к просмотру!",
|
||||
|
||||
"roomuser-heading-label" : u"Комната / Пользователь",
|
||||
"size-heading-label" : u"Size", # TODO: Translate into Russian
|
||||
"duration-heading-label" : u"Length", # TODO: Translate into Russian
|
||||
"filename-heading-label" : u"Filename", # TODO: Translate into Russian
|
||||
"size-heading-label" : u"Размер",
|
||||
"duration-heading-label" : u"Длительность",
|
||||
"filename-heading-label" : u"Имя файла",
|
||||
"notifications-heading-label" : u"Уведомления",
|
||||
"userlist-heading-label" : u"Кто что смотрит",
|
||||
|
||||
@ -599,33 +623,35 @@ ru = {
|
||||
|
||||
"file-menu-label" : u"&Файл", # & precedes shortcut key
|
||||
"openmedia-menu-label" : u"&Открыть видеофайл",
|
||||
"openstreamurl-menu-label" : u"Open &media stream URL", # TODO: Translate into Russian
|
||||
"openstreamurl-menu-label" : u"Открыть URL &потокового вещания",
|
||||
"exit-menu-label" : u"&Выход",
|
||||
"advanced-menu-label" : u"&Дополнительно",
|
||||
"window-menu-label" : u"&Window", # TODO: Translate into Russian
|
||||
"window-menu-label" : u"&Окна",
|
||||
"setoffset-menu-label" : u"Установить &смещение",
|
||||
"createcontrolledroom-menu-label" : u"&Create managed room", # TODO: Translate into Russian
|
||||
"identifyascontroller-menu-label" : u"&Identify as room operator", # TODO: Translate into Russian
|
||||
"createcontrolledroom-menu-label" : u"&Создать управляемую комнату",
|
||||
"identifyascontroller-menu-label" : u"&Войти как оператор комнаты",
|
||||
|
||||
"playback-menu-label" : u"&Playback", # TODO: Translate into Russian
|
||||
"playback-menu-label" : u"&Воспроизведение",
|
||||
|
||||
"help-menu-label" : u"&Помощь",
|
||||
"userguide-menu-label" : u"&Руководство Пользователя",
|
||||
"update-menu-label" : u"Check for &update", # TODO: Translate into Russian
|
||||
"update-menu-label" : u"Проверить &обновления",
|
||||
|
||||
"setoffset-msgbox-label" : u"Установить смещение",
|
||||
"offsetinfo-msgbox-label" : u"Смещение (см. как использовать на http://syncplay.pl/guide/):",
|
||||
"offsetinfo-msgbox-label" : u"Смещение (см. инструкцию на странице http://syncplay.pl/guide/):",
|
||||
|
||||
"promptforstreamurl-msgbox-label" : u"Open media stream URL", # TODO: Translate into Russian
|
||||
"promptforstreamurlinfo-msgbox-label" : u"Stream URL", # TODO: Translate into Russian
|
||||
"promptforstreamurl-msgbox-label" : u"Открыть URL потокового вещания",
|
||||
"promptforstreamurlinfo-msgbox-label" : u"URL потока",
|
||||
|
||||
"createcontrolledroom-msgbox-label" : u"Create managed room", # TODO: Translate into Russian
|
||||
"controlledroominfo-msgbox-label" : u"Enter name of managed room\r\n(see http://syncplay.pl/guide/ for usage instructions):", # TODO: Translate into Russian
|
||||
"createcontrolledroom-msgbox-label" : u"Создать управляемую комнату",
|
||||
"controlledroominfo-msgbox-label" : u"Введите имя управляемой комнаты\r\n(см. инструкцию на странице http://syncplay.pl/guide/):",
|
||||
|
||||
"identifyascontroller-msgbox-label" : u"Identify as room operator", # TODO: Translate into Russian
|
||||
"identifyinfo-msgbox-label" : u"Enter operator password for this room\r\n(see http://syncplay.pl/guide/ for usage instructions):", # TODO: Translate into Russian
|
||||
"identifyascontroller-msgbox-label" : u"Войти как оператор комнаты",
|
||||
"identifyinfo-msgbox-label" : u"Введите пароль оператора комнаты\r\n(см. инструкцию на странице http://syncplay.pl/guide/):",
|
||||
|
||||
"megabyte-suffix" : u" MB", # Technically it is a mebibyte # TODO: Translate into Russian
|
||||
"public-server-msgbox-label" : u"Select the public server for this viewing session", # TODO: Translate into Russian
|
||||
|
||||
"megabyte-suffix" : u" МБ", # Technically it is a mebibyte
|
||||
|
||||
# Tooltips
|
||||
|
||||
@ -636,6 +662,8 @@ ru = {
|
||||
|
||||
"executable-path-tooltip" : u"Расположение Вашего видеопроигрывателя (MPC-HC, VLC, mplayer2 или mpv).",
|
||||
"media-path-tooltip" : u"Расположение видеофайла или потока для просмотра. Обязательно для mpv и mplayer2.",
|
||||
"player-arguments-tooltip" : u"Передавать дополнительные аргументы командной строки этому проигрывателю.",
|
||||
"mediasearcdirectories-arguments-tooltip" : u"Directories where Syncplay will search for media files, e.g. when you are using the click to switch feature. Syncplay will look recursively through sub-folders.", # TODO: Translate into Russian
|
||||
|
||||
"more-tooltip" : u"Показать дополнительные настройки.",
|
||||
"filename-privacy-tooltip" : u"Режим приватности для передачи имени воспроизводимого файла на сервер.",
|
||||
@ -643,23 +671,23 @@ ru = {
|
||||
"privacy-sendraw-tooltip" : u"Отправляет эту информацию без шифрования. Рекомендуемая опция с наибольшей функциональностью.",
|
||||
"privacy-sendhashed-tooltip" : u"Отправляет хэш-сумму этой информации, делая ее невидимой для других пользователей.",
|
||||
"privacy-dontsend-tooltip" : u"Не отправлять эту информацию на сервер. Предоставляет наибольшую приватность.",
|
||||
"checkforupdatesautomatically-tooltip" : u"Regularly check with the Syncplay website to see whether a new version of Syncplay is available.", # TODO: Translate into Russian
|
||||
"checkforupdatesautomatically-tooltip" : u"Syncplay будет регулярно заходить на сайт и проверять наличие новых версий.",
|
||||
"slowondesync-tooltip" : u"Временно уменьшить скорость воспроизведения в целях синхронизации с другими зрителями. Не поддерживается в MPC-HC.",
|
||||
"dontslowdownwithme-tooltip" : u"Ваши лаги не будут влиять на других зрителей.",
|
||||
"pauseonleave-tooltip" : u"Приостановить воспроизведение, если Вы покинули комнату или кто-то из зрителей отключился от сервера.",
|
||||
"readyatstart-tooltip" : u"Set yourself as 'ready' at start (otherwise you are set as 'not ready' until you change your readiness state)", # TODO: Translate into Russian
|
||||
"readyatstart-tooltip" : u"Отметить Вас готовым к просмотру сразу же (по умолчанию Вы отмечены не готовым)",
|
||||
"forceguiprompt-tooltip" : u"Окно настройки не будет отображаться при открытии файла в Syncplay.", # (Inverted)
|
||||
"nostore-tooltip" : u"Запустить Syncplay с данной конфигурацией, но не сохранять изменения навсегда.", # (Inverted)
|
||||
"rewindondesync-tooltip" : u"Перематывать назад, когда это необходимо для синхронизации. Отключение этой опции может привести к большим рассинхронизациям!",
|
||||
"fastforwardondesync-tooltip" : u"Jump forward when out of sync with room operator (or your pretend position if 'Never slow down or rewind others' enabled).", # TODO: Translate into Russian
|
||||
"fastforwardondesync-tooltip" : u"Перематывать вперед при рассинхронизации с оператором комнаты (или если включена опция 'Никогда не замедлять и не перематывать видео другим').",
|
||||
"showosd-tooltip" : u"Отправлять сообщения Syncplay в видеопроигрыватель и отображать их поверх видео (OSD - On Screen Display).",
|
||||
"showosdwarnings-tooltip" : u"Показывать OSC-предупреждения, если проигрываются разные файлы или если Вы в комнате больше никого нет.",
|
||||
"showsameroomosd-tooltip" : u"Показывать OSD-уведомления о событиях, относящихся к комнате, в которой Вы находитесь.",
|
||||
"shownoncontrollerosd-tooltip" : u"Show OSD notifications for events relating to non-operators who are in controllerd rooms.", # TODO: Translate into Russian
|
||||
"shownoncontrollerosd-tooltip" : u"Показывать OSD-уведомления о событиях, относящихся к не-операторам в управляемой комнате.",
|
||||
"showdifferentroomosd-tooltip" : u"Показывать OSD-уведомления о событиях, относящихся к любым другим комнатам.",
|
||||
"showslowdownosd-tooltip" : u"Показывать уведомления о замедлении или перемотке в целях синхронизации.",
|
||||
"showdurationnotification-tooltip" : u"Полезно, когда сегмент составного файла отсутствует. Возможны ложные срабатывания.",
|
||||
"language-tooltip" : u"Language to be used by Syncplay.", # TODO: Translate into Russian
|
||||
"language-tooltip" : u"Язык, используемый Syncplay.",
|
||||
"unpause-always-tooltip" : u"If you press unpause it always sets you as ready and unpause, rather than just setting you as ready.", # TODO: Translate into Russian
|
||||
"unpause-ifalreadyready-tooltip" : u"If you press unpause when not ready it will set you as ready - press unpause again to unpause.", # TODO: Translate into Russian
|
||||
"unpause-ifothersready-tooltip" : u"If you press unpause when not ready, it will only upause if others are ready.", # TODO: Translate into Russian
|
||||
@ -667,11 +695,13 @@ ru = {
|
||||
|
||||
"help-tooltip" : u"Открыть Руководство Пользователя на Syncplay.pl.",
|
||||
"reset-tooltip" : u"Сбрасывает все настройки Syncplay в начальное состояние.",
|
||||
"update-server-list-tooltip" : u"Connect to syncplay.pl to update list of public servers.", # TODO: Translate to Russian
|
||||
|
||||
"joinroom-tooltip" : u"Покинуть комнату и зайти в другую, указанную комнату.",
|
||||
"seektime-msgbox-label" : u"Перемотать к определенному моменту времени (указывать в секундах или мин:сек). Используйте +/-, чтобы перемотать вперед/назад относительно настоящего момента.",
|
||||
"ready-tooltip" : "Indicates whether you are ready to watch.", # TODO: Translate into Russian
|
||||
"autoplay-tooltip" : "Auto-play when all users who have readiness indicator are ready and minimum user threshold met.", # TODO: Translate into Russian
|
||||
"ready-tooltip" : u"Показывает, готовы ли Вы к просмотру или нет.",
|
||||
"autoplay-tooltip" : u"Автоматическое воспроизведение, когда все пользователи с индикаторами готовности будут готовы и присутствует достаточное число пользователей.",
|
||||
"switch-to-file-tooltip" : u"Double click to switch to {}", # Filename # TODO: Translate to Russian
|
||||
|
||||
# In-userlist notes (GUI)
|
||||
"differentsize-note" : u"Размер файла не совпадает!",
|
||||
@ -683,10 +713,10 @@ ru = {
|
||||
"new-syncplay-available-motd-message" : u"<NOTICE> Вы используете Syncplay версии {}. Доступна более новая версия на http://syncplay.pl/ . </NOTICE>", # ClientVersion
|
||||
|
||||
# Server notifications
|
||||
"welcome-server-notification" : u"Welcome to Syncplay server, ver. {0}", # version # TODO: Translate into Russian
|
||||
"client-connected-room-server-notification" : u"{0}({2}) connected to room '{1}'", # username, host, room # TODO: Translate into Russian
|
||||
"client-left-server-notification" : u"{0} left server", # name # TODO: Translate into Russian
|
||||
"no-salt-notification" : u"PLEASE NOTE: To allow room operator passwords generated by this server instance to still work when the server is restarted, please add the following command line argument when running the Syncplay server in the future: --salt {}", #Salt # TODO: Translate into Russian
|
||||
"welcome-server-notification" : u"Добро пожаловать на сервер Syncplay версии {0}", # version
|
||||
"client-connected-room-server-notification" : u"{0}({2}) подключился(-лась) к комнате '{1}'", # username, host, room
|
||||
"client-left-server-notification" : u"{0} покинул(а) сервер", # name
|
||||
"no-salt-notification" : u"ВНИМАНИЕ: Чтобы сгенерированные сервером пароли операторов комнат работали после перезагрузки сервера, необходимо указать следующий аргумент командной строки при запуске сервера Syncplay: --salt {}", #Salt
|
||||
|
||||
# Server arguments
|
||||
"server-argument-description" : u'Решение для синхронного воспроизведения в VLC, MPlayer или MPC-HC через Интернет. Серверная часть',
|
||||
@ -694,8 +724,8 @@ ru = {
|
||||
"server-port-argument" : u'номер TCP порта сервера',
|
||||
"server-password-argument" : u'пароль к серверу',
|
||||
"server-isolate-room-argument" : u'должны ли комнаты быть изолированными?',
|
||||
"server-salt-argument" : u"random string used to generate managed room passwords", # TODO: Translate into Russian (note: as you may already be aware, 'salt' means Соль (криптография))
|
||||
"server-disable-ready-argument" : u"disable readiness feature", # TODO: Translate into Russian
|
||||
"server-salt-argument" : u"генерировать пароли к управляемым комнатам на основании указанной строки (соли)",
|
||||
"server-disable-ready-argument" : u"отключить статусы готов/не готов",
|
||||
"server-motd-argument" : u"путь к файлу, из которого будет извлекаться MOTD-сообщение",
|
||||
"server-messed-up-motd-unescaped-placeholders" : u"MOTD-сообщение содержит неэкранированные спец.символы. Все знаки $ должны быть продублированы ($$).",
|
||||
"server-messed-up-motd-too-long" : u"MOTD-сообщение слишком длинное: максимальная длина - {} символ(ов), текущая длина - {} символ(ов).",
|
||||
@ -746,9 +776,9 @@ de = {
|
||||
"not-all-ready" : u"Noch nicht bereit: {}", # Usernames
|
||||
"all-users-ready" : u"Alle sind bereit ({} Nutzer)", #Number of ready users
|
||||
"ready-to-unpause-notification" : u"Du bist bereit - noch einmal fortsetzen klicken zum abspielen",
|
||||
"set-as-ready-notification" : u"Du bist bereit", # TODO: Check German translation
|
||||
"set-as-not-ready-notification" : u"You are now set as not ready", # TODO: Translate into German
|
||||
"autoplaying-notification" : u"Starte in {}...",
|
||||
"set-as-ready-notification" : u"Du bist bereit",
|
||||
"set-as-not-ready-notification" : u"Du bist nicht bereit",
|
||||
"autoplaying-notification" : u"Starte in {}...", # Number of seconds until playback will start
|
||||
|
||||
"identifying-as-controller-notification" : u"Identifiziere als Raumleiter mit Passwort '{}'...", # TODO: find a better translation to "room operator"
|
||||
"failed-to-identify-as-controller-notification" : u"<{}> konnte sich nicht als Raumleiter identifizieren.",
|
||||
@ -803,7 +833,8 @@ de = {
|
||||
"vlc-interface-version-mismatch": u"Warnung: Du nutzt Version {} des VLC-Syncplay Interface-Moduls, Syncplay benötigt aber mindestens Version {}.", # VLC interface version, VLC interface min version
|
||||
"vlc-interface-oldversion-warning": u"Warnung: Es ist eine alte Version des Syncplay Interface-Moduls für VLC im VLC-Verzeichnis installiert. In der Syncplay-Anleitung unter http://syncplay.pl/guide/ [Englisch] findest du Details zur Installation des syncplay.lua-Skripts.",
|
||||
"vlc-interface-not-installed": u"Warnung: Es wurde kein Syncplay Interface-Modul für VLC im VLC-Verzeichnis gefunden. Daher wird, wenn du VLC 2.0 nutzt, die syncplay.lua die mit Syncplay mitgeliefert wurde, verwendet. Dies bedeutet allerdings, dass keine anderen Interface-Skripts und Erweiterungen geladen werden. In der Syncplay-Anleitung unter http://syncplay.pl/guide/ [Englisch] findest du Details zur Installation des syncplay.lua-Skripts.",
|
||||
"media-player-latency-warning": u"Warning: The media player took {} seconds to respond. If you experience syncing issues then close applications to free up system resources, and if that doesn't work then try a different media player.", # Seconds to respond # TODO: Translate into German
|
||||
"media-player-latency-warning": u"Warnung: Der Mediaplayer brauchte {} Sekunden zum Antworten. Wenn Probleme bei der Synchronisation auftreten, schließe bitte andere Anwendungen, um Ressourcen freizugeben. Sollte das nicht funktionieren, versuche es mit einem anderen Media-Player.", # Seconds to respond
|
||||
"mpv-unresponsive-error": u"mpv has not responded for {} seconds so appears to have malfunctioned. Please restart Syncplay.", # Seconds to respond # TODO: Translate to German
|
||||
|
||||
# Client prompts
|
||||
"enter-to-exit-prompt" : u"Enter drücken zum Beenden\n",
|
||||
@ -839,11 +870,16 @@ de = {
|
||||
"vlc-failed-versioncheck": u"Diese VLC-Version wird von Syncplay nicht unterstützt. Bitte nutze VLC 2.0",
|
||||
"vlc-failed-other" : u"Beim Laden des syncplay.lua Interface-Skripts durch VLC trat folgender Fehler auf: {}", # Syncplay Error
|
||||
|
||||
"not-supported-by-server-error" : "Dieses Feature wird vom Server nicht unterstützt. Es wird ein Server mit Syncplay Version {}+ benötigt, aktuell verwendet wird jedoch Version {}.", #minVersion, serverVersion
|
||||
"not-supported-by-server-error" : u"Dieses Feature wird vom Server nicht unterstützt. Es wird ein Server mit Syncplay Version {}+ benötigt, aktuell verwendet wird jedoch Version {}.", #minVersion, serverVersion
|
||||
|
||||
"invalid-seek-value" : u"Ungültige Zeitangabe",
|
||||
"invalid-offset-value" : u"Ungültiger Offset-Wert",
|
||||
|
||||
"switch-file-not-found-error" : u"Konnte nicht zur Datei '{0}' wechseln. Syncplay sucht im Ordner der aktuellen Datei und angegebenen Medien-Verzeichnissen.", # File not found, folder it was not found in
|
||||
"folder-search-timeout-error" : u"Die Suche nach Mediendateien in '{}' wurde abgebrochen weil sie zu lange gedauert hat. Dies tritt auf, wenn ein zu durchsuchender Medienordner zu viele Unterordner hat. Syncplay wird bis zum Neustart nur noch das Verzeichnis der aktuellen Datei durchsuchen.", #Folder
|
||||
|
||||
"failed-to-load-server-list-error" : u"Konnte die Liste der öffentlichen Server nicht laden. Bitte besuche http://www.syncplay.pl/ [Englisch] mit deinem Browser.",
|
||||
|
||||
# Client arguments
|
||||
"argument-description" : u'Syncplay ist eine Anwendung um mehrere MPlayer, MPC-HC und VLC-Instanzen über das Internet zu synchronisieren.',
|
||||
"argument-epilog" : u'Wenn keine Optionen angegeben sind, werden die _config-Werte verwendet',
|
||||
@ -876,7 +912,9 @@ de = {
|
||||
"media-setting-title" : u"Media-Player Einstellungen",
|
||||
"executable-path-label" : u"Pfad zum Media-Player:",
|
||||
"media-path-label" : u"Pfad zur Datei:",
|
||||
"player-arguments-label" : u"Playerparameter:",
|
||||
"browse-label" : u"Durchsuchen",
|
||||
"update-server-list-label" : u"Liste aktualisieren",
|
||||
|
||||
"more-title" : u"Mehr Einstellungen zeigen",
|
||||
"never-rewind-value" : u"Niemals",
|
||||
@ -901,13 +939,14 @@ de = {
|
||||
"showdifferentroomosd-label" : u"Zeige Ereignisse in anderen Räumen",
|
||||
"showslowdownosd-label" : u"Zeige Verlangsamungs/Zurücksetzungs-Benachrichtigung",
|
||||
"language-label" : u"Sprache:",
|
||||
"automatic-language" : u"Automatisch ({})", # Default language # TODO: Change to German translation of "default" / Current translation represents Idea better
|
||||
"automatic-language" : u"Automatisch ({})", # Default language
|
||||
"showdurationnotification-label" : u"Zeige Warnung wegen unterschiedlicher Dauer",
|
||||
"basics-label" : u"Grundlagen",
|
||||
"readiness-label" : u"Play/Pause", # TODO: Translate into German
|
||||
"readiness-label" : u"Play/Pause",
|
||||
"misc-label" : u"Diverse",
|
||||
"core-behaviour-title" : u"Verhalten des Raumes",
|
||||
"syncplay-internals-title" : u"Syncplay intern",
|
||||
"syncplay-mediasearchdirectories-title" : u"In diesen Verzeichnissen nach Medien suchen (ein Pfad pro Zeile)",
|
||||
"sync-label" : u"Synchronisation",
|
||||
"sync-otherslagging-title" : u"Wenn andere laggen...",
|
||||
"sync-youlaggging-title" : u"Wenn du laggst...",
|
||||
@ -916,11 +955,11 @@ de = {
|
||||
"messages-other-title" : u"Weitere Display-Einstellungen",
|
||||
"privacy-label" : u"Privatsphäre",
|
||||
"privacy-title" : u"Privatsphäreneinstellungen",
|
||||
"unpause-title" : u"If you press play, set as ready and:", # TODO: Translate into German
|
||||
"unpause-ifalreadyready-option" : u"Unpause if already set as ready", # TODO: Translate into German
|
||||
"unpause-ifothersready-option" : u"Unpause if already ready or others in room are ready (default)", # TODO: Translate into German
|
||||
"unpause-ifminusersready-option" : u"Unpause if already ready or if all others ready and min users ready", # TODO: Translate into German
|
||||
"unpause-always" : u"Always unpause", # TODO: Translate into German
|
||||
"unpause-title" : u"Wenn du Play drückst, auf Bereit setzen und:",
|
||||
"unpause-ifalreadyready-option" : u"Wiedergeben wenn bereits als Bereit gesetzt",
|
||||
"unpause-ifothersready-option" : u"Wiedergeben wenn bereits als Bereit gesetzt oder alle anderen bereit sind (Standard)",
|
||||
"unpause-ifminusersready-option" : u"Wiedergeben wenn bereits als Bereit gesetzt oder die minimale Anzahl anderer Nutzer bereit ist",
|
||||
"unpause-always" : u"Immer wiedergeben",
|
||||
|
||||
"help-label" : u"Hilfe",
|
||||
"reset-label" : u"Standardwerte zurücksetzen",
|
||||
@ -978,6 +1017,8 @@ de = {
|
||||
"identifyascontroller-msgbox-label" : u"Als Raumleiter identifizieren",
|
||||
"identifyinfo-msgbox-label" : u"Passwort des zentral gesteuerten Raums eingeben\r\n(siehe http://syncplay.pl/guide/ für eine Anleitung [Englisch]):",
|
||||
|
||||
"public-server-msgbox-label" : u"Einen öffentlichen Server für diese Sitzung auswählen",
|
||||
|
||||
"megabyte-suffix" : u" MB",
|
||||
|
||||
# Tooltips
|
||||
@ -989,6 +1030,8 @@ de = {
|
||||
|
||||
"executable-path-tooltip" : u"Pfad zum ausgewählten, unterstützten Mediaplayer (MPC-HC, VLC, mplayer2 or mpv).",
|
||||
"media-path-tooltip" : u"Pfad zum wiederzugebenden Video oder Stream. Notwendig für mpv und mplayer2.",
|
||||
"player-arguments-tooltip" : u"Zusätzliche Kommandozeilenparameter / -schalter für diesen Mediaplayer.",
|
||||
"mediasearcdirectories-arguments-tooltip" : u"Verzeichnisse, in denen Syncplay nach Mediendateien suchen soll, z.B. wenn du das Click-to-switch-Feature verwendest. Syncplay wird rekursiv Unterordner durchsuchen.", # TODO: Translate Click-to-switch? (or use as name for feature)
|
||||
|
||||
"more-tooltip" : u"Weitere Einstellungen anzeigen.",
|
||||
"filename-privacy-tooltip" : u"Privatheitsmodus beim Senden des Namens der aktuellen Datei zum Server.",
|
||||
@ -1015,18 +1058,20 @@ de = {
|
||||
"showslowdownosd-tooltip" : u"Meldungen bei Geschwindigkeitsänderung anzeigen.",
|
||||
"showdurationnotification-tooltip" : u"Nützlich, wenn z.B. ein Teil eines mehrteiligen Videos fehlt, kann jedoch auch fehlerhaft anschlagen.",
|
||||
"language-tooltip" : u"Die verwendete Sprache von Syncplay",
|
||||
"unpause-always-tooltip" : u"Fortsetzen startet immer (anstatt nur den Bereitschaftsstatus zu ändern)",
|
||||
"unpause-ifalreadyready-tooltip" : u"If you press unpause when not ready it will set you as ready - press unpause again to unpause.", # TODO: Translate into German
|
||||
"unpause-ifothersready-tooltip" : u"If you press unpause when not ready, it will only upause if others are ready.", # TODO: Translate into German
|
||||
"unpause-ifminusersready-tooltip" : u"If you press unpause when not ready, it will only upause if others are ready and minimum users threshold is met.", # TODO: Translate into German
|
||||
"unpause-always-tooltip" : u"Wiedergabe startet immer (anstatt nur den Bereitschaftsstatus zu ändern)",
|
||||
"unpause-ifalreadyready-tooltip" : u"Wenn du nicht bereit bist und Play drückst wirst du als bereit gesetzt - zum Starten der Wiedergabe nochmal drücken.",
|
||||
"unpause-ifothersready-tooltip" : u"Wenn du Play drückst und nicht bereit bist, wird nur gestartet, wenn alle anderen bereit sind.",
|
||||
"unpause-ifminusersready-tooltip" : u"Wenn du Play drückst und nicht bereit bist, wird nur gestartet, wenn die minimale Anzahl anderer Benutzer bereit ist.",
|
||||
|
||||
"help-tooltip" : u"Öffnet Hilfe auf syncplay.pl [Englisch]",
|
||||
"reset-tooltip" : u"Alle Einstellungen auf Standardwerte zurücksetzen.",
|
||||
"update-server-list-tooltip" : u"Mit syncplay.pl verbinden um die Liste öffentlicher Server zu aktualisieren.",
|
||||
|
||||
"joinroom-tooltip" : u"Den aktuellen Raum verlassen und stattdessen den angegebenen betreten.",
|
||||
"seektime-msgbox-label" : u"Springe zur angegebenen Zeit (in Sekunden oder min:sek). Verwende +/- zum relativen Springen.",
|
||||
"ready-tooltip" : "Zeigt an, ob du bereit zum anschauen bist",
|
||||
"autoplay-tooltip" : "Automatisch abspielen, wenn alle Nutzer bereit sind und die minimale Nutzerzahl erreicht ist.",
|
||||
"ready-tooltip" : u"Zeigt an, ob du bereit zum anschauen bist",
|
||||
"autoplay-tooltip" : u"Automatisch abspielen, wenn alle Nutzer bereit sind oder die minimale Nutzerzahl erreicht ist.",
|
||||
"switch-to-file-tooltip" : u"Doppelklicken um zu {} zu wechseln", # Filename
|
||||
|
||||
# In-userlist notes (GUI)
|
||||
"differentsize-note" : u"Verschiedene Größe!",
|
||||
|
||||
@ -207,8 +207,12 @@ class MplayerPlayer(BasePlayer):
|
||||
return constants.MPLAYER_ICONPATH
|
||||
|
||||
@staticmethod
|
||||
def getStartupArgs(path):
|
||||
return constants.MPLAYER_SLAVE_ARGS
|
||||
def getStartupArgs(path, userArgs):
|
||||
args = []
|
||||
if userArgs:
|
||||
args.extend(userArgs)
|
||||
args.extend(constants.MPLAYER_SLAVE_ARGS)
|
||||
return args
|
||||
|
||||
@staticmethod
|
||||
def isValidPlayerPath(path):
|
||||
@ -270,9 +274,7 @@ class MplayerPlayer(BasePlayer):
|
||||
filePath = None
|
||||
else:
|
||||
call.extend([filePath])
|
||||
if args:
|
||||
call.extend(args)
|
||||
call.extend(playerController.getStartupArgs(playerPath))
|
||||
call.extend(playerController.getStartupArgs(playerPath, args))
|
||||
# At least mpv may output escape sequences which result in syncplay
|
||||
# trying to parse something like
|
||||
# "\x1b[?1l\x1b>ANS_filename=blah.mkv". Work around this by
|
||||
|
||||
@ -22,8 +22,11 @@ class MpvPlayer(MplayerPlayer):
|
||||
return OldMpvPlayer(client, MpvPlayer.getExpandedPath(playerPath), filePath, args)
|
||||
|
||||
@staticmethod
|
||||
def getStartupArgs(path):
|
||||
args = constants.MPV_SLAVE_ARGS
|
||||
def getStartupArgs(path, userArgs):
|
||||
args = constants.MPV_ARGS
|
||||
if userArgs:
|
||||
args.extend(userArgs)
|
||||
args.extend(constants.MPV_SLAVE_ARGS)
|
||||
if constants.MPV_NEW_VERSION:
|
||||
args.extend(constants.MPV_SLAVE_ARGS_NEW)
|
||||
return args
|
||||
@ -87,6 +90,9 @@ class OldMpvPlayer(MpvPlayer):
|
||||
self.reactor.callFromThread(self._client.ui.showErrorMessage, getMessage("mpv-version-error"), True)
|
||||
self.drop()
|
||||
|
||||
elif "[ytdl_hook] Your version of youtube-dl is too old" in line:
|
||||
self._client.ui.showErrorMessage(line)
|
||||
|
||||
def _handleUnknownLine(self, line):
|
||||
self.mpvVersionErrorCheck(line)
|
||||
if "Playing: " in line:
|
||||
@ -100,6 +106,14 @@ class OldMpvPlayer(MpvPlayer):
|
||||
|
||||
class NewMpvPlayer(OldMpvPlayer):
|
||||
lastResetTime = None
|
||||
lastMPVPositionUpdate = None
|
||||
|
||||
def setPaused(self, value):
|
||||
if self._paused <> value:
|
||||
self._paused = not self._paused
|
||||
self._listener.sendLine('cycle pause')
|
||||
if value == False:
|
||||
self.lastMPVPositionUpdate = time.time()
|
||||
|
||||
def _getProperty(self, property_):
|
||||
floatProperties = ['length','time-pos']
|
||||
@ -109,7 +123,24 @@ class NewMpvPlayer(OldMpvPlayer):
|
||||
propertyID = property_
|
||||
self._listener.sendLine(u"print_text ""ANS_{}=${{{}}}""".format(property_, propertyID))
|
||||
|
||||
def getCalculatedPosition(self):
|
||||
if self.fileLoaded == False:
|
||||
return self._client.getGlobalPosition()
|
||||
|
||||
if self.lastMPVPositionUpdate is None:
|
||||
return self._client.getGlobalPosition()
|
||||
diff = time.time() - self.lastMPVPositionUpdate
|
||||
if diff > constants.MPV_UNRESPONSIVE_THRESHOLD:
|
||||
self.reactor.callFromThread(self._client.ui.showErrorMessage, getMessage("mpv-unresponsive-error").format(int(diff)), True)
|
||||
self.drop()
|
||||
if diff > constants.PLAYER_ASK_DELAY and not self._paused:
|
||||
self._client.ui.showDebugMessage("mpv did not response in time, so assuming position is {} ({}+{})".format(self._position + diff, self._position, diff))
|
||||
return self._position + diff
|
||||
else:
|
||||
return self._position
|
||||
|
||||
def _storePosition(self, value):
|
||||
self.lastMPVPositionUpdate = time.time()
|
||||
if self._recentlyReset():
|
||||
self._position = 0
|
||||
elif self._fileIsLoaded():
|
||||
@ -130,7 +161,7 @@ class NewMpvPlayer(OldMpvPlayer):
|
||||
self._getPosition()
|
||||
self._positionAsk.wait(constants.MPV_LOCK_WAIT_TIME)
|
||||
self._pausedAsk.wait(constants.MPV_LOCK_WAIT_TIME)
|
||||
self._client.updatePlayerStatus(self._paused, self._position)
|
||||
self._client.updatePlayerStatus(self._paused if self.fileLoaded else self._client.getGlobalPaused(), self.getCalculatedPosition())
|
||||
|
||||
def _preparePlayer(self):
|
||||
if self.delayedFilePath:
|
||||
@ -146,6 +177,10 @@ class NewMpvPlayer(OldMpvPlayer):
|
||||
self._clearFileLoaded()
|
||||
self._listener.sendLine(u'loadfile {}'.format(self._quoteArg(filePath)))
|
||||
|
||||
def setPosition(self, value):
|
||||
super(self.__class__, self).setPosition(value)
|
||||
self.lastMPVPositionUpdate = time.time()
|
||||
|
||||
def openFile(self, filePath, resetPosition=False):
|
||||
if resetPosition:
|
||||
self.lastResetTime = time.time()
|
||||
|
||||
@ -2,6 +2,7 @@ from ConfigParser import SafeConfigParser, DEFAULTSECT
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import ast
|
||||
from syncplay import constants, utils, version, milestone
|
||||
from syncplay.messages import getMessage, setLanguage, isValidLanguage
|
||||
from syncplay.players.playerFactory import PlayerFactory
|
||||
@ -31,6 +32,8 @@ class ConfigurationGetter(object):
|
||||
"room": "",
|
||||
"password": None,
|
||||
"playerPath": None,
|
||||
"perPlayerArguments": None,
|
||||
"mediaSearchDirectories": None,
|
||||
"file": None,
|
||||
"playerArgs": [],
|
||||
"playerClass": None,
|
||||
@ -104,6 +107,11 @@ class ConfigurationGetter(object):
|
||||
"autoplayInitialState",
|
||||
]
|
||||
|
||||
self._serialised = [
|
||||
"perPlayerArguments",
|
||||
"mediaSearchDirectories",
|
||||
]
|
||||
|
||||
self._numeric = [
|
||||
"slowdownThreshold",
|
||||
"rewindThreshold",
|
||||
@ -113,7 +121,7 @@ class ConfigurationGetter(object):
|
||||
|
||||
self._iniStructure = {
|
||||
"server_data": ["host", "port", "password"],
|
||||
"client_settings": ["name", "room", "playerPath", "slowdownThreshold", "rewindThreshold", "fastforwardThreshold", "slowOnDesync", "rewindOnDesync", "fastforwardOnDesync", "dontSlowDownWithMe", "forceGuiPrompt", "filenamePrivacyMode", "filesizePrivacyMode", "unpauseAction", "pauseOnLeave", "readyAtStart", "autoplayMinUsers", "autoplayInitialState"],
|
||||
"client_settings": ["name", "room", "playerPath", "perPlayerArguments", "slowdownThreshold", "rewindThreshold", "fastforwardThreshold", "slowOnDesync", "rewindOnDesync", "fastforwardOnDesync", "dontSlowDownWithMe", "forceGuiPrompt", "filenamePrivacyMode", "filesizePrivacyMode", "unpauseAction", "pauseOnLeave", "readyAtStart", "autoplayMinUsers", "autoplayInitialState", "mediaSearchDirectories"],
|
||||
"gui": ["showOSD", "showOSDWarnings", "showSlowdownOSD", "showDifferentRoomOSD", "showSameRoomOSD", "showNonControllerOSD", "showDurationNotification"],
|
||||
"general": ["language", "checkForUpdatesAutomatically", "lastCheckedForUpdates"]
|
||||
}
|
||||
@ -150,6 +158,12 @@ class ConfigurationGetter(object):
|
||||
elif self._config[key] == "False":
|
||||
self._config[key] = False
|
||||
|
||||
for key in self._serialised:
|
||||
if self._config[key] is None or self._config[key] == "":
|
||||
self._config[key] = {}
|
||||
elif isinstance(self._config[key], (str, unicode)):
|
||||
self._config[key] = ast.literal_eval(self._config[key])
|
||||
|
||||
for key in self._tristate:
|
||||
if self._config[key] == "True":
|
||||
self._config[key] = True
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
from PySide import QtCore, QtGui
|
||||
from PySide.QtCore import QSettings, Qt, QCoreApplication, QUrl
|
||||
from PySide.QtGui import QApplication, QLineEdit, QCursor, QLabel, QCheckBox, QDesktopServices, QIcon, QImage, QButtonGroup, QRadioButton, QDoubleSpinBox
|
||||
from PySide.QtGui import QApplication, QLineEdit, QCursor, QLabel, QCheckBox, QDesktopServices, QIcon, QImage, QButtonGroup, QRadioButton, QDoubleSpinBox, QPlainTextEdit
|
||||
from syncplay.players.playerFactory import PlayerFactory
|
||||
from datetime import datetime
|
||||
from syncplay import utils
|
||||
import os
|
||||
import sys
|
||||
from syncplay.messages import getMessage, getLanguages, setLanguage, getInitialLanguage
|
||||
@ -45,6 +46,7 @@ class ConfigDialog(QtGui.QDialog):
|
||||
self.automaticupdatesCheckbox.setChecked(False)
|
||||
|
||||
def moreToggled(self):
|
||||
self.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum)
|
||||
if self.moreToggling == False:
|
||||
self.moreToggling = True
|
||||
|
||||
@ -52,16 +54,24 @@ class ConfigDialog(QtGui.QDialog):
|
||||
self.tabListFrame.show()
|
||||
self.resetButton.show()
|
||||
self.nostoreCheckbox.show()
|
||||
self.playerargsTextbox.show()
|
||||
self.playerargsLabel.show()
|
||||
self.saveMoreState(True)
|
||||
self.tabListWidget.setCurrentRow(0)
|
||||
self.ensureTabListIsVisible()
|
||||
self.stackedFrame.setFixedHeight(self.stackedFrame.minimumSizeHint().height())
|
||||
else:
|
||||
self.tabListFrame.hide()
|
||||
self.resetButton.hide()
|
||||
self.nostoreCheckbox.hide()
|
||||
self.playerargsTextbox.hide()
|
||||
self.playerargsLabel.hide()
|
||||
self.saveMoreState(False)
|
||||
self.stackedLayout.setCurrentIndex(0)
|
||||
|
||||
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())
|
||||
self.moreToggling = False
|
||||
@ -77,15 +87,8 @@ class ConfigDialog(QtGui.QDialog):
|
||||
def openHelp(self):
|
||||
self.QtGui.QDesktopServices.openUrl(QUrl("http://syncplay.pl/guide/client/"))
|
||||
|
||||
def _isURL(self, path):
|
||||
if path is None:
|
||||
return False
|
||||
|
||||
if "http://" in path:
|
||||
return True
|
||||
|
||||
def safenormcaseandpath(self, path):
|
||||
if self._isURL(path):
|
||||
if utils.isURL(path):
|
||||
return path
|
||||
else:
|
||||
return os.path.normcase(os.path.normpath(path))
|
||||
@ -104,7 +107,7 @@ class ConfigDialog(QtGui.QDialog):
|
||||
foundpath = ""
|
||||
|
||||
if playerpath != None and playerpath != "":
|
||||
if self._isURL(playerpath):
|
||||
if utils.isURL(playerpath):
|
||||
foundpath = playerpath
|
||||
self.executablepathCombobox.addItem(foundpath)
|
||||
|
||||
@ -119,7 +122,7 @@ class ConfigDialog(QtGui.QDialog):
|
||||
self.executablepathCombobox.addItem(foundpath)
|
||||
|
||||
for path in playerpathlist:
|
||||
if self._isURL(path):
|
||||
if utils.isURL(path):
|
||||
if foundpath == "":
|
||||
foundpath = path
|
||||
if path != playerpath:
|
||||
@ -145,6 +148,20 @@ class ConfigDialog(QtGui.QDialog):
|
||||
self.executableiconLabel.setPixmap(QtGui.QPixmap.fromImage(self.executableiconImage))
|
||||
else:
|
||||
self.executableiconLabel.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage()))
|
||||
self.updatePlayerArguments(currentplayerpath)
|
||||
|
||||
def updatePlayerArguments(self, currentplayerpath):
|
||||
argumentsForPath = utils.getPlayerArgumentsByPathAsText(self.perPlayerArgs, currentplayerpath)
|
||||
self.playerargsTextbox.blockSignals(True)
|
||||
self.playerargsTextbox.setText(argumentsForPath)
|
||||
self.playerargsTextbox.blockSignals(False)
|
||||
|
||||
def changedPlayerArgs(self):
|
||||
currentplayerpath = self.executablepathCombobox.currentText()
|
||||
|
||||
if currentplayerpath:
|
||||
NewPlayerArgs = self.playerargsTextbox.text().split(u" ") if self.playerargsTextbox.text() else ""
|
||||
self.perPlayerArgs[self.executablepathCombobox.currentText()]=NewPlayerArgs
|
||||
|
||||
def languageChanged(self):
|
||||
setLanguage(unicode(self.languageCombobox.itemData(self.languageCombobox.currentIndex())))
|
||||
@ -188,6 +205,11 @@ class ConfigDialog(QtGui.QDialog):
|
||||
else:
|
||||
self.config["lastCheckedForUpdates"] = str(self.lastCheckedForUpdates)
|
||||
|
||||
def loadSavedPublicServerList(self):
|
||||
settings = QSettings("Syncplay", "Interface")
|
||||
settings.beginGroup("PublicServerList")
|
||||
self.publicServers = settings.value("publicServers", None)
|
||||
|
||||
def loadMediaBrowseSettings(self):
|
||||
settings = QSettings("Syncplay", "MediaBrowseDialog")
|
||||
settings.beginGroup("MediaBrowseDialog")
|
||||
@ -216,10 +238,34 @@ class ConfigDialog(QtGui.QDialog):
|
||||
settings.setValue("ShowMoreSettings", morestate)
|
||||
settings.endGroup()
|
||||
|
||||
def updateServerList(self):
|
||||
try:
|
||||
servers = utils.getListOfPublicServers()
|
||||
except IOError as e:
|
||||
self.showErrorMessage(unicode(e))
|
||||
return
|
||||
currentServer = self.hostCombobox.currentText()
|
||||
self.hostCombobox.clear()
|
||||
if servers:
|
||||
i = 0
|
||||
for server in servers:
|
||||
self.hostCombobox.addItem(server[1])
|
||||
self.hostCombobox.setItemData(i, server[0], Qt.ToolTipRole)
|
||||
i += 1
|
||||
settings = QSettings("Syncplay", "Interface")
|
||||
settings.beginGroup("PublicServerList")
|
||||
settings.setValue("publicServers", servers)
|
||||
self.hostCombobox.setEditText(currentServer)
|
||||
|
||||
def showErrorMessage(self, errorMessage):
|
||||
QtGui.QMessageBox.warning(self, "Syncplay", errorMessage)
|
||||
|
||||
def browseMediapath(self):
|
||||
self.loadMediaBrowseSettings()
|
||||
options = QtGui.QFileDialog.Options()
|
||||
if os.path.isdir(self.mediadirectory):
|
||||
if self.config["mediaSearchDirectories"] and os.path.isdir(self.config["mediaSearchDirectories"][0]):
|
||||
defaultdirectory = self.config["mediaSearchDirectories"][0]
|
||||
elif os.path.isdir(self.mediadirectory):
|
||||
defaultdirectory = self.mediadirectory
|
||||
elif os.path.isdir(QDesktopServices.storageLocation(QDesktopServices.MoviesLocation)):
|
||||
defaultdirectory = QDesktopServices.storageLocation(QDesktopServices.MoviesLocation)
|
||||
@ -239,9 +285,12 @@ class ConfigDialog(QtGui.QDialog):
|
||||
self.automaticUpdatePromptCheck()
|
||||
self.loadLastUpdateCheckDate()
|
||||
|
||||
self.config["perPlayerArguments"] = self.perPlayerArgs
|
||||
self.config["mediaSearchDirectories"] = utils.convertMultilineStringToList(self.mediasearchTextEdit.toPlainText())
|
||||
|
||||
self.processWidget(self, lambda w: self.saveValues(w))
|
||||
if self.hostTextbox.text():
|
||||
self.config['host'] = self.hostTextbox.text() if ":" in self.hostTextbox.text() else self.hostTextbox.text() + ":" + unicode(constants.DEFAULT_PORT)
|
||||
if self.hostCombobox.currentText():
|
||||
self.config['host'] = self.hostCombobox.currentText() if ":" in self.hostCombobox.currentText() else self.hostCombobox.currentText() + ":" + unicode(constants.DEFAULT_PORT)
|
||||
else:
|
||||
self.config['host'] = None
|
||||
self.config['playerPath'] = unicode(self.safenormcaseandpath(self.executablepathCombobox.currentText()))
|
||||
@ -361,6 +410,7 @@ class ConfigDialog(QtGui.QDialog):
|
||||
error = self.error
|
||||
if self.datacleared == True:
|
||||
error = constants.ERROR_MESSAGE_MARKER + "{}".format(getMessage("gui-data-cleared-notification"))
|
||||
self.error = error
|
||||
if config['host'] == None:
|
||||
host = ""
|
||||
elif ":" in config['host']:
|
||||
@ -368,10 +418,27 @@ class ConfigDialog(QtGui.QDialog):
|
||||
else:
|
||||
host = config['host'] + ":" + str(config['port'])
|
||||
|
||||
self.perPlayerArgs = self.config["perPlayerArguments"]
|
||||
self.mediaSearchDirectories = self.config["mediaSearchDirectories"]
|
||||
|
||||
self.connectionSettingsGroup = QtGui.QGroupBox(getMessage("connection-group-title"))
|
||||
self.hostTextbox = QLineEdit(host, self)
|
||||
self.loadSavedPublicServerList()
|
||||
self.hostCombobox = QtGui.QComboBox(self)
|
||||
if self.publicServers:
|
||||
i = 0
|
||||
for publicServer in self.publicServers:
|
||||
self.hostCombobox.addItem(publicServer[1])
|
||||
self.hostCombobox.setItemData(i, publicServer[0], Qt.ToolTipRole)
|
||||
i += 1
|
||||
self.hostCombobox.setEditable(True)
|
||||
self.hostCombobox.setEditText(host)
|
||||
self.hostCombobox.setFixedWidth(165)
|
||||
self.hostLabel = QLabel(getMessage("host-label"), self)
|
||||
self.findServerButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + 'arrow_refresh.png'), getMessage("update-server-list-label"))
|
||||
self.findServerButton.clicked.connect(self.updateServerList)
|
||||
self.findServerButton.setToolTip(getMessage("update-server-list-tooltip"))
|
||||
self.usernameTextbox = QLineEdit(self)
|
||||
|
||||
self.usernameTextbox.setObjectName("name")
|
||||
self.serverpassLabel = QLabel(getMessage("password-label"), self)
|
||||
self.defaultroomTextbox = QLineEdit(self)
|
||||
@ -380,7 +447,7 @@ class ConfigDialog(QtGui.QDialog):
|
||||
self.defaultroomLabel = QLabel(getMessage("room-label"), self)
|
||||
|
||||
self.hostLabel.setObjectName("host")
|
||||
self.hostTextbox.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "host")
|
||||
self.hostCombobox.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "host")
|
||||
self.usernameLabel.setObjectName("name")
|
||||
self.usernameTextbox.setObjectName("name")
|
||||
self.serverpassLabel.setObjectName("password")
|
||||
@ -390,16 +457,21 @@ class ConfigDialog(QtGui.QDialog):
|
||||
|
||||
self.connectionSettingsLayout = QtGui.QGridLayout()
|
||||
self.connectionSettingsLayout.addWidget(self.hostLabel, 0, 0)
|
||||
self.connectionSettingsLayout.addWidget(self.hostTextbox, 0, 1)
|
||||
self.connectionSettingsLayout.addWidget(self.hostCombobox, 0, 1)
|
||||
self.connectionSettingsLayout.addWidget(self.findServerButton, 0, 2)
|
||||
self.connectionSettingsLayout.addWidget(self.serverpassLabel, 1, 0)
|
||||
self.connectionSettingsLayout.addWidget(self.serverpassTextbox, 1, 1)
|
||||
self.connectionSettingsLayout.addWidget(self.serverpassTextbox, 1, 1, 1, 2)
|
||||
self.connectionSettingsLayout.addWidget(self.usernameLabel, 2, 0)
|
||||
self.connectionSettingsLayout.addWidget(self.usernameTextbox, 2, 1)
|
||||
self.connectionSettingsLayout.addWidget(self.usernameTextbox, 2, 1, 1, 2)
|
||||
self.connectionSettingsLayout.addWidget(self.defaultroomLabel, 3, 0)
|
||||
self.connectionSettingsLayout.addWidget(self.defaultroomTextbox, 3, 1)
|
||||
self.connectionSettingsLayout.addWidget(self.defaultroomTextbox, 3, 1, 1, 2)
|
||||
self.connectionSettingsGroup.setLayout(self.connectionSettingsLayout)
|
||||
self.connectionSettingsGroup.setMaximumHeight(self.connectionSettingsGroup.minimumSizeHint().height())
|
||||
|
||||
self.playerargsTextbox = QLineEdit("", self)
|
||||
self.playerargsTextbox.textEdited.connect(self.changedPlayerArgs)
|
||||
self.playerargsLabel = QLabel(getMessage("player-arguments-label"), self)
|
||||
|
||||
self.mediaplayerSettingsGroup = QtGui.QGroupBox(getMessage("media-setting-title"))
|
||||
self.executableiconImage = QtGui.QImage()
|
||||
self.executableiconLabel = QLabel(self)
|
||||
@ -423,6 +495,8 @@ class ConfigDialog(QtGui.QDialog):
|
||||
self.executablepathCombobox.setObjectName("executable-path")
|
||||
self.mediapathLabel.setObjectName("media-path")
|
||||
self.mediapathTextbox.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "media-path")
|
||||
self.playerargsLabel.setObjectName("player-arguments")
|
||||
self.playerargsTextbox.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "player-arguments")
|
||||
|
||||
self.mediaplayerSettingsLayout = QtGui.QGridLayout()
|
||||
self.mediaplayerSettingsLayout.addWidget(self.executablepathLabel, 0, 0)
|
||||
@ -432,6 +506,8 @@ class ConfigDialog(QtGui.QDialog):
|
||||
self.mediaplayerSettingsLayout.addWidget(self.mediapathLabel, 1, 0)
|
||||
self.mediaplayerSettingsLayout.addWidget(self.mediapathTextbox , 1, 2)
|
||||
self.mediaplayerSettingsLayout.addWidget(self.mediabrowseButton , 1, 3)
|
||||
self.mediaplayerSettingsLayout.addWidget(self.playerargsLabel, 2, 0, 1, 2)
|
||||
self.mediaplayerSettingsLayout.addWidget(self.playerargsTextbox, 2, 2, 1, 2)
|
||||
self.mediaplayerSettingsGroup.setLayout(self.mediaplayerSettingsLayout)
|
||||
|
||||
self.showmoreCheckbox = QCheckBox(getMessage("more-title"))
|
||||
@ -448,15 +524,13 @@ class ConfigDialog(QtGui.QDialog):
|
||||
self.errorLabel.setStyleSheet(constants.STYLE_SUCCESSLABEL)
|
||||
self.errorLabel.setText(error)
|
||||
self.errorLabel.setAlignment(Qt.AlignCenter)
|
||||
|
||||
self.basicOptionsLayout.addWidget(self.errorLabel, 0, 0)
|
||||
self.connectionSettingsGroup.setMaximumHeight(self.connectionSettingsGroup.minimumSizeHint().height())
|
||||
self.basicOptionsLayout.setAlignment(Qt.AlignTop)
|
||||
self.basicOptionsLayout.addWidget(self.connectionSettingsGroup)
|
||||
self.basicOptionsLayout.addSpacing(5)
|
||||
self.mediaplayerSettingsGroup.setMaximumHeight(self.mediaplayerSettingsGroup.minimumSizeHint().height())
|
||||
self.basicOptionsLayout.addWidget(self.mediaplayerSettingsGroup)
|
||||
|
||||
self.basicOptionsFrame.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum)
|
||||
self.basicOptionsFrame.setLayout(self.basicOptionsLayout)
|
||||
self.stackedLayout.addWidget(self.basicOptionsFrame)
|
||||
|
||||
@ -575,8 +649,21 @@ class ConfigDialog(QtGui.QDialog):
|
||||
self.automaticupdatesCheckbox.setObjectName("checkForUpdatesAutomatically")
|
||||
self.internalSettingsLayout.addWidget(self.automaticupdatesCheckbox)
|
||||
|
||||
## Media path directories
|
||||
|
||||
self.mediasearchSettingsGroup = QtGui.QGroupBox(getMessage("syncplay-mediasearchdirectories-title"))
|
||||
self.mediasearchSettingsLayout = QtGui.QVBoxLayout()
|
||||
self.mediasearchSettingsGroup.setLayout(self.mediasearchSettingsLayout)
|
||||
|
||||
self.mediasearchTextEdit = QPlainTextEdit(utils.getListAsMultilineString(self.mediaSearchDirectories))
|
||||
self.mediasearchTextEdit.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "mediasearcdirectories-arguments")
|
||||
self.mediasearchTextEdit.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap)
|
||||
self.mediasearchSettingsLayout.addWidget(self.mediasearchTextEdit)
|
||||
self.mediasearchSettingsGroup.setMaximumHeight(self.mediasearchSettingsGroup.minimumSizeHint().height())
|
||||
|
||||
self.miscLayout.addWidget(self.coreSettingsGroup)
|
||||
self.miscLayout.addWidget(self.internalSettingsGroup)
|
||||
self.miscLayout.addWidget(self.mediasearchSettingsGroup)
|
||||
self.miscLayout.setAlignment(Qt.AlignTop)
|
||||
self.stackedLayout.addWidget(self.miscFrame)
|
||||
|
||||
@ -814,20 +901,37 @@ class ConfigDialog(QtGui.QDialog):
|
||||
settings.beginGroup("Update")
|
||||
settings.setValue("lastChecked", None)
|
||||
settings.endGroup()
|
||||
settings.beginGroup("PublicServerList")
|
||||
settings.setValue("publicServers", None)
|
||||
settings.endGroup()
|
||||
if not leaveMore:
|
||||
settings = QSettings("Syncplay", "MoreSettings")
|
||||
settings.clear()
|
||||
self.datacleared = True
|
||||
|
||||
|
||||
def populateEmptyServerList(self):
|
||||
if self.publicServers is None:
|
||||
if self.config["checkForUpdatesAutomatically"] == True:
|
||||
self.updateServerList()
|
||||
else:
|
||||
currentServer = self.hostCombobox.currentText()
|
||||
self.publicServers = constants.FALLBACK_PUBLIC_SYNCPLAY_SERVERS
|
||||
i = 0
|
||||
for server in self.publicServers:
|
||||
self.hostCombobox.addItem(server[1])
|
||||
self.hostCombobox.setItemData(i, server[0], Qt.ToolTipRole)
|
||||
i += 1
|
||||
self.hostCombobox.setEditText(currentServer)
|
||||
|
||||
def __init__(self, config, playerpaths, error, defaultConfig):
|
||||
|
||||
from syncplay import utils
|
||||
self.config = config
|
||||
self.defaultConfig = defaultConfig
|
||||
self.playerpaths = playerpaths
|
||||
self.datacleared = False
|
||||
self.config['resetConfig'] = False
|
||||
self.subitems = {}
|
||||
self.publicServers = None
|
||||
|
||||
if self.config['clearGUIData'] == True:
|
||||
self.config['clearGUIData'] = False
|
||||
@ -866,14 +970,20 @@ class ConfigDialog(QtGui.QDialog):
|
||||
self.mainLayout.addWidget(self.stackedFrame, 0, 1)
|
||||
self.addBottomLayout()
|
||||
|
||||
|
||||
if self.getMoreState() == False:
|
||||
self.tabListFrame.hide()
|
||||
self.nostoreCheckbox.hide()
|
||||
self.resetButton.hide()
|
||||
self.playerargsTextbox.hide()
|
||||
self.playerargsLabel.hide()
|
||||
newHeight = self.connectionSettingsGroup.minimumSizeHint().height()+self.mediaplayerSettingsGroup.minimumSizeHint().height()+self.bottomButtonFrame.minimumSizeHint().height()+3
|
||||
if self.error:
|
||||
newHeight +=self.errorLabel.height()+3
|
||||
self.stackedFrame.setFixedHeight(newHeight)
|
||||
else:
|
||||
self.showmoreCheckbox.setChecked(True)
|
||||
self.tabListWidget.setCurrentRow(0)
|
||||
self.stackedFrame.setFixedHeight(self.stackedFrame.minimumSizeHint().height())
|
||||
|
||||
self.showmoreCheckbox.toggled.connect(self.moreToggled)
|
||||
|
||||
@ -885,4 +995,5 @@ class ConfigDialog(QtGui.QDialog):
|
||||
if constants.SHOW_TOOLTIPS:
|
||||
self.processWidget(self, lambda w: self.loadTooltips(w))
|
||||
self.processWidget(self, lambda w: self.loadValues(w))
|
||||
self.processWidget(self, lambda w: self.connectChildren(w))
|
||||
self.processWidget(self, lambda w: self.connectChildren(w))
|
||||
self.populateEmptyServerList()
|
||||
@ -4,11 +4,14 @@ from syncplay import utils, constants, version
|
||||
from syncplay.messages import getMessage
|
||||
import sys
|
||||
import time
|
||||
import urllib
|
||||
from datetime import datetime
|
||||
import re
|
||||
import os
|
||||
from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize
|
||||
from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize, isURL
|
||||
from functools import wraps
|
||||
from twisted.internet import task, threads
|
||||
import threading
|
||||
lastCheckedForUpdates = None
|
||||
|
||||
class UserlistItemDelegate(QtGui.QStyledItemDelegate):
|
||||
@ -17,15 +20,15 @@ class UserlistItemDelegate(QtGui.QStyledItemDelegate):
|
||||
|
||||
def sizeHint(self, option, index):
|
||||
size = QtGui.QStyledItemDelegate.sizeHint(self, option, index)
|
||||
if (index.column() == 0):
|
||||
if (index.column() == constants.USERLIST_GUI_USERNAME_COLUMN):
|
||||
size.setWidth(size.width() + constants.USERLIST_GUI_USERNAME_OFFSET)
|
||||
return size
|
||||
|
||||
def paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex):
|
||||
column = indexQModelIndex.column()
|
||||
if column == 0:
|
||||
if column == constants.USERLIST_GUI_USERNAME_COLUMN:
|
||||
currentQAbstractItemModel = indexQModelIndex.model()
|
||||
itemQModelIndex = currentQAbstractItemModel.index(indexQModelIndex.row(), 0, indexQModelIndex.parent())
|
||||
itemQModelIndex = currentQAbstractItemModel.index(indexQModelIndex.row(), constants.USERLIST_GUI_USERNAME_COLUMN, indexQModelIndex.parent())
|
||||
if sys.platform.startswith('win'):
|
||||
resourcespath = utils.findWorkingDir() + "\\resources\\"
|
||||
else:
|
||||
@ -56,6 +59,29 @@ class UserlistItemDelegate(QtGui.QStyledItemDelegate):
|
||||
isUserRow = indexQModelIndex.parent() != indexQModelIndex.parent().parent()
|
||||
if isUserRow:
|
||||
optionQStyleOptionViewItem.rect.setX(optionQStyleOptionViewItem.rect.x()+constants.USERLIST_GUI_USERNAME_OFFSET)
|
||||
if column == constants.USERLIST_GUI_FILENAME_COLUMN:
|
||||
if sys.platform.startswith('win'):
|
||||
resourcespath = utils.findWorkingDir() + "\\resources\\"
|
||||
else:
|
||||
resourcespath = utils.findWorkingDir() + "/resources/"
|
||||
currentQAbstractItemModel = indexQModelIndex.model()
|
||||
itemQModelIndex = currentQAbstractItemModel.index(indexQModelIndex.row(), constants.USERLIST_GUI_FILENAME_COLUMN, indexQModelIndex.parent())
|
||||
fileSwitchRole = currentQAbstractItemModel.data(itemQModelIndex, Qt.UserRole + constants.FILEITEM_SWITCH_ROLE)
|
||||
if fileSwitchRole == constants.FILEITEM_SWITCH_FILE_SWITCH:
|
||||
fileSwitchIconQPixmap = QtGui.QPixmap(resourcespath + "film_go.png")
|
||||
itemQPainter.drawPixmap (
|
||||
(optionQStyleOptionViewItem.rect.x()),
|
||||
optionQStyleOptionViewItem.rect.y(),
|
||||
fileSwitchIconQPixmap.scaled(16, 16, Qt.KeepAspectRatio))
|
||||
optionQStyleOptionViewItem.rect.setX(optionQStyleOptionViewItem.rect.x()+16)
|
||||
|
||||
elif fileSwitchRole == constants.FILEITEM_SWITCH_STREAM_SWITCH:
|
||||
streamSwitchIconQPixmap = QtGui.QPixmap(resourcespath + "world_go.png")
|
||||
itemQPainter.drawPixmap (
|
||||
(optionQStyleOptionViewItem.rect.x()),
|
||||
optionQStyleOptionViewItem.rect.y(),
|
||||
streamSwitchIconQPixmap.scaled(16, 16, Qt.KeepAspectRatio))
|
||||
optionQStyleOptionViewItem.rect.setX(optionQStyleOptionViewItem.rect.x()+16)
|
||||
QtGui.QStyledItemDelegate.paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex)
|
||||
|
||||
class MainWindow(QtGui.QMainWindow):
|
||||
@ -182,6 +208,149 @@ class MainWindow(QtGui.QMainWindow):
|
||||
else:
|
||||
super(MainWindow.PlaylistWidget, self).dropEvent(event)
|
||||
|
||||
class FileSwitchManager(object):
|
||||
def __init__(self):
|
||||
self.fileSwitchTimer = task.LoopingCall(self.updateInfo)
|
||||
self.fileSwitchTimer.start(constants.FOLDER_SEARCH_DOUBLE_CHECK_INTERVAL, True)
|
||||
|
||||
mediaFilesCache = {}
|
||||
filenameWatchlist = []
|
||||
currentDirectory = None
|
||||
mediaDirectories = None
|
||||
lock = threading.Lock()
|
||||
client = None
|
||||
currentWindow = None
|
||||
folderSearchEnabled = True
|
||||
disabledDir = None
|
||||
newInfo = False
|
||||
currentlyUpdating = False
|
||||
|
||||
@staticmethod
|
||||
def setWindow(window):
|
||||
MainWindow.FileSwitchManager.currentWindow = window
|
||||
|
||||
@staticmethod
|
||||
def setClient(newClient):
|
||||
MainWindow.FileSwitchManager.client = newClient
|
||||
|
||||
@staticmethod
|
||||
def setCurrentDirectory(curDir):
|
||||
MainWindow.FileSwitchManager.currentDirectory = curDir
|
||||
MainWindow.FileSwitchManager.updateInfo()
|
||||
|
||||
@staticmethod
|
||||
def setMediaDirectories(mediaDirs):
|
||||
MainWindow.FileSwitchManager.mediaDirectories = mediaDirs
|
||||
MainWindow.FileSwitchManager.updateInfo()
|
||||
|
||||
@staticmethod
|
||||
def checkForUpdate(self=None):
|
||||
if MainWindow.FileSwitchManager.newInfo:
|
||||
MainWindow.FileSwitchManager.newInfo = False
|
||||
MainWindow.FileSwitchManager.infoUpdated()
|
||||
|
||||
@staticmethod
|
||||
def updateInfo():
|
||||
if len(MainWindow.FileSwitchManager.filenameWatchlist) > 0 or len(MainWindow.FileSwitchManager.mediaFilesCache) == 0 and MainWindow.FileSwitchManager.currentlyUpdating == False:
|
||||
threads.deferToThread(MainWindow.FileSwitchManager._updateInfoThread).addCallback(MainWindow.FileSwitchManager.checkForUpdate)
|
||||
|
||||
@staticmethod
|
||||
def setFilenameWatchlist(unfoundFilenames):
|
||||
MainWindow.FileSwitchManager.filenameWatchlist = unfoundFilenames
|
||||
|
||||
@staticmethod
|
||||
def _updateInfoThread():
|
||||
if not MainWindow.FileSwitchManager.folderSearchEnabled:
|
||||
if MainWindow.FileSwitchManager.areWatchedFilenamesInCurrentDir():
|
||||
MainWindow.FileSwitchManager.newInfo = True
|
||||
return
|
||||
|
||||
with MainWindow.FileSwitchManager.lock:
|
||||
try:
|
||||
MainWindow.FileSwitchManager.currentlyUpdating = True
|
||||
dirsToSearch = MainWindow.FileSwitchManager.mediaDirectories
|
||||
|
||||
if dirsToSearch:
|
||||
newMediaFilesCache = {}
|
||||
startTime = time.time()
|
||||
for directory in dirsToSearch:
|
||||
for root, dirs, files in os.walk(directory):
|
||||
newMediaFilesCache[root] = files
|
||||
if time.time() - startTime > constants.FOLDER_SEARCH_TIMEOUT:
|
||||
if MainWindow.FileSwitchManager.client is not None and MainWindow.FileSwitchManager.currentWindow is not None:
|
||||
MainWindow.FileSwitchManager.disabledDir = directory
|
||||
MainWindow.FileSwitchManager.folderSearchEnabled = False
|
||||
if MainWindow.FileSwitchManager.areWatchedFilenamesInCurrentDir():
|
||||
MainWindow.FileSwitchManager.newInfo = True
|
||||
return
|
||||
|
||||
if MainWindow.FileSwitchManager.mediaFilesCache <> newMediaFilesCache:
|
||||
MainWindow.FileSwitchManager.mediaFilesCache = newMediaFilesCache
|
||||
MainWindow.FileSwitchManager.newInfo = True
|
||||
elif MainWindow.FileSwitchManager.areWatchedFilenamesInCurrentDir():
|
||||
MainWindow.FileSwitchManager.newInfo = True
|
||||
finally:
|
||||
MainWindow.FileSwitchManager.currentlyUpdating = False
|
||||
|
||||
@staticmethod
|
||||
def infoUpdated():
|
||||
if MainWindow.FileSwitchManager.areWatchedFilenamesInCache() or MainWindow.FileSwitchManager.areWatchedFilenamesInCurrentDir():
|
||||
MainWindow.FileSwitchManager.updateListOfWhoIsPlayingWhat()
|
||||
|
||||
@staticmethod
|
||||
def updateListOfWhoIsPlayingWhat():
|
||||
if MainWindow.FileSwitchManager.client is not None:
|
||||
MainWindow.FileSwitchManager.client.showUserList()
|
||||
|
||||
@staticmethod
|
||||
def findFilepath(filename):
|
||||
if filename is None:
|
||||
return
|
||||
|
||||
if MainWindow.FileSwitchManager.currentDirectory is not None:
|
||||
candidatePath = os.path.join(MainWindow.FileSwitchManager.currentDirectory,filename)
|
||||
if os.path.isfile(candidatePath):
|
||||
return candidatePath
|
||||
|
||||
if MainWindow.FileSwitchManager.mediaFilesCache is not None:
|
||||
for directory in MainWindow.FileSwitchManager.mediaFilesCache:
|
||||
files = MainWindow.FileSwitchManager.mediaFilesCache[directory]
|
||||
|
||||
if len(files) > 0 and filename in files:
|
||||
filepath = os.path.join(directory, filename)
|
||||
if os.path.isfile(filepath):
|
||||
return filepath
|
||||
|
||||
@staticmethod
|
||||
def areWatchedFilenamesInCurrentDir():
|
||||
if MainWindow.FileSwitchManager.filenameWatchlist is not None and MainWindow.FileSwitchManager.currentDirectory is not None:
|
||||
for filename in MainWindow.FileSwitchManager.filenameWatchlist:
|
||||
potentialPath = os.path.join(MainWindow.FileSwitchManager.currentDirectory,filename)
|
||||
if os.path.isfile(potentialPath):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def areWatchedFilenamesInCache():
|
||||
if MainWindow.FileSwitchManager.filenameWatchlist is not None:
|
||||
for filename in MainWindow.FileSwitchManager.filenameWatchlist:
|
||||
if MainWindow.FileSwitchManager.isFilenameInCache(filename):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def isFilenameInCurrentDir(filename):
|
||||
if filename is not None and MainWindow.FileSwitchManager.currentDirectory is not None:
|
||||
potentialPath = os.path.join(MainWindow.FileSwitchManager.currentDirectory,filename)
|
||||
if os.path.isfile(potentialPath):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def isFilenameInCache(filename):
|
||||
if filename is not None and MainWindow.FileSwitchManager.mediaFilesCache is not None:
|
||||
for directory in MainWindow.FileSwitchManager.mediaFilesCache:
|
||||
files = MainWindow.FileSwitchManager.mediaFilesCache[directory]
|
||||
if filename in files:
|
||||
return True
|
||||
|
||||
class topSplitter(QtGui.QSplitter):
|
||||
def createHandle(self):
|
||||
return self.topSplitterHandle(self.orientation(), self)
|
||||
@ -206,10 +375,12 @@ class MainWindow(QtGui.QMainWindow):
|
||||
|
||||
def addClient(self, client):
|
||||
self._syncplayClient = client
|
||||
MainWindow.FileSwitchManager.setClient(client)
|
||||
self.roomInput.setText(self._syncplayClient.getRoom())
|
||||
self.config = self._syncplayClient.getConfig()
|
||||
try:
|
||||
self.updateReadyState(self.config['readyAtStart'])
|
||||
self.FileSwitchManager.setMediaDirectories(self.config["mediaSearchDirectories"])
|
||||
self.updateReadyState(self.config['readyAtStart'])
|
||||
autoplayInitialState = self.config['autoplayInitialState']
|
||||
if autoplayInitialState is not None:
|
||||
self.autoplayPushButton.blockSignals(True)
|
||||
@ -241,13 +412,31 @@ class MainWindow(QtGui.QMainWindow):
|
||||
else:
|
||||
self.newMessage(time.strftime(constants.UI_TIME_FORMAT, time.localtime()) + message + "<br />")
|
||||
|
||||
def getFileSwitchState(self, filename):
|
||||
if filename:
|
||||
if filename == getMessage("nofile-note"):
|
||||
return constants.FILEITEM_SWITCH_NO_SWITCH
|
||||
if self._syncplayClient.userlist.currentUser.file and filename == self._syncplayClient.userlist.currentUser.file['name']:
|
||||
return constants.FILEITEM_SWITCH_NO_SWITCH
|
||||
if isURL(filename):
|
||||
return constants.FILEITEM_SWITCH_STREAM_SWITCH
|
||||
elif filename not in self.newWatchlist:
|
||||
if MainWindow.FileSwitchManager.findFilepath(filename):
|
||||
return constants.FILEITEM_SWITCH_FILE_SWITCH
|
||||
else:
|
||||
self.newWatchlist.extend([filename])
|
||||
return constants.FILEITEM_SWITCH_NO_SWITCH
|
||||
|
||||
def showUserList(self, currentUser, rooms):
|
||||
self._usertreebuffer = QtGui.QStandardItemModel()
|
||||
self._usertreebuffer.setHorizontalHeaderLabels(
|
||||
(getMessage("roomuser-heading-label"), getMessage("size-heading-label"), getMessage("duration-heading-label"), getMessage("filename-heading-label") ))
|
||||
usertreeRoot = self._usertreebuffer.invisibleRootItem()
|
||||
if self._syncplayClient.userlist.currentUser.file and self._syncplayClient.userlist.currentUser.file and os.path.isfile(self._syncplayClient.userlist.currentUser.file["path"]):
|
||||
MainWindow.FileSwitchManager.setCurrentDirectory(os.path.dirname(self._syncplayClient.userlist.currentUser.file["path"]))
|
||||
|
||||
for room in rooms:
|
||||
self.newWatchlist = []
|
||||
roomitem = QtGui.QStandardItem(room)
|
||||
font = QtGui.QFont()
|
||||
font.setItalic(True)
|
||||
@ -279,7 +468,17 @@ class MainWindow(QtGui.QMainWindow):
|
||||
if user.file:
|
||||
filesizeitem = QtGui.QStandardItem(formatSize(user.file['size']))
|
||||
filedurationitem = QtGui.QStandardItem("({})".format(formatTime(user.file['duration'])))
|
||||
filenameitem = QtGui.QStandardItem((user.file['name']))
|
||||
filename = user.file['name']
|
||||
if isURL(filename):
|
||||
filename = urllib.unquote(filename)
|
||||
filenameitem = QtGui.QStandardItem(filename)
|
||||
fileSwitchState = self.getFileSwitchState(user.file['name']) if room == currentUser.room else None
|
||||
if fileSwitchState != constants.FILEITEM_SWITCH_NO_SWITCH:
|
||||
filenameTooltip = getMessage("switch-to-file-tooltip").format(filename)
|
||||
else:
|
||||
filenameTooltip = filename
|
||||
filenameitem.setToolTip(filenameTooltip)
|
||||
filenameitem.setData(fileSwitchState, Qt.UserRole + constants.FILEITEM_SWITCH_ROLE)
|
||||
if currentUser.file:
|
||||
sameName = sameFilename(user.file['name'], currentUser.file['name'])
|
||||
sameSize = sameFilesize(user.file['size'], currentUser.file['size'])
|
||||
@ -323,6 +522,13 @@ class MainWindow(QtGui.QMainWindow):
|
||||
self.listTreeView.setRootIsDecorated(False)
|
||||
self.listTreeView.expandAll()
|
||||
self.updateListGeometry()
|
||||
MainWindow.FileSwitchManager.setFilenameWatchlist(self.newWatchlist)
|
||||
self.checkForDisabledDir()
|
||||
|
||||
def checkForDisabledDir(self):
|
||||
if MainWindow.FileSwitchManager.disabledDir is not None and MainWindow.FileSwitchManager.currentWindow is not None:
|
||||
self.showErrorMessage(getMessage("folder-search-timeout-error").format(MainWindow.FileSwitchManager.disabledDir))
|
||||
MainWindow.FileSwitchManager.disabledDir = None
|
||||
|
||||
def updateListGeometry(self):
|
||||
try:
|
||||
@ -353,9 +559,25 @@ class MainWindow(QtGui.QMainWindow):
|
||||
self.updateReadyIcon()
|
||||
|
||||
def roomClicked(self, item):
|
||||
username = item.sibling(item.row(), 0).data()
|
||||
filename = item.sibling(item.row(), 3).data()
|
||||
while item.parent().row() != -1:
|
||||
item = item.parent()
|
||||
self.joinRoom(item.sibling(item.row(), 0).data())
|
||||
roomToJoin = item.sibling(item.row(), 0).data()
|
||||
if roomToJoin <> self._syncplayClient.getRoom():
|
||||
self.joinRoom(item.sibling(item.row(), 0).data())
|
||||
elif username and filename and username <> self._syncplayClient.userlist.currentUser.username:
|
||||
if self._syncplayClient.userlist.currentUser.file and filename == self._syncplayClient.userlist.currentUser.file:
|
||||
return
|
||||
if isURL(filename):
|
||||
self._syncplayClient._player.openFile(filename)
|
||||
else:
|
||||
pathFound = MainWindow.FileSwitchManager.findFilepath(filename)
|
||||
if pathFound:
|
||||
self._syncplayClient._player.openFile(pathFound)
|
||||
else:
|
||||
MainWindow.FileSwitchManager.updateInfo()
|
||||
self.showErrorMessage(getMessage("switch-file-not-found-error").format(filename))
|
||||
|
||||
@needsClient
|
||||
def userListChange(self):
|
||||
@ -460,7 +682,13 @@ class MainWindow(QtGui.QMainWindow):
|
||||
|
||||
self.loadMediaBrowseSettings()
|
||||
options = QtGui.QFileDialog.Options()
|
||||
if os.path.isdir(self.mediadirectory):
|
||||
self.mediadirectory = ""
|
||||
currentdirectory = os.path.dirname(self._syncplayClient.userlist.currentUser.file["path"]) if self._syncplayClient.userlist.currentUser.file else None
|
||||
if currentdirectory and os.path.isdir(currentdirectory):
|
||||
defaultdirectory = currentdirectory
|
||||
elif self.config["mediaSearchDirectories"] and os.path.isdir(self.config["mediaSearchDirectories"][0]):
|
||||
defaultdirectory = self.config["mediaSearchDirectories"][0]
|
||||
elif os.path.isdir(self.mediadirectory):
|
||||
defaultdirectory = self.mediadirectory
|
||||
elif os.path.isdir(QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.MoviesLocation)):
|
||||
defaultdirectory = QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.MoviesLocation)
|
||||
@ -475,6 +703,7 @@ class MainWindow(QtGui.QMainWindow):
|
||||
if sys.platform.startswith('win'):
|
||||
fileName = fileName.replace("/", "\\")
|
||||
self.mediadirectory = os.path.dirname(fileName)
|
||||
self.FileSwitchManager.setCurrentDirectory(self.mediadirectory)
|
||||
self.saveMediaBrowseSettings()
|
||||
self._syncplayClient._player.openFile(fileName)
|
||||
|
||||
@ -842,7 +1071,7 @@ class MainWindow(QtGui.QMainWindow):
|
||||
self._syncplayClient.changeReadyState(self.readyPushButton.isChecked())
|
||||
else:
|
||||
self.showDebugMessage("Tried to change ready state too soon.")
|
||||
|
||||
|
||||
@needsClient
|
||||
def changeAutoplayThreshold(self, source=None):
|
||||
self._syncplayClient.changeAutoPlayThrehsold(self.autoplayThresholdSpinbox.value())
|
||||
@ -898,7 +1127,8 @@ class MainWindow(QtGui.QMainWindow):
|
||||
@needsClient
|
||||
def checkForUpdates(self, userInitiated=False):
|
||||
self.lastCheckedForUpdates = datetime.utcnow()
|
||||
updateStatus, updateMessage, updateURL = self._syncplayClient.checkForUpdate(userInitiated)
|
||||
updateStatus, updateMessage, updateURL, self.publicServerList = self._syncplayClient.checkForUpdate(userInitiated)
|
||||
|
||||
if updateMessage is None:
|
||||
if updateStatus == "uptodate":
|
||||
updateMessage = getMessage("syncplay-uptodate-notification")
|
||||
@ -975,6 +1205,10 @@ class MainWindow(QtGui.QMainWindow):
|
||||
settings.beginGroup("Update")
|
||||
settings.setValue("lastChecked", self.lastCheckedForUpdates)
|
||||
settings.endGroup()
|
||||
settings.beginGroup("PublicServerList")
|
||||
if self.publicServerList:
|
||||
settings.setValue("publicServers", self.publicServerList)
|
||||
settings.endGroup()
|
||||
|
||||
def loadSettings(self):
|
||||
settings = QSettings("Syncplay", "MainWindow")
|
||||
@ -997,10 +1231,19 @@ class MainWindow(QtGui.QMainWindow):
|
||||
settings = QSettings("Syncplay", "Interface")
|
||||
settings.beginGroup("Update")
|
||||
self.lastCheckedForUpdates = settings.value("lastChecked", None)
|
||||
settings.endGroup()
|
||||
settings.beginGroup("PublicServerList")
|
||||
self.publicServerList = settings.value("publicServers", None)
|
||||
|
||||
def __init__(self):
|
||||
super(MainWindow, self).__init__()
|
||||
FileSwitchManager = self.FileSwitchManager()
|
||||
FileSwitchManager.setWindow(self)
|
||||
self.newWatchlist = []
|
||||
self.publicServerList = []
|
||||
self.lastCheckedForUpdates = None
|
||||
self._syncplayClient = None
|
||||
self.folderSearchEnabled = True
|
||||
self.QtGui = QtGui
|
||||
if sys.platform.startswith('win'):
|
||||
self.resourcespath = utils.findWorkingDir() + "\\resources\\"
|
||||
|
||||
@ -9,6 +9,10 @@ import itertools
|
||||
import hashlib
|
||||
import random
|
||||
import string
|
||||
import urllib
|
||||
import ast
|
||||
|
||||
folderSearchEnabled = True
|
||||
|
||||
def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
|
||||
"""Retry calling the decorated function using an exponential backoff.
|
||||
@ -158,8 +162,11 @@ def blackholeStdoutForFrozenWindow():
|
||||
|
||||
# Relate to file hashing / difference checking:
|
||||
|
||||
def stripfilename(filename):
|
||||
def stripfilename(filename, stripURL):
|
||||
if filename:
|
||||
filename = urllib.unquote(filename)
|
||||
if stripURL:
|
||||
filename = filename.split(u"/")[-1]
|
||||
return re.sub(constants.FILENAME_STRIP_REGEX, "", filename)
|
||||
else:
|
||||
return ""
|
||||
@ -173,8 +180,8 @@ def stripRoomName(RoomName):
|
||||
else:
|
||||
return ""
|
||||
|
||||
def hashFilename(filename):
|
||||
return hashlib.sha256(stripfilename(filename).encode('utf-8')).hexdigest()[:12]
|
||||
def hashFilename(filename, stripURL = False):
|
||||
return hashlib.sha256(stripfilename(filename, stripURL).encode('utf-8')).hexdigest()[:12]
|
||||
|
||||
def hashFilesize(size):
|
||||
return hashlib.sha256(str(size)).hexdigest()[:12]
|
||||
@ -190,9 +197,10 @@ def sameHashed(string1raw, string1hashed, string2raw, string2hashed):
|
||||
return True
|
||||
|
||||
def sameFilename (filename1, filename2):
|
||||
stripURL = True if isURL(filename1) ^ isURL(filename2) else False
|
||||
if filename1 == constants.PRIVACY_HIDDENFILENAME or filename2 == constants.PRIVACY_HIDDENFILENAME:
|
||||
return True
|
||||
elif sameHashed(stripfilename(filename1), hashFilename(filename1), stripfilename(filename2), hashFilename(filename2)):
|
||||
elif sameHashed(stripfilename(filename1, stripURL), hashFilename(filename1, stripURL), stripfilename(filename2, stripURL), hashFilename(filename2, stripURL)):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@ -218,6 +226,47 @@ def meetsMinVersion(version, minVersion):
|
||||
return tuple(map(int, ver.split(".")))
|
||||
return versiontotuple(version) >= versiontotuple(minVersion)
|
||||
|
||||
def isURL(path):
|
||||
if path is None:
|
||||
return False
|
||||
elif "://" in path:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def getPlayerArgumentsByPathAsArray(arguments, path):
|
||||
if arguments and not isinstance(arguments, (str, unicode)) and arguments.has_key(path):
|
||||
return arguments[path]
|
||||
else:
|
||||
return None
|
||||
|
||||
def getPlayerArgumentsByPathAsText(arguments, path):
|
||||
argsToReturn = getPlayerArgumentsByPathAsArray(arguments, path)
|
||||
return " ".join(argsToReturn) if argsToReturn else ""
|
||||
|
||||
def getListAsMultilineString(pathArray):
|
||||
return u"\n".join(pathArray) if pathArray else ""
|
||||
|
||||
def convertMultilineStringToList(multilineString):
|
||||
return unicode.split(multilineString,u"\n") if multilineString else ""
|
||||
|
||||
def getListOfPublicServers():
|
||||
try:
|
||||
import urllib, syncplay, sys, messages, json
|
||||
params = urllib.urlencode({'version': syncplay.version, 'milestone': syncplay.milestone, 'release_number': syncplay.release_number,
|
||||
'language': messages.messages["CURRENT"]})
|
||||
f = urllib.urlopen(constants.SYNCPLAY_PUBLIC_SERVER_LIST_URL.format(params))
|
||||
response = f.read()
|
||||
response = response.replace("<p>","").replace("</p>","").replace("<br />","").replace("“","'").replace("”","'").replace(":’","'").replace("’","'").replace("′","'").replace("\n","").replace("\r","") # Fix Wordpress
|
||||
response = ast.literal_eval(response)
|
||||
|
||||
if response:
|
||||
return response
|
||||
else:
|
||||
raise IOError
|
||||
except:
|
||||
raise IOError(getMessage("failed-to-load-server-list-error"))
|
||||
|
||||
class RoomPasswordProvider(object):
|
||||
CONTROLLED_ROOM_REGEX = re.compile("^\+(.*):(\w{12})$")
|
||||
PASSWORD_REGEX = re.compile("[A-Z]{2}-\d{3}-\d{3}")
|
||||
@ -277,4 +326,5 @@ class RandomStringGenerator(object):
|
||||
return ''.join(random.choice(string.digits) for _ in xrange(quantity))
|
||||
|
||||
class NotControlledRoom(Exception):
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user